summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorhernot <christoph@out-world.com>2019-07-02 09:18:17 +0200
committerw0rp <w0rp@users.noreply.github.com>2019-07-02 08:18:17 +0100
commit46ab7c590404d60567fe57390607c3dc51933c0e (patch)
tree3261827c44d9a7d8fbe8b2154400b2959c0ff5fa
parent870058689063b4ba69851cb3f7e71726d2263cf0 (diff)
downloadale-46ab7c590404d60567fe57390607c3dc51933c0e.zip
Support csc, update mcsc (#2586)
* Added a new csc linter for C# code. * More output is now handled for mcsc.
-rw-r--r--ale_linters/cs/csc.vim95
-rw-r--r--ale_linters/cs/mcsc.vim34
-rw-r--r--doc/ale-cs.txt90
-rw-r--r--doc/ale-supported-languages-and-tools.txt1
-rw-r--r--doc/ale.txt1
-rw-r--r--supported-tools.md1
-rw-r--r--test/command_callback/test_cs_csc_command_callbacks.vader47
-rw-r--r--test/handler/test_csc_handler.vader98
-rw-r--r--test/handler/test_mcsc_handler.vader19
9 files changed, 374 insertions, 12 deletions
diff --git a/ale_linters/cs/csc.vim b/ale_linters/cs/csc.vim
new file mode 100644
index 00000000..308abc77
--- /dev/null
+++ b/ale_linters/cs/csc.vim
@@ -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
+\})
diff --git a/ale_linters/cs/mcsc.vim b/ale_linters/cs/mcsc.vim
index dd067eba..0e4e5667 100644
--- a/ale_linters/cs/mcsc.vim
+++ b/ale_linters/cs/mcsc.vim
@@ -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
diff --git a/doc/ale-cs.txt b/doc/ale-cs.txt
index 01e6348f..abcc43eb 100644
--- a/doc/ale-cs.txt
+++ b/doc/ale-cs.txt
@@ -7,10 +7,96 @@ 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*
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
index 87e1ecde..ec04d175 100644
--- a/doc/ale-supported-languages-and-tools.txt
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -53,6 +53,7 @@ Notes:
* `gcc`
* `uncrustify`
* C#
+ * `csc`!!
* `mcs`
* `mcsc`!!
* `uncrustify`
diff --git a/doc/ale.txt b/doc/ale.txt
index eea3d102..c1dab120 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -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|
diff --git a/supported-tools.md b/supported-tools.md
index f96f49a3..802054cd 100644
--- a/supported-tools.md
+++ b/supported-tools.md
@@ -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)
diff --git a/test/command_callback/test_cs_csc_command_callbacks.vader b/test/command_callback/test_cs_csc_command_callbacks.vader
new file mode 100644
index 00000000..c21ce209
--- /dev/null
+++ b/test/command_callback/test_cs_csc_command_callbacks.vader
@@ -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')
diff --git a/test/handler/test_csc_handler.vader b/test/handler/test_csc_handler.vader
new file mode 100644
index 00000000..3db5b6fd
--- /dev/null
+++ b/test/handler/test_csc_handler.vader
@@ -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',
+ \ ])
diff --git a/test/handler/test_mcsc_handler.vader b/test/handler/test_mcsc_handler.vader
index 8ae47357..c04f7d27 100644
--- a/test/handler/test_mcsc_handler.vader
+++ b/test/handler/test_mcsc_handler.vader
@@ -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',
+ \ ])