extending perl critic

Download Extending Perl Critic

If you can't read please download the document

Upload: joshuamcadams

Post on 16-Apr-2017

5.883 views

Category:

Technology


0 download

TRANSCRIPT

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Customizing and Extending Perl Critic

Josh McAdams

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

A Quick Review

- What is Perl Critic ... a static source code analyzer for Perl code ... a system of policies that are enforced on your code ... written by Jeffrey Ryan Thalhammer

#!/usr/bin/perl

print Hallo, Denmark\n;

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

A Quick Review

--(0)> perlcritic hello_denmark.pl Code before strictures are enabled at line 3, column 1. See page 429 of PBP. (Severity: 5)

... is the jarring your memory?

Running perlcritic...

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Customizing Perl Critic

- How do you customize Perl Critic ... we saw much of this in the Introduction talk ... you can ... ignore policies ... assign new severities to policies ... group policies into themes ... pass constructor arguments to policies

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Customizing Perl Critic

--(0)> cat ~/.perlcriticrcseverity = 2top = 5exclude = Editor::RequireEmacsFileVariables Miscellanea::RequireRcsKeywords

[ControlStructures::ProhibitPostfixControls]severity = 4allow = if unlesstheme = iffy

Looking into a .perlcriticrc file...

The default severity to reportHow many violations to reportPolicies to ignoreChange the default severity for a policyPass arguments to the policy constructor, in this case telling it to allow trailing ifs and unlessesApply a new theme... but what about extending Perl Critic?

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Extending Perl Critic

- Customizing is out-of-the-box easy, how do I extend Perl Critic? ... extending Perl Critic is as easy as adding new policy modules ... you can write your own, or grab some pre-written
extensions from CPAN

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Surveying The Land

- First, let's see what all policies Perl Critic provides for us ... there are 98 policies that you get in the core distribution ... the policies are all in the Perl::Critic::Policy namespace ... the policies are divided into 16 categories

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Core Policy Categories (1-4)

- BuiltinFunctions ... fun with builtins like ProhibitStringyEval

- ClassHierarchies ... oo rules like ProhibitExplicitISA

- CodeLayout ... picky stuff like RequireTidyCode

- ControlStructures ... control structure rules like ProhibitUnlessBlocks

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Core Policy Categories (5-8)

- Documentation ... POD rules like RequirePodAtEnd

- ErrorHandling ... the singular RequireCarping

- InputOutput ... I/O related like ProhibitTwoArgOpen

- Miscellanea ... random pickiness like RequireRcsKeywords

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Core Policy Categories (9-12)

- Modules ... module rules like RequireEndWithOne

- NamingConventions ... includes MyFavorite ProhibitMixedCaseSubs

- References ... the one and only ProhibitDoubleSigils

- RegularExpressions ... regex goodness like RequireExtendedFormatting

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Core Policy Categories (13-16)

- Subroutines ... subroutine related like RequireFinalReturn

- TestingAndDebugging ... tends to be prama-related things like RequireUseStrict

- ValuesAndExpressions ... rhs rules such as ProhibitEmptyQuotes

- Variables ... variable rules such as ProhibitLocalVars

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Core Policy Categories (the end)

- hey, stop complaining, I could have done all 98 :)

- so what if these polices aren't enough or just don't fit your organization? ... use someone else's extensions!

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::Bangs

- a collection of Perl Critic policies written by Andy Lester- readily available on CPAN- adds the following policies:

... ProhibitCommentedOutCode ... because your not really using it and it's in svn anyway

... ProhibitFlagComments ... don't TODO, JFDI

