perl critic in depth

Post on 31-Oct-2014

3.534 Views

Category:

Documents

3 Downloads

Preview:

Click to see full reader

DESCRIPTION

 

TRANSCRIPT

Jeffrey Thalhammer

S o f t w a r e S y s t e m s

jeff@imaginative-software.com

Thursday, September 30, 2010

San Francisco Perl MongersJuly 27, 2010

Some Ways Are Better Than Others

Thursday, September 30, 2010

HaveYou Ever?...

Thursday, September 30, 2010

print ‘Hello, my name is $name\n’;

Thursday, September 30, 2010

eval ‘reqire Some::Module’;

Thursday, September 30, 2010

if(not $foo && $bar or $baz){...}

Thursday, September 30, 2010

if($this == ‘that’){...}

Thursday, September 30, 2010

What Is Perl::Critic?

Thursday, September 30, 2010

What Is Perl::Critic?

• Static Source Code Analyzer

Thursday, September 30, 2010

What Is Perl::Critic?

• Static Source Code Analyzer

• Like “lint” but for Perl code

Thursday, September 30, 2010

What Is Perl::Critic?

• Static Source Code Analyzer

• Like “lint” but for Perl code

• Finds bugs & enforces style

Thursday, September 30, 2010

What Is Perl::Critic?

• Static Source Code Analyzer

• Like “lint” but for Perl code

• Finds bugs & enforces style

• Mostly based on Perl Best Practices

Thursday, September 30, 2010

Genesis

Thursday, September 30, 2010

Genesis

• Large legacy code base (~500k lines)

Thursday, September 30, 2010

Genesis

• Large legacy code base (~500k lines)

• Evolved organically over 10 years

Thursday, September 30, 2010

Genesis

• Large legacy code base (~500k lines)

• Evolved organically over 10 years

• Many developers with various skill levels

Thursday, September 30, 2010

Genesis

• Large legacy code base (~500k lines)

• Evolved organically over 10 years

• Many developers with various skill levels

• “Worst code I’ve ever seen.”

Thursday, September 30, 2010

Genesis

• Large legacy code base (~500k lines)

• Evolved organically over 10 years

• Many developers with various skill levels

• “Worst code I’ve ever seen.”

• “Most profitable code I’ve ever seen.”

Thursday, September 30, 2010

Genesis

Thursday, September 30, 2010

Genesis

• Highly inconsistent (anti-patterns)

Thursday, September 30, 2010

Genesis

• Highly inconsistent (anti-patterns)

• Extremely complicated code

Thursday, September 30, 2010

Genesis

• Highly inconsistent (anti-patterns)

• Extremely complicated code

• Much copy & pasted code

Thursday, September 30, 2010

Genesis

• Highly inconsistent (anti-patterns)

• Extremely complicated code

• Much copy & pasted code

• Cargo cult

Thursday, September 30, 2010

Behold!

Thursday, September 30, 2010

But How Can I Get Others To Learn Perl

Best Practices?

Thursday, September 30, 2010

Introducing PPI

Thursday, September 30, 2010

Introducing PPI

• By Adam Kennedy (a.k.a. alias)

Thursday, September 30, 2010

Introducing PPI

• By Adam Kennedy (a.k.a. alias)

• “Parse Perl Independently”

Thursday, September 30, 2010

Introducing PPI

• By Adam Kennedy (a.k.a. alias)

• “Parse Perl Independently”

• Parse and lex Perl code into DOM

Thursday, September 30, 2010

Introducing PPI

• By Adam Kennedy (a.k.a. alias)

• “Parse Perl Independently”

• Parse and lex Perl code into DOM

• Only perl can parse Perl

Thursday, September 30, 2010

Introducing PPI

• By Adam Kennedy (a.k.a. alias)

• “Parse Perl Independently”

• Parse and lex Perl code into DOM

• Only perl can parse Perl

• PPI comes pretty darn close

Thursday, September 30, 2010

And Perl::Critic Was Born

Thursday, September 30, 2010

Getting Feet Wet

package Foo;

sub showGreeting ($) {my $name = shift;return undef if not $name;open FH, “>out.txt”;print FH ‘Hello $name\n’;

}

File: Bar.pm

12345678

Thursday, September 30, 2010

Getting Feet Wet

