File Coverage

blib/lib/Perl/Critic/Policy/TooMuchCode/ProhibitDuplicateLiteral.pm
Criterion Covered Total %
statement 61 62 98.3
branch 20 22 90.9
condition 3 5 60.0
subroutine 11 12 91.6
pod 3 4 75.0
total 98 105 93.3


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::TooMuchCode::ProhibitDuplicateLiteral;
2 4     4   2921 use strict;
  4         14  
  4         116  
3 4     4   23 use warnings;
  4         10  
  4         138  
4 4     4   27 use List::Util 1.33 qw(any);
  4         91  
  4         395  
5 4     4   26 use Perl::Critic::Utils;
  4         14  
  4         57  
6 4     4   5607 use PPI;
  4         158298  
  4         221  
7 4     4   38 use parent 'Perl::Critic::Policy';
  4         12  
  4         34  
8              
9 0     0 1 0 sub default_themes { return qw( bugs maintenance ) }
10 13     13 1 131283 sub applies_to { return 'PPI::Document' }
11              
12             sub supported_parameters {
13             return ({
14 16     16 0 1276415 name => 'whitelist_numbers',
15             description => 'A comma-separated list of numbers that can be allowed to occur multiple times.',
16             default_string => "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -2, -3, -4, -5, -6, -7, -8, -9",
17             behavior => 'string',
18             parser => \&_parse_whitelist_numbers,
19             }, , {
20             name => 'whitelist',
21             description => 'A list of numbers or quoted strings that can be allowed to occur multiple times.',
22             default_string => "0 1",
23             behavior => 'string',
24             parser => \&_parse_whitelist,
25             });
26             }
27              
28             sub _parse_whitelist {
29 16     16   749 my ($self, $param, $value) = @_;
30 16         62 my $default = $param->get_default_string();
31              
32 16         92 my %whitelist;
33 16         58 for my $v (grep { defined } ($default, $value)) {
  32         164  
34 22         1870 my $parser = PPI::Document->new(\$v);
35 22 100       21039 for my $token (@{$parser->find('PPI::Token::Number') ||[]}) {
  22         123  
36 35         6473 $whitelist{ $token->content } = 1;
37             }
38 22 100       789 for my $token (@{$parser->find('PPI::Token::Quote') ||[]}) {
  22         70  
39 5         837 $whitelist{ $token->string } = 1;
40             }
41             }
42 16         3638 $self->{_whitelist} = \%whitelist;
43 16         78 return undef;
44             }
45              
46             sub _parse_whitelist_numbers {
47 16     16   19788 my ($self, $param, $value) = @_;
48 16         69 my $default = $param->get_default_string();
49 16 50 50     191 my %nums = map { $_ => 1 } grep { defined($_) && $_ ne '' } map { split /\s*,\s*/ } ($default, $value //'');
  304         783  
  304         907  
  32         383  
50 16         109 $self->{_whitelist_numbers} = \%nums;
51 16         65 return undef;
52             }
53              
54             sub violates {
55 13     13 1 200 my ($self, undef, $doc) = @_;
56 13         32 my %firstSeen;
57             my @violations;
58              
59 13 100       29 for my $el (@{ $doc->find('PPI::Token::Quote') ||[]}) {
  13         50  
60 22 50 66     395 next if $el->can("interpolations") && $el->interpolations();
61              
62 22         274 my $val = $el->string;
63 22 100       202 next if $self->{"_whitelist"}{$val};
64              
65 12 100       44 if ($firstSeen{"$val"}) {
66 3         26 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 9         67 $firstSeen{"$val"} = $el->location;
73             }
74             }
75              
76 13 100       1011 for my $el (@{ $doc->find('PPI::Token::Number') ||[]}) {
  13         44  
77 21         201 my $val = $el->content;
78 21 100       110 next if $self->{"_whitelist_numbers"}{$val};
79 16 100       48 next if $self->{"_whitelist"}{$val};
80 10 100       30 if ($firstSeen{$val}) {
81 4         21 push @violations, $self->violation(
82             "A duplicate literal value at line: " . $el->line_number . ", column: " . $el->column_number,
83             "Another literal value in the same piece of code.",
84             $el,
85             );
86             } else {
87 6         26 $firstSeen{$val} = $el->location;
88             }
89             }
90              
91 13         1159 return @violations;
92             }
93              
94             1;
95              
96             __END__
97              
98             =head1 NAME
99              
100             TooMuchCode::ProhibitDuplicateLiteral - Don't repeat yourself with identical literals
101              
102             =head1 DESCRIPTION
103              
104             This policy checks if there are string/number literals with identical
105             value in the same piece of perl code. Usually that's a small signal of
106             repeating and perhaps a small chance of refactoring.
107              
108             =head1 CONFIGURATION
109              
110             Some strings/numbers may be allowed to have duplicates by listing them
111             in the C<whitelist> parameter in the configs:
112              
113             [TooMuchCode:ProhibitDuplicateLiteral]
114             whitelist = 'present' "forty two" 42
115              
116             The values is a space-separated list of numbers or quoted string.
117              
118             The default values in the whitelist are: C<0 1>. This two numbers are
119             always part of whitelist and cannot be removed.
120              
121             Please be aware that, a string literal and its numerical literal
122             counterpart (C<1> vs C<"1">) are considered to be the
123             same. Whitelisting C<"42"> would also whitelist C<42> together.
124              
125             =head1 DEPRECATED CONFIGURATIONS
126              
127             The C<whitelist> parameter replace another parameter name C<whitelist_numbers>, which serves the same purpose but only numbers were supported.
128              
129             The default value of whitelist_numbers is 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -2, -3, -4, -5, -6, -7, -8, -9
130              
131             To opt-out more, add C<whitelist_numbers> like this in C<.perlcriticrc>
132              
133             [TooMuchCode::ProhibitDuplicateLiteral]
134             whitelist_numbers = 42, 10
135              
136             The numbers given to C<whitelist_numbers> are appended and there is no
137             way to remove default values.
138              
139             It is still supported in current release but will be removed in near
140             future. Please check the content of C<Changes>for the announcement.
141              
142             =cut