diff --git a/ale_linters/erlang/dialyzer.vim b/ale_linters/erlang/dialyzer.vim new file mode 100644 index 00000000..7af64c4f --- /dev/null +++ b/ale_linters/erlang/dialyzer.vim @@ -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 +\}) diff --git a/doc/ale-erlang.txt b/doc/ale-erlang.txt index ad3c1e5a..59993a99 100644 --- a/doc/ale-erlang.txt +++ b/doc/ale-erlang.txt @@ -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* diff --git a/doc/ale.txt b/doc/ale.txt index 7e6ac443..ac3661fc 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -1989,6 +1989,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| diff --git a/test/command_callback/test_erlang_dialyzer_command_callback.vader b/test/command_callback/test_erlang_dialyzer_command_callback.vader new file mode 100644 index 00000000..5e21c053 --- /dev/null +++ b/test/command_callback/test_erlang_dialyzer_command_callback.vader @@ -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' diff --git a/test/handler/test_erlang_dialyzer_handler.vader b/test/handler/test_erlang_dialyzer_handler.vader new file mode 100644 index 00000000..afd5c597 --- /dev/null +++ b/test/handler/test_erlang_dialyzer_handler.vader @@ -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(''), [''])