File Coverage

blib/lib/Perl/Critic/Policy/ValuesAndExpressions/RequireInterpolationOfMetachars.pm
Criterion Covered Total %
statement 56 56 100.0
branch 19 20 95.0
condition 5 6 83.3
subroutine 17 17 100.0
pod 5 6 83.3
total 102 105 97.1


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