File Coverage

blib/lib/Perl/Critic/Policy/TooMuchCode/ProhibitDuplicateLiteral.pm
Criterion Covered Total %
statement 52 53 98.1
branch 17 18 94.4
condition 2 3 66.6
subroutine 10 11 90.9
pod 3 4 75.0
total 84 89 94.3


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::TooMuchCode::ProhibitDuplicateLiteral;
2 4     4   2687 use strict;
  4         11  
  4         122  
3 4     4   22 use warnings;
  4         10  
  4         135  
4 4     4   21 use List::Util 1.33 qw(any);
  4         102  
  4         349  
5 4     4   51 use Perl::Critic::Utils;
  4         38  
  4         60  
6 4     4   5790 use PPI;
  4         163806  
  4         183  
7 4     4   31 use parent 'Perl::Critic::Policy';
  4         21  
  4         37  
8              
9 0     0 1 0 sub default_themes { return qw( bugs maintenance ) }
10 13     13 1 119563 sub applies_to { return 'PPI::Document' }
11              
12             sub supported_parameters {
13             return ({
14 16     16 0 1301011 name => 'allowlist',
15             description => 'A list of numbers or quoted strings that can be allowed to occur multiple times.',
16             default_string => "0 1",
17             behavior => 'string',
18             parser => \&_parse_allowlist,
19             });
20             }
21              
22             sub _parse_allowlist {
23 16     16   17260 my ($self, $param, $value) = @_;
24 16         58 my $default = $param->get_default_string();
25              
26 16         90 my %allowlist;
27 16         43 for my $v (grep { defined } ($default, $value)) {
  32         104  
28 22         1813 my $parser = PPI::Document->new(\$v);
29 22 100       18711 for my $token (@{$parser->find('PPI::Token::Number') ||[]}) {
  22         103  
30 35         5558 $allowlist{ $token->content } = 1;
31             }
32 22 100       792 for my $token (@{$parser->find('PPI::Token::Quote') ||[]}) {
  22         61  
33 5         820 $allowlist{ $token->string } = 1;
34             }
35             }
36 16         3807 $self->{_allowlist} = \%allowlist;
37 16         60 return undef;
38             }
39              
40             sub violates {
41 13     13 1 139 my ($self, undef, $doc) = @_;
42 13         25 my %firstSeen;
43             my @violations;
44              
45 13 100       22 for my $el (@{ $doc->find('PPI::Token::Quote') ||[]}) {
  13         40  
46 22 50 66     395 next if $el->can("interpolations") && $el->interpolations();
47              
48 22         216 my $val = $el->string;
49 22 100       158 next if $self->{"_allowlist"}{$val};
50              
51 12 100       53 if ($firstSeen{"$val"}) {
52 3         20 push @violations, $self->violation(
53             "A duplicate literal: '$el->string'",
54             "Another literal value in the same piece of code.",
55             $el,
56             );
57             } else {
58 9         37 $firstSeen{"$val"} = $el->location;
59             }
60             }
61              
62 13 100       780 for my $el (@{ $doc->find('PPI::Token::Number') ||[]}) {
  13         40  
63 21         182 my $val = $el->content;
64 21 100       127 next if $self->{"_allowlist"}{$val};
65 10 100       26 if ($firstSeen{$val}) {
66 4         23 push @violations, $self->violation(
67             "A duplicate literal: $el->content",
68             "Another literal value in the same piece of code.",
69             $el,
70             );
71             } else {
72 6         25 $firstSeen{$val} = $el->location;
73             }
74             }
75              
76 13         880 return @violations;
77             }
78              
79             1;
80              
81             __END__
82              
83             =head1 NAME
84              
85             TooMuchCode::ProhibitDuplicateLiteral - Don't repeat yourself with identical literals
86              
87             =head1 DESCRIPTION
88              
89             This policy checks if there are string/number literals with identical
90             value in the same piece of perl code. Usually that's a small signal of
91             repeating and perhaps a small chance of refactoring.
92              
93             =head1 CONFIGURATION
94              
95             Some strings/numbers may be allowed to have duplicates by listing them
96             in the C<allowlist> parameter in the configs:
97              
98             [TooMuchCode::ProhibitDuplicateLiteral]
99             allowlist = 'present' "forty two" 42
100              
101             The values is a space-separated list of numbers or quoted string.
102              
103             The default values in the allowlist are: C<0 1>. These two numbers are
104             always part of allowlist and cannot be removed.
105              
106             Please be aware that, a string literal and its numerical literal
107             counterpart (C<1> vs C<"1">) are considered to be the same. Adding
108             C<"42"> to the allowlist is the same as adding C<42>.
109              
110             =cut