File Coverage

blib/lib/Perl/Critic/Policy/Freenode/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::Freenode::WhileDiamondDefaultAssignment;
2              
3 1     1   663 use strict;
  1         2  
  1         31  
4 1     1   40 use warnings;
  1         4  
  1         37  
5              
6 1     1   8 use Perl::Critic::Utils qw(:severities :classification :ppi);
  1         2  
  1         51  
7 1     1   374 use parent 'Perl::Critic::Policy';
  1         2  
  1         6  
8              
9             our $VERSION = '0.030';
10              
11 1     1   72 use constant DESC => '<>/<<>>/readline/readdir/each result not explicitly assigned in while condition';
  1         2  
  1         77  
12 1     1   9 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         2  
  1         473  
13              
14 7     7 0 25437 sub supported_parameters { () }
15 32     32 1 360 sub default_severity { $SEVERITY_HIGH }
16 0     0 1 0 sub default_themes { 'freenode' }
17 7     7 1 149667 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 6693 my ($self, $elem) = @_;
27 110 100 100     214 return () unless $elem eq 'while' or $elem eq 'for';
28            
29 56   50     724 my $next = $elem->snext_sibling || return ();
30            
31             # Detect for (;<>;)
32 56 100       1287 if ($elem eq 'for') {
    50          
33 8 50       88 return () unless $next->isa('PPI::Structure::For');
34 8         56 my @statements = grep { $_->isa('PPI::Statement') } $next->children;
  16         72  
35 8 50       22 return () unless @statements >= 2;
36 8         12 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       958 if ($next->isa('PPI::Structure::Condition')) {
42 24         65 $next = $next->schild(0);
43 24 50 33     305 return () unless defined $next and $next->isa('PPI::Statement');
44 24         53 $next = $next->schild(0);
45 24 50       224 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     124 if ($next->isa('PPI::Token::Word') and exists $bad_functions{$next} and is_function_call $next) {
      66        
50 20         4563 return $self->violation(DESC, EXPL, $elem);
51             }
52             }
53            
54 24         197 return ();
55             }
56              
57             1;
58              
59             =head1 NAME
60              
61             Perl::Critic::Policy::Freenode::WhileDiamondDefaultAssignment - Don't use while
62             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::Freenode>.
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>