mirror of
https://github.com/dense-analysis/ale.git
synced 2026-10-25 23:27:27 +08:00
elixir: Fix mix linter and support warnings (#5140)
CI / Build (push) Canceled after 0s
CI / Lint (push) Canceled after 0s
CI / Lua (push) Canceled after 0s
CI / Neovim 0.10 Linux (push) Canceled after 0s
CI / Neovim 0.12 Linux (push) Canceled after 0s
CI / Vim 8.2 Linux (push) Canceled after 0s
CI / Vim 9.2 Linux (push) Canceled after 0s
CI / Neovim 0.10 Windows (push) Canceled after 0s
CI / Neovim 0.12 Windows (push) Canceled after 0s
CI / Vim 8.2 Windows (push) Canceled after 0s
CI / Vim 9.2 Windows (push) Canceled after 0s
CI / Build (push) Canceled after 0s
CI / Lint (push) Canceled after 0s
CI / Lua (push) Canceled after 0s
CI / Neovim 0.10 Linux (push) Canceled after 0s
CI / Neovim 0.12 Linux (push) Canceled after 0s
CI / Vim 8.2 Linux (push) Canceled after 0s
CI / Vim 9.2 Linux (push) Canceled after 0s
CI / Neovim 0.10 Windows (push) Canceled after 0s
CI / Neovim 0.12 Windows (push) Canceled after 0s
CI / Vim 8.2 Windows (push) Canceled after 0s
CI / Vim 9.2 Windows (push) Canceled after 0s
* elixir: Don't use temp dir for mix linter The mix linter sets `MIX_BUILD_PATH` to a temporary directory on every run, which is very slow and inefficient. We can just use the default build path to speed this up. The default directory is `_build` inside the project, this is also automatically added to `.gitignore` by `mix new` so hopefully shouldn't cause any unwanted clutter. The `%s` argument is removed since `mix compile` doesn't actually support passing a filename. The output stream is changed to `stderr` since that's where the errors appear. Other options: - `mix compile` has a `--no-compile` flag, but this misses most errors including basic syntax errors - `mix format` has a `--dry-run` flag, but this only catches basic syntax errors and misses other errors like undefined functions Fixes https://github.com/dense-analysis/ale/issues/3816 * elixir: Fix error parsing for mix linter and support warnings The error parsing stopped working at some point, probably when Elixir introduced its new error formatting in for 1.16 in 2023.
This commit is contained in:
1 parent
ca4a4dd33c
commit
422c0e8ef4
3 files changed
+137
-32
No files matched your search
+63
-20
@@ -1,45 +1,88 @@
|
||||
" Author: evnu - https://github.com/evnu
|
||||
" Author: colbydehart - https://github.com/colbydehart
|
||||
" Author: toupeira - https://github.com/toupeira
|
||||
" Description: Mix compile checking for Elixir files
|
||||
|
||||
function! ale_linters#elixir#mix#Handle(buffer, lines) abort
|
||||
" Matches patterns like the following:
|
||||
" Example output from Elixir 1.20.0:
|
||||
"
|
||||
" Error format
|
||||
" ** (CompileError) apps/sim/lib/sim/server.ex:87: undefined function update_in/4
|
||||
" warning: variable "a" is unused (if the variable is not meant to be used, prefix it with an underscore)
|
||||
" │
|
||||
" 16 │ a = missing_var
|
||||
" │ ~
|
||||
" │
|
||||
" └─ lib/foo.ex:16:5: Foo.hello/0
|
||||
"
|
||||
" TODO: Warning format
|
||||
" warning: variable "foobar" does not exist and is being expanded to "foobar()", please use parentheses to remove the ambiguity or change the variable name
|
||||
let l:pattern = '\v\(([^\)]+Error)\) ([^:]+):([^:]+): (.+)$'
|
||||
let l:output = []
|
||||
" warning: variable "b" is unused (if the variable is not meant to be used, prefix it with an underscore)
|
||||
" │
|
||||
" 17 │ b = another_missing_var
|
||||
" │ ~
|
||||
" │
|
||||
" └─ lib/foo.ex:17:5: Foo.hello/0
|
||||
"
|
||||
" error: undefined variable "another_missing_var"
|
||||
" │
|
||||
" 17 │ b = another_missing_var
|
||||
" │ ^^^^^^^^^^^^^^^^^^^
|
||||
" │
|
||||
" └─ lib/foo.ex:17:9: Foo.hello/0
|
||||
"
|
||||
" error: undefined variable "missing_var"
|
||||
" │
|
||||
" 16 │ a = missing_var
|
||||
" │ ^^^^^^^^^^^
|
||||
" │
|
||||
" └─ lib/foo.ex:16:9: Foo.hello/0
|
||||
"
|
||||
"
|
||||
" == Compilation error in file lib/foo.ex ==
|
||||
" ** (CompileError) lib/foo.ex: cannot compile module Foo (errors have been logged)
|
||||
"
|
||||
" NOTE: The line with the error class is not captured because it always
|
||||
" seems to be either a `CompileError` or a `SyntaxError`, with a generic
|
||||
" message.
|
||||
"
|
||||
let l:pattern_text = '\v^ (warning|error): (.+)$'
|
||||
let l:pattern_file = '\v^ └─ ([^ :]+):([0-9]+):([0-9]+)?'
|
||||
|
||||
for l:match in ale#util#GetMatches(a:lines, l:pattern)
|
||||
let l:type = 'E'
|
||||
let l:text = l:match[4]
|
||||
let l:match_texts = ale#util#GetMatches(a:lines, l:pattern_text)
|
||||
let l:match_files = ale#util#GetMatches(a:lines, l:pattern_file)
|
||||
|
||||
let l:output = []
|
||||
let l:index = 0
|
||||
|
||||
" Always add the full output as detail
|
||||
let l:detail = join(a:lines, "\n")
|
||||
|
||||
for l:match_text in l:match_texts
|
||||
let l:type = toupper(l:match_text[1][0])
|
||||
let l:match_file = get(l:match_files, l:index, [])
|
||||
|
||||
if !ale#path#IsBufferPath(a:buffer, get(l:match_file, 1))
|
||||
continue
|
||||
endif
|
||||
|
||||
call add(l:output, {
|
||||
\ 'bufnr': a:buffer,
|
||||
\ 'lnum': l:match[3] + 0,
|
||||
\ 'col': 0,
|
||||
\ 'type': l:type,
|
||||
\ 'text': l:text,
|
||||
\ 'detail': l:detail,
|
||||
\ 'text': get(l:match_text, 2, v:null),
|
||||
\ 'lnum': str2nr(get(l:match_file, 2, v:null)),
|
||||
\ 'col': str2nr(get(l:match_file, 3, v:null)),
|
||||
\})
|
||||
|
||||
let l:index += 1
|
||||
endfor
|
||||
|
||||
return l:output
|
||||
endfunction
|
||||
|
||||
function! ale_linters#elixir#mix#GetCommand(buffer) abort
|
||||
let l:temp_dir = ale#command#CreateDirectory(a:buffer)
|
||||
|
||||
return ale#Env('MIX_BUILD_PATH', l:temp_dir) . 'mix compile %s'
|
||||
endfunction
|
||||
|
||||
call ale#linter#Define('elixir', {
|
||||
\ 'name': 'mix',
|
||||
\ 'executable': 'mix',
|
||||
\ 'cwd': function('ale#handlers#elixir#FindMixProjectRoot'),
|
||||
\ 'command': function('ale_linters#elixir#mix#GetCommand'),
|
||||
\ 'command': '%e compile',
|
||||
\ 'output_stream': 'stderr',
|
||||
\ 'callback': 'ale_linters#elixir#mix#Handle',
|
||||
\ 'lint_file': 1,
|
||||
\})
|
||||
@@ -1,20 +1,85 @@
|
||||
Before:
|
||||
runtime ale_linters/elixir/mix.vim
|
||||
call ale#test#SetFilename('/testplugin/lib/foo.ex')
|
||||
|
||||
After:
|
||||
call ale#linter#Reset()
|
||||
|
||||
Execute(The mix handler should parse lines correctly):
|
||||
let input_lines = [
|
||||
\ ' warning: variable "a" is unused (if the variable is not meant to be used, prefix it with an underscore)',
|
||||
\ ' │',
|
||||
\ ' 16 │ a = missing_var',
|
||||
\ ' │ ~',
|
||||
\ ' │',
|
||||
\ ' └─ /testplugin/lib/foo.ex:16:5: Foo.hello/0',
|
||||
\ '',
|
||||
\ ' warning: variable "b" is unused (if the variable is not meant to be used, prefix it with an underscore)',
|
||||
\ ' │',
|
||||
\ ' 17 │ b = another_missing_var',
|
||||
\ ' │ ~',
|
||||
\ ' │',
|
||||
\ ' └─ /testplugin/lib/foo.ex:17:5: Foo.hello/0',
|
||||
\ '',
|
||||
\ ' error: undefined variable "another_missing_var"',
|
||||
\ ' │',
|
||||
\ ' 17 │ b = another_missing_var',
|
||||
\ ' │ ^^^^^^^^^^^^^^^^^^^',
|
||||
\ ' │',
|
||||
\ ' └─ /testplugin/lib/foo.ex:17:9: Foo.hello/0',
|
||||
\ '',
|
||||
\ ' error: undefined variable "missing_var"',
|
||||
\ ' │',
|
||||
\ ' 16 │ a = missing_var',
|
||||
\ ' │ ^^^^^^^^^^^',
|
||||
\ ' │',
|
||||
\ ' └─ /testplugin/lib/foo.ex:16:9: Foo.hello/0',
|
||||
\ '',
|
||||
\ ' error: undefined variable "unrelated_var"',
|
||||
\ ' │',
|
||||
\ ' 16 │ a = unrelated_var',
|
||||
\ ' │ ^^^^^^^^^^^',
|
||||
\ ' │',
|
||||
\ ' └─ /testplugin/lib/unrelated.ex:16:9: Foo.hello/0',
|
||||
\ '',
|
||||
\ '',
|
||||
\ '== Compilation error in file /testplugin/lib/foo.ex ==',
|
||||
\ '** (CompileError) /testplugin/lib/foo.ex: cannot compile module Foo (errors have been logged)',
|
||||
\ ]
|
||||
|
||||
AssertEqual
|
||||
\ [
|
||||
\ {
|
||||
\ 'bufnr': 347,
|
||||
\ 'lnum': 87,
|
||||
\ 'col': 0,
|
||||
\ 'text': 'undefined function update_in/4',
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'lnum': 16,
|
||||
\ 'col': 5,
|
||||
\ 'type': 'W',
|
||||
\ 'text': 'variable "a" is unused (if the variable is not meant to be used, prefix it with an underscore)',
|
||||
\ 'detail': join(input_lines, "\n"),
|
||||
\ },
|
||||
\ {
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'lnum': 17,
|
||||
\ 'col': 5,
|
||||
\ 'type': 'W',
|
||||
\ 'text': 'variable "b" is unused (if the variable is not meant to be used, prefix it with an underscore)',
|
||||
\ 'detail': join(input_lines, "\n"),
|
||||
\ },
|
||||
\ {
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'lnum': 17,
|
||||
\ 'col': 9,
|
||||
\ 'type': 'E',
|
||||
\ 'text': 'undefined variable "another_missing_var"',
|
||||
\ 'detail': join(input_lines, "\n"),
|
||||
\ },
|
||||
\ {
|
||||
\ 'bufnr': bufnr(''),
|
||||
\ 'lnum': 16,
|
||||
\ 'col': 9,
|
||||
\ 'type': 'E',
|
||||
\ 'text': 'undefined variable "missing_var"',
|
||||
\ 'detail': join(input_lines, "\n"),
|
||||
\ },
|
||||
\ ],
|
||||
\ ale_linters#elixir#mix#Handle(347, [
|
||||
\ '** (CompileError) apps/sim/lib/sim/server.ex:87: undefined function update_in/4'
|
||||
\ ])
|
||||
\ ale_linters#elixir#mix#Handle(bufnr(''), input_lines)
|
||||
@@ -1,19 +1,16 @@
|
||||
Before:
|
||||
call ale#assert#SetUpLinterTest('elixir', 'mix')
|
||||
call ale#test#SetFilename('../test-files/elixir/mix_project/lib/app.ex')
|
||||
let g:env_prefix = ale#Env('MIX_BUILD_PATH', 'TEMP_DIR')
|
||||
|
||||
After:
|
||||
unlet! g:env_prefix
|
||||
|
||||
call ale#assert#TearDownLinterTest()
|
||||
|
||||
Execute(The default mix command should be correct):
|
||||
AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/elixir/mix_project')
|
||||
AssertLinter 'mix', g:env_prefix . 'mix compile %s'
|
||||
AssertLinter 'mix', ale#Escape('mix') . ' compile'
|
||||
|
||||
Execute(Build mix commands with an umbrella root):
|
||||
call ale#test#SetFilename('../test-files/elixir/umbrella_project/apps/mix_project/lib/app.ex')
|
||||
|
||||
AssertLinterCwd ale#path#Simplify(g:dir . '/../test-files/elixir/umbrella_project')
|
||||
AssertLinter 'mix', g:env_prefix . 'mix compile %s'
|
||||
AssertLinter 'mix', ale#Escape('mix') . ' compile'
|
||||
Reference in new issue
Block a user