... ProhibitNoPlan ... plan your tests, test your plan

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::Bangs (cont'd)

- and a few more:

... ProhibitNumberedNames ... because $my_variable2 isn't very creative

... ProhibitRefProtoOrProto ... ref($proto) || $proto for determining class name is typically wrong

... ProhibitVagueNames ... $data, $info, $wtf?

... is that still not enough for you?

The fully qualified name is Perl::Critic::Policy::Bangs::ProhibitNumberedNames

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::More

- then how about more, Chris Dolan's Perl::Critic::More that is:

... CodeLayout::RequireASCII ... no high-bit code characters for you!

... Editor::RequireEmacsFileVariables ... special emacs variables... I'll have to take your word on this one

... Modules::PerlMinimumVersion ... how backwards-compatible are you?

... Modules::RequirePerlVersion ... use 5.8.0

... how about one more set?

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::Lax

- is Perl Critic too strict, the try Ricardo Signes's Perl::Critic::Lax:

... ProhibitEmptyQuotes::ExceptAsFallback ... $got || ''; looks too good not to use

... ProhibitStringyEval::ExceptForRequire ... stringy evals are bad and all, but sometimes you just gotta use them

... RequireEndWithTrueConst ... because sometimes humor is more important

... RequireExplicitPackage::ExceptForPragmata ... because your not always in need of a package, but you can always use warnings and strict

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

CPAN'd Extensions

- we've seen a pretty good mix of Perl Critic extensions that are available on CPAN:

... Perl::Critic::Bangs

... Perl::Critic::More

... Perl::Critic::Lax

... but these won't always do the trick

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Creating Your Own Policies

- Sometimes you just have to warm up the editor and code your own ... how difficult is it?

- It is actually surprisingly easy to create your own policies, you just need to create a module in the Perl::Critic::Policy namespace that has some required subroutines.

... let's see what it takes

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- We'll start by looking at a core Policy that requires that you use the block form of grep

- Here's what it is looking for:

@matches = grep /pattern/, @list; #not ok @matches = grep { /pattern/ } @list; #ok

... real code please!

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- There's a little administrative overhead:

package Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep;

use strict;use warnings;use Perl::Critic::Utils qw{ :severities :classification :ppi };use base 'Perl::Critic::Policy';

our $VERSION = 1.051;

Declare your packageImport some handy utilitiesInherit from the core Policy class

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Some information required by Perl Critic:

my $desc = q{Expression form of "grep"};

my $expl = [ 169 ];

A description of what you policy doesEither a page reference for Perl Best Practices or an explaination of what the offending code is doing and instructions on how to fix it.

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Some information required by Perl Critic:

sub supported_parameters { return() }

sub default_severity { return $SEVERITY_HIGH}

Returns a list of constructor parameters that the module supports, in this case none*Returns a numeric value that indicates the default severity for the module. There are five constants exported from a Perl Critic utility module:

$SEVERITY_HIGHEST = 5 $SEVERITY_HIGH = 4 $SEVERITY_MEDIUM = 3 $SEVERITY_LOW = 2 $SEVERITY_LOWEST = 1

Why these aren't 'brutal' through 'gental' I don't know* See ControlStructures::ProhibitPostfixControls for an example

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Some information required by Perl Critic:

sub default_themes { return qw( core bugs pbp ) }

sub applies_to { return 'PPI::Token::Word' }

Returns a list of default themes that identify this policy; you probably won't be using coreReturns a list of PPI package names that the policy is applied to. Since all of the PPI DOM objects could be potentially analyzed by every Perl Critic policy, this provides a filter that gets passes your policy only the elements that you care about.

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- And now the method that does all of the work:

sub violates { my ( $self, $elem, undef ) = @_;

return if $elem ne 'grep'; return if ! is_function_call($elem);

my $arg = first_arg($elem); return if !$arg; return if $arg->isa('PPI::Structure::Block');

return $self->violation( $desc, $expl, $elem );}

The method is passed the policy object, a PPI document object representing the current element in the code, and the entire PPI document.If a problem is found, the violation method is called with the the policy description, explanation of the issue, and offending PPI object as arguments... I guess you can tweak the description and explanation if you want.

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Let's take a closer look at the meat of the subroutine:

return if $elem ne 'grep';

return if ! is_function_call($elem);

We know we are getting PPI::Token::Words, which are subclasses of PPI::Element, which in turn overrides 'ne' to return the actual content of the element... in this case the actual word. We only want to consider 'grep'is_function_call is one of those handy subroutines exported by Perl::Critic::Utils. In this case, it does the dirty work involved in determining if a given word is a function call based on the PPI::Token::Word's context in the document. We only want to consider grep if it is actually the function call to grep.

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Only a little more to go

my $arg = first_arg($elem);

return if !$arg;

return if $arg->isa('PPI::Structure::Block');

first_arg is another one of those Perl Critic utility methods. In this case, it is plucking the first argument passed to the grep function.If there is no argument, we can't tell if it is null, so assume it's not wrong.Finally we see if the first argument to grep is a block. If it is, then everything is okay.

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Let's look at the code one more time

sub violates { my ( $self, $elem, undef ) = @_;

return if $elem ne 'grep'; return if ! is_function_call($elem);

my $arg = first_arg($elem); return if !$arg; return if $arg->isa('PPI::Structure::Block');

return $self->violation( $desc, $expl, $elem );}

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

BuiltinFunctions::RequireBlockGrep

- Do you see the basic idea behind the code? ... We try to find every excuse for why the code is not in violation ... are you a grep? ... are you a grep function? ... do you have any arguments? ... is your first argument a block? ... and finally, we return a violation ... innocent until proven guilty ... your code doesn't have to flow this way, but it tends to be a good way to approach the problem

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

What Did We Learn

- Building custom policies is pretty easy; it requires: ... a little bit of setup code ... a package name (Perl::Critic::Policy::) ... a description of the policy ... an explanation of the issue ... a default set of themes ... a default severity ... a list of PPI elements to consider ... an optional list of accepted constructor arguments ... a violates method to do all of the work ... but this is typically a small amount of work ... most policies can be implemented in tens of lines of code

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::Utils and PPI

- So what makes policy building tricky? ... working with Perl::Critic::Utils and PPI ... both are easy to use, but it takes some time to remember all of the functionality that they provide

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Perl::Critic::Utils

- Provides 33 importable subroutines for analyzing and working with PPI elements ... some examples: ... is_hash_key ... is_label_pointer ... is_perl_bareword ... is_perl_builtin_with_optional_argument- Provides 21 importable constants ... some examples: ... $COMMA ... $FATCOMMA

... you really just have to RTFM

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

PPI

- Provides 62 types of PDOM objects representing pieces of ... some examples: ... PPI::Document::Fragment ... PPI::Statement::Package ... PPI::Structure::ForLoop ... PPI::Token::Whitespace ... PPI::Token::QuoteLike::Backtick- Any of the PDOM types could be included in your applies_to() list of elements that you want to consider in your policy

... there are so many of these that you really have to RTFM

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

PPI::Element

- Most of the PPI PDOM objects you'll be interested in inherit from PPI::Element, which provides it's own set of utility methods to help you poke around the PDOM passed to your violates() method. ... some examples: ... significant is the element really significant to the code ... next_sibling next element in the PDOM ... snext_sibling same as above, only skips insignificant elements ... previous_token previous PPI::Token element- There are a lot of these that let you walk around the code

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Testing Your Policies

- Testing policies is actually quite easy also... if you peek into the Perl Critic source and borrow code from them that is :)

