Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Advance C# linter based on mcs -t:module #952

Merged
merged 3 commits into from Sep 28, 2017
Merged

Conversation

hernot
Copy link
Contributor

@hernot hernot commented Sep 25, 2017

Added addtional c# linter called mcsc using the module target to not only catch syntax errors but also indicate compilation errors and possibly missing assemblies etc. the lint_file flag is set to 1 to avoid delays on large directory trees.

Still missing is a possibility to define subdirectories which should be excluded from linting.

And suggestions for improvement wellcome.

Have added a ale-cs.txt file in doc describing the existing mcs and mcsc linter. as this is not included in the files on github.

The existing c-charp linter used the --syntax check mode of the mono mcs
compiler only. The new mcsc linter tries to compile the files located in
a directory tree located bejond the specified source directory or the
current one if no source is explicitly specified. The resulting module
target is placed in a temporary file managed by ale.
The existing c-charp linter used the --syntax check mode of the mono mcs
compiler only. The new mcsc linter tries to compile the files located in
a directory tree located bejond the specified source directory or the
current one if no source is explicitly specified. The resulting module
target is placed in a temporary file managed by ale.
Copy link
Member

@w0rp w0rp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Look through the codebase and try and write some smaller and easier to read code.

You will need to write Vader tests to cover your new command callback and your error handler.

\ . ' "' . join(glob('**/*.cs',v:false,v:true),'" "') . '"'
exe 'cd ' . l:cwd
return l:cmd
endfunction
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is some very poorly formatted code. Lose all of the type checking, and use blank lines and comments here or there.

\ . ' ' . l:assemblies
\ . ' -out:' . l:out
\ . ' -t:module'
\ . ' "' . join(glob('**/*.cs',v:false,v:true),'" "') . '"'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't use glob like this. It's painfully slow on many machines.

doc/ale-cs.txt Outdated
the assembly files specified by |g:ale_cs_mcsc_assemblies| or
|b:ale_cs_mcsc_assemblies|. The mcs -unsafe option is set implicitly and has
not to be added using |g:ale_cs_mcsc_options| or |b:ale_cs_mcsc_options|
variable.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The formatting of the text is very strange here. It looks like things are randomly indented. I find it hard to read.

doc/ale-cs.txt Outdated
line using the -lib: flag of mcs.

g:ale_cs_mcsc_assemblies *g:ale_cs_mcsc_assemblies*
*b:ale_cs_mcsc_assemblies*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to align your help tags to the right margin.

doc/ale-cs.txt Outdated
not empty the list of assemblies will be added to the mcsc command
line using the -r: flag of mcs. To change the search path mcs uses to
locate the specified assembly files use |g:ale_cs_mcsc_assembly_path| or
|b:ale_cs_mcsc_assembly_path| variables
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try and explain things more succinctly, and use short sentences.

\ 'executable': 'mcs',
\ 'command_callback': 'ale_linters#cs#mcsc#GetCommand',
\ 'callback': 'ale_linters#cs#mcsc#Handle',
\ 'lint_file': 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bad indentation.

README.md Outdated
@@ -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|
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You need to mark this with !!, as you've set lint_file to 1.

You need to document this in doc/ale.txt too.

hernot added a commit to hernot/ale that referenced this pull request Sep 26, 2017
Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Addec comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

Ok i have addressed all your recommendations. The only one i at max be able to address partially, if i get some instruction and support form your side is the writing of vader tests. I do not know vader and i do have no docker setup here. So even if able to write a test, i fear I would not be able to verify that it is really testing what it should test. Any advice? Any suggestions?

Ah and the strange formatting of source and doc was due to mix of and white space, fixed.

hernot added a commit to hernot/ale that referenced this pull request Sep 26, 2017
Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Added comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
- addresses findings by vader
Copy link
Member

@w0rp w0rp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is looking a lot better. 👍

You should write Vader tests for your new functions. Look at the tests in the test/handler and test/command_callback directories.


" switch back to the working directory to the path
" stored above.
exe 'cd ' . l:cwd
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You shouldn't execute a command to change the working directory. Just include cd ... && in the command you want ALE to run.

" it seems not to work reliably, which is why vim built in
" glob function is used to collect the list of '*.cs'
" files.
let l:cmd = 'cd ' . l:base . ';'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can just use return here instead, with the advice above.

