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

Add support for Erlang dialyzer #2509

Merged
88 changes: 88 additions & 0 deletions ale_linters/erlang/dialyzer.vim
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
" Author: Autoine Gagne - https://github.com/AntoineGagne
" Description: Define a checker that runs dialyzer on Erlang files.

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:rebar3_profile = ale_linters#erlang#dialyzer#GetRebar3Profile(a:buffer)
let l:plt_file_directory = ale#path#FindNearestDirectory(a:buffer, '_build' . l:rebar3_profile)
let l:plt_file = split(globpath(l:plt_file_directory, '/*_plt'), '\n')
Copy link
Member

Choose a reason for hiding this comment

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

Check if l:plt_file_directory is empty before running globpath.


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 = fnameescape(ale_linters#erlang#dialyzer#GetExecutable(a:buffer))
\ . ' -n'
\ . ' --plt ' . fnameescape(ale_linters#erlang#dialyzer#GetPlt(a:buffer))
Copy link
Member

Choose a reason for hiding this comment

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

Use ale#Escape() for escaping command line arguments. fnameescape is only intended for use with :execute.

\ . ' -Wunmatched_returns'
\ . ' -Werror_handling'
\ . ' -Wrace_conditions'
\ . ' -Wunderspecs'
\ . ' %s'

This comment was marked as resolved.


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'

This comment was marked as resolved.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The tool doesn't provide any information as to whether it is a warning or an error so I chose to make the reported information into warnings.

\})
endif
endfor

return l:output
endfunction

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')

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')
\})
29 changes: 29 additions & 0 deletions doc/ale-erlang.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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*

g:ale_erlang_erlc_options *g:ale_erlang_erlc_options*
Expand Down
1 change: 1 addition & 0 deletions doc/ale.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1991,6 +1991,7 @@ documented in additional help files.
elm-lsp...............................|ale-elm-elm-lsp|
elm-make..............................|ale-elm-elm-make|
erlang..................................|ale-erlang-options|
dialyzer..............................|ale-erlang-dialyzer|
erlc..................................|ale-erlang-erlc|
syntaxerl.............................|ale-erlang-syntaxerl|
eruby...................................|ale-eruby-options|
Expand Down
27 changes: 27 additions & 0 deletions test/handler/test_erlang_dialyzer_handler.vader
Original file line number Diff line number Diff line change
@@ -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(''), [''])