File Coverage

blib/lib/Perl/Critic/Policy/Bangs/ProhibitNumberedNames.pm
Criterion Covered Total %
statement 58 60 96.6
branch 13 14 92.8
condition n/a
subroutine 11 12 91.6
pod 5 6 83.3
total 87 92 94.5


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Bangs::ProhibitNumberedNames;
2              
3 5     5   2708 use strict;
  5         10  
  5         117  
4 5     5   21 use warnings;
  5         10  
  5         93  
5 5     5   23 use Perl::Critic::Utils;
  5         10  
  5         64  
6 5     5   4144 use base 'Perl::Critic::Policy';
  5         46  
  5         2734  
7              
8             our $VERSION = '1.12';
9              
10             sub supported_parameters {
11             return (
12             {
13 17     17 0 55524 name => 'exceptions',
14             description => 'Things to allow in variable and subroutine names.',
15             behavior => 'string list',
16             default_string => 'base64 md5 rc4 sha0 sha1 sha256 utf8 x11 win32',
17             },
18             {
19             name => 'add_exceptions',
20             description => 'Additional things to allow in variable and subroutine names.',
21             behavior => 'string list',
22             },
23             );
24             }
25              
26 8     8 1 88 sub default_severity { return $SEVERITY_MEDIUM }
27 0     0 1 0 sub default_themes { return qw( bangs maintenance ) }
28 12     12 1 54394 sub applies_to { return 'PPI::Statement::Variable', 'PPI::Statement::Sub' }
29              
30             =head1 NAME
31              
32             Perl::Critic::Policy::Bangs::ProhibitNumberedNames - Prohibit variables and subroutines with names that end in digits.
33              
34             =head1 AFFILIATION
35              
36             This Policy is part of the L<Perl::Critic::Bangs> distribution.
37              
38             =head1 DESCRIPTION
39              
40             Similar variables should be meaningfully different. A lazy way to
41             differentiate similar variables is by tacking a number at the end.
42              
43             my $total = $price * $quantity;
44             my $total2 = $total + ($total * $taxrate);
45             my $total3 = $total2 + $shipping;
46              
47             The difference between C<$total> and C<$total3> is not described
48             by the silly "3" at the end. Instead, it should be:
49              
50             my $merch_total = $price * $quantity;
51             my $subtotal = $merch_total + ($merch_total * $taxrate);
52             my $grand_total = $subtotal + $shipping;
53              
54             Both variable and subroutine names are checked.
55              
56             See
57             L<http://www.oreillynet.com/onlamp/blog/2004/03/the_worlds_two_worst_variable.html>
58             for more of my ranting on this.
59              
60             =head1 CONFIGURATION
61              
62             This policy has two options: C<exceptions> and C<add_exceptions>.
63              
64             =head2 C<exceptions>
65              
66             This policy starts with a list of numbered names that are legitimate
67             to have ending with a number:
68              
69             base64 md5 rc4 sha0 sha1 sha256 utf8 x11 win32
70              
71             The exceptions for the policy also apply to names based on the exceptions.
72             If C<$base64> is acceptable as an exception, so is C<$calculated_base64>.
73             The exception must be separated from the left part of the name by at
74             least one underscore to be recognized.
75              
76             The exceptions are case-insensitive. C<$UTF8> and C<$utf8> are both
77             seen the same as far as being exceptions.
78              
79             To replace the list of exceptions, specify a value for the
80             C<exceptions> option.
81              
82             [Bangs::ProhibitNumberedNames]
83             exceptions = logan7 babylon5
84              
85             =head2 C<add_exceptions>
86              
87             To add exceptions to the list, give a value for C<add_exceptions> in
88             your F<.perlcriticrc> file like this:
89              
90             [Bangs::ProhibitNumberedNames]
91             add_exceptions = adam12 route66
92              
93             =cut
94              
95             sub initialize_if_enabled {
96 14     14 1 33134 my ( $self, $config ) = @_;
97              
98             $self->{_exceptions} =
99 14         29 { %{ $self->{_exceptions} }, %{ $self->{_add_exceptions} } };
  14         60  
  14         62  
100              
101 14         55 return $TRUE;
102             }
103              
104             sub _init_exception_regexes {
105 6     6   13 my $self = shift;
106              
107 6         14 my @regexes;
108 6         12 for my $exception ( keys %{$self->{_exceptions}} ) {
  6         34  
109 40         324 push( @regexes, qr/.*_\Q$exception\E$/ );
110             }
111              
112 6         25 $self->{_exception_regexes} = \@regexes;
113              
114 6         12 return;
115             }
116              
117             sub violates {
118 25     25 1 515 my ( $self, $elem, $doc ) = @_;
119              
120 25         44 my @violations;
121              
122 25         48 my $type = ref($elem);
123 25 100       70 if ( $type eq 'PPI::Statement::Variable' ) {
    100          
    50          
124 20         70 for my $symbol ( $elem->symbols ) {
125             # make $basename be the variable name with no sigils or namespaces.
126 22         749 my $fullname = $symbol->canonical;
127 22         274 my $basename = $fullname;
128 22         47 $basename =~ s/.*:://;
129 22         68 $basename =~ s/^[\$@%]//;
130              
131 22         63 push( @violations, $self->_potential_violation( $symbol, $fullname, $basename, 'Variable' ) );
132             }
133             }
134             elsif ( $type eq 'PPI::Statement::Sub' ) {
135 4         17 my $fullname = $elem->name;
136 4         106 my $basename = $fullname;
137 4         11 $basename =~ s/.*:://;
138              
139 4         12 push( @violations, $self->_potential_violation( $elem, $fullname, $basename, 'Subroutine' ) );
140             }
141             elsif ( $type eq 'PPI::Statement::Scheduled' ) {
142             # Ignore BEGIN, INIT, etc
143             }
144             else {
145 0         0 die "Unknown type $type";
146             }
147              
148 25         1509 return @violations;
149             }
150              
151             sub _potential_violation {
152 26     26   45 my $self = shift;
153 26         43 my $symbol = shift;
154 26         44 my $fullname = shift;
155 26         51 my $basename = shift;
156 26         39 my $what = shift;
157              
158 26 100       132 if ( $basename =~ /\D+\d+$/ ) {
159 19         53 $basename = lc $basename;
160              
161             # Check to see if it's an exact match for an exception.
162             # $md5 is excepted by "md5"
163 19 100       64 return if $self->{_exceptions}{$basename};
164              
165             # Check to see if they match the end of the variable regexes.
166             # $foo_md5 is excepted by "md5"
167 10 100       44 $self->_init_exception_regexes unless $self->{_exception_regexes};
168 10         18 for my $re ( @{$self->{_exception_regexes}} ) {
  10         26  
169 52 100       163 return if $basename =~ $re; # We're OK via exception
170             }
171              
172 8         21 my $desc = qq{$what named "$fullname"};
173 8         21 my $expl = "$what names should not be differentiated only by digits";
174 8         31 return $self->violation( $desc, $expl, $symbol );
175             }
176              
177 7         20 return;
178             }
179              
180             1;
181              
182             __END__
183             =head1 AUTHOR
184              
185             Andy Lester C<< <andy at petdance.com> >>
186              
187             =head1 COPYRIGHT
188              
189             Copyright (c) 2006-2013 Andy Lester
190              
191             This library is free software; you can redistribute it and/or modify
192             it under the terms of the Artistic License 2.0.
193              
194             =cut