\ . 'mcs -unsafe'
\ . ' ' . ale#Var(a:buffer, 'cs_mcsc_options')
\ . ' ' . l:path
\ . ' ' . l:assemblies
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try and include these additional arguments only if they aren't empty. Have a look at other linters for examples.

\ . ' ' . l:assemblies
\ . ' -out:' . l:out
\ . ' -t:module'
\ . ' "' . join(glob('**/*.cs',v:false,v:true),'" "') . '"'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

glob will perform very badly on many machines. Can you pass just the one file instead, or something like that?

let l:cwd = expand('#' . a:buffer . ':p:h')
" get the base path of the source tree to lint and if
" not empty and exists switch working directory to it
let l:base = ale#Var(a:buffer,'cs_mcsc_source')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a space after commas, here and above.

" get the base path of the source tree to lint and if
" not empty and exists switch working directory to it
let l:base = ale#Var(a:buffer,'cs_mcsc_source')
if isdirectory(l:base)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a blank line above if statements which do not start a new block.

" 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

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete the blank line here.

@@ -0,0 +1,107 @@
" 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', '')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might like to use ale#Set for these variables. Look at the codebase for other examples.

@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

Hi
I would appreciate if that workaround would not be necessary but with my installed version of mono mcs compiler the mcs compiler hang nine times out of 10 when trying to use the built in -recurse flag. If it would have worked 10 out of 10 times i would have used that instead of the glob function of vim without resorting to the cd solution if you have better idea for how to properly implement the workaround please let me know.

I decided against including the ale_cs_mcsc_source path inside the glob pattern to keep command string as short as possible even if several hundred(* files have to be included in the build.

(*)addmitted a rather poorly organized source tree and and likely indicating of design flaws, still if not all files are included in the linting command (see above) users would receive errors about missing assemblies for classes, types and namespaces defined by source files from the same source tree.

Currently i do have not the time and resources to investigating the reason for mcs hanging when using -recurse flag, in worst submitting a bugreport or even a bugfix to mono project and than adding version checks disabling linter if version with broken -recurse is installed.

@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

What the hack, now retrying the -recurse flag every thing works as expected. No idea why, what and where the problem was. Need to keep an eye on this. Never the less for the next commit i simplified to using -recurse flag of mcs compiler. Added a NOTE to the help that the case that mcsc does not indicate errors in the currently editor is related to mcs is a multipass comiler which stops after found errors at a specific comilation stage, not advancing to the next.

Will have a look into the tests, hope i do not make things even worse than now

@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

a question does a test_mcs_handerl.vader not exist or was it not commited as the mcs linter was already present in the github master branch. If not present at all should i also add a vader test for the mcs like i did for the help files or just for the mcsc i have added?

hernot added a commit to hernot/ale that referenced this pull request Sep 26, 2017
Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Added comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
- addresses findings by vader
- implements tests for mcs.vim and mcsc.vim linter
@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

Ok added the tests for mcs.vim which was missing them in the master tree and for mcsc.vim
The continuous-integration/travis-ci/pr test seems from what i can see fail cause i have inserted mcsc in ale-vim.txt file. not sure. please check your self and let me know if that would be the only reason.

hernot added a commit to hernot/ale that referenced this pull request Sep 26, 2017
Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Added comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
- addresses findings by vader
- removed call to getcwd and cd in vim script
- handler expands file names relative to route of source tree into
  absolute pathes. Fixes errors not being marked when vim is started
  from subdirectory of source tree.
- implements tests for mcs.vim and mcsc.vim linter
hernot added a commit to hernot/ale that referenced this pull request Sep 26, 2017
Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Added comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
- addresses findings by vader
- removed call to getcwd and cd in vim script
- handler expands file names relative to route of source tree into
  absolute pathes. Fixes errors not being marked when vim is started
  from subdirectory of source tree.
- implements tests for mcs.vim and mcsc.vim linter
@hernot
Copy link
Contributor Author

hernot commented Sep 26, 2017

Finally, fixe some nasty behavior, when vim is not run from base directory of source tree.

Copy link
Member

@w0rp w0rp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better without the glob() call now, which could execute pretty slowly.

See my comments on the tests. Assertions for exact string matches will be better.


let b:command = ale_linters#cs#mcsc#GetCommand(bufnr(''))

Assert match( b:command, '^\s*cd\s\+"."\s*;\s*mcs\s\+-unsafe\s\+-out:\S\+\s\+-t:module\s\+-recurse:"\*\.cs"\s*$' ) == 0,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The regular expressions here are very hard to read, so it's hard to make sense of what the tests are testing. I recommend writing tests for exact strings with AssertEqual instead. You will probably need to build certain parts of the strings base on the filename, etc.

Copy link
Contributor Author

@hernot hernot Sep 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is in this case not possible cause the GetCommand registers a temporary file with ale, which differs at every call so the assert is the only possibility to make test succeed, unless in your test machinery you have an AssertFunction or some other function which helps to handle exactly that case.

Please any advice.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recommend replacing just the temporary file in the actual string with some other sequence of characters. Then you can use an exact string for the expected string.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could substitute -out:[^ ]* or something like that with -out:TEMP or just anything really.

Copy link
Contributor Author

@hernot hernot Sep 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you mean

let b:command = subsitute(b:command,'/\+tmp/\+\S\+','/tmp/some_tmp_file.tmp')

and than use this modified string in the AssertEqual

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, something like that. But bear in mind that the directory will be different from /tmp on Mac and Windows, so you should probably search for some characters after '-out:'.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure i do. I finish the tests and the fixes commit and as soon as they are working properly i let you know

\ 'executable': 'mcs',
\ 'command_callback': 'ale_linters#cs#mcsc#GetCommand',
\ 'callback': 'ale_linters#cs#mcsc#Handle',
\ 'lint_file': 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation is wrong here.

" 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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a single space after commas.

" 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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a single blank line before any if statement which doesn't start a new level of indentation.


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')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add unlet! b:command to your After block above. Otherwise this variable might end up being used in some other test.

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, '')
Copy link
Member

