File Coverage

blib/lib/Perl/Critic/Policy/Community/WhileDiamondDefaultAssignment.pm
Criterion Covered Total %
statement 40 41 97.5
branch 17 22 77.2
condition 12 17 70.5
subroutine 10 11 90.9
pod 4 5 80.0
total 83 96 86.4


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Community::WhileDiamondDefaultAssignment;
2              
3 1     1   482 use strict;
  1         2  
  1         29  
4 1     1   5 use warnings;
  1         3  
  1         27  
5              
6 1     1   6 use Perl::Critic::Utils qw(:severities :classification :ppi);
  1         2  
  1         63  
7 1     1   351 use parent 'Perl::Critic::Policy';
  1         2  
  1         7  
8              
9             our $VERSION = 'v1.0.2';
10              
11 1     1   83 use constant DESC => '<>/<<>>/readline/readdir/each result not explicitly assigned in while condition';
  1         2  
  1         77  
12 1     1   7 use constant EXPL => 'When used alone in a while condition, the <>/<<>> operator, readline, readdir, and each functions assign their result to $_, but do not localize it. Assign the result to an explicit lexical variable instead (my $line = <...>, my $dir = readdir ...)';
  1         3  
  1         417  
13              
14 7     7 0 24464 sub supported_parameters { () }
15 32     32 1 342 sub default_severity { $SEVERITY_HIGH }
16 0     0 1 0 sub default_themes { 'community' }
17 7     7 1 147097 sub applies_to { 'PPI::Token::Word' }
18              
19             my %bad_functions = (
20             each => 1,
21             readdir => 1,
22             readline => 1,
23             );
24              
25             sub violates {
26 110     110 1 6923 my ($self, $elem) = @_;
27 110 100 100     214 return () unless $elem eq 'while' or $elem eq 'for';
28            
29 56   50     763 my $next = $elem->snext_sibling || return ();
30            
31             # Detect for (;<>;)
32 56 100       1219 if ($elem eq 'for') {
    50          
33 8 50       91 return () unless $next->isa('PPI::Structure::For');
34 8         54 my @statements = grep { $_->isa('PPI::Statement') } $next->children;
  16         101  
35 8 50       21 return () unless @statements >= 2;
36 8         39 my $middle = $statements[1];
37 8 100 66     19 return $self->violation(DESC, EXPL, $elem) if $middle->schildren
38             and $middle->schild(0)->isa('PPI::Token::QuoteLike::Readline');
39             } elsif ($elem eq 'while') {
40             # while (<>) {} or ... while <>
41 48 100       929 if ($next->isa('PPI::Structure::Condition')) {
42 24         57 $next = $next->schild(0);
43 24 50 33     298 return () unless defined $next and $next->isa('PPI::Statement');
44 24         47 $next = $next->schild(0);
45 24 50       215 return () unless defined $next;
46             }
47            
48 48 100       144 return $self->violation(DESC, EXPL, $elem) if $next->isa('PPI::Token::QuoteLike::Readline');
49 40 100 100     113 if ($next->isa('PPI::Token::Word') and exists $bad_functions{$next} and is_function_call $next) {
      66        
50 20         4586 return $self->violation(DESC, EXPL, $elem);
51             }
52             }
53            
54 24         198 return ();
55             }
56              
57             1;
58              
59             =head1 NAME
60              
61             Perl::Critic::Policy::Community::WhileDiamondDefaultAssignment - Don't use
62             while with implicit assignment to $_
63              
64             =head1 DESCRIPTION
65              
66             The diamond operator C<E<lt>E<gt>> (or C<E<lt>E<lt>E<gt>E<gt>>), and functions
67             C<readline()>, C<readdir()>, and C<each()> are extra magical in a while
68             condition: if it is the only thing in the condition, it will assign its result
69             to C<$_>, but it does not localize C<$_> to the while loop. (Note, this also
70             applies to a C<for (;E<lt>E<gt>;)> construct.) This can unintentionally confuse
71             outer loops that are already using C<$_> to iterate. In addition, using C<$_>
72             at all means that your loop can get confused by other code which does not
73             politely localize its usage of the global variable. To avoid these
74             possibilities, assign the result of the diamond operator or these functions to
75             an explicit lexical variable.
76              
77             while (<$fh>) { ... } # not ok
78             while (<<>>) { ... } # not ok
79             ... while <STDIN>; # not ok
80             for (;<>;) { ... } # not ok
81             while (readline $fh) { ... } # not ok
82             while (readdir $dh) { ... } # not ok
83              
84             while (my $line = <$fh>) { ... } # ok
85             while (my $line = <<>>) { ... } # ok
86             ... while $line = <STDIN>; # ok
87             for (;my $line = <>;) { ... } # ok
88             while (my $line = readline $fh) { ... } # ok
89             while (my $dir = readdir $dh) { ... } # ok
90              
91             =head1 AFFILIATION
92              
93             This policy is part of L<Perl::Critic::Community>.
94              
95             =head1 CONFIGURATION
96              
97             This policy is not configurable except for the standard options.
98              
99             =head1 AUTHOR
100              
101             Dan Book, C<dbook@cpan.org>
102              
103             =head1 COPYRIGHT AND LICENSE
104              
105             Copyright 2015, Dan Book.
106              
107             This library is free software; you may redistribute it and/or modify it under
108             the terms of the Artistic License version 2.0.
109              
110             =head1 SEE ALSO
111              
112             L<Perl::Critic>