Add support for Rust using rustc and cargo (#230)

* Add rustc checker for rust files

* Add documentation for rustc

* Use a nice helper function

* Add cargo as linter

* Complete the doc for rust linters

* Put l: in front of every local variable

* Apply the requested stylistic changes
This commit is contained in:
EinfachToll 2017-01-12 10:33:55 +01:00 committed by w0rp
parent 75485d53f6
commit 9c5f092b4f
4 changed files with 158 additions and 0 deletions

View file

@ -77,6 +77,7 @@ name. That seems to be the fairest way to arrange this table.
| Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) |
| Python | [flake8](http://flake8.pycqa.org/en/latest/), [pylint](https://www.pylint.org/) |
| Ruby | [rubocop](https://github.com/bbatsov/rubocop) |
| Rust | [rustc](https://www.rust-lang.org/), cargo (see `:help ale-integration-rust` for configuration instructions) |
| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint), [stylelint](https://github.com/stylelint/stylelint) |
| SCSS | [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint), [stylelint](https://github.com/stylelint/stylelint) |
| Scala | [scalac](http://scala-lang.org) |

View file

@ -0,0 +1,21 @@
" Author: Daniel Schemala <istjanichtzufassen@gmail.com>
" Description: rustc invoked by cargo for rust files
function! ale_linters#rust#cargo#cargo_or_not_cargo(bufnr)
if ale#util#FindNearestFile(a:bufnr, 'Cargo.toml') !=# ''
return 'cargo'
else
" if there is no Cargo.toml file, we don't use cargo even if it exists,
" so we return '', because executable('') apparently always fails
return ''
endif
endfunction
call ale#linter#Define('rust', {
\ 'name': 'cargo',
\ 'executable_callback': 'ale_linters#rust#cargo#cargo_or_not_cargo',
\ 'command': 'cargo rustc -- --error-format=json -Z no-trans',
\ 'callback': 'ale_linters#rust#rustc#handle_rustc_errors',
\ 'output_stream': 'stderr',
\})

View file

@ -0,0 +1,99 @@
" Author: Daniel Schemala <istjanichtzufassen@gmail.com>
" Description: rustc for rust files
if !exists('g:ale_rust_ignore_error_codes')
let g:ale_rust_ignore_error_codes = []
endif
function! ale_linters#rust#rustc#handle_rustc_errors(buffer_number, errorlines)
let l:file_name = fnamemodify(bufname(a:buffer_number), ':t')
let l:output = []
for l:errorline in a:errorlines
" ignore everything that is not Json
if l:errorline !~# '^{'
continue
endif
let l:error = json_decode(l:errorline)
if !empty(l:error.code) && index(g:ale_rust_ignore_error_codes, l:error.code.code) > -1
continue
endif
for l:span in l:error.spans
if l:span.is_primary &&
\ (l:span.file_name ==# l:file_name || l:span.file_name ==# '<anon>')
call add(l:output, {
\ 'bufnr': a:buffer_number,
\ 'lnum': l:span.line_start,
\ 'vcol': 0,
\ 'col': l:span.byte_start,
\ 'nr': -1,
\ 'text': l:error.message,
\ 'type': toupper(l:error.level[0]),
\})
else
" when the error is caused in the expansion of a macro, we have
" to bury deeper
let l:root_cause = s:find_error_in_expansion(l:span, l:file_name)
if !empty(l:root_cause)
call add(l:output, {
\ 'bufnr': a:buffer_number,
\ 'lnum': l:root_cause[0],
\ 'vcol': 0,
\ 'col': l:root_cause[1],
\ 'nr': -1,
\ 'text': l:error.message,
\ 'type': toupper(l:error.level[0]),
\})
endif
endif
endfor
endfor
return l:output
endfunction
" returns: a list [lnum, col] with the location of the error or []
function! s:find_error_in_expansion(span, file_name)
if a:span.file_name ==# a:file_name
return [a:span.line_start, a:span.byte_start]
endif
if !empty(a:span.expansion)
return s:find_error_in_expansion(a:span.expansion.span, a:file_name)
endif
return []
endfunction
function! ale_linters#rust#rustc#rustc_command(buffer_number)
" Try to guess the library search path. If the project is managed by cargo,
" it's usually <project root>/target/debug/deps/ or
" <project root>/target/release/deps/
let l:cargo_file = ale#util#FindNearestFile(a:buffer_number, 'Cargo.toml')
if l:cargo_file !=# ''
let l:project_root = fnamemodify(l:cargo_file, ':h')
let l:dependencies = '-L ' . l:project_root . '/target/debug/deps -L ' .
\ l:project_root . '/target/release/deps'
else
let l:dependencies = ''
endif
return 'rustc --error-format=json -Z no-trans ' . l:dependencies . ' -'
endfunction
call ale#linter#Define('rust', {
\ 'name': 'rustc',
\ 'executable': 'rustc',
\ 'command_callback': 'ale_linters#rust#rustc#rustc_command',
\ 'callback': 'ale_linters#rust#rustc#handle_rustc_errors',
\ 'output_stream': 'stderr',
\})

View file

@ -30,8 +30,11 @@ CONTENTS *ale-contents*
4.18. ruby-rubocop..........................|ale-linter-options-ruby-rubocop|
4.19. chktex................................|ale-linter-options-chktex|
4.20. lacheck...............................|ale-linter-options-lacheck|
4.21. stylelint.............................|ale-linter-options-stylelint|
4.22. rustc.................................|ale-linter-options-rustc|
5. Linter Integration Notes...................|ale-linter-integration|
5.1. merlin................................|ale-linter-integration-ocaml-merlin|
5.2. rust...................................|ale-integration-rust|
6. Commands/Keybinds..........................|ale-commands|
7. API........................................|ale-api|
8. Special Thanks.............................|ale-special-thanks|
@ -88,6 +91,7 @@ The following languages and tools are supported.
* Pug: 'pug-lint'
* Puppet: 'puppet', 'puppet-lint'
* Python: 'flake8', 'pylint'
* Rust: 'rustc' (see |ale-integration-rust|)
* Ruby: 'rubocop'
* SASS: 'sasslint', 'stylelint'
* SCSS: 'sasslint', 'scsslint', 'stylelint'
@ -774,6 +778,19 @@ g:ale_scss_stylelint_use_global *g:ale_scss_stylelint_use_global
global version of stylelint, in preference to locally installed versions of
stylelint in node_modules.
------------------------------------------------------------------------------
4.22. rustc *ale-linter-options-rustc*
g:ale_rust_ignore_error_codes *g:ale_rust_ignore_error_codes*
Type: |List| of |String|s
Default: []
This variable can contain error codes which will be ignored. For example, to
ignore most errors regarding failed imports, put this in your .vimrc
>
let g:ale_rust_ignore_error_codes = ['E0432', 'E0433']
===============================================================================
5. Linter Integration Notes *ale-linter-integration*
@ -787,6 +804,26 @@ Some linters may have requirements for some other plugins being installed.
detailed instructions
(https://github.com/the-lambda-church/merlin/wiki/vim-from-scratch).
-------------------------------------------------------------------------------
5.2. rust *ale-integration-rust*
Since Vim does not detect the rust file type out-of-the-box, you need the
runtime files for rust from here: https://github.com/rust-lang/rust.vim
Note that there are two possible linters for rust files:
1. rustc -- The Rust compiler is used to check the currently edited file.
So, if your project consists of multiple files, you will get some errors
when you use e.g. a struct which is defined in another file. You can use
|g:ale_rust_ignore_error_codes| to ignore some of these errors.
2. cargo -- If your project is managed by Cargo, the whole project is
checked. That means that all errors are properly shown, but cargo can
only operate on the files written on disk. That means it is highly
recommended to turn off |g:ale_lint_on_text_changed| and to turn on
|g:ale_lint_on_save|.
Also note that rustc 1.12. or later is needed.
===============================================================================
6. Commands/Keybinds *ale-commands*