@w0rp w0rp Sep 27, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sometmpefile will cause problems here with \ in paths on Windows. I recommend using something simpler like '-out:TEMP_FILE' and testing for that. You're replacing it anyway.

Implements suggestions and recommendations suggested by the first review
of the "Advance C# linter based on mcs -t:module (dense-analysis#952)" pull request.

- Clarifies and simplifies description of linters and options
- Added links to help file and marked the mcsc linter as to be run only
  when file in buffer is saved or loaded.
- Added comments to the mcsc.vim file to clarify code
- removed type checks considered not necessary be reviewer.
- addresses findings by vader
- removed call to getcwd and cd in vim script
- handler expands file names relative to route of source tree into
  absolute pathes. Fixes errors not being marked when vim is started
  from subdirectory of source tree.
- implements tests for mcs.vim and mcsc.vim linter
@hernot
Copy link
Contributor Author

hernot commented Sep 27, 2017

Ok all fixed.

Also removed Assert match in ale_cs_mcs_command_callbacks.vader using a substitute to condense multiple whithespaces into a single space char.

@w0rp
Copy link
Member

w0rp commented Sep 27, 2017

Check out the results in Travis CI. doc/ale.txt and README.md are out of sync.

@hernot
Copy link
Contributor Author

hernot commented Sep 28, 2017

The not ins sync problem is not that the tables are not in sync, it is just that the test script stumbles over the greediness of regular expressions in line

53 | sed 's/see.*(,|$)/\1/g' \

This removes in the following line everything starting at the first see till to the end of the line

| C# | mcs see:help ale-cs-mcs for details, mcsc !! see:help ale-cs-mcsc for details and configuration|

with the consequence that only mcs is reported.

I suggest to improve line 53 of the test script as follows

53 | sed 's/see[^,]*(,|$)/\1/g' \

With this change both tables appear in sync as are. Further down in the README.MD sombody solved the same problem by putting the see of the first entry between (). But is that the desired solution as the the () are already used to indicate the links to the compiler, linter project pages. Further it is not consistent with any other see following the last entry of the lists eg.

| Kotlin | kotlinc !!, ktlint !! see :help ale-integration-kotlin for configuration instructions

There is no () around the see. What would be the conformant formatting of the table you suggest markdown would require. In any case i would recommend the fix for properly handling of the greediness of regular expressions.

Similra for Rust

the cargo linter is not embraced in []

@w0rp
Copy link
Member

w0rp commented Sep 28, 2017

Okay, makes sense. I'll fix it on master.

@w0rp w0rp merged commit 9fc01bd into dense-analysis:master Sep 28, 2017
@w0rp
Copy link
Member

w0rp commented Sep 28, 2017

Cheers! 🍻

@w0rp
Copy link
Member

w0rp commented Sep 28, 2017

I implemented the regex you suggested. I think that'll do the trick for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants