Support csc, update mcsc (#2586)
* Added a new csc linter for C# code. * More output is now handled for mcsc.
This commit is contained in:
parent
8700586890
commit
46ab7c5904
9 changed files with 374 additions and 12 deletions
95
ale_linters/cs/csc.vim
Normal file
95
ale_linters/cs/csc.vim
Normal file
|
@ -0,0 +1,95 @@
|
|||
call ale#Set('cs_csc_options', '')
|
||||
call ale#Set('cs_csc_source', '')
|
||||
call ale#Set('cs_csc_assembly_path', [])
|
||||
call ale#Set('cs_csc_assemblies', [])
|
||||
|
||||
function! s:GetWorkingDirectory(buffer) abort
|
||||
let l:working_directory = ale#Var(a:buffer, 'cs_csc_source')
|
||||
|
||||
if !empty(l:working_directory)
|
||||
return l:working_directory
|
||||
endif
|
||||
|
||||
return expand('#' . a:buffer . ':p:h')
|
||||
endfunction
|
||||
|
||||
function! ale_linters#cs#csc#GetCommand(buffer) abort
|
||||
" Pass assembly paths via the -lib: parameter.
|
||||
let l:path_list = ale#Var(a:buffer, 'cs_csc_assembly_path')
|
||||
|
||||
let l:lib_option = !empty(l:path_list)
|
||||
\ ? '/lib:' . join(map(copy(l:path_list), 'ale#Escape(v:val)'), ',')
|
||||
\ : ''
|
||||
|
||||
" Pass paths to DLL files via the -r: parameter.
|
||||
let l:assembly_list = ale#Var(a:buffer, 'cs_csc_assemblies')
|
||||
|
||||
let l:r_option = !empty(l:assembly_list)
|
||||
\ ? '/r:' . join(map(copy(l:assembly_list), 'ale#Escape(v:val)'), ',')
|
||||
\ : ''
|
||||
|
||||
" register temporary module target file with ale
|
||||
" register temporary module target file with ALE.
|
||||
let l:out = ale#command#CreateFile(a:buffer)
|
||||
|
||||
" The code is compiled as a module and the output is redirected to a
|
||||
" temporary file.
|
||||
return ale#path#CdString(s:GetWorkingDirectory(a:buffer))
|
||||
\ . 'csc /unsafe'
|
||||
\ . ale#Pad(ale#Var(a:buffer, 'cs_csc_options'))
|
||||
\ . ale#Pad(l:lib_option)
|
||||
\ . ale#Pad(l:r_option)
|
||||
\ . ' /out:' . l:out
|
||||
\ . ' /t:module'
|
||||
\ . ' /recurse:' . ale#Escape('*.cs')
|
||||
endfunction
|
||||
|
||||
function! ale_linters#cs#csc#Handle(buffer, lines) abort
|
||||
" Look for lines like the following.
|
||||
"
|
||||
" Tests.cs(12,29): error CSXXXX: ; expected
|
||||
"
|
||||
" NOTE: pattern also captures file name as linter compiles all
|
||||
" files within the source tree rooted at the specified source
|
||||
" path and not just the file loaded in the buffer
|
||||
let l:patterns = [
|
||||
\ '^\v(.+\.cs)\((\d+),(\d+)\)\:\s+([^ ]+)\s+([cC][sS][^ ]+):\s(.+)$',
|
||||
\ '^\v([^ ]+)\s+([Cc][sS][^ ]+):\s+(.+)$',
|
||||
\]
|
||||
let l:output = []
|
||||
|
||||
let l:dir = s:GetWorkingDirectory(a:buffer)
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:patterns)
|
||||
if len(l:match) > 6 && strlen(l:match[5]) > 2 && l:match[5][:1] is? 'CS'
|
||||
call add(l:output, {
|
||||
\ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:match[3] + 0,
|
||||
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
|
||||
\ 'code': l:match[5],
|
||||
\ 'text': l:match[6] ,
|
||||
\})
|
||||
elseif strlen(l:match[2]) > 2 && l:match[2][:1] is? 'CS'
|
||||
call add(l:output, {
|
||||
\ 'filename':'<csc>',
|
||||
\ 'lnum': -1,
|
||||
\ 'col': -1,
|
||||
\ 'type': l:match[1] is# 'error' ? 'E' : 'W',
|
||||
\ 'code': l:match[2],
|
||||
\ 'text': l:match[3],
|
||||
\})
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('cs',{
|
||||
\ 'name': 'csc',
|
||||
\ 'output_stream': 'stdout',
|
||||
\ 'executable': 'csc',
|
||||
\ 'command': function('ale_linters#cs#csc#GetCommand'),
|
||||
\ 'callback': 'ale_linters#cs#csc#Handle',
|
||||
\ 'lint_file': 1
|
||||
\})
|
|
@ -52,20 +52,34 @@ function! ale_linters#cs#mcsc#Handle(buffer, lines) abort
|
|||
" NOTE: pattern also captures file name as linter compiles all
|
||||
" files within the source tree rooted at the specified source
|
||||
" path and not just the file loaded in the buffer
|
||||
let l:pattern = '^\v(.+\.cs)\((\d+),(\d+)\)\: ([^ ]+) ([^ ]+): (.+)$'
|
||||
let l:patterns = [
|
||||
\ '^\v(.+\.cs)\((\d+),(\d+)\)\:\s+([^ ]+)\s+([cC][sS][^ ]+):\s(.+)$',
|
||||
\ '^\v([^ ]+)\s+([Cc][sS][^ ]+):\s+(.+)$',
|
||||
\]
|
||||
let l:output = []
|
||||
|
||||
let l:dir = s:GetWorkingDirectory(a:buffer)
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
call add(l:output, {
|
||||
\ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:match[3] + 0,
|
||||
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
|
||||
\ 'code': l:match[5],
|
||||
\ 'text': l:match[6],
|
||||
\})
|
||||
for l:match in ale#util#GetMatches(a:lines, l:patterns)
|
||||
if len(l:match) > 6 && strlen(l:match[5]) > 2 && l:match[5][:1] is? 'CS'
|
||||
call add(l:output, {
|
||||
\ 'filename': ale#path#GetAbsPath(l:dir, l:match[1]),
|
||||
\ 'lnum': l:match[2] + 0,
|
||||
\ 'col': l:match[3] + 0,
|
||||
\ 'type': l:match[4] is# 'error' ? 'E' : 'W',
|
||||
\ 'code': l:match[5],
|
||||
\ 'text': l:match[6] ,
|
||||
\})
|
||||
elseif strlen(l:match[2]) > 2 && l:match[2][:1] is? 'CS'
|
||||
call add(l:output, {
|
||||
\ 'filename':'<mcs>',
|
||||
\ 'lnum': -1,
|
||||
\ 'col': -1,
|
||||
\ 'type': l:match[1] is# 'error' ? 'E' : 'W',
|
||||
\ 'code': l:match[2],
|
||||
\ 'text': l:match[3],
|
||||
\})
|
||||
endif
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
|
|
|
@ -6,11 +6,97 @@ In addition to the linters that are provided with ALE, C# code can be checked
|
|||
with the OmniSharp plugin. See here: https://github.com/OmniSharp/omnisharp-vim
|
||||
|
||||
|
||||
===============================================================================
|
||||
csc *ale-cs-csc*
|
||||
|
||||
The |ale-cs-csc| linter checks for semantic errors when files are opened or
|
||||
saved.
|
||||
|
||||
See |ale-lint-file-linters| for more information on linters which do not
|
||||
check for problems while you type.
|
||||
|
||||
The csc linter uses the mono csc compiler providing full c# 7 and newer
|
||||
support to generate a temporary module target file (/t:module). The module
|
||||
includes including all '*.cs' files contained in the directory tree rooted
|
||||
at the path defined by the |g:ale_cs_csc_source| or |b:ale_cs_csc_source|
|
||||
variabl and all sub directories.
|
||||
|
||||
It will in future replace the |ale-cs-mcs| and |ale-cs-mcsc| linters as both
|
||||
utilizer the mcsc compiler which according to mono porject ist further
|
||||
developed and as of writint these lines only receives maintenance updates.
|
||||
The down is that the csc compiler does not support the -sytax option any more
|
||||
and therefore |ale-cs-csc| linter doese not offer any as you type syntax
|
||||
checking like the |ale-cs-mcsc| linter doesn't.
|
||||
|
||||
The paths to search for additional assembly files can be specified using the
|
||||
|g:ale_cs_csc_assembly_path| or |b:ale_cs_csc_assembly_path| variables.
|
||||
|
||||
NOTE: ALE will not find any errors in files apart from syntax errors if any
|
||||
one of the source files contains a syntax error. Syntax errors must be fixed
|
||||
first before other errors will be shown.
|
||||
|
||||
|
||||
g:ale_cs_csc_options *g:ale_cs_csc_options*
|
||||
*b:ale_cs_csc_options*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This option can be set to pass additional arguments to the `csc` compiler.
|
||||
|
||||
For example, to add the dotnet package which is not added per default: >
|
||||
|
||||
let g:ale_cs_mcs_options = ' /warn:4 /langversion:7.2'
|
||||
<
|
||||
NOTE: the `/unsafe` option is always passed to `csc`.
|
||||
|
||||
|
||||
g:ale_cs_csc_source *g:ale_cs_csc_source*
|
||||
*b:ale_cs_csc_source*
|
||||
Type: |String|
|
||||
Default: `''`
|
||||
|
||||
This variable defines the root path of the directory tree searched for the
|
||||
'*.cs' files to be linted. If this option is empty, the source file's
|
||||
directory will be used.
|
||||
|
||||
NOTE: Currently it is not possible to specify sub directories and
|
||||
directory sub trees which shall not be searched for *.cs files.
|
||||
|
||||
|
||||
g:ale_cs_csc_assembly_path *g:ale_cs_csc_assembly_path*
|
||||
*b:ale_cs_csc_assembly_path*
|
||||
Type: |List|
|
||||
Default: `[]`
|
||||
|
||||
This variable defines a list of path strings to be searched for external
|
||||
assembly files. The list is passed to the csc compiler using the `/lib:`
|
||||
flag.
|
||||
|
||||
|
||||
g:ale_cs_csc_assemblies *g:ale_cs_csc_assemblies*
|
||||
*b:ale_cs_csc_assemblies*
|
||||
Type: |List|
|
||||
Default: `[]`
|
||||
|
||||
This variable defines a list of external assembly (*.dll) files required
|
||||
by the mono mcs compiler to generate a valid module target. The list is
|
||||
passed the csc compiler using the `/r:` flag.
|
||||
|
||||
For example: >
|
||||
|
||||
" Compile C# programs with the Unity engine DLL file on Mac.
|
||||
let g:ale_cs_mcsc_assemblies = [
|
||||
\ '/Applications/Unity/Unity.app/Contents/Frameworks/Managed/UnityEngine.dll',
|
||||
\ 'path-to-unityproject/obj/Debug',
|
||||
\]
|
||||
<
|
||||
|
||||
===============================================================================
|
||||
mcs *ale-cs-mcs*
|
||||
|
||||
The `mcs` linter looks only for syntax errors while you type. See |ale-cs-mcsc|
|
||||
for the separately configured linter for checking for semantic errors.
|
||||
The `mcs` linter looks only for syntax errors while you type. See
|
||||
|ale-cs-mcsc| for the separately configured linter for checking for semantic
|
||||
errors.
|
||||
|
||||
|
||||
g:ale_cs_mcs_options *g:ale_cs_mcs_options*
|
||||
|
|
|
@ -53,6 +53,7 @@ Notes:
|
|||
* `gcc`
|
||||
* `uncrustify`
|
||||
* C#
|
||||
* `csc`!!
|
||||
* `mcs`
|
||||
* `mcsc`!!
|
||||
* `uncrustify`
|
||||
|
|
|
@ -1975,6 +1975,7 @@ documented in additional help files.
|
|||
uncrustify............................|ale-cpp-uncrustify|
|
||||
ccls..................................|ale-cpp-ccls|
|
||||
c#......................................|ale-cs-options|
|
||||
csc...................................|ale-cs-csc|
|
||||
mcs...................................|ale-cs-mcs|
|
||||
mcsc..................................|ale-cs-mcsc|
|
||||
uncrustify............................|ale-cs-uncrustify|
|
||||
|
|
|
@ -62,6 +62,7 @@ formatting.
|
|||
* [gcc](https://gcc.gnu.org/)
|
||||
* [uncrustify](https://github.com/uncrustify/uncrustify)
|
||||
* C#
|
||||
* [csc](http://www.mono-project.com/docs/about-mono/languages/csharp/) :floppy_disk: see:`help ale-cs-csc` for details and configuration
|
||||
* [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) see:`help ale-cs-mcs` for details
|
||||
* [mcsc](http://www.mono-project.com/docs/about-mono/languages/csharp/) :floppy_disk: see:`help ale-cs-mcsc` for details and configuration
|
||||
* [uncrustify](https://github.com/uncrustify/uncrustify)
|
||||
|
|
47
test/command_callback/test_cs_csc_command_callbacks.vader
Normal file
47
test/command_callback/test_cs_csc_command_callbacks.vader
Normal file
|
@ -0,0 +1,47 @@
|
|||
Before:
|
||||
call ale#assert#SetUpLinterTest('cs', 'csc')
|
||||
|
||||
After:
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(The csc linter should return the correct default command):
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
Execute(The options should be be used in the command):
|
||||
let g:ale_cs_csc_options = ''
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
Execute(The souce path should be be used in the command):
|
||||
let g:ale_cs_csc_source = '../foo/bar'
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString('../foo/bar')
|
||||
\ . 'csc /unsafe /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
Execute(The list of search pathes for assemblies should be be used in the command if not empty):
|
||||
let g:ale_cs_csc_assembly_path = ['/usr/lib/mono', '../foo/bar']
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe'
|
||||
\ . ' /lib:' . ale#Escape('/usr/lib/mono') . ',' . ale#Escape('../foo/bar')
|
||||
\ . ' /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
let g:ale_cs_csc_assembly_path = []
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
Execute(The list of assemblies should be be used in the command if not empty):
|
||||
let g:ale_cs_csc_assemblies = ['foo.dll', 'bar.dll']
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe'
|
||||
\ . ' /r:' . ale#Escape('foo.dll') . ',' . ale#Escape('bar.dll')
|
||||
\ . ' /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
||||
|
||||
let g:ale_cs_csc_assemblies = []
|
||||
|
||||
AssertLinter 'csc', ale#path#CdString(g:dir)
|
||||
\ . 'csc /unsafe /out:TEMP /t:module /recurse:' . ale#Escape('*.cs')
|
98
test/handler/test_csc_handler.vader
Normal file
98
test/handler/test_csc_handler.vader
Normal file
|
@ -0,0 +1,98 @@
|
|||
Before:
|
||||
Save g:ale_cs_csc_source
|
||||
|
||||
unlet! g:ale_cs_csc_source
|
||||
|
||||
call ale#test#SetDirectory('/testplugin/test/handler')
|
||||
call ale#test#SetFilename('Test.cs')
|
||||
|
||||
runtime ale_linters/cs/csc.vim
|
||||
|
||||
After:
|
||||
unlet! g:ale_cs_csc_source
|
||||
|
||||
call ale#test#RestoreDirectory()
|
||||
call ale#linter#Reset()
|
||||
|
||||
Execute(The csc handler should work with the default of the buffer's directory):
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': 12,
|
||||
\ 'col' : 29,
|
||||
\ 'text': '; expected',
|
||||
\ 'code': 'CS1001',
|
||||
\ 'type': 'E',
|
||||
\ 'filename': ale#path#Simplify(g:dir . '/Test.cs'),
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#cs#csc#Handle(bufnr(''), [
|
||||
\ 'Test.cs(12,29): error CS1001: ; expected',
|
||||
\ 'Compilation failed: 2 error(s), 1 warnings',
|
||||
\ ])
|
||||
|
||||
Execute(The csc handler should handle cannot find symbol errors):
|
||||
let g:ale_cs_csc_source = '/home/foo/project/bar'
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': 12,
|
||||
\ 'col' : 29,
|
||||
\ 'text': '; expected',
|
||||
\ 'code': 'CS1001',
|
||||
\ 'type': 'E',
|
||||
\ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'),
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': 101,
|
||||
\ 'col': 0,
|
||||
\ 'text': 'Unexpected processor directive (no #if for this #endif)',
|
||||
\ 'code': 'CS1028',
|
||||
\ 'type': 'E',
|
||||
\ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'),
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': 10,
|
||||
\ 'col': 12,
|
||||
\ 'text': 'some warning',
|
||||
\ 'code': 'CS0123',
|
||||
\ 'type': 'W',
|
||||
\ 'filename': ale#path#Simplify('/home/foo/project/bar/Test.cs'),
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#cs#csc#Handle(bufnr(''), [
|
||||
\ 'Test.cs(12,29): error CS1001: ; expected',
|
||||
\ 'Test.cs(101,0): error CS1028: Unexpected processor directive (no #if for this #endif)',
|
||||
\ 'Test.cs(10,12): warning CS0123: some warning',
|
||||
\ 'Compilation failed: 2 error(s), 1 warnings',
|
||||
\ ])
|
||||
|
||||
Execute(The csc handler should handle non file specific compiler errors without reporting overal status report as error):
|
||||
let g:ale_cs_csc_source = '/home/foo/project/bar'
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': -1,
|
||||
\ 'col' : -1,
|
||||
\ 'text': 'No source files specified.',
|
||||
\ 'code': 'CS2008',
|
||||
\ 'type': 'W',
|
||||
\ 'filename': '<csc>',
|
||||
\ },
|
||||
\ {
|
||||
\ 'lnum': -1,
|
||||
\ 'col': -1,
|
||||
\ 'text': 'Outputs without source must have the /out option specified',
|
||||
\ 'code': 'CS1562',
|
||||
\ 'type': 'E',
|
||||
\ 'filename': '<csc>',
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#cs#csc#Handle(bufnr(''), [
|
||||
\ 'Microsoft (R) Visual C# Compiler version 2.8.2.62916 (2ad4aabc)',
|
||||
\ 'Copyright (C) Microsoft Corporation. All rights reserved.',
|
||||
\ 'warning CS2008: No source files specified.',
|
||||
\ 'error CS1562: Outputs without source must have the /out option specified',
|
||||
\ ])
|
|
@ -67,3 +67,22 @@ Execute(The mcs handler should handle cannot find symbol errors):
|
|||
\ 'Test.cs(10,12): warning CS0123: some warning',
|
||||
\ 'Compilation failed: 2 error(s), 1 warnings',
|
||||
\ ])
|
||||
|
||||
Execute(The mcsc handler should handle non file specific compiler errors without reporting overal status report as error):
|
||||
let g:ale_cs_mcsc_source = '/home/foo/project/bar'
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'lnum': -1,
|
||||
\ 'col' : -1,
|
||||
\ 'text': 'No files to compile were specified',
|
||||
\ 'code': 'CS2008',
|
||||
\ 'type': 'E',
|
||||
\ 'filename': '<mcs>',
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#cs#mcsc#Handle(bufnr(''), [
|
||||
\ 'error CS2008: No files to compile were specified',
|
||||
\ 'Compilation failed: 1 error(s), 0 warnings',
|
||||
\ ])
|
||||
|
|
Reference in a new issue