Add support for Erlang dialyzer (#2509)

* Add support for Erlang dialyzer
* Add an option to specify rebar3 profile

In doing so, the use of the `**` wildcard becomes unnecessary.
This commit is contained in:
Antoine Gagné 2019-05-19 16:16:17 -04:00 committed by w0rp
parent 652d991077
commit 3b7c86e401
5 changed files with 187 additions and 0 deletions

View file

@ -0,0 +1,93 @@
" Author: Autoine Gagne - https://github.com/AntoineGagne
" Description: Define a checker that runs dialyzer on Erlang files.
let g:ale_erlang_dialyzer_executable =
\ get(g:, 'ale_erlang_dialyzer_executable', 'dialyzer')
let g:ale_erlang_dialyzer_plt_file =
\ get(g:, 'ale_erlang_dialyzer_plt_file', '')
let g:ale_erlang_dialyzer_rebar3_profile =
\ get(g:, 'ale_erlang_dialyzer_rebar3_profile', 'default')
function! ale_linters#erlang#dialyzer#GetRebar3Profile(buffer) abort
return ale#Var(a:buffer, 'erlang_dialyzer_rebar3_profile')
endfunction
function! ale_linters#erlang#dialyzer#FindPlt(buffer) abort
let l:plt_file = ''
let l:rebar3_profile = ale_linters#erlang#dialyzer#GetRebar3Profile(a:buffer)
let l:plt_file_directory = ale#path#FindNearestDirectory(a:buffer, '_build' . l:rebar3_profile)
if !empty(l:plt_file_directory)
let l:plt_file = split(globpath(l:plt_file_directory, '/*_plt'), '\n')
endif
if !empty(l:plt_file)
return l:plt_file[0]
endif
if !empty($REBAR_PLT_DIR)
return expand('$REBAR_PLT_DIR/dialyzer/plt')
endif
return expand('$HOME/.dialyzer_plt')
endfunction
function! ale_linters#erlang#dialyzer#GetPlt(buffer) abort
let l:plt_file = ale#Var(a:buffer, 'erlang_dialyzer_plt_file')
if !empty(l:plt_file)
return l:plt_file
endif
return ale_linters#erlang#dialyzer#FindPlt(a:buffer)
endfunction
function! ale_linters#erlang#dialyzer#GetExecutable(buffer) abort
return ale#Var(a:buffer, 'erlang_dialyzer_executable')
endfunction
function! ale_linters#erlang#dialyzer#GetCommand(buffer) abort
let l:command = ale#Escape(ale_linters#erlang#dialyzer#GetExecutable(a:buffer))
\ . ' -n'
\ . ' --plt ' . ale#Escape(ale_linters#erlang#dialyzer#GetPlt(a:buffer))
\ . ' -Wunmatched_returns'
\ . ' -Werror_handling'
\ . ' -Wrace_conditions'
\ . ' -Wunderspecs'
\ . ' %s'
return l:command
endfunction
function! ale_linters#erlang#dialyzer#Handle(buffer, lines) abort
" Match patterns like the following:
"
" erl_tidy_prv_fmt.erl:3: Callback info about the provider behaviour is not available
let l:pattern = '^\S\+:\(\d\+\): \(.\+\)$'
let l:output = []
for l:line in a:lines
let l:match = matchlist(l:line, l:pattern)
if len(l:match) != 0
let l:code = l:match[2]
call add(l:output, {
\ 'lnum': str2nr(l:match[1]),
\ 'lcol': 0,
\ 'text': l:code,
\ 'type': 'W'
\})
endif
endfor
return l:output
endfunction
call ale#linter#Define('erlang', {
\ 'name': 'dialyzer',
\ 'executable': function('ale_linters#erlang#dialyzer#GetExecutable'),
\ 'command': function('ale_linters#erlang#dialyzer#GetCommand'),
\ 'callback': function('ale_linters#erlang#dialyzer#Handle'),
\ 'lint_file': 1
\})

View file

