Add support for multiline messages (#3686)

This is achieved by switching to JSON, which makes it much easier to
avoid confusion between an error message and the next one. It also
spares us from having to deal with regular expressions, and eliminates
some edge cases that no longer need to be tested.
This commit is contained in:
Grégoire Paris 2021-07-09 16:59:36 +02:00 committed by GitHub
parent 774ab4b4f0
commit 2a5a7baffc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 31 additions and 54 deletions

View File

@ -32,8 +32,8 @@ function! ale_linters#php#phpstan#GetCommand(buffer, version) abort
\ : '' \ : ''
let l:error_format = ale#semver#GTE(a:version, [0, 10, 3]) let l:error_format = ale#semver#GTE(a:version, [0, 10, 3])
\ ? ' --error-format raw' \ ? ' --error-format json'
\ : ' --errorFormat raw' \ : ' --errorFormat json'
return '%e analyze --no-progress' return '%e analyze --no-progress'
\ . l:error_format \ . l:error_format
@ -44,17 +44,17 @@ function! ale_linters#php#phpstan#GetCommand(buffer, version) abort
endfunction endfunction
function! ale_linters#php#phpstan#Handle(buffer, lines) abort function! ale_linters#php#phpstan#Handle(buffer, lines) abort
" Matches against lines like the following: let l:res = ale#util#FuzzyJSONDecode(a:lines, {'files': []})
"
" filename.php:15:message
" C:\folder\filename.php:15:message
let l:pattern = '^\([a-zA-Z]:\)\?[^:]\+:\(\d\+\):\(.*\)$'
let l:output = [] let l:output = []
for l:match in ale#util#GetMatches(a:lines, l:pattern) if type(l:res.files) is v:t_list
return l:output
endif
for l:err in l:res.files[expand('#' . a:buffer .':p')].messages
call add(l:output, { call add(l:output, {
\ 'lnum': l:match[2] + 0, \ 'lnum': l:err.line,
\ 'text': l:match[3], \ 'text': l:err.message,
\ 'type': 'E', \ 'type': 'E',
\}) \})
endfor endfor

View File

@ -14,7 +14,9 @@ Execute(Output without errors should be parsed correctly):
AssertEqual AssertEqual
\ [], \ [],
\ ale_linters#php#phpstan#Handle(bufnr(''), []) \ ale_linters#php#phpstan#Handle(bufnr(''), [
\ '{"totals":{"errors":0,"file_errors":0},"files":[],"errors":[]}',
\ ])
Execute(Output with some errors should be parsed correctly): Execute(Output with some errors should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/foo/test.php') call ale#test#SetFilename('phpstan-test-files/foo/test.php')
@ -37,38 +39,13 @@ Execute(Output with some errors should be parsed correctly):
\ 'type': 'E' \ 'type': 'E'
\ } \ }
\ ], \ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [ \ ale_linters#php#phpstan#Handle(bufnr(''), [json_encode(
\ 'phpstan-test-files/foo/test.php:9:Call to method format() on an unknown class DateTimeImutable.', \ {
\ 'phpstan-test-files/foo/test.php:16:Sample message.', \ "totals":{"errors": 0, "file_errors": 11},
\ 'phpstan-test-files/foo/test.php:192:Invalid command testCommand.', \ "files":{expand('%:p'): {"errors": 3, "messages": [
\]) \ {"message": "Call to method format() on an unknown class DateTimeImutable.", "line":9},
\ {"message": "Sample message.", "line":16},
Execute(Output should be parsed correctly with Windows paths): \ {"message": "Invalid command testCommand.", "line": 192}
call ale#test#SetFilename('phpstan-test-files/foo/test.php') \ ]}}
\ },
AssertEqual \)])
\ [
\ {
\ 'lnum': 9,
\ 'text': 'Access to an undefined property Test::$var.',
\ 'type': 'E'
\ }
\ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [
\ 'D:\phpstan-test-files\foo\test.php:9:Access to an undefined property Test::$var.',
\])
Execute(Output for .inc files should be parsed correctly):
call ale#test#SetFilename('phpstan-test-files/test.inc')
AssertEqual
\ [
\ {
\ 'lnum': 9,
\ 'text': 'Access to an undefined property Test::$var.',
\ 'type': 'E'
\ }
\ ],
\ ale_linters#php#phpstan#Handle(bufnr(''), [
\ '/phpstan-test-files/foo/test.inc:9:Access to an undefined property Test::$var.',
\])