[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Package declaration must match filename at line 1, column 1. Correct the filename or package statement. (Severity: 5)Subroutine prototypes used at line 3, column 1. See page 194 of PBP. (Severity: 5)Code before strictures are enabled at line 3, column 1. See page 429 of PBP. (Severity: 5)"return" statement with explicit "undef" at line 5, column 3. See page 199 of PBP. (Severity: 5)Bareword file handle opened at line 6, column 3. See pages 202,204 of PBP. (Severity: 5)Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

USAGE: perlcritic FILENAME

Thursday, September 30, 2010

Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

A Closer Look

Thursday, September 30, 2010

Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

Description

A Closer Look

Thursday, September 30, 2010

Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

Description Location

A Closer Look

Thursday, September 30, 2010

Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

Description Location

PBP Reference

A Closer Look

Thursday, September 30, 2010

Two-argument "open" used at line 6, column 3. See page 207 of PBP. (Severity: 5)

Description Location

PBP Reference Severity

A Closer Look

Thursday, September 30, 2010

package Bar;

use strict;use warnings;

sub showGreeting {my $name = shift;return if not $name;open my $fh, ‘>’, “out.txt”;print $fh ‘Hello $name’;

}

File: Bar.pm

1234567891011

Fixing Violations

Thursday, September 30, 2010

[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Bar.pm source OK

USAGE: perlcritic FILENAME

Try Again

Thursday, September 30, 2010

[jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm Module does not end with "1;" at line 6, column 1. Must end with a recognizable true value. (Severity: 4)Subroutine "showGreeting" does not end with "return" at line 6, column 1. See page 197 of PBP. (Severity: 4)Close filehandles as soon as possible after opening them at line 9, column 1. See page 209 of PBP. (Severity: 4)

USAGE: perlcritic --serverity N FILENAME

Tightening The Screws

Thursday, September 30, 2010

package Bar;

use strict;use warnings;

sub showGreeting {my $name = shift;return if not $name;open my $fh, ‘>’, $name;print $fh ‘Hello $name’;close $fh;return 1;}

1;

123456789101112131415

File: Bar.pm

Thursday, September 30, 2010

One More Time

[jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm Bar.pm source OK

USAGE: perlcritic --serverity N FILENAME

Thursday, September 30, 2010

USAGE: perlcritic --serverity N FILENAME

Thursday, September 30, 2010

USAGE: perlcritic --serverity N FILENAME[jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2)

Thursday, September 30, 2010

USAGE: perlcritic --serverity N FILENAME[jeff@callahan:/Users/jeff]$ perlcritic --severity 2 Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2)

[jeff@callahan:/Users/jeff]$ perlcritic --severity 1 Bar.pm RCS keywords $Id$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)Subroutine "showGreeting" is not all lower case or all upper case at line 6, column 1. See pages 45,46 of PBP. (Severity: 1)Return value of "open" ignored at line 9, column 1. Check the return value of "open" for success. (Severity: 3)Return value of flagged function ignored - open at line 9, column 1. See pages 208,278 of PBP. (Severity: 1)File handle for "print" or "printf" is not braced at line 10, column 1. See page 217 of PBP. (Severity: 1)Return value of flagged function ignored - print at line 10, column 1. See pages 208,278 of PBP. (Severity: 1)String *may* require interpolation at line 10, column 11. See page 51 of PBP. (Severity: 1)Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2)Return value of flagged function ignored - close at line 11, column 1. See pages 208,278 of PBP. (Severity: 1)

Thursday, September 30, 2010

Five Levels Of Severity

Thursday, September 30, 2010

Five Levels Of Severity

• 5 (Highest): Likely bug or widely accepted practice

Thursday, September 30, 2010

Five Levels Of Severity

• 5 (Highest): Likely bug or widely accepted practice

• 1 (Lowest): Purely cosmetic or highly controversial

Thursday, September 30, 2010

Five Levels Of Severity

• 5 (Highest): Likely bug or widely accepted practice

• 1 (Lowest): Purely cosmetic or highly controversial

• Severities are determined by Policy authors

Thursday, September 30, 2010

Five Levels Of Severity

• 5 (Highest): Likely bug or widely accepted practice

• 1 (Lowest): Purely cosmetic or highly controversial

• Severities are determined by Policy authors

• Also have names (brutal, cruel, harsh, stern, gentle)

Thursday, September 30, 2010

Five Levels Of Severity

• 5 (Highest): Likely bug or widely accepted practice

• 1 (Lowest): Purely cosmetic or highly controversial

• Severities are determined by Policy authors

• Also have names (brutal, cruel, harsh, stern, gentle)

• Can be configured

Thursday, September 30, 2010

Configuration perlcritic [-12345 | --brutal | --cruel | --harsh | --stern | --gentle] [--severity number | name] [{-p | --profile} file | --noprofile] [--top [ number ]] [--theme expression] [--include pattern] [--exclude pattern] [{-s | --single-policy} pattern] [--only | --noonly] [--profile-strictness {warn|fatal|quiet}] [--force | --noforce] [--statistics] [--statistics-only] [--count | -C] [--verbose {number | format}] [--color | --nocolor] [--pager pager] [--quiet] [--color-severity-highest color_specification] [--color-severity-high color_specification] [--color-severity-medium color_specification] [--color-severity-low color_specification] [--color-severity-lowest color_specification] [--files-with-violations | -l] [--files-without-violations | -L] {FILE | DIRECTORY | STDIN}

perlcritic --profile-proto

perlcritic { --list | --list-enabled | --list-themes | --doc pattern [...] }

perlcritic { --help | --options | --man | --version }

Thursday, September 30, 2010

Configuration

Thursday, September 30, 2010

Configuration

• .perlcriticrc file

Thursday, September 30, 2010

Configuration

• .perlcriticrc file

• In home directory or project directory

Thursday, September 30, 2010

Configuration

• .perlcriticrc file

• In home directory or project directory

• INI-style syntax

Thursday, September 30, 2010

# Global options hereseverity = 2pager = /usr/bin/lesscolor = 1

File: ~/.perlcriticrc

These will be the defaults, unless

overridden at the command-line

Configuration

Thursday, September 30, 2010

USAGE: perlcritic FILENAME[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm RCS keywords $Revision$, $HeadURL$, $Date$ not found at line 1, column 1. See page 441 of PBP. (Severity: 2)No "$VERSION" variable found at line 1, column 1. See page 404 of PBP. (Severity: 2)Return value of "close" ignored at line 11, column 1. Check the return value of "close" for success. (Severity: 2)

Default severity is now 2, as defined in .perlcriticrc file

Thursday, September 30, 2010

USAGE: perlcritic --verbose 8 FILENAME

[jeff@callahan:/Users/jeff]$ perlcritic --verbose 8 Bar.pm [Miscellanea::RequireRcsKeywords] RCS keywords $Revision$, $Source$, $Date$ not found at line 1, column 1. (Severity: 2)[Modules::RequireVersionVar] No "$VERSION" variable found at line 1, column 1. (Severity: 2)[InputOutput::RequireCheckedClose] Return value of "close" ignored at line 11, column 1. (Severity: 2)

Now showing name of the Policy

Getting More Information

Thursday, September 30, 2010

# Global options hereseverity = 2verbose = 8

# Policy options here[Modules::RequireVersionVar]severity = 1

# These policies are disabled[-Miscellanea::RequireRcsKeywords][-InputOutput::RequireCheckedClose]

File: ~/.perlcriticrc

Configuration

Thursday, September 30, 2010

[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm Bar.pm source OK

Now running with severity = 2, and

verbose = 8

Thursday, September 30, 2010

Eleven Levels Of Verbosity

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

• Some of the available details:

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

• Some of the available details:

• Message, diagnostic (PBP pages), POD extract

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

• Some of the available details:

• Message, diagnostic (PBP pages), POD extract

• Line, column, file name, file path, severity

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

• Some of the available details:

• Message, diagnostic (PBP pages), POD extract

• Line, column, file name, file path, severity

• Source code, PPI element, policy name

Thursday, September 30, 2010

Eleven Levels Of Verbosity

• Increasing levels of detail

• Can be customized with printf-style formats:“%m near %s at line %l in file %f”

• Some of the available details:

• Message, diagnostic (PBP pages), POD extract

• Line, column, file name, file path, severity

• Source code, PPI element, policy name

• Integrates with editor’s compile-mode (vim, emacs)Thursday, September 30, 2010

Lots of Policies

Thursday, September 30, 2010

Lots of Policies

• 128 Policies in Perl::Critic core

Thursday, September 30, 2010

Lots of Policies

• 128 Policies in Perl::Critic core

• 85 Policies taken directly from PBP

Thursday, September 30, 2010

Lots of Policies

• 128 Policies in Perl::Critic core

• 85 Policies taken directly from PBP

• Dozens of add-on Policies in CPAN

Thursday, September 30, 2010

Lots of Policies

• 128 Policies in Perl::Critic core

• 85 Policies taken directly from PBP

• Dozens of add-on Policies in CPAN

• Covering all aspects of programming

Thursday, September 30, 2010

Just A Taste

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

• NamingConventions::ProhibitAmbiguousNames

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

• NamingConventions::ProhibitAmbiguousNames

• Subroutines::ProhibitExcessComplexity

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

• NamingConventions::ProhibitAmbiguousNames

• Subroutines::ProhibitExcessComplexity

• CodeLayout::RequireTidyCode

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

• NamingConventions::ProhibitAmbiguousNames

• Subroutines::ProhibitExcessComplexity

• CodeLayout::RequireTidyCode

• RegularExpressions::ProhibitFixedStringMatches

Thursday, September 30, 2010

Just A Taste• BuiltinFunctions::ProhibitVoidMap

• ControlStructures::ProhibitUnreachableCode

• NamingConventions::ProhibitAmbiguousNames

• Subroutines::ProhibitExcessComplexity

• CodeLayout::RequireTidyCode

• RegularExpressions::ProhibitFixedStringMatches

• Documentation::RequirePodSections

Thursday, September 30, 2010

Tips For Choosing Policies

Thursday, September 30, 2010

Tips For Choosing Policies• Use --profile-proto option to generate your

first .perlcriticrc file.

Thursday, September 30, 2010

Tips For Choosing Policies• Use --profile-proto option to generate your

first .perlcriticrc file.

• Start with --severity 5 and work your way down.

Thursday, September 30, 2010

Tips For Choosing Policies• Use --profile-proto option to generate your

first .perlcriticrc file.

• Start with --severity 5 and work your way down.

• Use the --doc option to view Policy documentation.

Thursday, September 30, 2010

Tips For Choosing Policies• Use --profile-proto option to generate your

first .perlcriticrc file.

• Start with --severity 5 and work your way down.

• Use the --doc option to view Policy documentation.

• Disable or reconfigure Policies that you don’t agree with.

Thursday, September 30, 2010

Tips For Choosing Policies• Use --profile-proto option to generate your

first .perlcriticrc file.

• Start with --severity 5 and work your way down.

• Use the --doc option to view Policy documentation.

• Disable or reconfigure Policies that you don’t agree with.

• Don’t try to do it all at once.

Thursday, September 30, 2010

#!/usr/bin/perl

print “Hi\n” if $^O eq ‘darwin’;print “Hello\n” if $^O =~ /win/i;

Bending The Rules

Thursday, September 30, 2010

#!/usr/bin/perl

print “Hi\n” if $^O eq ‘darwin’;print “Hello\n” if $^O =~ /win/i;

Bending The Rules

• This would violate ProhibitPostfixControls

Thursday, September 30, 2010

#!/usr/bin/perl

print “Hi\n” if $^O eq ‘darwin’;print “Hello\n” if $^O =~ /win/i;

Bending The Rules

• This would violate ProhibitPostfixControls

• But we really like the way it reads

Thursday, September 30, 2010

#!/usr/bin/perl

## no critic

print “Hi\n” if $^O eq ‘darwin’;print “Hello\n” if $^O =~ /win/i;

## use critic

Bending The Rules

• ## no critic disables Perl::Critic until end of block or until ## use critic

Thursday, September 30, 2010

#!/usr/bin/perl

frobulate($this) if $that; ## no critic

Bending The Rules

• When attached to a line of code ## no critic disables Perl::Critic for that line only

Thursday, September 30, 2010

#!/usr/bin/perl

frobulate($this) if $that; ## no critic (ProhibitPostfixControls)

Bending The Rules

You can also specify exactly which Policies to disable

Thursday, September 30, 2010

Perl::Critic Applied

$> perlcritic --verbose 8 MyModule.pm

#!/usr/bin/perl

use strict;use warnings;use criticism ‘brutal’; ...

As command-line tool...

As pragma...

Thursday, September 30, 2010

Perl::Critic Applied

#!/usr/bin/perl

use Test::Perl::Critic;all_critic_ok();

As test library...

Thursday, September 30, 2010

Tips For Working With Legacy Code

Thursday, September 30, 2010

Tips For Working With Legacy Code

• Use --statistics option to identify hotspots.

Thursday, September 30, 2010

Tips For Working With Legacy Code

• Use --statistics option to identify hotspots.

• Segregate new code from old code.

Thursday, September 30, 2010

Tips For Working With Legacy Code

• Use --statistics option to identify hotspots.

• Segregate new code from old code.

• Use “no critic” annotations around blocks of nasty legacy code.

Thursday, September 30, 2010

Tips For Working With Legacy Code

• Use --statistics option to identify hotspots.

• Segregate new code from old code.

• Use “no critic” annotations around blocks of nasty legacy code.

• Don’t mix clean-up commits with regular work.

Thursday, September 30, 2010

Tips For Working With Legacy Code

• Use --statistics option to identify hotspots.

• Segregate new code from old code.

• Use “no critic” annotations around blocks of nasty legacy code.

• Don’t mix clean-up commits with regular work.

• Use Test::Perl::Critic::Progressive to chip away at the problem.

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

• TAP-compatible test module.

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

• TAP-compatible test module.

• Always passes the first time.

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

• TAP-compatible test module.

• Always passes the first time.

• Makes a record of violations (in a local file).

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

• TAP-compatible test module.

• Always passes the first time.

• Makes a record of violations (in a local file).

• Subsequent runs will fail if new violations are added.

Thursday, September 30, 2010

Test::Perl::Critic::Progressive

• TAP-compatible test module.

• Always passes the first time.

• Makes a record of violations (in a local file).

• Subsequent runs will fail if new violations are added.

• Works best as part of continuous integration system.

Thursday, September 30, 2010

Extending Perl::Critic

Thursday, September 30, 2010

Extending Perl::Critic

• Each Policy is a module in the Perl::Critic::Policy namespace.

Thursday, September 30, 2010

Extending Perl::Critic

• Each Policy is a module in the Perl::Critic::Policy namespace.

• Each Policy extends the Perl::Critic::Policy abstract base class.

Thursday, September 30, 2010

Extending Perl::Critic

• Each Policy is a module in the Perl::Critic::Policy namespace.

• Each Policy extends the Perl::Critic::Policy abstract base class.

• Automatically loaded via Module::Pluggable.

Thursday, September 30, 2010

Extending Perl::Critic

• Each Policy is a module in the Perl::Critic::Policy namespace.

• Each Policy extends the Perl::Critic::Policy abstract base class.

• Automatically loaded via Module::Pluggable.

• Sometimes as little as 20 lines of code.

Thursday, September 30, 2010

Extending Perl::Critic

• Each Policy is a module in the Perl::Critic::Policy namespace.

• Each Policy extends the Perl::Critic::Policy abstract base class.

• Automatically loaded via Module::Pluggable.

• Sometimes as little as 20 lines of code.

• See POD for step-by-step tutorial.

Thursday, September 30, 2010

Extending Perl::Critic

Thursday, September 30, 2010

Extending Perl::Critic

• Policies do not have to be from PBP.

Thursday, September 30, 2010

Extending Perl::Critic

• Policies do not have to be from PBP.

• Your Policies can conflict with existing ones.

Thursday, September 30, 2010

Extending Perl::Critic

• Policies do not have to be from PBP.

• Your Policies can conflict with existing ones.

• Policies can be general or domain-specific.

Thursday, September 30, 2010

if ($foo && ($bar || $baz) || !($bar && $baz) || $qux) {...}

Suppose We Want To Ban Really Complex

Boolean Expressions...

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Put Policy in Perl::Critic::Policy namespace

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Policies organized by PBP table of contents

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Some boilerplate

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Extend the Policy baseclass

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Define default attributes

Thursday, September 30, 2010

package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;

use strict;use warnings;use Readonly;

use Perl::Critic::Utils ‘:severities’;use base ‘Perl::Critic::Policy’;

sub default_severity{ return $SEVERITY_HIGH }sub default_theme{ return qw(complexity) }sub supported_params{ return () }

Thursday, September 30, 2010

sub default_severity{ return $SEVERITY_HIGH };sub default_theme{ return qw(complexity) };sub supported_params{ return () }

Readonly::Scalar my $DESC => 'Too many boolean operators';

Readonly::Scalar my $EXPL => 'Complex expressions are hard to read';

Thursday, September 30, 2010

sub default_severity{ return $SEVERITY_HIGH };sub default_theme{ return qw(complexity) };sub supported_params{ return () }

Readonly::Scalar my $DESC => 'Too many boolean operators';

Readonly::Scalar my $EXPL => 'Complex expressions are hard to read';

Just declaring some variables to store the Description and

Explanation

Thursday, September 30, 2010

sub default_severity{ return $SEVERITY_HIGH };sub default_theme{ return qw(complexity) };sub supported_params{ return () }

Readonly::Scalar my $DESC => 'Too many boolean operators';

Readonly::Scalar my $EXPL => 'Complex expressions are hard to read';

Thursday, September 30, 2010

USAGE: ppidump ‘some source code’USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ( ... ) PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... }

How Does PPI See Your Code?

Thursday, September 30, 2010

USAGE: ppidump ‘some source code’USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ( ... ) PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... }

How Does PPI See Your Code?

This is where we want to look for

violations

Thursday, September 30, 2010

USAGE: ppidump ‘some source code’USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ( ... ) PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... }

