File Coverage

blib/lib/Perl/Critic/Policy/Variables/ProhibitNumericNamesWithLeadingZero.pm
Criterion Covered Total %
statement 88 89 98.8
branch 22 34 64.7
condition n/a
subroutine 20 21 95.2
pod 4 5 80.0
total 134 149 89.9


line stmt bran cond sub pod time code
1              
2             use 5.006001;
3 2     2   435071 use strict;
  2         14  
4 2     2   13 use warnings;
  2         4  
  2         48  
5 2     2   10  
  2         4  
  2         55  
6             use English qw{ -no_match_vars };
7 2     2   11  
  2         5  
  2         14  
8             use PPIx::QuoteLike 0.011;
9 2     2   1739 use PPIx::QuoteLike::Constant 0.011 qw{
  2         281984  
  2         78  
10 2         116 LOCATION_LINE
11             LOCATION_LOGICAL_LINE
12             LOCATION_CHARACTER
13             };
14 2     2   19  
  2         27  
15             use PPIx::QuoteLike 0.011;
16 2     2   13 use PPIx::Regexp 0.071;
  2         36  
  2         42  
17 2     2   11 use Readonly;
  2         44  
  2         46  
18 2     2   9 # use Scalar::Util qw{ refaddr };
  2         4  
  2         98  
19              
20             use Perl::Critic::Utils qw< :booleans :characters hashify :severities >;
21 2     2   16  
  2         4  
  2         127  
22             use base 'Perl::Critic::Policy';
23 2     2   613  
  2         4  
  2         1014  
24             our $VERSION = '0.001';
25              
26             #-----------------------------------------------------------------------------
27              
28             Readonly::Scalar my $DESC => q<Numeric variable name %s starts with 0>;
29             Readonly::Scalar my $EXPL =>
30             q<Numeric variable names may not start with 0, except for $0 itself>; ## no critic (RequireInterpolationOfMetachars)
31              
32             Readonly::Scalar my $PACKAGE => '_' . __PACKAGE__;
33              
34             Readonly::Scalar my $LEFT_BRACE => q<{>; # } Seems P::C::U should have
35              
36             Readonly::Hash my %IS_COMMA => hashify( $COMMA, $FATCOMMA );
37             Readonly::Hash my %LOW_PRECEDENCE_BOOLEAN => hashify( qw{ and or xor } );
38              
39             Readonly::Array my @DOUBLE_QUOTISH => qw{
40             PPI::Token::Quote::Double
41             PPI::Token::Quote::Interpolate
42             PPI::Token::QuoteLike::Backtick
43             PPI::Token::QuoteLike::Command
44             PPI::Token::QuoteLike::Readline
45             PPI::Token::HereDoc
46             };
47             Readonly::Array my @REGEXP_ISH => qw{
48             PPI::Token::Regexp::Match
49             PPI::Token::Regexp::Substitute
50             PPI::Token::QuoteLike::Regexp
51             };
52              
53             #-----------------------------------------------------------------------------
54              
55              
56 3     3 0 26765  
57             #-----------------------------------------------------------------------------
58 20     20 1 247  
59 0     0 1 0 # my ( $self, $elem, $document ) = @_;
60 2     2 1 36398 my ( $self, undef, $document ) = @_;
61              
62             return ( map { $_->[0] }
63             sort { $a->[1] <=> $b->[1] || $a->[2] <=> $b->[2] }
64             map { [ $_, $_->line_number(), $_->column_number() ] }
65             $self->_critique_element( $document )
66 2     2 1 47 );
67             }
68 20         37  
69 55 50       117 #-----------------------------------------------------------------------------
70 2         9  
  20         547  
