File Coverage

blib/lib/Perl/Critic/Policy/ValuesAndExpressions/ProhibitMagicNumbers.pm
Criterion Covered Total %
statement 61 165 36.9
branch 8 116 6.9
condition 0 26 0.0
subroutine 15 22 68.1
pod 6 7 85.7
total 90 336 26.7


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers;
2              
3 40     40   28807 use 5.010001;
  40         160  
4 40     40   1430 use strict;
  40         88  
  40         770  
5 40     40   194 use warnings;
  40         102  
  40         2356  
6              
7 40     40   219 use Readonly;
  40         111  
  40         2269  
8              
9 40     40   295 use Perl::Critic::Utils qw{ :booleans :characters :severities :data_conversion };
  40         111  
  40         2224  
10              
11 40     40   14247 use parent 'Perl::Critic::Policy';
  40         103  
  40         252  
12              
13             our $VERSION = '1.150';
14              
15             #----------------------------------------------------------------------------
16              
17             Readonly::Scalar my $EXPL =>
18             q{Unnamed numeric literals make code less maintainable};
19             Readonly::Scalar my $USE_READONLY_OR_CONSTANT =>
20             ' Use the Readonly or Const::Fast module or the "constant" pragma instead';
21             Readonly::Scalar my $TYPE_NOT_ALLOWED_SUFFIX =>
22             ") are not allowed.$USE_READONLY_OR_CONSTANT";
23              
24             Readonly::Scalar my $UNSIGNED_NUMBER =>
25             qr{
26             \d+ (?: [$PERIOD] \d+ )? # 1, 1.5, etc.
27             | [$PERIOD] \d+ # .3, .7, etc.
28             }xms;
29             Readonly::Scalar my $SIGNED_NUMBER => qr/ [-+]? $UNSIGNED_NUMBER /xms;
30              
31             Readonly::Scalar my $RANGE =>
32             qr{
33             \A
34             ($SIGNED_NUMBER)
35             [$PERIOD] [$PERIOD]
36             ($SIGNED_NUMBER)
37             (?:
38             [$COLON] by [$LEFT_PAREN]
39             ($UNSIGNED_NUMBER)
40             [$RIGHT_PAREN]
41             )?
42             \z
43             }xms;
44              
45             Readonly::Scalar my $SPECIAL_ARRAY_SUBSCRIPT_EXEMPTION => -1;
46              
47             #----------------------------------------------------------------------------
48              
49             sub supported_parameters {
50             return (
51             {
52 93     93 0 3204 name => 'allowed_values',
53             description => 'Individual and ranges of values to allow, and/or "all_integers".',
54             default_string => '0 1 2',
55             parser => \&_parse_allowed_values,
56             },
57             {
58             name => 'allowed_types',
59             description => 'Kind of literals to allow.',
60             default_string => 'Float',
61             behavior => 'enumeration',
62             enumeration_values => [ qw{ Binary Exp Float Hex Octal } ],
63             enumeration_allow_multiple_values => 1,
64             },
65             {
66             name => 'allow_to_the_right_of_a_fat_comma',
67             description =>
68             q[Should anything to the right of a "=>" be allowed?],
69             default_string => '1',
70             behavior => 'boolean',
71             },
72             {
73             name => 'constant_creator_subroutines',
74             description => q{Names of subroutines that create constants},
75             behavior => 'string list',
76             list_always_present_values => [
77             qw<
78             Readonly Readonly::Scalar Readonly::Array Readonly::Hash
79             const
80             >,
81             ],
82             },
83             );
84             }
85              
86 74     74 1 331 sub default_severity { return $SEVERITY_LOW }
87 74     74 1 336 sub default_themes { return qw( core maintenance certrec ) }
88 1     1 1 4 sub applies_to { return 'PPI::Token::Number' }
89              
90 5     5 1 37 sub default_maximum_violations_per_document { return 10; }
91              
92             #----------------------------------------------------------------------------
93              
94             sub initialize_if_enabled {
95 24     24 1 94 my ($self, $config) = @_;
96              
97 24         128 $self->_determine_checked_types();
98              
99 24         85 return $TRUE;
100             }
101              
102             sub _parse_allowed_values {
103 91     91   461 my ($self, $parameter, $config_string) = @_;
104              
105 91         445 my ( $all_integers_allowed, $allowed_values )
106             = _determine_allowed_values($config_string);
107              
108 91         257 my $allowed_string = ' is not one of the allowed literal values (';
109 91 50       319 if ($all_integers_allowed) {
110 0         0 $allowed_string .= 'all integers';
111              
112 0 0       0 if ( %{$allowed_values} ) {
  0         0  
113 0         0 $allowed_string .= ', ';
114             }
115             }
116             $allowed_string
117 91         247 .= ( join ', ', sort { $a <=> $b } keys %{$allowed_values} ) . ').'
  251         1233  
  91         862  
118             . $USE_READONLY_OR_CONSTANT;
119              
120 91         335 $self->{_allowed_values} = $allowed_values;
121 91         276 $self->{_all_integers_allowed} = $all_integers_allowed;
122 91         308 $self->{_allowed_string} = $allowed_string;
123              
124 91         293 return;
125             }
126              
127             sub _determine_allowed_values {
128 91     91   291 my ($config_string) = @_;
129              
130 91         264 my @allowed_values;
131             my @potential_allowed_values;
132 91         250 my $all_integers_allowed = 0;
133              
134 91 100       447 if ( defined $config_string ) {
135             my @allowed_values_strings =
136 2         29 grep {$_} split m/\s+/xms, $config_string;
  6         20  
137              
138 2         12 foreach my $value_string (@allowed_values_strings) {
139 4 50       159 if ($value_string eq 'all_integers') {
    50          
    0          
140 0         0 $all_integers_allowed = 1;
141             } elsif ( $value_string =~ m/ \A $SIGNED_NUMBER \z /xms ) {
142 4         25 push @potential_allowed_values, $value_string + 0;
143             } elsif ( $value_string =~ m/$RANGE/xms ) {
144 0         0 my ( $minimum, $maximum, $increment ) = ($1, $2, $3);
145 0   0     0 $increment ||= 1;
146              
147 0         0 $minimum += 0;
148 0         0 $maximum += 0;
149 0         0 $increment += 0;
150              
151 0         0 for ( ## no critic (ProhibitCStyleForLoops)
152             my $value = $minimum;
153             $value <= $maximum;
154             $value += $increment
155             ) {
156 0         0 push @potential_allowed_values, $value;
157             }
158             } else {
159 0         0 die q{Invalid value for allowed_values: }, $value_string,
160             q{. Must be a number, a number range, or},
161             qq{ "all_integers".\n};
162             }
163             }
164              
165 2 50       19 if ($all_integers_allowed) {
166 0         0 @allowed_values = grep { $_ != int $_ } @potential_allowed_values; ## no critic ( BuiltinFunctions::ProhibitUselessTopic )
  0         0  
167             } else {
168 2         13 @allowed_values = @potential_allowed_values;
169             }
170             } else {
171 89         318 @allowed_values = (2);
172             }
173              
174 91 50       338 if ( not $all_integers_allowed ) {
175 91         264 push @allowed_values, 0, 1;
176             }
177 91         414 my %allowed_values = hashify(@allowed_values);
178              
179 91         520 return ( $all_integers_allowed, \%allowed_values );
180             }
181              
182             sub _determine_checked_types {
183 24     24   95 my ($self) = @_;
184              
185 24         269 my %checked_types = (
186             'PPI::Token::Number::Binary' => 'Binary literals (',
187             'PPI::Token::Number::Float' => 'Floating-point literals (',
188             'PPI::Token::Number::Exp' => 'Exponential literals (',
189             'PPI::Token::Number::Hex' => 'Hexadecimal literals (',
190             'PPI::Token::Number::Octal' => 'Octal literals (',
191             'PPI::Token::Number::Version' => 'Version literals (',
192             );
193              
194             # This will be set by the enumeration behavior specified in
195             # supported_parameters() above.
196 24         86 my $allowed_types = $self->{_allowed_types};
197              
198 24         56 foreach my $allowed_type ( keys %{$allowed_types} ) {
  24         141  
199 24         130 delete $checked_types{"PPI::Token::Number::$allowed_type"};
200              
201 24 50       122 if ( $allowed_type eq 'Exp' ) {
202              
203             # because an Exp isa(Float).
204 0         0 delete $checked_types{'PPI::Token::Number::Float'};
205             }
206             }
207              
208 24         92 $self->{_checked_types} = \%checked_types;
209              
210 24         73 return;
211             }
212              
213              
214             sub violates {
215 0     0 1   my ( $self, $elem, undef ) = @_;
216              
217 0 0         if ( $self->{_allow_to_the_right_of_a_fat_comma} ) {
218 0 0         return if _element_is_to_the_right_of_a_fat_comma($elem);
219             }
220              
221 0 0         return if _element_is_in_an_include_readonly_or_version_statement(
222             $self, $elem,
223             );
224 0 0         return if _element_is_in_a_plan_statement($elem);
225 0 0         return if _element_is_in_a_constant_subroutine($elem);
226 0 0         return if _element_is_a_package_statement_version_number($elem);
227              
228 0           my $literal = $elem->literal();
229 0 0 0       if (
      0        
      0        
      0        
      0        
230             defined $literal
231             and not (
232             $self->{_all_integers_allowed}
233             and int $literal == $literal
234             )
235             and not defined $self->{_allowed_values}{$literal}
236             and not (
237             _element_is_sole_component_of_a_subscript($elem)
238             and $literal == $SPECIAL_ARRAY_SUBSCRIPT_EXEMPTION
239             )
240             ) {
241             return
242             $self->violation(
243             $elem->content() . $self->{_allowed_string},
244 0           $EXPL,
245             $elem,
246             );
247             }
248              
249              
250 0           my ( $number_type, $type_string );
251              
252 0           while (
253 0           ( $number_type, $type_string ) = ( each %{ $self->{_checked_types} } )
254             ) {
255 0 0         if ( $elem->isa($number_type) ) {
256             return
257 0           $self->violation(
258             $type_string . $elem->content() . $TYPE_NOT_ALLOWED_SUFFIX,
259             $EXPL,
260             $elem,
261             );
262             }
263             }
264              
265 0           return;
266             }
267              
268             sub _element_is_to_the_right_of_a_fat_comma {
269 0     0     my ($elem) = @_;
270              
271 0 0         my $previous = $elem->sprevious_sibling() or return;
272              
273 0 0         $previous->isa('PPI::Token::Operator') or return;
274              
275 0           return $previous->content() eq q[=>];
276             }
277              
278             sub _element_is_sole_component_of_a_subscript {
279 0     0     my ($elem) = @_;
280              
281 0           my $parent = $elem->parent();
282 0 0 0       if ( $parent and $parent->isa('PPI::Statement::Expression') ) {
283 0 0         if ( $parent->schildren() > 1 ) {
284 0           return 0;
285             }
286              
287 0           my $grandparent = $parent->parent();
288 0 0 0       if (
289             $grandparent
290             and $grandparent->isa('PPI::Structure::Subscript')
291             ) {
292 0           return 1;
293             }
294             }
295              
296 0           return 0;
297             }
298              
299             sub _element_is_in_an_include_readonly_or_version_statement {
300 0     0     my ($self, $elem) = @_;
301              
302 0           my $parent = $elem->parent();
303 0           while ($parent) {
304 0 0         if ( $parent->isa('PPI::Statement') ) {
305 0 0         return 1 if $parent->isa('PPI::Statement::Include');
306              
307 0 0         if ( $parent->isa('PPI::Statement::Variable') ) {
308 0 0         if ( $parent->type() eq 'our' ) {
309 0           my @variables = $parent->variables();
310 0 0 0       if (
311             scalar @variables == 1
312             and $variables[0] eq '$VERSION' ## no critic (RequireInterpolationOfMetachars)
313             ) {
314 0           return 1;
315             }
316             }
317              
318 0           return 0;
319             }
320              
321 0           my $first_token = $parent->first_token();
322 0 0         if ( $first_token->isa('PPI::Token::Word') ) {
    0          
323 0 0         if ( $self->{_constant_creator_subroutines}{
324             $first_token->content() } ) {
325 0           return 1;
326             }
327             } elsif ($parent->isa('PPI::Structure::Block')) {
328 0           return 0;
329             }
330             }
331              
332 0           $parent = $parent->parent();
333             }
334              
335 0           return 0;
336             }
337              
338             # Allow "plan tests => 39;".
339              
340             Readonly::Scalar my $PLAN_STATEMENT_MINIMUM_TOKENS => 4;
341              
342             sub _element_is_in_a_plan_statement {
343 0     0     my ($elem) = @_;
344              
345 0           my $parent = $elem->parent();
346 0 0         return 0 if not $parent;
347              
348 0 0         return 0 if not $parent->isa('PPI::Statement');
349              
350 0           my @children = $parent->schildren();
351 0 0         return 0 if @children < $PLAN_STATEMENT_MINIMUM_TOKENS;
352              
353 0 0         return 0 if not $children[0]->isa('PPI::Token::Word');
354 0 0         return 0 if $children[0]->content() ne 'plan';
355              
356 0 0         return 0 if not $children[1]->isa('PPI::Token::Word');
357 0 0         return 0 if $children[1]->content() ne 'tests';
358              
359 0 0         return 0 if not $children[2]->isa('PPI::Token::Operator');
360 0 0         return 0 if $children[2]->content() ne '=>';
361              
362 0           return 1;
363             }
364              
365             sub _element_is_in_a_constant_subroutine {
366 0     0     my ($elem) = @_;
367              
368 0           my $parent = $elem->parent();
369 0 0         return 0 if not $parent;
370              
371 0 0         return 0 if not $parent->isa('PPI::Statement');
372              
373 0           my $following = $elem->snext_sibling();
374 0 0         if ($following) {
375 0 0         return 0 if not $following->isa('PPI::Token::Structure');
376 0 0         return 0 if $following->content() ne $SCOLON;
377 0 0         return 0 if $following->snext_sibling();
378             }
379              
380 0           my $preceding = $elem->sprevious_sibling();
381 0 0         if ($preceding) {
382 0 0         return 0 if not $preceding->isa('PPI::Token::Word');
383 0 0         return 0 if $preceding->content() ne 'return';
384 0 0         return 0 if $preceding->sprevious_sibling();
385             }
386              
387 0 0         return 0 if $parent->snext_sibling();
388 0 0         return 0 if $parent->sprevious_sibling();
389              
390 0           my $grandparent = $parent->parent();
391 0 0         return 0 if not $grandparent;
392              
393 0 0         return 0 if not $grandparent->isa('PPI::Structure::Block');
394              
395 0           my $greatgrandparent = $grandparent->parent();
396 0 0         return 0 if not $greatgrandparent;
397 0 0         return 0 if not $greatgrandparent->isa('PPI::Statement::Sub');
398              
399 0           return 1;
400             }
401              
402             sub _element_is_a_package_statement_version_number {
403 0     0     my ($elem) = @_;
404              
405 0 0         my $parent = $elem->statement()
406             or return 0;
407              
408 0 0         $parent->isa( 'PPI::Statement::Package' )
409             or return 0;
410              
411 0 0         my $version = $parent->schild( 2 )
412             or return 0;
413              
414 0           return $version == $elem;
415             }
416              
417             1;
418              
419             __END__
420              
421             #----------------------------------------------------------------------------
422              
423             =pod
424              
425             =for stopwords
426              
427             =head1 NAME
428              
429             Perl::Critic::Policy::ValuesAndExpressions::ProhibitMagicNumbers - Don't use values that don't explain themselves.
430              
431              
432             =head1 AFFILIATION
433              
434             This Policy is part of the core L<Perl::Critic|Perl::Critic>
435             distribution.
436              
437              
438             =head1 DESCRIPTION
439              
440             What is a "magic number"? A magic number is a number that appears in
441             code without any explanation; e.g. C<$bank_account_balance *=
442             57.492;>. You look at that number and have to wonder where that
443             number came from. Since you don't understand the significance of the
444             number, you don't understand the code.
445              
446             In general, numeric literals other than C<0> or C<1> in should not be used.
447             Use the L<constant|constant> pragma or the L<Readonly|Readonly> or
448             L<Const::Fast|Const::Fast> modules to give a descriptive name to the number.
449              
450             There are, of course, exceptions to when this rule should be applied.
451             One good example is positioning of objects in some container like
452             shapes on a blueprint or widgets in a user interface. In these cases,
453             the significance of a number can readily be determined by context.
454              
455             The maximum number of violations per document for this policy defaults
456             to 10.
457              
458              
459             =head2 Ways in which this module applies this rule.
460              
461             By default, this rule is relaxed in that C<2> is permitted to allow
462             for common things like alternation, the STDERR file handle, etc.
463              
464             Numeric literals are allowed in C<use> and C<require> statements to
465             allow for things like Perl version restrictions and
466             L<Test::More|Test::More> plans. Declarations of C<$VERSION> package
467             variables are permitted. Use of C<Readonly>, C<Readonly::Scalar>,
468             C<Readonly::Array>, and C<Readonly::Hash> from the
469             L<Readonly|Readonly> module are obviously valid, but use of
470             C<Readonly::Scalar1>, C<Readonly::Array1>, and C<Readonly::Hash1> are
471             specifically not supported.
472              
473             Use of binary, exponential, hexadecimal, octal, and version numbers,
474             even for C<0> and C<1>, outside of C<use>/C<require>/C<Readonly>
475             statements aren't permitted (but you can change this).
476              
477             There is a special exemption for accessing the last element of an
478             array, i.e. C<$x[-1]>.
479              
480              
481             $x = 0; # ok
482             $x = 0.0; # ok
483             $x = 1; # ok
484             $x = 1.0; # ok
485             $x = 1.5; # not ok
486             $x = 0b0 # not ok
487             $x = 0b1 # not ok
488             $x = 0x00 # not ok
489             $x = 0x01 # not ok
490             $x = 000 # not ok
491             $x = 001 # not ok
492             $x = 0e1 # not ok
493             $x = 1e1 # not ok
494              
495             $frobnication_factor = 42; # not ok
496             use constant FROBNICATION_FACTOR => 42; # ok
497              
498              
499             use 5.6.1; # ok
500             use Test::More plan => 57; # ok
501             plan tests => 39; # ok
502             our $VERSION = 0.22; # ok
503              
504              
505             $x = $y[-1] # ok
506             $x = $y[-2] # not ok
507              
508              
509              
510             foreach my $solid (1..5) { # not ok
511             ...
512             }
513              
514              
515             use Readonly;
516              
517             Readonly my $REGULAR_GEOMETRIC_SOLIDS => 5;
518              
519             foreach my $solid (1..$REGULAR_GEOMETRIC_SOLIDS) { #ok
520             ...
521             }
522              
523              
524             =head1 CONFIGURATION
525              
526             This policy has four options: C<allowed_values>, C<allowed_types>,
527             C<allow_to_the_right_of_a_fat_comma>, and C<constant_creator_subroutines>.
528              
529              
530             =head2 C<allowed_values>
531              
532             The C<allowed_values> parameter is a whitespace delimited set of
533             permitted number I<values>; this does not affect the permitted formats
534             for numbers. The defaults are equivalent to having the following in
535             your F<.perlcriticrc>:
536              
537             [ValuesAndExpressions::ProhibitMagicNumbers]
538             allowed_values = 0 1 2
539              
540             Note that this policy forces the values C<0> and C<1> into the
541             permitted values. Thus, specifying no values,
542              
543             allowed_values =
544              
545             is the same as simply listing C<0> and C<1>:
546              
547             allowed_values = 0 1
548              
549             The special C<all_integers> value, not surprisingly, allows all
550             integral values to pass, subject to the restrictions on number types.
551              
552             Ranges can be specified as two (possibly fractional) numbers separated
553             by two periods, optionally suffixed with an increment using the Perl 6
554             C<:by()> syntax. E.g.
555              
556             allowed_values = 7..10
557              
558             will allow 0, 1, 7, 8, 9, and 10 as literal values. Using fractional
559             values like so
560              
561             allowed_values = -3.5..-0.5:by(0.5)
562              
563             will permit -3.5, -3, -2.5, -2, -2.5, -1, -0.5, 0, and 1.
564             Unsurprisingly, the increment defaults to 1, which means that
565              
566             allowed_values = -3.5..-0.5
567              
568             will make -3.5, -2.5, -2.5, -0.5, 0, and 1 valid.
569              
570             Ranges are not lazy, i.e. you'd better have a lot of memory available
571             if you use a range of C<1..1000:by(0.01)>. Also remember that all of
572             this is done using floating-point math, which means that
573             C<1..10:by(0.3333)> is probably not going to be very useful.
574              
575             Specifying an upper limit that is less than the lower limit will
576             result in no values being produced by that range. Negative increments
577             are not permitted.
578              
579             Multiple ranges are permitted.
580              
581             To put this all together, the following is a valid, though not likely
582             to be used, F<.perlcriticrc> entry:
583              
584             [ValuesAndExpressions::ProhibitMagicNumbers]
585             allowed_values = 3.1415269 82..103 -507.4..57.8:by(0.2) all_integers
586              
587              
588             =head2 C<allowed_types>
589              
590             The C<allowed_types> parameter is a whitespace delimited set of
591             subclasses of L<PPI::Token::Number|PPI::Token::Number>.
592              
593             Decimal integers are always allowed. By default, floating-point
594             numbers are also allowed.
595              
596             For example, to allow hexadecimal literals, you could configure this
597             policy like
598              
599             [ValuesAndExpressions::ProhibitMagicNumbers]
600             allowed_types = Hex
601              
602             but without specifying anything for C<allowed_values>, the allowed
603             hexadecimal literals will be C<0x00>, C<0x01>, and C<0x02>. Note,
604             also, as soon as you specify a value for this parameter, you must
605             include C<Float> in the list to continue to be able to use floating
606             point literals. This effect can be used to restrict literals to only
607             decimal integers:
608              
609             [ValuesAndExpressions::ProhibitMagicNumbers]
610             allowed_types =
611              
612             If you permit exponential notation, you automatically also allow
613             floating point values because an exponential is a subclass of
614             floating-point in L<PPI|PPI>.
615              
616              
617             =head2 C<allow_to_the_right_of_a_fat_comma>
618              
619             If this is set, you can put any number to the right of a fat comma.
620              
621             my %hash = ( a => 4512, b => 293 ); # ok
622             my $hash_ref = { a => 4512, b => 293 }; # ok
623             some_subroutine( a => 4512, b => 293 ); # ok
624              
625             Currently, this only means I<directly> to the right of the fat comma. By
626             default, this value is I<true>.
627              
628              
629             =head2 C<constant_creator_subroutines>
630              
631             This parameter allows you to specify the names of subroutines that create
632             constants, in addition to C<Readonly>, C<Const::Fast>, and friends. For
633             example, if you use a custom C<Const::Fast>-like module that supports a
634             C<create_constant> subroutine to create constants, you could add something
635             like the following to your F<.perlcriticrc>:
636              
637             [ValuesAndExpressions::ProhibitMagicNumbers]
638             constant_creator_subroutines = create_constant
639              
640             If you have more than one name to add, separate them by whitespace.
641              
642             The subroutine name should appear exactly as it is in your code. For example,
643             if your code does not import the creating subroutine
644             subroutine, you would need to configure this policy as something like
645              
646             [ValuesAndExpressions::ProhibitMagicNumbers]
647             constant_creator_subroutines = create_constant Constant::Create::create_constant
648              
649              
650             =head1 BUGS
651              
652             There is currently no way to permit version numbers in regular code,
653             even if you include them in the C<allowed_types>. Some may actually
654             consider this a feature.
655              
656              
657             =head1 AUTHOR
658              
659             Elliot Shank C<< <perl@galumph.com> >>
660              
661              
662             =head1 COPYRIGHT
663              
664             Copyright (c) 2006-2011 Elliot Shank.
665              
666             This program is free software; you can redistribute it and/or modify
667             it under the same terms as Perl itself. The full text of this license
668             can be found in the LICENSE file included with this module.
669              
670             =cut
671              
672             # Local Variables:
673             # mode: cperl
674             # cperl-indent-level: 4
675             # fill-column: 78
676             # indent-tabs-mode: nil
677             # c-indentation-style: bsd
678             # End:
679             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :