From 7d174b00561f2cb6a6ad71b839b1b6fc6bd88e3b Mon Sep 17 00:00:00 2001 From: Ardis Date: Sun, 16 Jul 2017 16:04:25 +0200 Subject: [PATCH] Added phpstan linter for php. (#772) * Added phpstan linter for php. --- README.md | 2 +- ale_linters/php/phpstan.vim | 40 +++++++ doc/ale-php.txt | 19 ++++ doc/ale.txt | 3 +- test/test_phpstan_executable_detection.vader | 113 +++++++++++++++++++ 5 files changed, 175 insertions(+), 2 deletions(-) create mode 100644 ale_linters/php/phpstan.vim create mode 100644 test/test_phpstan_executable_detection.vader diff --git a/README.md b/README.md index 38ad43e3..e94d70a6 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ name. That seems to be the fairest way to arrange this table. | Objective-C++ | [clang](http://clang.llvm.org/) | | OCaml | [merlin](https://github.com/the-lambda-church/merlin) see `:help ale-integration-ocaml-merlin` for configuration instructions | Perl | [perl -c](https://perl.org/), [perl-critic](https://metacpan.org/pod/Perl::Critic) | -| PHP | [hack](http://hacklang.org/), [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org) | +| PHP | [hack](http://hacklang.org/), [php -l](https://secure.php.net/), [phpcs](https://github.com/squizlabs/PHP_CodeSniffer), [phpmd](https://phpmd.org), [phpstan](https://github.com/phpstan/phpstan) | | Pod | [proselint](http://proselint.com/)| | Pug | [pug-lint](https://github.com/pugjs/pug-lint) | | Puppet | [puppet](https://puppet.com), [puppet-lint](https://puppet-lint.com) | diff --git a/ale_linters/php/phpstan.vim b/ale_linters/php/phpstan.vim new file mode 100644 index 00000000..69173ef4 --- /dev/null +++ b/ale_linters/php/phpstan.vim @@ -0,0 +1,40 @@ +" Author: medains , ardis +" Description: phpstan for PHP files + +" Set to change the ruleset +let g:ale_php_phpstan_executable = get(g:, 'ale_php_phpstan_executable', 'phpstan') +let g:ale_php_phpstan_level = get(g:, 'ale_php_phpstan_level', '4') + +function! ale_linters#php#phpstan#GetCommand(buffer) abort + return ale#Var(a:buffer, 'php_phpstan_executable') + \ . ' analyze -l' + \ . ale#Var(a:buffer, 'php_phpstan_level') + \ . ' --errorFormat raw' + \ . ' %s' +endfunction + +function! ale_linters#php#phpstan#Handle(buffer, lines) abort + " Matches against lines like the following: + " + " filename.php:15:message + " C:\folder\filename.php:15:message + let l:pattern = '^\([a-zA-Z]:\)\?[^:]\+:\(\d\+\):\(.*\)$' + let l:output = [] + + for l:match in ale#util#GetMatches(a:lines, l:pattern) + call add(l:output, { + \ 'lnum': l:match[2] + 0, + \ 'text': l:match[3], + \ 'type': 'W', + \}) + endfor + + return l:output +endfunction + +call ale#linter#Define('php', { +\ 'name': 'phpstan', +\ 'executable': 'phpstan', +\ 'command_callback': 'ale_linters#php#phpstan#GetCommand', +\ 'callback': 'ale_linters#php#phpstan#Handle', +\}) diff --git a/doc/ale-php.txt b/doc/ale-php.txt index 51902ca1..4109673a 100644 --- a/doc/ale-php.txt +++ b/doc/ale-php.txt @@ -43,5 +43,24 @@ g:ale_php_phpmd_ruleset *g:ale_php_phpmd_ruleset* the available phpmd rulesets +------------------------------------------------------------------------------ +phpstan *ale-php-stan* + +g:ale_php_phpstan_executable *g:ale_php_phpstan_executable* + *b:ale_php_phpstan_executable* + Type: |String| + Default: `'phpstan'` + + This variable sets executable used for phpstan. + + +g:ale_php_phpstan_level *g:ale_php_phpstan_level* + *b:ale_php_phpstan_level* + Type: |Number| + Default: `4` + + This variable controls the rule levels. 0 is the loosest and 4 is the + strictest. + =============================================================================== vim:tw=78:ts=2:sts=2:sw=2:ft=help:norl: diff --git a/doc/ale.txt b/doc/ale.txt index 642d3883..883525f9 100644 --- a/doc/ale.txt +++ b/doc/ale.txt @@ -74,6 +74,7 @@ CONTENTS *ale-contents* php...................................|ale-php-options| phpcs...............................|ale-php-phpcs| phpmd...............................|ale-php-phpmd| + phpstan.............................|ale-php-phpstan| pug...................................|ale-pug-options| puglint.............................|ale-pug-puglint| python................................|ale-python-options| @@ -195,7 +196,7 @@ The following languages and tools are supported. * Objective-C++: 'clang' * OCaml: 'merlin' (see |ale-linter-integration-ocaml-merlin|) * Perl: 'perl' (-c flag), 'perlcritic' -* PHP: 'hack', 'php' (-l flag), 'phpcs', 'phpmd' +* PHP: 'hack', 'php' (-l flag), 'phpcs', 'phpmd', 'phpstan' * Pod: 'proselint' * Pug: 'pug-lint' * Puppet: 'puppet', 'puppet-lint' diff --git a/test/test_phpstan_executable_detection.vader b/test/test_phpstan_executable_detection.vader new file mode 100644 index 00000000..24ba8cd8 --- /dev/null +++ b/test/test_phpstan_executable_detection.vader @@ -0,0 +1,113 @@ +Before: + Save g:ale_php_phpstan_executable + Save g:ale_php_phpstan_level + + let g:ale_php_phpstan_executable = 'phpstan_test' + + call ale#test#SetDirectory('/testplugin/test') + + runtime ale_linters/php/phpstan.vim + +After: + Restore + + call ale#test#RestoreDirectory() + call ale#linter#Reset() + +Execute(project with level set to 3): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + let g:ale_php_phpstan_level = 3 + + AssertEqual + \ 'phpstan_test analyze -l3 --errorFormat raw %s', + \ ale_linters#php#phpstan#GetCommand(bufnr('')) + +Execute(project with default level): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + + AssertEqual + \ 'phpstan_test analyze -l4 --errorFormat raw %s', + \ ale_linters#php#phpstan#GetCommand(bufnr('')) + +Execute(parse output without errors): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + + AssertEqual + \ [], + \ ale_linters#php#phpstan#Handle(bufnr(''), [" 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%"]) + +Execute(parse output with one error): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + + AssertEqual + \ [ + \ { + \ 'lnum': 9, + \ 'text': 'Access to an undefined property Test::$var.', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#php#phpstan#Handle(bufnr(''), [ + \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%', + \ 'phpstan-test-files/foo/test.php:9:Access to an undefined property Test::$var.', + \]) + +Execute(parse output with three errors): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + + AssertEqual + \ [ + \ { + \ 'lnum': 9, + \ 'text': 'Call to method format() on an unknown class DateTimeImutable.', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 16, + \ 'text': 'Sample message.', + \ 'type': 'W' + \ }, + \ { + \ 'lnum': 192, + \ 'text': 'Invalid command testCommand.', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#php#phpstan#Handle(bufnr(''), [ + \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%', + \ '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.', + \ 'phpstan-test-files/foo/test.php:192:Invalid command testCommand.', + \]) + +Execute(parse output for windows filesystem): + call ale#test#SetFilename('phpstan-test-files/foo/test.php') + + AssertEqual + \ [ + \ { + \ 'lnum': 9, + \ 'text': 'Access to an undefined property Test::$var.', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#php#phpstan#Handle(bufnr(''), [ + \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%', + \ 'D:\phpstan-test-files\foo\test.php:9:Access to an undefined property Test::$var.', + \]) + +Execute(parse output for not php file): + call ale#test#SetFilename('phpstan-test-files/test.inc') + + AssertEqual + \ [ + \ { + \ 'lnum': 9, + \ 'text': 'Access to an undefined property Test::$var.', + \ 'type': 'W' + \ } + \ ], + \ ale_linters#php#phpstan#Handle(bufnr(''), [ + \ ' 1/1 [▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓] 100%', + \ '/phpstan-test-files/foo/test.inc:9:Access to an undefined property Test::$var.', + \])