- All that you have to do is create a Perl::Critic object and your policy object and then just run the policy on a suite of code samples

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Testing Your Policies

- First set use some modules and then set up some tests

use warnings;use strict;use Test::More;use Perl::Critic;

my @to_validate = ( 'grep {}', 'grep { 1 }', 'grip ""', );

my @to_violate = ( 'grep ""', 'grep "1"', );

plan tests => @to_validate + @to_violate;

I like setting up pieces of code that I expect to be valid and pieces that I expect to violate my policyAnd dynamically creating my test plan based on the number of code samples I'm using

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Testing Your Policies

- Then create a Perl::Critic object and an object of your policy

my $c = Perl::Critic->new( -profile => 'NONE' );

my $policy = Perl::Critic::Policy::BuiltinFunctions::RequireBlockGrep->new();

$c->add_policy( -policy => $policy );

Just create the two objects and add your policy as the one-and-only policy

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Testing Your Policies

- And finally apply your policy to the code samples

ok( ( scalar $c->critique( \$_ ) == 0 ), 'policy on valid code' ) for (@to_validate);

ok( ( scalar $c->critique( \$_ ) > 0 ), 'policy on invalid code' ) for (@to_violate);

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Testing Your Policies

- We have tests!

--(0)> prove test.pl test....ok All tests successful.Files=1, Tests=6, 1 wallclock secs ( 0.86 cusr + 0.18 csys = 1.04 CPU)

Customizing and Extending Perl Critic

Nordic Perl Workshop 2007

Questions?

Comments?

Criticisms?