How Does PPI See Your Code?

Thursday, September 30, 2010

USAGE: ppidump ‘some source code’USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ( ... ) PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... }

How Does PPI See Your Code?

The offensive code is in a child node

Thursday, September 30, 2010

USAGE: ppidump ‘some source code’USAGE: ppidump STDIN

$> ./tools/ppidump 'if($foo && $bar && $baz){}'PPI::Document PPI::Statement::Compound PPI::Token::Word 'if' PPI::Structure::Condition ( ... ) PPI::Statement::Expression PPI::Token::Symbol '$foo' PPI::Token::Operator '&&' PPI::Token::Symbol '$bar' PPI::Token::Operator '&&' PPI::Token::Symbol '$baz' PPI::Structure::Block { ... }

How Does PPI See Your Code?

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Declare which types of PPI elements we are interested in

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

} As document is traversed, we get called each time a

PPI::Structure::Conditional is encountered

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

} We also get a reference to the entire document

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Your job is to figure out if this $elem is violating the Policy

Thursday, September 30, 2010

sub applies_to { return ‘PPI::Structure::Condition’ }

sub violates {my ( $self, $elem, $doc ) = @_;# meat goes here!

}

Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}

Search recursively for all Operators

Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}

Count those that are boolean

Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}

Using regex to determine type of operator

Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}