@ -3,6 +3,35 @@ ALE Erlang Integration *ale-erlang-options*
=============================================================================== ===============================================================================
dialyzer *ale-erlang-dialyzer*
g:ale_erlang_dialyzer_executable *g:ale_erlang_dialyzer_executable*
*b:ale_erlang_dialyzer_executable*
Type: |String|
Default: `'dialyzer'`
This variable can be changed to specify the dialyzer executable.
g:ale_erlang_dialyzer_plt_file *g:ale_erlang_dialyzer_plt_file*
*b:ale_erlang_dialyzer_plt_file*
Type: |String|
This variable can be changed to specify the path to the PLT file. By
default, it will search for the PLT file inside the `_build` directory. If
there isn't one, it will fallback to the path `$REBAR_PLT_DIR/dialyzer/plt`.
Otherwise, it will default to `$HOME/.dialyzer_plt`.
g:ale_erlang_dialyzer_rebar3_profile *g:ale_erlang_dialyzer_rebar3_profile*
*b:ale_erlang_dialyzer_rebar3_profile*
Type: |String|
Default: `'default'`
This variable can be changed to specify the profile that is used to
run dialyzer with rebar3.
-------------------------------------------------------------------------------
erlc *ale-erlang-erlc* erlc *ale-erlang-erlc*
g:ale_erlang_erlc_options *g:ale_erlang_erlc_options* g:ale_erlang_erlc_options *g:ale_erlang_erlc_options*

View file

@ -1989,6 +1989,7 @@ documented in additional help files.
elm-lsp...............................|ale-elm-elm-lsp| elm-lsp...............................|ale-elm-elm-lsp|
elm-make..............................|ale-elm-elm-make| elm-make..............................|ale-elm-elm-make|
erlang..................................|ale-erlang-options| erlang..................................|ale-erlang-options|
dialyzer..............................|ale-erlang-dialyzer|
erlc..................................|ale-erlang-erlc| erlc..................................|ale-erlang-erlc|
syntaxerl.............................|ale-erlang-syntaxerl| syntaxerl.............................|ale-erlang-syntaxerl|
eruby...................................|ale-eruby-options| eruby...................................|ale-eruby-options|

View file

@ -0,0 +1,37 @@
Before:
call ale#assert#SetUpLinterTest('erlang', 'dialyzer')
After:
call ale#assert#TearDownLinterTest()
Execute(The default command should be correct.):
AssertLinter 'dialyzer',
\ ale#Escape('dialyzer')
\ . ' -n --plt ' . ale#Escape(expand('$HOME/.dialyzer_plt'))
\ . ' -Wunmatched_returns'
\ . ' -Werror_handling'
\ . ' -Wrace_conditions'
\ . ' -Wunderspecs'
\ . ' %s'
Execute(The command should accept configured executable.):
let b:ale_erlang_dialyzer_executable = '/usr/bin/dialyzer'
AssertLinter '/usr/bin/dialyzer',
\ ale#Escape('/usr/bin/dialyzer')
\ . ' -n --plt ' . ale#Escape(expand('$HOME/.dialyzer_plt'))
\ . ' -Wunmatched_returns'
\ . ' -Werror_handling'
\ . ' -Wrace_conditions'
\ . ' -Wunderspecs'
\ . ' %s'
Execute(The command should accept configured PLT file.):
let b:ale_erlang_dialyzer_plt_file = 'custom-plt'
AssertLinter 'dialyzer',
\ ale#Escape('dialyzer')
\ . ' -n --plt ' . ale#Escape(expand('custom-plt'))
\ . ' -Wunmatched_returns'
\ . ' -Werror_handling'
\ . ' -Wrace_conditions'
\ . ' -Wunderspecs'
\ . ' %s'

View file

@ -0,0 +1,27 @@
Before:
runtime ale_linters/erlang/dialyzer.vim
After:
call ale#linter#Reset()
Execute(The dialyzer handler should handle error messages.):
AssertEqual
\[
\ {
\ 'lnum': 3,
\ 'lcol': 0,
\ 'text': 'Callback info about the provider behaviour is not available',
\ 'type': 'W'
\ }
\],
\ ale_linters#erlang#dialyzer#Handle(bufnr(''), ['erl_tidy_prv_fmt.erl:3: Callback info about the provider behaviour is not available'])
Execute(The dialyzer handler should handle empty file.):
AssertEqual
\[],
\ ale_linters#erlang#dialyzer#Handle(bufnr(''), [])
Execute(The dialyzer handler should handle empty lines.):
AssertEqual
\[],
\ ale_linters#erlang#dialyzer#Handle(bufnr(''), [''])