To use under emacs, simply add the following line to your .emacs, then when you visit a perl file, you can use Ctrl-Return to run perl_checker on this file
(global-set-key [(control return)] (lambda () (interactive) (save-some-buffers 1) (compile (concat "perl_checker --restrict-to-files " (buffer-file-name (current-buffer))))))
To use with vim, use something like:
perl_checker --restrict-to-files scanner.pm > errors.err ; vim -c ':copen 4' -c ':so /usr/share/vim/ftplugin/perl_checker.vim' -qwhere /usr/share/vim/ftplugin/perl_checker.vim is
" Error formats setlocal efm= \%EFile\ \"%f\"\\,\ line\ %l\\,\ characters\ %c-%*\\d:, \%EFile\ \"%f\"\\,\ line\ %l\\,\ character\ %c:%m, \%+EReference\ to\ unbound\ regexp\ name\ %m, \%Eocamlyacc:\ e\ -\ line\ %l\ of\ \"%f\"\\,\ %m, \%Wocamlyacc:\ w\ -\ %m, \%-Zmake%.%#, \%C%m
perl_checker is a checker: it is not a big deal to die horribly on a weird perl expression, it tells the programmer what to write instead. The issue is that perl_checker includes inter-modules analysis, and it implies being able to parse non-perl_checker compliant modules. A solution for this is perl_checker fake modules. No perfect solution though.
| local $xxx ||= $yyy | applying ||= on a new initialized variable is wrong |
| xxx(!my $xxx) | applying not on a new initialized variable is wrong |
| $1 =~ s/xxx/yyy/ | do not modify the result of a match (eg: $1) |
| $xxx[1, 2] | you must give only one argument |
| $xxx[] | you must give one argument |
| my $_x = 'xxx' if $xxx; | replace "my $foo = ... if <cond>" with "my $foo = <cond> && ..." |
| $xxx or my $_x = 'xxx'; | replace "<cond> or my $foo = ..." with "my $foo = !<cond> && ..." |
| '' || 'xxx' | <constant> || ... is the same as ... |
| if ($xxx = '') {} | are you sure you did not mean "==" instead of "="? |
| N("xxx$yyy") | don't use interpolated translated string, use %s or %d instead |
| if ($xxx && $yyy = xxx()) {} | invalid lvalue |
| 1 + 2 >> 3 | missing parentheses (needed for clarity) |
|
$xxx ? $yyy = 1 : $zzz = 2;
| missing parentheses (needed for clarity) invalid lvalue |
| N_("xxx") . 'yyy' | N_("xxx") . "yyy" is dumb since the string "xxx" will never get translated |
| join(@l) | first argument of join() must be a scalar |
| join(',', 'foo') | join('...', $foo) is the same as $foo |
| if_($xxx) | not enough parameters |
| push @l | you must give some arguments to push |
| push $xxx, 1 | push is expecting an array |
| pop $xxx | pop is expecting an array and nothing else |
| my (@l2, $xxx) = @l; | @l2 takes all the arguments, $xxx is undef in any case |
| $bad | undeclared variable $bad |
| { my $a } | unused variable $a |
| my $xxx; yyy($xxx); my $xxx; | redeclared variable $xxx |
| { my $xxx; $xxx = 1 } | variable $xxx assigned, but not read |
| $a | undeclared variable $a |
| use bad; | can't find package bad |
|
use pkg3 ':bad';
bad(); | package pkg3 doesn't export tag :bad unknown function bad |
|
use pkg3 ':missing_fs';
f(); | name &f is not defined in package pkg3 name &f0 is not defined in package pkg3 |
|
use pkg3 'f';
f(); | name &f is not defined in package pkg3 |
|
foreach (%h) {}
| context hash is not compatible with context list foreach with a hash is usually an error |
| map { 'xxx' } %h | a hash is not a valid parameter to function map |
| $xxx = ('yyy', 'zzz') | context tuple(string, string) is not compatible with context scalar |
| @l ||= 'xxx' | "||=" is only useful with a scalar |
| length @l | never use "length @l", it returns the length of the string int(@l) |
| %h . 'yyy' | context hash is not compatible with context string |
|
'xxx' > 'yyy'
| context string is not compatible with context float context string is not compatible with context float |
| 1 cmp 2 | you should use a number operator, not the string operator "cmp" (or replace the number with a string) |
| $xxx == undef | context undef is not compatible with context float |
| my ($xxx) = 1 | context int is not compatible with context tuple(scalar) |
| ($xxx, $yyy) = 1 | context int is not compatible with context tuple(scalar, scalar) |
| ($xxx, $yyy) = (1, 2, 3) | context tuple(int, int, int) is not compatible with context tuple(scalar, scalar) |
| @l eq '3' | context array is not compatible with context string |
| qw(a b) > 2 | context tuple(string, string) is not compatible with context float |
| %h > 0 | context hash is not compatible with context float |
|
%h eq 0
| context hash is not compatible with context string you should use a number operator, not the string operator "eq" (or replace the number with a string) |
| @{$xxx} | @{$xxx} can be written @$xxx |
| $h{"yyy"} | {"yyy"} can be written {yyy} |
| "$xxx" | $xxx is better written without the double quotes |
| $xxx->{yyy}->{zzz} | the arrow "->" is unneeded |
| "xxx\$xxx" | you can replace "xxx\$xxx" with 'xxx$xxx', that way you don't need to escape <$> |
| "xxx\$xxx" | you can replace "xxx\$xxx" with 'xxx$xxx', that way you don't need to escape <$> |
| "xxx\"$xxx" | you can replace "xxx\"xxx" with qq(xxx"xxx), that way you don't need to escape <"> |
| "xxx\"xxx'" | you can replace "xxx\"xxx" with qq(xxx"xxx), that way you don't need to escape <"> |
| /xxx\'xxx/ | you can replace \' with ' |
| /xxx\;xxx/ | you can replace \; with ; |
| /\// | change the delimit character / to get rid of this escape |
| { nop(); } | spurious ";" before closing block |
| +1 | don't use unary + |
| return ($xxx) | unneeded parentheses |
| if (($xxx eq $yyy) || $zzz) {} | unneeded parentheses |
| if (($xxx =~ /yyy/) || $zzz) {} | unneeded parentheses |
| nop() foreach ($xxx, $yyy); | unneeded parentheses |
| ($xxx) ||= 'xxx' | remove the parentheses |
| $o->m0() | remove these unneeded parentheses |
| $o = xxx() if !$o; | "$foo = ... if !$foo" can be written "$foo ||= ..." |
| $o = xxx() unless $o; | "$foo = ... unless $foo" can be written "$foo ||= ..." |
| $o or $o = xxx(); | "$foo or $foo = ..." can be written "$foo ||= ..." |
| $_ =~ s/xxx/yyy/ | "$_ =~ s/regexp/.../" can be written "s/regexp/.../" |
| $xxx =~ /^yyy$/ | "... =~ /^yyy$/" is better written "... eq 'yyy'" |
| /xxx.*/ | you can remove ".*" at the end of your regexp |
| /xxx.*$/ | you can remove ".*$" at the end of your regexp |
| /[^\s]/ | you can replace [^\s] with \S |
| /[^\w]/ | you can replace [^\w] with \W |
| $xxx ? $xxx : $yyy | you can replace "$foo ? $foo : $bar" with "$foo || $bar" |
| my @l = (); | no need to initialize variables, it's done by default |
| $l[$#l] | you can replace $#l with -1 |
| $#l == 0 | $#x == 0 is better written @x == 1 |
| $#l == -1 | $#x == -1 is better written @x == 0 |
| $#l < 0 | change your expression to use @xxx instead of $#xxx |
| $l[@l] = 1 | "$a[@a] = ..." is better written "push @a, ..." |
| xxx(@_) | replace xxx(@_) with &xxx |
| member($xxx, keys %h) | you can replace "member($xxx, keys %yyy)" with "exists $yyy{$xxx}" |
| !($xxx =~ /.../) | !($var =~ /.../) is better written $var !~ /.../ |
| !($xxx == 1) | !($foo == $bar) is better written $foo != $bar |
| !($xxx eq 'foo') | !($foo eq $bar) is better written $foo ne $bar |
| grep { !member($_, qw(a b c)) } @l | you can replace "grep { !member($_, ...) } @l" with "difference2([ @l ], [ ... ])" |
| any { $_ eq 'foo' } @l | you can replace "any { $_ eq ... } @l" with "member(..., @l)" |
|
foreach (@l) {
push @l2, $_ if yyy($_); } | use "push @l2, grep { ... } ..." instead of "foreach (...) { push @l2, $_ if ... }" or sometimes "@l2 = grep { ... } ..." |
|
foreach (@l) {
push @l2, yyy($_); } | use "push @l2, map { ... } ..." instead of "foreach (...) { push @l2, ... }" or sometimes "@l2 = map { ... } ..." |
|
foreach (@l) {
push @l2, yyy($_) if zzz($_); } | use "push @l2, map { ... ? ... : () } ..." instead of "foreach (...) { push @l2, ... if ... }" or sometimes "@l2 = map { ... ? ... : () } ..." or sometimes "@l2 = map { if_(..., ...) } ..." |
|
foreach (@l) {
if (xxx($_)) { $xxx = $_; last; } } | use "$xxx = find { ... } ..." |
| if (grep { xxx() } @l) {} | in boolean context, use "any" instead of "grep" |
| $xxx = grep { xxx() } @l; | you may use "find" instead of "grep" |
|
$xxx ? $yyy : ()
| you may use if_() here beware that the short-circuit semantic of ?: is not kept if you want to keep the short-circuit behaviour, replace () with @{[]} and there will be no warning anymore |
| system(qq(foo "$xxx")) | instead of quoting parameters you should give a list of arguments |
| system("mkdir", $xxx) | you can replace system("mkdir ...") with mkdir(...) |
|
sub xxx { 'yyy' }
| if the function doesn't take any parameters, please use the empty prototype. example "sub foo() { ... }" |
|
sub xxx {
my ($o_xxx, $yyy) = @_; ($o_xxx, $yyy); } | an non-optional argument must not follow an optional argument |
|
sub xxx {
my (@xxx, $yyy) = @_; @xxx, $yyy; } | an array must be the last variable in a prototype |
| bad() | unknown function bad |
|
sub f0() {}
f0('yyy') | too many parameters |
|
sub f2 { my ($x, $_y) = @_; $x }
f2('yyy') | not enough parameters |
| N("xxx %s yyy") | not enough parameters |
| bad->yyy | unknown package bad |
| pkg->bad | unknown method bad starting in package pkg |
| $xxx->bad | unknown method bad |
| $xxx->m1 | not enough parameters |
| $xxx->m0('zzz') | too many parameters |
| $xxx->m0_or_2('zzz') | not enough or too many parameters |
| die; xxx(); | unreachable code |
| exit 1; xxx(); | unreachable code |
|
if ($xxx or $yyy) {}
| value should be dropped context () is not compatible with context bool |
|
if ($xxx and $yyy) {}
| value should be dropped context () is not compatible with context bool |
| $xxx && yyy(); | value is dropped |
| `xxx`; | value is dropped |
| /(.*)/; | value is dropped |
| 'xxx'; | value is dropped |
| 'xxx' if $xxx; | value is dropped |
| map { xxx($_) } @l; | if you don't use the return value, use "foreach" instead of "map" |
|
$xxx = chomp;
| () context not accepted here context () is not compatible with context scalar |
|
$xxx = push @l, 1
| () context not accepted here context () is not compatible with context scalar |
|
sub xxx
{} | you should not have a carriage-return (\n) here |
|
xxx
($xxx); | you should not have a carriage-return (\n) here |
| xxx( $xxx) | you should not have a space here |
| $xxx ++ | you should not have a space here |
| my($_xxx, $_yyy) | you should have a space here |
| xxx ($xxx) | you should not have a space here |
| 'foo'.'bar' | you should have a space here |
|
if ($xxx) {
xxx() } | missing ";" |
|
if ($xxx) {
xxx(); }; | unneeded ";" |
| $xxx <<= 2 | don't use "<<=", use the expanded version instead |
| m@xxx@ | don't use m@...@, replace @ with / ! , or | |
| s:xxx:yyy: | don't use s:...:, replace : with / ! , or | |
| qw/a b c/ | don't use qw/.../, use qw(...) instead |
| qw{a b c} | don't use qw{...}, use qw(...) instead |
| q{xxx} | don't use q{...}, use q(...) instead |
| qq{xxx} | don't use qq{...}, use qq(...) instead |
| qx(xxx) | don't use qx(...), use `...` instead |
| -xxx | don't use -xxx, use '-xxx' instead |
| not $xxx | don't use "not", use "!" instead |
| $1 =~ /xxx/ | do not use the result of a match (eg: $1) to match another pattern |
| $xxx =~ "yyy" | use a regexp, not a string |
| xxx() =~ s/xxx/yyy/ | you can only use s/// on a variable |
| $1 =~ /xxx/ | do not use the result of a match (eg: $1) to match another pattern |
| grep /xxx/, @l | always use "grep" with a block (eg: grep { ... } @list) |
| for (@l) {} | write "foreach" instead of "for" |
| foreach ($xxx = 0; $xxx < 9; $xxx++) {} | write "for" instead of "foreach" |
| foreach $xxx (@l) {} | don't use for without "my"ing the iteration variable |
| foreach ($xxx) {} | you are using the special trick to locally set $_ with a value, for this please use "for" instead of "foreach" |
| unless ($xxx) {} else {} | don't use "else" with "unless" (replace "unless" with "if") |
| unless ($xxx) {} elsif ($yyy) {} | don't use "elsif" with "unless" (replace "unless" with "if") |
| zzz() unless $xxx || $yyy; | don't use "unless" when the condition is complex, use "if" instead |
| $$xxx{yyy} | for complex dereferencing, use "->" |
| wantarray | please use wantarray() instead of wantarray |
| eval | please use "eval $_" instead of "eval" |
| local *F; open F, "foo"; | use a scalar instead of a bareword (eg: occurrences of F with $F) |
| $xxx !~ s/xxx/yyy/ | use =~ instead of !~ and negate the return value |
| pkg::nop $xxx; | use parentheses around argument (otherwise it might cause syntax errors if the package is "require"d and not "use"d |
| new foo $xxx | you must parenthesize parameters: "new Class(...)" instead of "new Class ..." |
| *xxx = *yyy | "*xxx = *yyy" is better written "*xxx = \&yyy" |
|
$_xxx = 1
| variable $_xxx must not be used (variable with name _XXX are reserved for unused variables) |
|
sub f2 { my ($x, $_y) = @_; $x }
f2(@l); # ok f2(xxx()); # bad | not enough parameters |
|
$xxx = <<"EOF";
foo EOF | Don't use <<"MARK", use <<MARK instead |