File Coverage

blib/lib/Perl/Critic/Policy/Variables/RequireLocalizedPunctuationVars.pm
Criterion Covered Total %
statement 46 46 100.0
branch 22 24 91.6
condition 9 9 100.0
subroutine 13 13 100.0
pod 4 5 80.0
total 94 97 96.9


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars;
2              
3 40     40   27261 use 5.010001;
  40         201  
4 40     40   242 use strict;
  40         115  
  40         859  
5 40     40   222 use warnings;
  40         108  
  40         971  
6 40     40   237 use Readonly;
  40         137  
  40         2261  
7              
8 40     40   309 use Perl::Critic::Utils qw{ :severities :classification $EMPTY hashify};
  40         114  
  40         2124  
9 40     40   13524 use parent 'Perl::Critic::Policy';
  40         140  
  40         270  
10              
11             our $VERSION = '1.148';
12              
13             #-----------------------------------------------------------------------------
14              
15             Readonly::Scalar my $PACKAGE_RX => qr/::/xms;
16             Readonly::Hash my %EXCEPTIONS => hashify(qw(
17             $_
18             $ARG
19             @_
20             ));
21             Readonly::Scalar my $DESC => q{Magic variable "%s" should be assigned as "local"};
22             Readonly::Scalar my $EXPL => [ 81, 82 ];
23              
24             #-----------------------------------------------------------------------------
25              
26             sub supported_parameters {
27             return (
28             {
29 105     105 0 2450 name => 'allow',
30             description =>
31             q<Global variables to exclude from this policy.>,
32             default_string => $EMPTY,
33             behavior => 'string list',
34             list_always_present_values => [ qw< $_ $ARG @_ > ],
35             },
36             );
37             }
38              
39 590     590 1 2208 sub default_severity { return $SEVERITY_HIGH }
40 92     92 1 457 sub default_themes { return qw(core pbp bugs certrec ) }
41 47     47 1 172 sub applies_to { return 'PPI::Token::Operator' }
42              
43             #-----------------------------------------------------------------------------
44              
45             sub violates {
46 1774     1774 1 3800 my ( $self, $elem, undef ) = @_;
47              
48 1774 100       4491 return if $elem->content() ne q{=};
49              
50 1743         11003 my $destination = $elem->sprevious_sibling;
51 1743 50       48945 return if !$destination; # huh? assignment in void context??
52 1743         6332 while ($destination->isa('PPI::Structure::Subscript')) {
53 46 50       120 $destination = $destination->sprevious_sibling()
54             or return;
55             }
56              
57 1743 100       9714 if (my $var = $self->_is_non_local_magic_dest($destination)) {
58 516         2240 return $self->violation( sprintf( $DESC, $var ), $EXPL, $elem );
59             }
60 1227         8957 return; # OK
61             }
62              
63             sub _is_non_local_magic_dest {
64 3281     3281   6684 my ($self, $elem) = @_;
65              
66 3281         5123 state $local_or_my = { hashify( 'local', 'my' ) };
67              
68             # Quick exit if in good form
69 3281         7365 my $modifier = $elem->sprevious_sibling;
70             return
71             if
72             $modifier
73             && $modifier->isa('PPI::Token::Word')
74 3281 100 100     67235 && $local_or_my->{$modifier->content()};
      100        
75              
76             # Implementation note: Can't rely on PPI::Token::Magic,
77             # unfortunately, because we need English too
78              
79 2573 100 100     10697 if ($elem->isa('PPI::Token::Symbol')) {
    100          
80 1035 100       2736 return $self->_is_magic_var($elem) ? $elem : undef;
81             }
82             elsif (
83             $elem->isa('PPI::Structure::List')
84             or $elem->isa('PPI::Statement::Expression')
85             ) {
86 1228         9323 for my $child ($elem->schildren) {
87 1538         13446 my $var = $self->_is_non_local_magic_dest($child);
88 1538 100       6018 return $var if $var;
89             }
90             }
91              
92 930         2085 return;
93             }
94              
95             #-----------------------------------------------------------------------------
96              
97             sub _is_magic_var {
98 1035     1035   2115 my ($self, $elem) = @_;
99              
100 1035         2881 my $variable_name = $elem->symbol();
101 1035 100       45311 return if $self->{_allow}{$variable_name};
102 880 100       3756 return 1 if $elem->isa('PPI::Token::Magic'); # optimization(?), and
103             # helps with PPI 1.118 carat
104             # bug. This bug is gone as of
105             # 1.208, which is required for
106             # P::C 1.113. RT 65514
107 624 100       1949 return if not is_perl_global( $elem );
108              
109 260         2893 return 1;
110             }
111              
112             1;
113              
114             __END__
115              
116             #-----------------------------------------------------------------------------
117              
118             =pod
119              
120             =head1 NAME
121              
122             Perl::Critic::Policy::Variables::RequireLocalizedPunctuationVars - Magic variables should be assigned as "local".
123              
124              
125             =head1 AFFILIATION
126              
127             This Policy is part of the core L<Perl::Critic|Perl::Critic>
128             distribution.
129              
130              
131             =head1 DESCRIPTION
132              
133             Punctuation variables (and their English.pm equivalents) are global
134             variables. Messing with globals is dangerous in a complex program as
135             it can lead to very subtle and hard to fix bugs. If you must change a
136             magic variable in a non-trivial program, do it in a local scope.
137              
138             For example, to slurp a filehandle into a scalar, it's common to set
139             the record separator to undef instead of a newline. If you choose to
140             do this (instead of using L<Path::Tiny|Path::Tiny>!) then be sure to
141             localize the global and change it for as short a time as possible.
142              
143             # BAD:
144             $/ = undef;
145             my $content = <$fh>;
146              
147             # BETTER:
148             my $content;
149             {
150             local $/ = undef;
151             $content = <$fh>;
152             }
153              
154             # A popular idiom:
155             my $content = do { local $/ = undef; <$fh> };
156              
157             This policy also allows the use of C<my>. Perl prevents using C<my>
158             with "proper" punctuation variables, but allows C<$a>, C<@ARGV>, the
159             names declared by L<English|English>, etc. This is not a good coding
160             practice, however it is not the concern of this specific policy to
161             complain about that.
162              
163             There are exemptions for C<$_> and C<@_>, and the English equivalent
164             C<$ARG>.
165              
166              
167             =head1 CONFIGURATION
168              
169             You can configure your own exemptions using the C<allow> option:
170              
171             [Variables::RequireLocalizedPunctuationVars]
172             allow = @ARGV $ARGV
173              
174             These are added to the default exemptions.
175              
176              
177             =head1 CREDITS
178              
179             Initial development of this policy was supported by a grant from the
180             Perl Foundation.
181              
182              
183             =head1 AUTHOR
184              
185             Chris Dolan <cdolan@cpan.org>
186              
187              
188             =head1 COPYRIGHT
189              
190             Copyright (c) 2007-2011 Chris Dolan. Many rights reserved.
191              
192             This program is free software; you can redistribute it and/or modify
193             it under the same terms as Perl itself. The full text of this license
194             can be found in the LICENSE file included with this module.
195              
196             =cut
197              
198             # Local Variables:
199             # mode: cperl
200             # cperl-indent-level: 4
201             # fill-column: 78
202             # indent-tabs-mode: nil
203             # c-indentation-style: bsd
204             # End:
205             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :