File Coverage

blib/lib/Perl/Critic/Policy/ValuesAndExpressions/RequireInterpolationOfMetachars.pm
Criterion Covered Total %
statement 55 56 98.2
branch 21 22 95.4
condition 5 6 83.3
subroutine 16 16 100.0
pod 5 6 83.3
total 102 106 96.2


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::ValuesAndExpressions::RequireInterpolationOfMetachars;
2              
3 40     40   29909 use 5.010001;
  40         175  
4 40     40   255 use strict;
  40         115  
  40         919  
5 40     40   224 use warnings;
  40         116  
  40         990  
6              
7 40     40   212 use Readonly;
  40         100  
  40         2100  
8              
9 40     40   280 use Perl::Critic::Utils qw< :booleans :characters :severities >;
  40         110  
  40         3793  
10 40     40   13194 use parent 'Perl::Critic::Policy';
  40         1328  
  40         266  
11              
12             #-----------------------------------------------------------------------------
13              
14             our $VERSION = '1.146';
15              
16             #-----------------------------------------------------------------------------
17              
18             Readonly::Scalar my $DESC => q<String *may* require interpolation>;
19             Readonly::Scalar my $EXPL => [ 51 ];
20              
21             #-----------------------------------------------------------------------------
22              
23             sub supported_parameters {
24             return (
25             {
26 112     112 0 2153 name => 'rcs_keywords',
27             description => 'RCS keywords to ignore in potential interpolation.',
28             default_string => $EMPTY,
29             behavior => 'string list',
30             },
31             );
32             }
33              
34 127     127 1 537 sub default_severity { return $SEVERITY_LOWEST }
35 84     84 1 382 sub default_themes { return qw(core pbp cosmetic) }
36              
37             sub applies_to {
38 52     52 1 206 return qw< PPI::Token::Quote::Single PPI::Token::Quote::Literal >;
39             }
40              
41             #-----------------------------------------------------------------------------
42              
43             sub initialize_if_enabled {
44 68     68 1 280 my ($self, $config) = @_;
45              
46 68         207 my $rcs_keywords = $self->{_rcs_keywords};
47 68         154 my @rcs_keywords = keys %{$rcs_keywords};
  68         277  
48              
49 68 100       295 if (@rcs_keywords) {
50 1         4 my $rcs_regexes = [ map { qr/ \$ $_ [^\n\$]* \$ /xms } @rcs_keywords ];
  2         57  
51 1         4 $self->{_rcs_regexes} = $rcs_regexes;
52             }
53              
54 68         292 return $TRUE;
55             }
56              
57             sub violates {
58 219     219 1 499 my ( $self, $elem, undef ) = @_;
59              
60             # The string() method strips off the quotes
61 219         672 my $string = $elem->string();
62 219 100       1785 return if not _needs_interpolation($string);
63 80 100       1017 return if _looks_like_email_address($string);
64 73 100       178 return if _looks_like_use_vars($elem);
65              
66 60         408 my $rcs_regexes = $self->{_rcs_regexes};
67 60 100 66     192 return if $rcs_regexes and _contains_rcs_variable($string, $rcs_regexes);
68              
69 53         184 return $self->violation( $DESC, $EXPL, $elem );
70             }
71              
72             #-----------------------------------------------------------------------------
73              
74             sub _needs_interpolation {
75 219     219   473 my ($string) = @_;
76              
77             return
78             # Contains a $ or @ not followed by "{}".
79 219   100     1505 $string =~ m< [\$\@] (?! [{] [}] ) \S+ >xms
80             # Contains metachars
81             # Note that \1 ... are not documented (that I can find), but are
82             # treated the same way as \0 by S_scan_const in toke.c, at least
83             # for regular double-quotish strings. Not, obviously, where
84             # regexes are involved.
85             || $string =~ m<
86             (?: \A | [^\\] )
87             (?: \\{2} )*
88             \\ [tnrfbae01234567xcNluLUEQ]
89             >xms;
90             }
91              
92             #-----------------------------------------------------------------------------
93              
94             # Stolen from Email::Address, which is deprecated. Since we are not modifying
95             # the original code at all, we are less stringent in being Critic-compliant.
96              
97             ## no critic ( RegularExpressions::RequireDotMatchAnything )
98             ## no critic ( RegularExpressions::RequireLineBoundaryMatching )
99             ## no critic ( RegularExpressions::ProhibitEscapedMetacharacters )
100              
101             my $CTL = q{\x00-\x1F\x7F}; ## no critic ( ValuesAndExpressions::RequireInterpolationOfMetachars )
102             my $special = q{()<>\\[\\]:;@\\\\,."}; ## no critic ( ValuesAndExpressions::RequireInterpolationOfMetachars )
103              
104             my $text = qr/[^\x0A\x0D]/x;
105             my $quoted_pair = qr/\\$text/x;
106             my $ctext = qr/(?>[^()\\]+)/x;
107             my $ccontent = qr/$ctext|$quoted_pair/x;
108             my $comment = qr/\s*\((?:\s*$ccontent)*\s*\)\s*/x;
109             my $cfws = qr/$comment|\s+/x;
110             my $atext = qq/[^$CTL$special\\s]/;
111             my $atom = qr/$cfws*$atext+$cfws*/x;
112             my $dot_atom_text = qr/$atext+(?:\.$atext+)*/x;
113             my $dot_atom = qr/$cfws*$dot_atom_text$cfws*/x;
114             my $qtext = qr/[^\\"]/x;
115             my $qcontent = qr/$qtext|$quoted_pair/x;
116             my $quoted_string = qr/$cfws*"$qcontent*"$cfws*/x;
117             my $local_part = qr/$dot_atom|$quoted_string/x;
118             my $dtext = qr/[^\[\]\\]/x;
119             my $dcontent = qr/$dtext|$quoted_pair/x;
120             my $domain_literal = qr/$cfws*\[(?:\s*$dcontent)*\s*\]$cfws*/x;
121             my $domain = qr/$dot_atom|$domain_literal/x;
122             my $addr_spec = qr/$local_part\@$domain/x;
123              
124             sub _looks_like_email_address {
125 80     80   184 my ($string) = @_;
126              
127 80 100       341 return if index ($string, q<@>) < 0;
128 19 100       75 return if $string =~ m< \W \@ >xms;
129 16 100       61 return if $string =~ m< \A \@ \w+ \b >xms;
130              
131 10         177 return $string =~ $addr_spec;
132             }
133              
134             #-----------------------------------------------------------------------------
135              
136             sub _contains_rcs_variable {
137 7     7   16 my ($string, $rcs_regexes) = @_;
138              
139 7         13 foreach my $regex ( @{$rcs_regexes} ) {
  7         18  
140 13 100       94 return $TRUE if $string =~ m/$regex/xms;
141             }
142              
143 0         0 return;
144             }
145              
146             #-----------------------------------------------------------------------------
147              
148             sub _looks_like_use_vars {
149 73     73   154 my ($elem) = @_;
150              
151 73         133 my $statement = $elem;
152 73         370 while ( not $statement->isa('PPI::Statement::Include') ) {
153 239 100       1670 $statement = $statement->parent() or return;
154             }
155              
156 14 50       115 return if $statement->type() ne q<use>;
157 14         315 return $statement->module() eq q<vars>;
158             }
159              
160             1;
161              
162             __END__
163              
164             #-----------------------------------------------------------------------------
165              
166             =pod
167              
168             =for stopwords RCS
169              
170             =head1 NAME
171              
172             Perl::Critic::Policy::ValuesAndExpressions::RequireInterpolationOfMetachars - Warns that you might have used single quotes when you really wanted double-quotes.
173              
174              
175             =head1 AFFILIATION
176              
177             This Policy is part of the core L<Perl::Critic|Perl::Critic>
178             distribution.
179              
180              
181             =head1 DESCRIPTION
182              
183             This policy warns you if you use single-quotes or C<q//> with a string
184             that has unescaped metacharacters that may need interpolation. Its
185             hard to know for sure if a string really should be interpolated
186             without looking into the symbol table. This policy just makes an
187             educated guess by looking for metacharacters and sigils which usually
188             indicate that the string should be interpolated.
189              
190              
191             =head2 Exceptions
192              
193             =over
194              
195             =item *
196              
197             Variable names to C<use vars>:
198              
199             use vars '$x'; # ok
200             use vars ('$y', '$z'); # ok
201             use vars qw< $a $b >; # ok
202              
203              
204             =item *
205              
206             Things that look like e-mail addresses:
207              
208             print 'john@foo.com'; # ok
209             $address = 'suzy.bar@baz.net'; # ok
210              
211             =back
212              
213              
214             =head1 CONFIGURATION
215              
216             The C<rcs_keywords> option allows you to stop this policy from complaining
217             about things that look like RCS variables, for example, in deriving values for
218             C<$VERSION> variables.
219              
220             For example, if you've got code like
221              
222             our ($VERSION) = (q<$Revision$> =~ m/(\d+)/mx);
223              
224             You can specify
225              
226             [ValuesAndExpressions::RequireInterpolationOfMetachars]
227             rcs_keywords = Revision
228              
229             in your F<.perlcriticrc> to provide an exemption.
230              
231              
232             =head1 NOTES
233              
234             Perl's own C<warnings> pragma also warns you about this.
235              
236              
237             =head1 SEE ALSO
238              
239             L<Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals|Perl::Critic::Policy::ValuesAndExpressions::ProhibitInterpolationOfLiterals>
240              
241              
242             =head1 AUTHOR
243              
244             Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>
245              
246              
247             =head1 COPYRIGHT
248              
249             Copyright (c) 2005-2011 Imaginative Software Systems. All rights reserved.
250              
251             This program is free software; you can redistribute it and/or modify
252             it under the same terms as Perl itself. The full text of this license
253             can be found in the LICENSE file included with this module.
254              
255             =cut
256              
257             # Local Variables:
258             # mode: cperl
259             # cperl-indent-level: 4
260             # fill-column: 78
261             # indent-tabs-mode: nil
262             # c-indentation-style: bsd
263             # End:
264             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :