[tpm] Checking perl syntax in isolation.

Mike Stok mike at stok.ca
Tue Apr 13 19:02:07 PDT 2010

Recently I was asked to come up with a script to check the syntax of perl programs and modules to give a level of confidence that code checked into a repository didn't have any obvious problems - later building and testing will find other issues.  I'll describe the outlines of what I have done, in case someone else has done it differently or more effectively.

This check was to be done on the source tree as pushed to the repository, so Perl files might not be in the same relative positions as when they are deployed, and no equivalent of a CPAN module's "perl Makefile.PL && make ..." would have been done.  The server running the checks would only have a "standard" perl installation with the usual core modules.  As it is intended to be run over multiple existing projects it would be best if there was no magic per project configuration (e.g. setting up @INC by checking a file in a known location).

The script is run by a shell wrapper which does a find to find all the .pl and .pm files, there's no snooping to try and figure out if a file is a Perl script.

If I can assume that no source filters are allowed, and that the Perl on the server running the checks is "close enough" to the Perl running on the production hosts then the best I was able to come up with was something that can deal with code like this:


use Some::Unlikely::Module 'foo';
use Another::Module ':all';

use strict;
use warnings;

my $x =  foo('bar');
print "Hello\n" if $x     # missing ;
for my $y ( 1 .. 10 ) {

exit 1;

sub :banana ice_cream {
    my ($arg) = @_;

    return $arg ? 'baz' : 'plugh';


The first attempts failed: perl -wc gives up if it can't locate Some/Unlikely/Module.pm, if I remove the ; after the print then perlcritic -5 considers the source OK:

$ perl -wc try.pl
Can't locate Some/Unlikely/Module.pm in @INC (@INC contains: /home/mstok/perl5/lib/perl5/i486-linux-gnu-thread-multi /home/mstok/perl5/lib/perl5 /home/mstok/perl5/lib/perl5/i486-linux-gnu-thread-multi /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl .) at try.pl line 3.
$ perlcritic -5 try.pl 
try.pl source OK
$ bin/check-perl try.pl
syntax error at try.pl line 11, near "$x
for "
try.pl had compilation errors.

What I have ended up doing is using PPI::XS to tokenize the file.  I then look at a the use / require / no directives and keep "use warnings;" and "use vars ... ;", turn "use strict;" into "use strict; no strict 'subs';", and delete all other use / reqires.  That gets rid of issues with uninstalled modules as the use (or require) has been removed.

Then I peel out all subroutine attributes, as they often rely on the effects of a use I have just deleted to be parsed.

Then I write the file out to a temporaray file, and call perl -c on it to check the syntax (or perl -wc if I'm not being lenient), filtering stdout & stderr to replace the temporary file name with the original so as not to confuse the reader of the output.

This seems to pick up most of the trivial errors (the kind where I have tested the code, realize there's some debugging left in it, delete the debugging, and maybe the odd }, check it in, and run out to lunch...).

So has anyone else tried this, and if so how did you end up doing it? Was it successful, or were there better techniques for catching this type of thing?

I'll have to see if I can show the code, but the outline should be enough to give a rough idea of what I was up to.



Mike Stok <mike at stok.ca>

The "`Stok' disclaimers" apply.

More information about the toronto-pm mailing list