Merge pull request #952 from hernot/master

Add mcsc for checking C# code.
This commit is contained in:
w0rp 2017-09-28 13:29:55 +01:00 committed by GitHub
commit 9fc01bd246
8 changed files with 426 additions and 2 deletions

View File

@ -76,7 +76,7 @@ formatting.
| C | [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint), [gcc](https://gcc.gnu.org/), [clang](http://clang.llvm.org/), [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [clang-format](https://clang.llvm.org/docs/ClangFormat.html)|
| C++ (filetype cpp) | [clang](http://clang.llvm.org/), [clangcheck](http://clang.llvm.org/docs/ClangCheck.html) !!, [clangtidy](http://clang.llvm.org/extra/clang-tidy/) !!, [cppcheck](http://cppcheck.sourceforge.net), [cpplint](https://github.com/google/styleguide/tree/gh-pages/cpplint) !!, [gcc](https://gcc.gnu.org/), [clang-format](https://clang.llvm.org/docs/ClangFormat.html)|
| CUDA | [nvcc](http://docs.nvidia.com/cuda/cuda-compiler-driver-nvcc/index.html) |
| C# | [mcs](http://www.mono-project.com/docs/about-mono/languages/csharp/) |
| C# | [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/) !! see:`help ale-cs-mcsc` for details and configuration|
| Chef | [foodcritic](http://www.foodcritic.io/) |
| CMake | [cmakelint](https://github.com/richq/cmake-lint) |
| CoffeeScript | [coffee](http://coffeescript.org/), [coffeelint](https://www.npmjs.com/package/coffeelint) |

87
ale_linters/cs/mcsc.vim Normal file
View File

@ -0,0 +1,87 @@
" general mcs options which are likely to stay constant across
" source trees like -pkg:dotnet
let g:ale_cs_mcsc_options = get(g:, 'ale_cs_mcsc_options', '')
" path string pointing the linter to the base path of the
" source tree to check
let g:ale_cs_mcsc_source = get(g:, 'ale_cs_mcsc_source','.')
" list of search paths for additional assemblies to consider
let g:ale_cs_mcsc_assembly_path = get(g:, 'ale_cs_mcsc_assembly_path',[])
" list of assemblies to consider
let g:ale_cs_mcsc_assemblies = get(g:, 'ale_cs_mcsc_assemblies',[])
function! ale_linters#cs#mcsc#GetCommand(buffer) abort
" if list of assembly search paths is not empty convert it to
" appropriate -lib: parameter of mcs
let l:path = ale#Var(a:buffer, 'cs_mcsc_assembly_path')
if !empty(l:path)
let l:path = '-lib:"' . join(l:path, '","') .'"'
else
let l:path =''
endif
" if list of assemblies to link is not empty convert it to the
" appropriate -r: parameter of mcs
let l:assemblies = ale#Var(a:buffer, 'cs_mcsc_assemblies')
if !empty(l:assemblies)
let l:assemblies = '-r:"' . join(l:assemblies, '","') . '"'
else
let l:assemblies =''
endif
" register temporary module target file with ale
let l:out = tempname()
call ale#engine#ManageFile(a:buffer, l:out)
" assemble linter command string to be executed by ale
" implicitly set -unsafe mcs flag set compilation
" target to module (-t:module), direct mcs output to
" temporary file (-out)
"
return 'cd "' . ale#Var(a:buffer, 'cs_mcsc_source') . '";'
\ . 'mcs -unsafe'
\ . ' ' . ale#Var(a:buffer, 'cs_mcsc_options')
\ . ' ' . l:path
\ . ' ' . l:assemblies
\ . ' -out:' . l:out
\ . ' -t:module'
\ . ' -recurse:"*.cs"'
endfunction
function! ale_linters#cs#mcsc#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:pattern = '^\(.\+\.cs\)(\(\d\+\),\(\d\+\)): \(.\+\): \(.\+\)'
let l:output = []
let l:source = ale#Var(a:buffer, 'cs_mcsc_source')
for l:match in ale#util#GetMatches(a:lines, l:pattern)
call add(l:output, {
\ 'filename': fnamemodify(l:source . '/' . l:match[1], ':p'),
\ 'lnum': l:match[2] + 0,
\ 'col': l:match[3] + 0,
\ 'text': l:match[4] . ': ' . l:match[5],
\ 'type': l:match[4] =~# '^error' ? 'E' : 'W',
\})
endfor
return l:output
endfunction
call ale#linter#Define('cs',{
\ 'name': 'mcsc',
\ 'output_stream': 'stderr',
\ 'executable': 'mcs',
\ 'command_callback': 'ale_linters#cs#mcsc#GetCommand',
\ 'callback': 'ale_linters#cs#mcsc#Handle',
\ 'lint_file': 1
\})

102
doc/ale-cs.txt Normal file
View File

@ -0,0 +1,102 @@
===============================================================================
ALE C# Integration *ale-cs-options*
===============================================================================
mcs *ale-cs-mcs*
The mcs linter checks the syntax of the '*.cs' file loaded in the current
buffer only. It uses the --parse option of the mcs compiler and implicitly
sets the -unsafe flag.
g:ale_cs_mcs_options *g:ale_cs_mcs_options*
*b:ale_cs_mcs_options*
Type: String
Default: `''`
This variable can be changed to pass additional flags given to mcs.
NOTE: The -unsafe flag is selected implicitly and thus does not need to be
explicitly included in the |g:ale_cs_mcs_options| or |b:ale_cs_mcs_options|
parameter.
===============================================================================
mcsc *ale-cs-mcsc*
The mcsc linter uses the mono mcs compiler 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_mcsc_source| or |b:ale_cs_mcsc_source| variable.
variable and all sub directories.
The paths to search for additional assembly ('*.dll') files can be
specified using the |g:ale_cs_mcsc_assembly_path| or
|b:ale_cs_mcsc_assembly_path| variable. The additional assembly files ('*.dll')
can be included through the |g:ale_cs_mcsc_assemblies| or
|b:ale_cs_mcsc_assemblies| parameter.
NOTE: mcs compiles sources in multiple phases. It stops compilation after
finding errors during the current phase.
For example assume a file named 'FileWithTypeError.cs' is edited and saved
which contains a Type error. In the same directory tree a file named
'FileWithSyntaxError.cs' exists which contains a syntax error
(eg.: a missing '{').
In that case mcs and thus mcsc linter will stop after the syntax check phase is
finished and report the syntax error in the file 'FileWithSyntaxError.cs'. The
Type error in the file 'FileWithTypeError.cs is not seen jet.
The only possibility to find the error in in 'FileWithTypeError.cs' is to fix
the syntax error in 'FileWithSyntaxError.cs' first. After saving mcs will
successfully pass the syntax check phase and advance to the next compilation
phase at which the Type error hidden in 'FileWithTypeError.cs' is found and
now can be indicated by ale.
g:ale_cs_mcsc_options *g:ale_cs_mcsc_options*
*b:ale_cs_mcsc_options*
Type: |String|
Default: `''`
This parameter can be used to define additional flags and parameters independent
of the source tree to be linted. The specified string is directly passed to
mcs compiler without any further change.
For example, to add the dotnet package which is not added per default
let g:ale_cs_mcs_options = '-pkg:dotnet'
NOTE: The mcs -unsafe option is included implicitly per default. Therefore it
is not necessary to specify it explicitly through the |g:ale_cs_mcsc_options|
or |b:ale_cs_mcsc_options| parameter.
g:ale_cs_mcsc_source *g:ale_cs_mcsc_source*
*b:ale_cs_mcsc_source*
Type: |String|
Default: `''`
This variable defines the root path of the directory tree searched for the
'*.cs' files to be linted. If empty the current working directory is 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_mcsc_assembly_path *g:ale_cs_mcsc_assembly_path*
*b:ale_cs_mcsc_assembly_path*
Type: |List|
Default: `[]`
This variable defines a list of path strings to be searched for external
assembly ('*.dll') files. The list is passed to the mcs compiler using the
'-lib:' flag.
g:ale_cs_mcsc_assemblies *g:ale_cs_mcsc_assemblies*
*b:ale_cs_mcsc_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 mcs compiler using the '-r:' flag.
===============================================================================
vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl:

View File

@ -36,6 +36,9 @@ CONTENTS *ale-contents*
cppcheck............................|ale-cpp-cppcheck|
cpplint.............................|ale-cpp-cpplint|
gcc.................................|ale-cpp-gcc|
c#....................................|ale-cs-options|
mcs.................................|ale-cs-mcs|
mcsc................................|ale-cs-mcsc|
css...................................|ale-css-options|
prettier............................|ale-css-prettier|
stylelint...........................|ale-css-stylelint|
@ -222,7 +225,7 @@ Notes:
* C: `cppcheck`, `cpplint`!!, `gcc`, `clang`, `clangtidy`!!, `clang-format`
* C++ (filetype cpp): `clang`, `clangcheck`!!, `clangtidy`!!, `cppcheck`, `cpplint`!!, `gcc`, `clang-format`
* CUDA: `nvcc`!!
* C#: `mcs`
* C#: `mcs`, `mcsc`!!
* Chef: `foodcritic`
* CMake: `cmakelint`
* CoffeeScript: `coffee`, `coffeelint`

View File

@ -0,0 +1,34 @@
Before:
Save g:ale_cs_mcs_options
unlet! g:ale_cs_mcs_options
runtime ale_linters/cs/mcs.vim
let b:command_tail = ' -unsafe --parse'
After:
Restore
unlet! b:command_tail
unlet! b:ale_cs_mcs_options
call ale#linter#Reset()
Execute(Check for proper default command):
let b:command = ale_linters#cs#mcs#GetCommand(bufnr(''))
let b:command = substitute(b:command,'\s\+',' ','g')
AssertEqual
\ b:command,
\ 'mcs -unsafe --parse %t'
Execute(The options should be be used in the command):
let b:ale_cs_mcs_options = '-pkg:dotnet'
let b:command = ale_linters#cs#mcs#GetCommand(bufnr(''))
let b:command = substitute(b:command,'\s\+',' ','g')
AssertEqual
\ b:command,
\ 'mcs' . b:command_tail . ' ' . b:ale_cs_mcs_options . ' %t',

View File

@ -0,0 +1,120 @@
Before:
Save g:ale_cs_mcsc_options
Save g:ale_cs_mcsc_source
Save g:ale_cs_mcsc_assembly_path
Save g:ale_cs_mcsc_assemblies
unlet! g:ale_cs_mcsc_options
unlet! g:ale_cs_mcsc_source
unlet! g:ale_cs_mcsc_assembly_path
unlet! g:ale_cs_mcsc_assemblies
let g:temppath = fnamemodify(tempname(), ':p:h')
let g:temppathpattern = substitute(escape(g:temppath, '\\/.*$^~[]'), '[\\/]*$', '[\\\\/]\\+\\S\\+','')
let g:sometempfile = fnamemodify(g:temppath .'/some_temp_file.tmp', ':p')
runtime ale_linters/cs/mcsc.vim
After:
Restore
unlet! b:ale_cs_mcsc_options
unlet! g:ale_cs_mcsc_source
unlet! g:ale_cs_mcsc_assembly_path
unlet! g:ale_cs_mcsc_assemblies
unlet! g:temppath
unlet! g:temppathpattern
unlet! g:sometempfile
call ale#linter#Reset()
Execute(Check for proper default command):
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command, '\s\+', ' ', 'g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
Execute(The options should be be used in the command):
let g:ale_cs_mcsc_options = '-pkg:dotnet'
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command, '\s\+', ' ', 'g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe ' . g:ale_cs_mcsc_options . ' -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
Execute(The souce path should be be used in the command):
call ale#engine#Cleanup(bufnr(''))
call ale#engine#InitBufferInfo(bufnr(''))
let g:ale_cs_mcsc_source='../foo/bar'
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command, '\s\+', ' ', 'g')
AssertEqual
\ b:command,
\ 'cd "' . g:ale_cs_mcsc_source . '";mcs -unsafe -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
Execute(The list of search pathes for assemblies should be be used in the command if not empty):
call ale#engine#Cleanup(bufnr(''))
call ale#engine#InitBufferInfo(bufnr(''))
let g:ale_cs_mcsc_assembly_path = [
\ '/usr/lib/mono',
\ '../foo/bar'
\]
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command, '\s\+', ' ', 'g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe -lib:"' . join(g:ale_cs_mcsc_assembly_path,'","') . '" -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
let g:ale_cs_mcsc_assembly_path = [
\]
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command, '\s\+', ' ', 'g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
Execute(The list of assemblies should be be used in the command if not empty):
call ale#engine#Cleanup(bufnr(''))
call ale#engine#InitBufferInfo(bufnr(''))
let g:ale_cs_mcsc_assemblies = [
\ 'foo.dll',
\ 'bar.dll'
\]
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command,'\s\+',' ','g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe -r:"' . join(g:ale_cs_mcsc_assemblies,'","') . '" -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'
let g:ale_cs_mcsc_assemblies = [
\]
let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))
let b:command = substitute(b:command, '-out:' . g:temppathpattern, '-out:' . g:sometempfile, '')
let b:command = substitute(b:command,'\s\+',' ','g')
AssertEqual
\ b:command,
\ 'cd ".";mcs -unsafe -out:' . g:sometempfile . ' -t:module -recurse:"*.cs"'

View File

@ -0,0 +1,34 @@
Before:
runtime ale_linters/cs/mcs.vim
After:
call ale#linter#Reset()
Execute(The mcs handler should handle cannot find symbol errors):
AssertEqual
\ [
\ {
\ 'lnum': 12,
\ 'col' : 29,
\ 'text': 'error CS1001: ; expected',
\ 'type': 'E',
\ },
\ {
\ 'lnum': 101,
\ 'col': 0,
\ 'text': 'error CS1028: Unexpected processor directive (no #if for this #endif)',
\ 'type': 'E',
\ },
\ {
\ 'lnum': 10,
\ 'col': 12,
\ 'text': 'warning CS0123: some warning',
\ 'type': 'W',
\ },
\ ],
\ ale_linters#cs#mcs#Handle(347, [
\ 'Tests.cs(12,29): error CS1001: ; expected',
\ 'Tests.cs(101,0): error CS1028: Unexpected processor directive (no #if for this #endif)',
\ 'Tests.cs(10,12): warning CS0123: some warning',
\ 'Compilation failed: 2 error(s), 1 warnings',
\ ])

View File

@ -0,0 +1,44 @@
Before:
Save g:ale_cs_mcsc_source
unlet! g:ale_cs_mcsc_source
runtime ale_linters/cs/mcsc.vim
After:
unlet! g:ale_cs_mcsc_source
call ale#linter#Reset()
Execute(The mcs handler should handle cannot find symbol errors):
let g:ale_cs_mcsc_source='/home/foo/project/bar'
AssertEqual
\ [
\ {
\ 'lnum': 12,
\ 'col' : 29,
\ 'text': 'error CS1001: ; expected',
\ 'type': 'E',
\ 'filename': '/home/foo/project/bar/Test.cs'
\ },
\ {
\ 'lnum': 101,
\ 'col': 0,
\ 'text': 'error CS1028: Unexpected processor directive (no #if for this #endif)',
\ 'type': 'E',
\ 'filename': '/home/foo/project/bar/Test.cs'
\ },
\ {
\ 'lnum': 10,
\ 'col': 12,
\ 'text': 'warning CS0123: some warning',
\ 'type': 'W',
\ 'filename': '/home/foo/project/bar/Test.cs'
\ },
\ ],
\ ale_linters#cs#mcsc#Handle(347, [
\ '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',
\ ])