71             my ( $self, $elem ) = @_;
72              
73             my @violations;
74              
75             # Find $0nnn
76             push @violations, $self->_find_bare_violations( $elem );
77              
78 26     26   17966 # Find ${0nnn}
79             push @violations, $self->_find_bracketed_violations( $elem );
80 26         46  
81             # Find "$0nnn" and "${0nnn}"
82             push @violations, $self->_find_string_violations( $elem );
83 26         58  
84             # Find m/$0nnn/ and m/${0nnn}/
85             push @violations, $self->_find_regex_violations( $elem );
86 26         70  
87             return @violations;
88             }
89 26         73  
90             #-----------------------------------------------------------------------------
91              
92 26         82 # Find $0nnn
93             my ( $self, $elem ) = @_;
94 26         204  
95             my @violations;
96              
97             # Find $0nnn
98             foreach my $sym ( @{ $elem->find( 'PPI::Token::Magic' ) || [] } ) {
99             my $name = $sym->symbol();
100             $name =~ m/ \W 0 [0-9]+ \z /smx ## no critic (ProhibitEnumeratedClasses)
101 26     26   46 or next;
102             push @violations, $self->violation(
103 26         41 sprintf( $DESC, $name ),
104             $EXPL,
105             $sym,
106 26 50       43 );
  26         62  
107 27         4282 }
108 27 100       937  
109             return @violations;
110 18         102 }
111              
112             #-----------------------------------------------------------------------------
113              
114             # Find ${0nnn}
115             my ( $self, $elem ) = @_;
116              
117 26         2147 my @violations;
118              
119             foreach my $cast ( @{ $elem->find( 'PPI::Token::Cast' ) || [] }) {
120             my $block = $cast->snext_sibling()
121             or next;
122             $block->isa( 'PPI::Structure::Block' )
123             or next;
124 26     26   51 my @tokens = @{ $block->find( 'PPI::Token' ) || [] }
125             or next;
126 26         44 @tokens > 1
127             and next;
128 26 100       42 $tokens[0]->isa( 'PPI::Token::Number' )
  26         59  
129 3 50       184 or next;
130             $tokens[0]->content() =~ m/ \A 0 [0-9]+ \z /smx ## no critic (ProhibitEnumeratedClasses)
131 3 50       70 or next;
132             push @violations, $self->violation(
133 3 50       6 sprintf( $DESC, join q<>, $cast->content(), $block->content() ),
  3 50       19  
134             $EXPL,
135 3 50       614 $cast,
136             );
137 3 50       11 }
138              
139 3 100       9 return @violations;
140             }
141 2         23  
142             #-----------------------------------------------------------------------------
143              
144             # Find "$0nnn" and "${0nnn}"
145             my ( $self, $elem ) = @_;
146              
147             my @violations;
148 26         3945  
149             foreach my $class ( @DOUBLE_QUOTISH ) {
150             foreach my $ppi_str ( @{ $elem->find( $class ) || [] } ) {
151             my $ppix_str = PPIx::QuoteLike->new( $ppi_str )
152             or next;
153             foreach my $interp (
154             @{ $ppix_str->find( 'PPIx::QuoteLike::Token::Interpolation' ) || [] }
155 26     26   54 ) {
156             # NOTE that policy Variables::ProhibitUnusedVarsStricter
157 26         41 # uses a wrapper for $elem->ppi() because it has to link
158             # the little PPI documents it makes out of strings to
159 26         96 # the parent PPI document. This is enforced by test
160 156 100       18414 # xt/author/require_wrapper.t. This policy has (so far)
  156         393  
161 18 50       532 # no need to link the two documents together. If it
162             # develops the need, copy this test in, fix all
163 18         12992 # violatioms, and be prepared to rewrite calls to
164 18 50       51 # parent() and top().
165             push @violations, $self->_critique_element( $interp->ppi() );
166             }
167             }
168             }
169              
170             return @violations;
171             }
172              
173             #-----------------------------------------------------------------------------
174              
175 18         1137 # Find m/$0nnn/ and m/${0nnn}/
176             my ( $self, $elem ) = @_;
177              
178             my @violations;
179              
180 26         3655 foreach my $class ( @REGEXP_ISH ) {
181             foreach my $ppi_re ( @{ $elem->find( $class ) || [] } ) {
182             my $ppix_re = PPIx::Regexp->new( $ppi_re )
183             or next;
184             foreach my $code (
185             @{ $ppix_re->find( 'PPIx::Regexp::Token::Code' ) || [] }
186             ) {
187 26     26   54 # NOTE see previous note.
188             push @violations, $self->_critique_element( $code->ppi() );
189 26         44 }
190             }
191 26         61 }
192 78 100       7434  
  78         195  
193 6 50       582 return @violations;
194             }
195 6         23930  
196 6 50       17 #-----------------------------------------------------------------------------
197              
198             1;
199 6         989  
200              
201             #-----------------------------------------------------------------------------
202              
203             =pod
204 26         3474  
205             =head1 NAME
206              
207             Perl::Critic::Policy::Variables::ProhibitNumericNamesWithLeadingZero - Don't use numeric variable names with leading zeroes.
208              
209              
210             =head1 AFFILIATION
211              
212             This Policy is stand-alone, and is not part of the core
213             L<Perl::Critic|Perl::Critic>.
214              
215              
216             =head1 DESCRIPTION
217              
218             Numeric variable names with leading zeroes are unsupported by Perl, and
219             can lead to obscure bugs. In particular, they are not (or not
220             straightforwardly) accessible as C<${0nnn}>.
221              
222             Starting with Perl 5.32, these variables represent a syntax error, so
223             this policy is useless with current Perls. On the other hand, it may be
224             useful for those with an older code base, especially if they are
225             preparing to upgrade it.
226              
227             =head1 CONFIGURATION
228              
229             This policy supports no configuration items above and beyond the
230             standard ones.
231              
232             =head1 AUTHOR
233              
234             Thomas R. Wyant, III F<wyant at cpan dot org>
235              
236             =head1 COPYRIGHT
237              
238             Copyright (C) 2022 Thomas R. Wyant, III
239              
240             =head1 LICENSE
241              
242             This program is free software; you can redistribute it and/or modify it
243             under the same terms as Perl 5.10.0. For more details, see the full text
244             of the licenses in the directory LICENSES.
245              
246             This program is distributed in the hope that it will be useful, but
247             without any warranty; without even the implied warranty of
248             merchantability or fitness for a particular purpose.
249              
250             =cut
251              
252             # Local Variables:
253             # mode: cperl
254             # cperl-indent-level: 4
255             # fill-column: 72
256             # indent-tabs-mode: nil
257             # c-indentation-style: bsd
258             # End:
259             # ex: set ts=8 sts=4 sw=4 tw=72 ft=perl expandtab shiftround :