10. What Is Perl::Critic?
• Static Source Code Analyzer
• Like “lint” but for Perl code
Thursday, September 30, 2010
11. What Is Perl::Critic?
• Static Source Code Analyzer
• Like “lint” but for Perl code
• Finds bugs & enforces style
Thursday, September 30, 2010
12. 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
14. Genesis
• Large legacy code base (~500k lines)
Thursday, September 30, 2010
15. Genesis
• Large legacy code base (~500k lines)
• Evolved organically over 10 years
Thursday, September 30, 2010
16. Genesis
• Large legacy code base (~500k lines)
• Evolved organically over 10 years
• Many developers with various skill levels
Thursday, September 30, 2010
17. 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
18. 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
27. Introducing PPI
• By Adam Kennedy (a.k.a. alias)
Thursday, September 30, 2010
28. Introducing PPI
• By Adam Kennedy (a.k.a. alias)
• “Parse Perl Independently”
Thursday, September 30, 2010
29. Introducing PPI
• By Adam Kennedy (a.k.a. alias)
• “Parse Perl Independently”
• Parse and lex Perl code into DOM
Thursday, September 30, 2010
30. 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
31. 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
33. Getting Feet Wet
File: Bar.pm
1 package Foo;
2
3 sub showGreeting ($) {
4 my $name = shift;
5 return undef if not $name;
6 open FH, “>out.txt”;
7 print FH ‘Hello $namen’;
8 }
Thursday, September 30, 2010
34. Getting Feet Wet
USAGE: perlcritic FILENAME
[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)
Thursday, September 30, 2010
35. A Closer Look
Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)
Thursday, September 30, 2010
36. A Closer Look
Description
Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)
Thursday, September 30, 2010
37. A Closer Look
Description Location
Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)
Thursday, September 30, 2010
38. A Closer Look
Description Location
Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)
PBP
Reference
Thursday, September 30, 2010
39. A Closer Look
Description Location
Two-argument "open" used at line 6, column 3.
See page 207 of PBP. (Severity: 5)
PBP
Severity
Reference
Thursday, September 30, 2010
40. Fixing Violations
File: Bar.pm
1 package Bar;
2
3 use strict;
4 use warnings;
5
6 sub showGreeting {
7 my $name = shift;
8 return if not $name;
9 open my $fh, ‘>’, “out.txt”;
10 print $fh ‘Hello $name’;
11 }
Thursday, September 30, 2010
41. Try Again
USAGE: perlcritic FILENAME
[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
Bar.pm source OK
Thursday, September 30, 2010
42. Tightening The Screws
USAGE: perlcritic --serverity N FILENAME
[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)
Thursday, September 30, 2010
43. File: Bar.pm
1 package Bar;
2
3 use strict;
4 use warnings;
5
6 sub showGreeting {
7 my $name = shift;
8 return if not $name;
9 open my $fh, ‘>’, $name;
10 print $fh ‘Hello $name’;
11 close $fh;
12 return 1;
13 }
14
15 1;
Thursday, September 30, 2010
44. One More Time
USAGE: perlcritic --serverity N FILENAME
[jeff@callahan:/Users/jeff]$ perlcritic --severity 4 Bar.pm
Bar.pm source OK
Thursday, September 30, 2010
46. 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
47. 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)
Thursday, September 30, 2010
49. Five Levels Of Severity
• 5 (Highest): Likely bug or widely accepted practice
Thursday, September 30, 2010
50. Five Levels Of Severity
• 5 (Highest): Likely bug or widely accepted practice
• 1 (Lowest): Purely cosmetic or highly controversial
Thursday, September 30, 2010
51. 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
52. 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
53. 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
56. Configuration
• .perlcriticrc file
Thursday, September 30, 2010
57. Configuration
• .perlcriticrc file
• In home directory or project directory
Thursday, September 30, 2010
58. Configuration
• .perlcriticrc file
• In home directory or project directory
• INI-style syntax
Thursday, September 30, 2010
59. Configuration
File: ~/.perlcriticrc
# Global options here
severity = 2
pager = /usr/bin/less
color = 1
These will be the
defaults, unless
overridden at the
command-line
Thursday, September 30, 2010
60. 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
61. Getting More
Information
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
Thursday, September 30, 2010
62. Configuration
File: ~/.perlcriticrc
# Global options here
severity = 2
verbose = 8
# Policy options here
[Modules::RequireVersionVar]
severity = 1
# These policies are disabled
[-Miscellanea::RequireRcsKeywords]
[-InputOutput::RequireCheckedClose]
Thursday, September 30, 2010
63. Now running with
severity = 2, and
verbose = 8
[jeff@callahan:/Users/jeff]$ perlcritic Bar.pm
Bar.pm source OK
Thursday, September 30, 2010
65. Eleven Levels Of
Verbosity
• Increasing levels of detail
Thursday, September 30, 2010
66. 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
67. 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
68. 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
69. 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
70. 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
71. 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
73. Lots of Policies
• 128 Policies in Perl::Critic core
Thursday, September 30, 2010
74. Lots of Policies
• 128 Policies in Perl::Critic core
• 85 Policies taken directly from PBP
Thursday, September 30, 2010
75. 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
76. 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
78. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
Thursday, September 30, 2010
79. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
Thursday, September 30, 2010
80. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
• NamingConventions::ProhibitAmbiguousNames
Thursday, September 30, 2010
81. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
• NamingConventions::ProhibitAmbiguousNames
• Subroutines::ProhibitExcessComplexity
Thursday, September 30, 2010
82. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
• NamingConventions::ProhibitAmbiguousNames
• Subroutines::ProhibitExcessComplexity
• CodeLayout::RequireTidyCode
Thursday, September 30, 2010
83. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
• NamingConventions::ProhibitAmbiguousNames
• Subroutines::ProhibitExcessComplexity
• CodeLayout::RequireTidyCode
• RegularExpressions::ProhibitFixedStringMatches
Thursday, September 30, 2010
84. Just A Taste
• BuiltinFunctions::ProhibitVoidMap
• ControlStructures::ProhibitUnreachableCode
• NamingConventions::ProhibitAmbiguousNames
• Subroutines::ProhibitExcessComplexity
• CodeLayout::RequireTidyCode
• RegularExpressions::ProhibitFixedStringMatches
• Documentation::RequirePodSections
Thursday, September 30, 2010
86. Tips For Choosing Policies
• Use --profile-proto option to generate your
first .perlcriticrc file.
Thursday, September 30, 2010
87. 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
88. 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
89. 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
90. 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
91. Bending The Rules
#!/usr/bin/perl
print “Hin” if $^O eq ‘darwin’;
print “Hellon” if $^O =~ /win/i;
Thursday, September 30, 2010
92. Bending The Rules
#!/usr/bin/perl
print “Hin” if $^O eq ‘darwin’;
print “Hellon” if $^O =~ /win/i;
• This would violate ProhibitPostfixControls
Thursday, September 30, 2010
93. Bending The Rules
#!/usr/bin/perl
print “Hin” if $^O eq ‘darwin’;
print “Hellon” if $^O =~ /win/i;
• This would violate ProhibitPostfixControls
• But we really like the way it reads
Thursday, September 30, 2010
94. Bending The Rules
#!/usr/bin/perl
## no critic
print “Hin” if $^O eq ‘darwin’;
print “Hellon” if $^O =~ /win/i;
## use critic
• ## no critic disables Perl::Critic until end
of block or until ## use critic
Thursday, September 30, 2010
95. Bending The Rules
#!/usr/bin/perl
frobulate($this) if $that; ## no critic
• When attached to a line of code ## no
critic disables Perl::Critic for that line only
Thursday, September 30, 2010
96. Bending The Rules
#!/usr/bin/perl
frobulate($this) if $that; ## no critic (ProhibitPostfixControls)
You can also specify exactly
which Policies to disable
Thursday, September 30, 2010
97. Perl::Critic Applied
As command-line tool...
$> perlcritic --verbose 8 MyModule.pm
As pragma...
#!/usr/bin/perl
use strict;
use warnings;
use criticism ‘brutal’;
...
Thursday, September 30, 2010
98. Perl::Critic Applied
As test library...
#!/usr/bin/perl
use Test::Perl::Critic;
all_critic_ok();
Thursday, September 30, 2010
100. Tips For Working With
Legacy Code
• Use --statistics option to identify hotspots.
Thursday, September 30, 2010
101. Tips For Working With
Legacy Code
• Use --statistics option to identify hotspots.
• Segregate new code from old code.
Thursday, September 30, 2010
102. 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
103. 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
104. 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
108. 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
109. 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
110. 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
112. Extending Perl::Critic
• Each Policy is a module in the Perl::Critic::Policy
namespace.
Thursday, September 30, 2010
113. 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
114. 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
115. 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
116. 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
118. Extending Perl::Critic
• Policies do not have to be from PBP.
Thursday, September 30, 2010
119. Extending Perl::Critic
• Policies do not have to be from PBP.
• Your Policies can conflict with existing ones.
Thursday, September 30, 2010
120. 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
121. Suppose We Want To
Ban Really Complex
Boolean Expressions...
if ($foo && ($bar || $baz) || !($bar && $baz) || $qux) {
...
}
Thursday, September 30, 2010
123. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;
Put Policy in
use strict;
Perl::Critic::Policy namespace
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
125. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;
Policies organized by
use strict;
PBP table of contents
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
129. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;
use strict;
use warnings;
use Readonly; Extend the Policy baseclass
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
131. package Perl::Critic::Policy::ValuesAndExpressions::ProhibitComplexBoolean;
use strict;
use warnings;
use Readonly;
use Perl::Critic::Utils ‘:severities’;
use base ‘Perl::Critic::Policy’;
Define default attributes
sub default_severity{ return $SEVERITY_HIGH }
sub default_theme{ return qw(complexity) }
sub supported_params{ return () }
Thursday, September 30, 2010
133. 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
134. Just declaring some variables to
store the Description and
Explanation
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
135. 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
136. How Does PPI See Your Code?
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 { ... }
Thursday, September 30, 2010
137. How Does PPI See Your Code?
USAGE: ppidump ‘some source code’
USAGE: ppidump STDIN
$> ./tools/ppidump 'if($foo && $bar && $baz){}'
PPI::Document
PPI::Statement::Compound
PPI::Token::Word 'if' This is where we
PPI::Structure::Condition ... )
(
want to look for
PPI::Statement::Expression
PPI::Token::Symbol '$foo' violations
PPI::Token::Operator '&&'
PPI::Token::Symbol '$bar'
PPI::Token::Operator '&&'
PPI::Token::Symbol '$baz'
PPI::Structure::Block { ... }
Thursday, September 30, 2010
138. How Does PPI See Your Code?
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 { ... }
Thursday, September 30, 2010
139. How Does PPI See Your Code?
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' The offensive code
PPI::Token::Operator '&&' is in a child node
PPI::Token::Symbol '$bar'
PPI::Token::Operator '&&'
PPI::Token::Symbol '$baz'
PPI::Structure::Block { ... }
Thursday, September 30, 2010
140. How Does PPI See Your Code?
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 { ... }
Thursday, September 30, 2010
141. sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
142. Declare which types of PPI
elements we are interested in
sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
143. sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
144. 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
145. sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
146. 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
147. sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
148. 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
149. sub applies_to { return ‘PPI::Structure::Condition’ }
sub violates {
my ( $self, $elem, $doc ) = @_;
# meat goes here!
}
Thursday, September 30, 2010
150. 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
151. 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 )
}
} Search recursively for all
Operators
sub _is_boolean {
my ($elem) = @_;
return $elem->content()
=~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
152. 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
153. 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 )
}
} Count those that are
boolean
sub _is_boolean {
my ($elem) = @_;
return $elem->content()
=~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
154. 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
155. 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 )
}
}
Using regex to determine
sub _is_boolean { type of operator
my ($elem) = @_;
return $elem->content()
=~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
156. 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
157. 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 )
}
} Return a violation if count
exceeds maximum
sub _is_boolean {
my ($elem) = @_;
return $elem->content()
=~ m/^ and | or | not | && | || $/x;
}
Thursday, September 30, 2010
158. 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
160. Testing Your Policy
• Use Test::Perl::Critic::Policy.
Thursday, September 30, 2010
161. Testing Your Policy
• Use Test::Perl::Critic::Policy.
• Specify test cases using POD-like notation.
Thursday, September 30, 2010
162. 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
163. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## 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) {}
Thursday, September 30, 2010
164. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## name Within limits Give test a name
## 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) {}
Thursday, September 30, 2010
165. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## 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) {}
Thursday, September 30, 2010
166. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## name Within limits
## failures 0 How many violations to expect
## 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) {}
Thursday, September 30, 2010
167. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## 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) {}
Thursday, September 30, 2010
168. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## name Within limits
## failures 0
## cut
Write offending code
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) {}
Thursday, September 30, 2010
169. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## 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) {}
Thursday, September 30, 2010
170. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## name Within limits
## failures 0
## cut
if ($foo && $bar) {}
while ($foo or $bar || $baz) {}
#----------------------------------------
## name Too many operators
Code region ends when
## failures 2 next “## name” is found
## cut
if ($foo && $bar or not $baz and $quxx) {}
while ($foo or $bar || $baz && quxx and not $quip) {}
Thursday, September 30, 2010
171. File: t/ValuesAndExpressions/ProhbitComplexBoolean.run
#----------------------------------------
## 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) {}
Thursday, September 30, 2010