Return a violation if count exceeds maximum

Thursday, September 30, 2010

sub violates {my ( $self, $elem, $doc ) = @_;my $ops = $elem->find(‘PPI::Token::Operator’);my $count = grep { _is_boolean($_) } @{$ops}if ($count > 3) {return $self->violation($DESC, $EXPL, $elem )

}}

sub _is_boolean {my ($elem) = @_;return $elem->content()=~ m/^ and | or | not | && | \|\| $/x;

}Thursday, September 30, 2010

Testing Your Policy

Thursday, September 30, 2010

Testing Your Policy

• Use Test::Perl::Critic::Policy.

Thursday, September 30, 2010

Testing Your Policy

• Use Test::Perl::Critic::Policy.

• Specify test cases using POD-like notation.

Thursday, September 30, 2010

Testing Your Policy

• Use Test::Perl::Critic::Policy.

• Specify test cases using POD-like notation.

• Write Perl as Perl, not strings.

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Give test a name

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

How many violations to expect

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Write offending code

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Code region ends when next “## name” is found

Thursday, September 30, 2010

#----------------------------------------## name Within limits## failures 0## cut

if ($foo && $bar) {}while ($foo or $bar || $baz) {}

#----------------------------------------## name Too many operators## failures 2## cut

if ($foo && $bar or not $baz and $quxx) {}while ($foo or $bar || $baz && quxx and not $quip) {}

File: t/ValuesAndExpressions/ProhbitComplexBoolean.run

Thursday, September 30, 2010

Questions?

Thursday, September 30, 2010

Thank You!

Thursday, September 30, 2010

Jeffrey Thalhammer

S o f t w a r e S y s t e m s

jeff@imaginative-software.com

Thursday, September 30, 2010

top related