Perl::Critic - Automatically review your code

[ Perl tips index ]
[ Subscribe to Perl tips ]

The best way to do things in Perl keeps changing, just as the language and the art of programming does. The code you wrote 10 years ago might have been best practice back then, but chances are it doesn't look too good by modern standards. Package filehandles, obscure special variables, and using select to control buffering are still supported by Perl, but they're not things that should exist in modern code.

Perl::Critic reads your source code and tells you what you're doing wrong. It uses Damian Conway's book Perl Best Practices as a starting point, however it's extensible enough that you can change its policies to enable, disable and customise what it complains about.

Whether you're writing new code, or maintaining old code, Perl::Critic can help you avoid bad practices and even teach you new techniques you may not have known existed.

Levels of severity

Chances are, for any serious code, Perl::Critic is going to find a lot of things to complain about. To make using it easier, there are five levels of severity that you can pick from. These are: gentle, stern, harsh, cruel and brutal.

Gentle turns on only the most important and worrisome errors that Perl::Critic finds. The stern severity notes all the things that Perl::Critic is pretty sure you will want to fix.

The later three levels of severity get more and more picky, down to complaining about your indentation and bracing style, whether you've commented out any code, and the modifiers used on your regular expressions.

It's recommended you start using Perl::Critic with gentle feedback, and proceed to the more picky levels in an incremental manner. You may not agree with all of Perl::Critic's feedback, but we'll see in a moment how we can customise it to suit our needs.

Perl::Critic online

The easiest way to use Perl::Critic is to play with the web interface at http://perlcritic.com/. Here you can upload your Perl file, select a severity level and see what it complains about. This is a great way to play with Perl::Critic but becomes impractical if you have lots of files you want to critique or files containing sensitive information.

perlcritic on the command-line

If you need more power than uploading your script to the web interface, you can install Perl::Critic on from CPAN. Then you can use the command-line tool perlcritic. This is part of the Perl::Critic distribution and can be used as follows:

    perlcritic --gentle myprogram.pl

We can also test all the files in a directory (this time using the stern severity level):

    perlcritic --stern directory/

If you don't want to type full severity levels, you can use numerical shortcuts. The following are equivalent to the full lines above:

    perlcritic -5 myprogram.pl
    perlcritic -4 directory/

By default, perlcritic assumes a level of gentle if no severity level is set.

The perlcritic command generates a colour-coded list of things in your code that it has issue with. Example output (without the colour) might look like:

    Symbols are exported by default at line 40, column 1.
    Use '@EXPORT_OK' or '%EXPORT_TAGS' instead.  (Severity: 4)

    Always unpack @_ first at line 60, column 1.
    See page 178 of PBP.  (Severity: 4)

    Subroutine does not end with "return" at line 60, column 1.
    See page 197 of PBP.  (Severity: 4)

    Expression form of "eval" at line 71, column 3.
    See page 161 of PBP.(Severity : 5)

    Stricture disabled at line 80, column 3.
    See page 429 of PBP.  (Severity: 5)

    Expression form of "eval" at line 106, column 4.
    See page 161 of PBP.  (Severity: 5)

    Mixed high and low-precedence booleans at line 158, column 6.
    See page 70 of PBP.  (Severity: 4)

    Variable declared in conditional statement at line 223, column 2.
    Declare variables outside of the condition.  (Severity: 5)

    "return" statement with explicit "undef" at line 227, column 2.
    See page 199 of PBP.  (Severity: 5)

Understanding the output

Many of the diagnostics perlcritic provides by default are designed to only take up one line, although in our output above we've reformatted them to two lines for clarity. The brief format is to prevent the user being overwhelmed by output when they already know what the errors mean. However, when you're just starting, you'll want additional help understanding what you've done wrong - especially if you don't have a copy of Perl Best Practices to look up for the page references.

perlcritic comes with pre-defined verbosity levels from 1 to 11, with 8 and above providing policy names which can be looked up using perldoc, and 10 and above providing full policy information.

  perlcritic --stern --verbose 10 lib/MyModule.pm

  Symbols are exported by default at line 40, column 1.
    Modules::ProhibitAutomaticExportation (Severity: 4)
      When using Exporter, symbols placed in the `@EXPORT' variable are
      automatically exported into the caller's namespace. Although convenient,
      this practice is not polite, and may cause serious problems if the
      caller declares the same symbols. The best practice is to place your
      symbols in `@EXPORT_OK' or `%EXPORT_TAGS' and let the caller choose
      exactly which symbols to export.

          package Foo;

          use base qw(Exporter);
          our @EXPORT      = qw(&foo &bar);                  # not ok
          our @EXPORT_OK   = qw(&foo &bar);                  # ok
          our %EXPORT_TAGS = ( all => [ qw(&foo &bar) ] );   # ok

You can also configure your own format for these diagnostics.

Excluding and including rules

There will be times when you disagree with the rules Perl::Critic uses. For example, at Perl Training Australia we often write code which uses the constant pragma to allow perl to optimise code away at compile-time. However Perl::Critic complains about this on the more strict severity levels.

It's possible to exclude specific lines from criticism by using a special no critic comment with a double-hash:

    use constant WINDOWS => ($^O eq 'MSWin32');  ## no critic

However it's also possible to tell Perl::Critic at the time of invocation to exclude this rule entirely:

    perlcritic --gentle --verbose 10 --exclude ProhibitConstantPragma myprogram.pl

It's also possible to specifically include rules or groups of rules, even if they're more severe that your chosen severity level. In the following example, we enable all the code layout diagnostics:

    perlcritic --gentle --verbose 10 --exclude ProhibitConstantPragma --include CodeLayout myprogram.pl

Profiles

If you find yourself using too many options on the command-line, rather than aliasing it, put the information into a profile. By default, perlcritic will look for a .perlcriticrc (or _perlcriticrc for Windows) file in your current directory first, and then in your home directory. This allows developers to have per-project configuration, as well as their own personal preferences.

If we added the following to our .perlcriticrc file:

    severity = gentle
    verbose  = 10
    exclude  = ProhibitConstantPragma
    include  = CodeLayout

then we can condense the previous example back to:

    perlcritic myprogram.pl

Perl::Critic and testing

As all good programmers write code with detailed test suites, you will be glad to know that there's a very easy way to use Perl::Critic in your test suite. Just use the Test::Perl::Critic module from the CPAN:

    use Test::Perl::Critic;
    use Test::More tests => 1;
    critic_ok($file);

For CPAN distributions, the recommended usage is a little longer, to ensure that end-users don't have to unnecessarily run what are normally author-only tests. Consult the Test::Perl::Critic documentation for more information.

Progressive testing

Adding coding standards to legacy code can be challenging. Rather than trying to make all of your code Perl::Critic compliant at once, you may find it useful to use Test::Perl::Critic::Progressive. This is designed to prevent further deterioration of your code, and does so by breaking on a test run if there are more Perl::Critic violations than the last successful test. When run as part of your revision control commit tests, this will ensure that the code in your repository can only get better.

    use Test::Perl::Critic::Progressive qw( progressive_critic_ok );
    progressive_critic_ok();

Further reading

[ Perl tips index ]
[ Subscribe to Perl tips ]


This Perl tip and associated text is copyright Perl Training Australia. You may freely distribute this text so long as it is distributed in full with this Copyright noticed attached.

If you have any questions please don't hesitate to contact us:

Email: contact@perltraining.com.au
Phone: 03 9354 6001 (Australia)
International: +61 3 9354 6001

Valid XHTML 1.0 Valid CSS