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   2934 use strict;
  4         10  
  4         125  
3 4     4   22 use warnings;
  4         11  
  4         144  
4 4     4   26 use List::Util 1.33 qw(any);
  4         109  
  4         413  
5 4     4   30 use Perl::Critic::Utils;
  4         13  
  4         65  
6 4     4   5560 use PPI;
  4         159328  
  4         171  
7 4     4   34 use parent 'Perl::Critic::Policy';
  4         11  
  4         29  
8              
9 0     0 1 0 sub default_themes { return qw( bugs maintenance ) }
10 13     13 1 133428 sub applies_to { return 'PPI::Document' }
11              
12             sub supported_parameters {
13             return ({
14 16     16 0 1283611 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   19168 my ($self, $param, $value) = @_;
24 16         77 my $default = $param->get_default_string();
25              
26 16         112 my %allowlist;
27 16         63 for my $v (grep { defined } ($default, $value)) {
  32         118  
28 22         1935 my $parser = PPI::Document->new(\$v);
29 22 100       21898 for my $token (@{$parser->find('PPI::Token::Number') ||[]}) {
  22         119  
30 35         7011 $allowlist{ $token->content } = 1;
31             }
32 22 100       755 for my $token (@{$parser->find('PPI::Token::Quote') ||[]}) {
  22         84  
33 5         857 $allowlist{ $token->string } = 1;
34             }
35             }
36 16         4044 $self->{_allowlist} = \%allowlist;
37 16         79 return undef;
38             }
39              
40             sub violates {
41 13     13 1 202 my ($self, undef, $doc) = @_;
42 13         43 my %firstSeen;
43             my @violations;
44              
45 13 100       30 for my $el (@{ $doc->find('PPI::Token::Quote') ||[]}) {
  13         68  
46 22 50 66     388 next if $el->can("interpolations") && $el->interpolations();
47              
48 22         275 my $val = $el->string;
49 22 100       211 next if $self->{"_allowlist"}{$val};
50              
51 12 100       48 if ($firstSeen{"$val"}) {
52 3         25 push @violations, $self->violation(
53             "A duplicate literal value at line: " . $el->line_number . ", column: " . $el->column_number,
54             "Another literal value in the same piece of code.",
55             $el,
56             );
57             } else {
58 9         47 $firstSeen{"$val"} = $el->location;
59             }
60             }
61              
62 13 100       1042 for my $el (@{ $doc->find('PPI::Token::Number') ||[]}) {
  13         43  
63 21         199 my $val = $el->content;
64 21 100       121 next if $self->{"_allowlist"}{$val};
65 10 100       39 if ($firstSeen{$val}) {
66 4         23 push @violations, $self->violation(
67             "A duplicate literal value at line: " . $el->line_number . ", column: " . $el->column_number,
68             "Another literal value in the same piece of code.",
69             $el,
70             );
71             } else {
72 6         29 $firstSeen{$val} = $el->location;
73             }
74             }
75              
76 13         1197 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