File Coverage

blib/lib/Perl/Critic/Policy/Variables/NameReuse.pm
Criterion Covered Total %
statement 43 44 97.7
branch 11 20 55.0
condition 13 24 54.1
subroutine 9 10 90.0
pod 4 5 80.0
total 80 103 77.6


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Variables::NameReuse;
2              
3 1     1   272845 use strict;
  1         9  
  1         31  
4 1     1   5 use warnings;
  1         3  
  1         48  
5              
6             our $VERSION = 'v0.1.0';
7              
8 1     1   6 use Perl::Critic::Utils qw(:severities :classification :ppi);
  1         10  
  1         94  
9 1     1   425 use parent 'Perl::Critic::Policy';
  1         2  
  1         9  
10              
11 1     1   17396 use constant EXPL => 'Using the same name for multiple types of variables can be confusing, e.g. %foo and $foo. Use different names for different variables.';
  1         4  
  1         528  
12              
13 11     11 0 46749 sub supported_parameters { () }
14 19     19 1 2485 sub default_severity { $SEVERITY_LOW }
15 0     0 1 0 sub default_themes { () }
16 11     11 1 74113 sub applies_to { 'PPI::Document' }
17              
18             sub violates {
19 11     11 1 118 my ($self, $elem) = @_;
20            
21 11         22 my @violations;
22             my %seen;
23              
24 11   50     38 my $symbols = $elem->find('PPI::Token::Symbol') || [];
25 11         154 foreach my $symbol (@$symbols) {
26 32 50       124 next if $symbol->isa('PPI::Token::Magic'); # skip magic variables
27 32         112 my $actual = $symbol->symbol;
28 32 50       2076 next if $actual =~ m/::/; # let's not concern ourselves with fully qualified package variables
29 32 50       147 (my $name = $actual) =~ s/^[\$\@\%]// or next;
30 32 50 33     138 next if $name eq 'INC' or $name eq 'ARGV';
31 32   66     165 $seen{$name}{$actual} //= $symbol;
32             }
33              
34 11   100     43 my $indexes = $elem->find('PPI::Token::ArrayIndex') || [];
35 11         159 foreach my $symbol (@$indexes) {
36 3 50       9 next if $symbol =~ m/::/; # let's not concern ourselves with fully qualified package variables
37 3 50       26 (my $name = $symbol) =~ s/^\$\#// or next;
38 3 50       40 next if $name =~ m/^\W$/; # skip magic variables
39 3 50 33     24 next if $name eq 'INC' or $name eq 'ARGV';
40 3         9 my $actual = '@' . $name;
41 3   66     19 $seen{$name}{$actual} //= $symbol;
42             }
43              
44 11         47 foreach my $name (keys %seen) {
45 15         210 my $by_actual = $seen{$name};
46 15 100       56 if (keys %$by_actual > 1) {
47 8         50 my @sorted = sort { (($by_actual->{$a}->logical_line_number // 0) <=> ($by_actual->{$b}->logical_line_number // 0))
48 13 50 50     288 || (($by_actual->{$a}->visual_column_number // 0) <=> ($by_actual->{$b}->visual_column_number // 0)) } keys %$by_actual;
      50        
      50        
      50        
49 8         472 push @violations, $self->violation("Reused variable name '$name' for '$_'", EXPL, $by_actual->{$_}) for @sorted;
50             }
51             }
52            
53 11         1290 return @violations;
54             }
55              
56             1;
57              
58             =head1 NAME
59              
60             Perl::Critic::Policy::Variables::NameReuse - Don't reuse names for different
61             types of variables
62              
63             =head1 SYNOPSIS
64              
65             perlcritic --single-policy=Variables::NameReuse script.pl
66             perlcritic --single-policy=Variables::NameReuse lib/
67              
68             # .perlcriticrc
69             severity = 1
70             only = 1
71             [Variables::NameReuse]
72              
73             =head1 DESCRIPTION
74              
75             This policy checks for the existence of multiple variables with the same name
76             in a file. This can be confusing especially when accessing elements of
77             variables or using L<list or key-value slices|perldata/Slices>. For example,
78             the code could access both C<$foo> and C<$foo[0]> but these actually refer to
79             the unrelated variables C<$foo> and C<@foo>.
80              
81             my $foo = @foo; # not ok
82             my @bar = @bar{'a','b'}; # not ok
83             my $count = @foo; # ok
84             my @values = @bar{'a','b'}; # ok
85              
86             =head1 AFFILIATION
87              
88             This policy has no affiliation.
89              
90             =head1 CONFIGURATION
91              
92             This policy is not configurable except for the standard options.
93              
94             =head1 BUGS
95              
96             Report any issues on the public bugtracker.
97              
98             =head1 AUTHOR
99              
100             Dan Book <dbook@cpan.org>
101              
102             =head1 COPYRIGHT AND LICENSE
103              
104             This software is Copyright (c) 2018 by Dan Book.
105              
106             This is free software, licensed under:
107              
108             The Artistic License 2.0 (GPL Compatible)
109              
110             =head1 SEE ALSO
111              
112             L<Perl::Critic::Policy::Variables::ProhibitReusedNames> - instead prohibits
113             redeclaring the same variable name across different scopes