File Coverage

blib/lib/Perl/Critic/Policy/Community/DeprecatedFeatures.pm
Criterion Covered Total %
statement 104 107 97.2
branch 104 108 96.3
condition 159 193 82.3
subroutine 13 14 92.8
pod 4 5 80.0
total 384 427 89.9


line stmt bran cond sub pod time code
1              
2             use strict;
3 1     1   426 use warnings;
  1         2  
  1         22  
4 1     1   5  
  1         2  
  1         21  
5             use List::Util 'any', 'none';
6 1     1   5 use Perl::Critic::Utils qw(:severities :classification :ppi);
  1         2  
  1         62  
7 1     1   5 use parent 'Perl::Critic::Policy';
  1         2  
  1         50  
8 1     1   314  
  1         1  
  1         6  
9             our $VERSION = 'v1.0.3';
10              
11              
12 4     4 0 31219 my %features = (
13 75     75 1 603 ':=' => 'Use of := as an empty attribute list is deprecated in perl v5.12.0, use = alone.',
14 0     0 1 0 '$[' => 'Use of $[ is deprecated in perl v5.12.0. See Array::Base and String::Base.',
15 4     4 1 171828 '/\\C/' => 'Use of the \\C character class in regular expressions is deprecated in perl v5.20.0. To examine a string\'s UTF-8-encoded byte representation, encode it to UTF-8.',
16             '?PATTERN?' => 'Use of ? as a match regex delimiter without an initial m is deprecated in perl v5.14.0. Use m?PATTERN? instead.',
17             'autoderef' => 'Use of each/keys/pop/push/shift/splice/unshift/values on a reference is an experimental feature that is removed in perl v5.24.0. Dereference the array or hash to use these functions on it.',
18             'Bare here-doc' => 'Use of bare << to create a here-doc with an empty string terminator is deprecated in perl 5. Use a quoted empty string like <<\'\'.',
19             'chdir(\'\')' => 'Use of chdir(\'\') or chdir(undef) to chdir home is deprecated in perl v5.8.0. Use chdir() instead.',
20             'defined on array/hash' => 'Use of defined() on an array or hash is deprecated in perl v5.6.2. The array or hash can be tested directly to check for non-emptiness: if (@foo) { ... }',
21             'do SUBROUTINE(LIST)' => 'Use of do to call a subroutine is deprecated in perl 5.',
22             'NBSP in \\N{...}' => 'Use of the "no-break space" character in character names is deprecated in perl v5.22.0.',
23             'POSIX character function' => 'Several character matching functions in POSIX.pm are deprecated in perl v5.20.0: isalnum, isalpha, iscntrl, isdigit, isgraph, islower, isprint, ispunct, isspace, isupper, and isxdigit. Regular expressions are a more portable and correct way to test character strings.',
24             'POSIX::tmpnam()' => 'The tmpnam() function from POSIX is deprecated in perl v5.22.0. Use File::Temp instead.',
25             'qw(...) as parentheses' => 'Use of qw(...) as parentheses is deprecated in perl v5.14.0. Wrap the list in literal parentheses when required, such as in a foreach loop.',
26             'require ::Foo::Bar' => 'Bareword require starting with a double colon is an error in perl v5.26.0.',
27             'UNIVERSAL->import()' => 'The method UNIVERSAL->import() (or passing import arguments to "use UNIVERSAL") is deprecated in perl v5.12.0.',
28             );
29              
30             my %posix_deprecated = map { ($_ => 1, "POSIX::$_" => 1) }
31             qw(isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit);
32              
33             my %autoderef_functions = map { ($_ => 1) }
34             qw(each keys pop push shift splice unshift values);
35              
36             my ($self, $feature, $elem) = @_;
37             my $desc = "$feature is deprecated";
38             my $expl = $features{$feature} // "$feature is deprecated or removed from recent versions of Perl.";
39             return $self->violation($desc, $expl, $elem);
40             }
41              
42 75     75   316 my ($self, $elem) = @_;
43 75         184 my $next;
44 75   33     177 my $prev;
45 75         214 my $parent;
46             my @args;
47             my @violations;
48             if ($elem->isa('PPI::Statement')) {
49 1161     1161 1 5087 if ($elem->isa('PPI::Statement::Include')) {
50 1161         3413 # use UNIVERSAL ...;
51             if ($elem->type eq 'use' and defined $elem->module and $elem->module eq 'UNIVERSAL') {
52 1161         0 my @args = $elem->arguments;
53 1161         0 if (!@args or !$args[0]->isa('PPI::Structure::List') or $args[0]->schildren) {
54 1161         0 push @violations, $self->_violation('UNIVERSAL->import()', $elem);
55 1161 100       3472 }
    100          
56 199 100       480 }
57             # require ::Foo::Bar
58 13 100 66     42 if (defined $elem->module and $elem->module =~ m/^::/) {
      100        
59 3         181 push @violations, $self->_violation('require ::Foo::Bar', $elem);
60 3 100 100     115 }
      66        
61 2         6 }
62             } elsif ($elem->isa('PPI::Token')) {
63             if ($elem->isa('PPI::Token::Symbol')) {
64             # $[
65 13 100 66     751 if ($elem eq '$[') {
66 3         114 push @violations, $self->_violation('$[', $elem);
67             }
68             } elsif ($elem->isa('PPI::Token::Operator')) {
69             # :=
70 903 100       5480 if ($elem eq ':' and $next = $elem->next_sibling and $next->isa('PPI::Token::Operator') and $next eq '=') {
    100          
    100          
    100          
    100          
    100          
    100          
71             push @violations, $self->_violation(':=', $elem);
72 92 100       228 # ?PATTERN? - PPI parses this as multiple ? operators
73 1         115 } elsif ($elem eq '?' and $parent = $elem->parent and $parent->isa('PPI::Statement')) {
74             $next = $elem->snext_sibling;
75             until (!$next or ($next->isa('PPI::Token::Operator') and $next eq '?')) {
76             $next = $next->snext_sibling;
77 47 100 66     83 }
    100 100        
    100 66        
      66        
      66        
      66        
      66        
78 1         50 # If the statement has a : operator, this is probably a ternary operator.
79             # PPI also tends to detect the : as a loop label.
80             if ($next and none { ($_->isa('PPI::Token::Operator') and $_ eq ':') or $_->isa('PPI::Token::Label') } $parent->schildren) {
81 6         158 push @violations, $self->_violation('?PATTERN?', $elem);
82 6   100     140 }
      100        
83 14         242 # Bare here-doc - differentiate this from the legitimate << operator
84             } elsif ($elem eq '<<' and (!($next = $elem->snext_sibling)
85             or ($next->isa('PPI::Token::Operator') and $next ne '~' and $next ne '!' and $next ne '+' and $next ne '-')
86             or ($next->isa('PPI::Token::Structure') and $next ne '(' and $next ne '{' and $next ne '['))) {
87 6 100 100 17   201 push @violations, $self->_violation('Bare here-doc', $elem);
  17 100 100     189  
88 1         3 }
89             } elsif ($elem->isa('PPI::Token::Word')) {
90             # UNIVERSAL->import()
91             if ($elem eq 'UNIVERSAL'
92             and $next = $elem->snext_sibling and $next->isa('PPI::Token::Operator') and $next eq '->'
93             and $next = $next->snext_sibling and $next->isa('PPI::Token::Word') and $next eq 'import') {
94 4         301 push @violations, $self->_violation('UNIVERSAL->import()', $next);
95             # for $x qw(...)
96             } elsif (($elem eq 'for' or $elem eq 'foreach') and !$elem->sprevious_sibling) {
97             $next = $elem->snext_sibling;
98 152 100 66     283 until (!$next or $next->isa('PPI::Structure::List')
    100 100        
    100 66        
    100 66        
      33        
      66        
      66        
      100        
      66        
      100        
      100        
99             or $next->isa('PPI::Token::QuoteLike::Words')) {
100             $next = $next->snext_sibling;
101 1         83 }
102             if ($next and $next->isa('PPI::Token::QuoteLike::Words')) {
103             push @violations, $self->_violation('qw(...) as parentheses', $next);
104 2         71 }
105 2   66     71 # do SUBROUTINE(LIST)
      100        
106             } elsif ($elem eq 'do' and $next = $elem->snext_sibling) {
107 4         57 if ((($next->isa('PPI::Token::Word') and is_function_call $next)
108             or ($next->isa('PPI::Token::Symbol') and ($next->raw_type eq '&' or $next->raw_type eq '$')))
109 2 100 66     61 and ($next = $next->snext_sibling and $next->isa('PPI::Structure::List'))) {
110 1         4 push @violations, $self->_violation('do SUBROUTINE(LIST)', $elem);
111             }
112             # avoid false positives for method calls
113             } elsif (!($prev = $elem->sprevious_sibling) or !$prev->isa('PPI::Token::Operator') or $prev ne '->') {
114 6 100 66     377 # POSIX character function or POSIX::tmpnam()
      66        
      66        
115             if (exists $posix_deprecated{$elem} or $elem eq 'tmpnam' or $elem eq 'POSIX::tmpnam') {
116             my $is_posix = $elem =~ m/^POSIX::/ ? 1 : 0;
117 3         346 (my $function_name = $elem) =~ s/^POSIX:://;
118             unless ($is_posix) {
119             my $includes = $elem->document->find('PPI::Statement::Include') || [];
120             foreach my $stmt (grep { ($_->module // '') eq 'POSIX' } @$includes) {
121             my @args = $stmt->arguments;
122 139 100 100     7886 $is_posix = 1 if !@args or any { $_ =~ m/\b\Q$function_name\E\b/ } @args;
    100 100        
    100 66        
    100 66        
      66        
123 48 100       290 }
124 48         245 }
125 48 100       213 if ($is_posix) {
126 36   50     91 push @violations, $self->_violation('POSIX::tmpnam()', $elem) if $function_name eq 'tmpnam';
127 36   50     175843 push @violations, $self->_violation('POSIX character function', $elem) if exists $posix_deprecated{$elem};
  96         1282  
128 36         879 }
129 36 100 100 24   1107 # defined array/hash
  24         63  
130             } elsif ($elem eq 'defined' and $next = $elem->snext_sibling) {
131             $next = $next->schild(0) if $next->isa('PPI::Structure::List');
132 48 100       707 if ($next and $next->isa('PPI::Token::Symbol')
133 36 100       92 and ($next->raw_type eq '@' or $next->raw_type eq '%')
134 36 100       754 and $next->raw_type eq $next->symbol_type) {
135             push @violations, $self->_violation('defined on array/hash', $elem);
136             }
137             # autoderef
138 8 100       380 } elsif (exists $autoderef_functions{$elem} and $next = $elem->snext_sibling) {
139 8 100 66     76 $next = $next->schild(0) if $next->isa('PPI::Structure::List');
      100        
      100        
      66        
140             $next = $next->schild(0) if $next and $next->isa('PPI::Statement::Expression');
141             if ($next and $next->isa('PPI::Token::Symbol') and $next->raw_type eq '$') {
142 2         114 my $is_postderef;
143             until (!$next or ($next->isa('PPI::Token::Structure') and $next eq ';')
144             or ($next->isa('PPI::Token::Operator') and $next eq ',')) {
145             $next = $next->snext_sibling;
146 21 100       1091 if ($next and $next->isa('PPI::Token::Cast') and ($next eq '@*' or $next eq '%*')) {
147 21 100 100     187 $is_postderef = 1;
148 21 100 100     137 last;
      100        
149 11         72 }
150 11   66     76 }
      100        
      100        
      100        
151             push @violations, $self->_violation('autoderef', $elem) unless $is_postderef;
152 29         137 }
153 29 50 100     645 } elsif ($elem eq 'chdir' and $next = $elem->snext_sibling) {
      66        
      100        
154 3         55 $next = $next->schild(0) if $next->isa('PPI::Structure::List');
155 3         7 $next = $next->schild(0) if $next and $next->isa('PPI::Statement::Expression');
156             if ($next and (($next->isa('PPI::Token::Quote') and !length $next->string)
157             or ($next->isa('PPI::Token::Word') and $next eq 'undef'))) {
158 11 100       88 push @violations, $self->_violation('chdir(\'\')', $elem);
159             }
160             }
161 7 100       420 }
162 7 100 100     80 } elsif ($elem->isa('PPI::Token::Regexp')) {
163 7 100 100     62 # ?PATTERN?
      100        
164             if ($elem->isa('PPI::Token::Regexp::Match') and ($elem->get_delimiters)[0] eq '??' and $elem !~ m/^m/) {
165 3         37 push @violations, $self->_violation('?PATTERN?', $elem);
166             }
167             if (!$elem->isa('PPI::Token::Regexp::Transliterate')) {
168             push @violations, $self->_violates_interpolated($elem);
169             }
170             } elsif ($elem->isa('PPI::Token::HereDoc')) {
171 8 50 100     37 # Bare here-doc
      66        
172 0         0 if ($elem eq '<<') {
173             push @violations, $self->_violation('Bare here-doc', $elem);
174 8 50       140 }
175 8         16 } elsif ($elem->isa('PPI::Token::QuoteLike')) {
176             if ($elem->isa('PPI::Token::QuoteLike::Regexp') or $elem->isa('PPI::Token::QuoteLike::Backtick') or $elem->isa('PPI::Token::QuoteLike::Command')) {
177             push @violations, $self->_violates_interpolated($elem);
178             }
179 3 50       7 } elsif ($elem->isa('PPI::Token::Quote')) {
180 0         0 if ($elem->isa('PPI::Token::Quote::Double') or $elem->isa('PPI::Token::Quote::Interpolate')) {
181             push @violations, $self->_violates_interpolated($elem);
182             }
183 12 100 100     84 }
      100        
184 8         33 }
185             return @violations;
186             }
187 14 100 100     73  
188 5         15 my ($self, $elem) = @_;
189             my @violations;
190             # NBSP in \N{...}
191             my $contents;
192 1161         16649 if ($elem->isa('PPI::Token::Regexp') or $elem->isa('PPI::Token::QuoteLike::Regexp')) {
193             $contents = $elem->get_match_string;
194             # /\C/
195             push @violations, $self->_violation('/\\C/', $elem) if $contents =~ m/(?<!\\)\\C/;
196 21     21   35 } elsif ($elem->isa('PPI::Token::Quote')) {
197 21         33 $contents = $elem->string;
198             } else {
199             # Backticks and qx elements have no contents method
200 21 100 100     90 $contents = $elem;
    100          
201 12         38 }
202             push @violations, $self->_violation('NBSP in \\N{...}', $elem) if $contents =~ m/\\N\{[^}]*\x{a0}[^}]*\}/;
203 12 100       192 return @violations;
204             }
205 5         22  
206             1;
207              
208 4         6 =head1 NAME
209              
210 21 100       394 Perl::Critic::Policy::Community::DeprecatedFeatures - Avoid features that have
211 21         1017 been deprecated or removed from Perl
212              
213             =head1 DESCRIPTION
214              
215             While L<Perl::Critic::Policy::Community::StrictWarnings> will expose usage of
216             deprecated or removed features when a modern perl is used, this policy will
217             detect such features in use regardless of perl version, to assist in keeping
218             your code modern and forward-compatible.
219              
220             =head1 FEATURES
221              
222             =head2 :=
223              
224             Because the whitespace between an attribute list and assignment operator is not
225             significant, it was possible to specify assignment to a variable with an empty
226             attribute list with a construction like C<my $foo := 'bar'>. This is deprecated
227             in perl v5.12.0 to allow the possibility of a future C<:=> operator. Avoid the
228             issue by either putting whitespace between the C<:> and C<=> characters or
229             simply omitting the empty attribute list.
230              
231             =head2 $[
232              
233             The magic L<perlvar/"$["> variable was used in very old perls to determine the
234             index of the first element of arrays or the first character in substrings, and
235             also allow modifying this value. It was discouraged from the start of Perl 5,
236             its functionality changed in v5.10.0, deprecated in v5.12.0, re-implemented as
237             L<arybase>.pm in v5.16.0, and it is essentially a synonym for C<0> under
238             C<use v5.16> or C<no feature "array_base">. While it is probably a bad idea in
239             general, the modules L<Array::Base> and L<String::Base> can now be used to
240             replace this functionality.
241              
242             =head2 /\C/
243              
244             The C<\C> regular expression character class would match a single byte of the
245             internal representation of the string, which was dangerous because it violated
246             the logical character abstraction of Perl strings, and substitutions using it
247             could result in malformed UTF-8 sequences. It was deprecated in perl v5.20.0
248             and removed in perl v5.24.0. Instead, explicitly encode the string to UTF-8
249             using L<Encode> to examine its UTF-8-encoded byte representation.
250              
251             =head2 ?PATTERN?
252              
253             The C<?PATTERN?> regex match syntax is deprecated in perl v5.14.0 and removed
254             in perl v5.22.0. Use C<m?PATTERN?> instead.
255              
256             =head2 autoderef
257              
258             An experimental feature was introduced in perl v5.14.0 to allow calling various
259             builtin functions (which operate on arrays or hashes) on a reference, which
260             would automatically dereference the operand. This led to ambiguity when passed
261             objects that overload both array and hash dereferencing, and so was removed in
262             perl v5.24.0. Instead, explicitly dereference the reference when calling these
263             functions. The functions affected are C<each>, C<keys>, C<pop>, C<push>,
264             C<shift>, C<splice>, C<unshift>, and C<values>.
265              
266             =head2 Bare here-doc
267              
268             Using C< << > to initiate a here-doc would create it with an empty terminator,
269             similar to C< <<'' >, so the here-doc would terminate on the next empty line.
270             Omitting the quoted empty string has been deprecated since perl 5, and is a
271             fatal error in perl v5.28.0.
272              
273             =head2 chdir('')
274              
275             Passing an empty string or C<undef> to C<chdir()> would change to the home
276             directory, but this usage is deprecated in perl v5.8.0 and throws an error in
277             perl v5.24.0. Instead, call C<chdir()> with no arguments for this behavior.
278              
279             =head2 defined on array/hash
280              
281             Using the function C<defined()> on an array or hash probably does not do what
282             you expected, and is deprecated in perl v5.6.2 and throws a fatal error in perl
283             v5.22.0. To check if an array or hash is non-empty, test if it has elements.
284              
285             if (@foo) { ... }
286             if (keys %bar) { ... }
287              
288             =head2 do SUBROUTINE(LIST)
289              
290             This form of C<do> to call a subroutine has been deprecated since perl 5, and
291             is removed in perl v5.20.0.
292              
293             =head2 NBSP in \N{...}
294              
295             Use of the "no-break space" character in L<character names|charnames> is
296             deprecated in perl v5.22.0 and an error in perl v5.26.0.
297              
298             =head2 POSIX character functions
299              
300             Several character matching functions in L<POSIX>.pm are deprecated in perl
301             v5.20.0. See the L<POSIX> documentation for more details. Most uses of these
302             functions can be replaced with appropriate regex matches.
303              
304             isalnum, isalpha, iscntrl, isdigit, isgraph, islower, isprint, ispunct, isspace, issuper, isxdigit
305              
306             =head2 POSIX::tmpnam()
307              
308             The C<tmpnam()> function from L<POSIX>.pm is deprecated in perl v5.22.0 and
309             removed in perl v5.26.0. Use L<File::Temp> instead.
310              
311             =head2 qw(...) as parentheses
312              
313             Literal parentheses are required for certain statements such as a
314             C<for my $foo (...) { ... }> construct. Using a C<qw(...)> list literal without
315             surrounding parentheses in this syntax is deprecated in perl v5.14.0 and a
316             syntax error in perl v5.18.0. Wrap the literal in parentheses:
317             C<for my $foo (qw(...)) { ... }>.
318              
319             =head2 require ::Foo::Bar
320              
321             A bareword C<require> (or C<use>) starting with a double colon would
322             inadvertently translate to a path starting with C</>. Starting in perl v5.26.0,
323             this is a fatal error.
324              
325             =head2 UNIVERSAL->import()
326              
327             The method C<< UNIVERSAL->import() >> and similarly passing import arguments to
328             C<use UNIVERSAL> is deprecated in perl v5.12.0 and throws a fatal error in perl
329             v5.22.0. Calling C<use UNIVERSAL> with no arguments is not an error, but serves
330             no purpose.
331              
332             =head1 AFFILIATION
333              
334             This policy is part of L<Perl::Critic::Community>.
335              
336             =head1 CONFIGURATION
337              
338             This policy is not configurable except for the standard options.
339              
340             =head1 CAVEATS
341              
342             This policy is incomplete, as many deprecations are difficult to test for
343             statically. It is recommended to use L<perlbrew> or L<perl-build> to test your
344             code under newer versions of Perl, with C<warnings> enabled.
345              
346             =head1 AUTHOR
347              
348             Dan Book, C<dbook@cpan.org>
349              
350             =head1 COPYRIGHT AND LICENSE
351              
352             Copyright 2015, Dan Book.
353              
354             This library is free software; you may redistribute it and/or modify it under
355             the terms of the Artistic License version 2.0.
356              
357             =head1 SEE ALSO
358              
359             L<Perl::Critic>