View File

@ -25,27 +25,27 @@ Execute(Custom executables should be used for the executable and command):
let g:ale_php_phpstan_executable = 'phpstan_test' let g:ale_php_phpstan_executable = 'phpstan_test'
AssertLinter 'phpstan_test', AssertLinter 'phpstan_test',
\ ale#Escape('phpstan_test') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('4') . ' %s' \ ale#Escape('phpstan_test') . ' analyze --no-progress --errorFormat json -l ' . ale#Escape('4') . ' %s'
Execute(project with level set to 3): Execute(project with level set to 3):
call ale#test#SetFilename('phpstan-test-files/foo/test.php') call ale#test#SetFilename('phpstan-test-files/foo/test.php')
let g:ale_php_phpstan_level = 3 let g:ale_php_phpstan_level = 3
AssertLinter 'phpstan', AssertLinter 'phpstan',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('3') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json -l ' . ale#Escape('3') . ' %s'
Execute(Custom phpstan configuration file): Execute(Custom phpstan configuration file):
let g:ale_php_phpstan_configuration = 'phpstan_config' let g:ale_php_phpstan_configuration = 'phpstan_config'
AssertLinter 'phpstan', AssertLinter 'phpstan',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -c ' . ale#Escape('phpstan_config') . ' -l ' . ale#Escape('4') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json -c ' . ale#Escape('phpstan_config') . ' -l ' . ale#Escape('4') . ' %s'
Execute(Choose the right format for error format param): Execute(Choose the right format for error format param):
GivenCommandOutput ['0.10.3'] GivenCommandOutput ['0.10.3']
AssertLinter 'phpstan', [ AssertLinter 'phpstan', [
\ ale#Escape('phpstan') . ' --version', \ ale#Escape('phpstan') . ' --version',
\ ale#Escape('phpstan') . ' analyze --no-progress --error-format raw -l ' . ale#Escape('4') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --error-format json -l ' . ale#Escape('4') . ' %s'
\ ] \ ]
Execute(Configuration file exists in current directory): Execute(Configuration file exists in current directory):
@ -55,7 +55,7 @@ Execute(Configuration file exists in current directory):
AssertLinter 'phpstan', [ AssertLinter 'phpstan', [
\ ale#Escape('phpstan') . ' --version', \ ale#Escape('phpstan') . ' --version',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json %s'
\ ] \ ]
Execute(Configuration dist file exists in current directory): Execute(Configuration dist file exists in current directory):
@ -75,7 +75,7 @@ Execute(Configuration file exists in current directory, but force phpstan level)
AssertLinter 'phpstan', [ AssertLinter 'phpstan', [
\ ale#Escape('phpstan') . ' --version', \ ale#Escape('phpstan') . ' --version',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -l ' . ale#Escape('7') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json -l ' . ale#Escape('7') . ' %s'
\ ] \ ]
Execute(Configuration file exists in current directory, but force phpstan configuration): Execute(Configuration file exists in current directory, but force phpstan configuration):
@ -85,11 +85,11 @@ Execute(Configuration file exists in current directory, but force phpstan config
AssertLinter 'phpstan', [ AssertLinter 'phpstan', [
\ ale#Escape('phpstan') . ' --version', \ ale#Escape('phpstan') . ' --version',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -c ' . ale#Escape('phpstan.custom.neon') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json -c ' . ale#Escape('phpstan.custom.neon') . ' %s'
\ ] \ ]
Execute(Autoload parameter is added to the command): Execute(Autoload parameter is added to the command):
let g:ale_php_phpstan_autoload = 'autoload.php' let g:ale_php_phpstan_autoload = 'autoload.php'
AssertLinter 'phpstan', AssertLinter 'phpstan',
\ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat raw -a ' . ale#Escape('autoload.php') . ' -l ' . ale#Escape('4') . ' %s' \ ale#Escape('phpstan') . ' analyze --no-progress --errorFormat json -a ' . ale#Escape('autoload.php') . ' -l ' . ale#Escape('4') . ' %s'