File Coverage

blib/lib/Perl/Critic/Policy/Compatibility/PerlMinimumVersionAndWhy.pm
Criterion Covered Total %
statement 47 346 13.5
branch 1 134 0.7
condition 0 195 0.0
subroutine 16 83 19.2
pod 2 3 66.6
total 66 761 8.6


line stmt bran cond sub pod time code
1             # Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
2              
3             # Perl-Critic-Pulp is free software; you can redistribute it and/or modify
4             # it under the terms of the GNU General Public License as published by the
5             # Free Software Foundation; either version 3, or (at your option) any later
6             # version.
7             #
8             # Perl-Critic-Pulp is distributed in the hope that it will be useful, but
9             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
10             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
11             # for more details.
12             #
13             # You should have received a copy of the GNU General Public License along
14             # with Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
15              
16             package Perl::Critic::Policy::Compatibility::PerlMinimumVersionAndWhy;
17 40     40   29549 use 5.006;
  40         141  
18 40     40   190 use strict;
  40         95  
  40         687  
19 40     40   175 use warnings;
  40         70  
  40         859  
20 40     40   512 use version (); # but don't import qv()
  40         1552  
  40         904  
21              
22             # 1.208 for PPI::Token::QuoteLike::Regexp get_modifiers()
23 40     40   596 use PPI 1.208;
  40         87355  
  40         1187  
24              
25             # 1.084 for Perl::Critic::Document highest_explicit_perl_version()
26 40     40   630 use Perl::Critic::Policy 1.084;
  40         88031  
  40         891  
27 40     40   214 use base 'Perl::Critic::Policy';
  40         92  
  40         4157  
28 40     40   452 use Perl::Critic::Utils qw(parse_arg_list);
  40         112  
  40         1677  
29 40     40   658 use Perl::Critic::Pulp::Utils;
  40         117  
  40         2898  
30              
31             # uncomment this to run the ### lines
32             # use Smart::Comments;
33              
34             our $VERSION = 97;
35              
36 40         3146 use constant supported_parameters =>
37             ({ name => 'above_version',
38             description => 'Check only things above this version of Perl.',
39             behavior => 'string',
40             parser => \&Perl::Critic::Pulp::Utils::parameter_parse_version,
41             },
42             { name => 'skip_checks',
43             description => 'Version checks to skip (space separated list).',
44             behavior => 'string',
45 40     40   301 });
  40         115  
46 40     40   234 use constant default_severity => $Perl::Critic::Utils::SEVERITY_LOW;
  40         84  
  40         1994  
47 40     40   233 use constant default_themes => qw(pulp compatibility);
  40         11162  
  40         2088  
48 40     40   225 use constant applies_to => 'PPI::Document';
  40         92  
  40         11175  
49              
50              
51             sub initialize_if_enabled {
52 2     2 1 1044535 my ($self, $config) = @_;
53             # ask that Perl::MinimumVersion is available and still has its
54             # undocumented %CHECKS to mangle below
55 2 50       6 eval { require Perl::MinimumVersion;
  2         280  
56 0           scalar %Perl::MinimumVersion::CHECKS }
57             or return 0;
58              
59 0           _setup_extra_checks();
60             }
61              
62             sub violates {
63 0     0 1   my ($self, $document) = @_;
64              
65 0           my %skip_checks;
66 0 0         if (defined (my $skip_checks = $self->{_skip_checks})) {
67 0           @skip_checks{split / /, $self->{_skip_checks}} = (); # hash slice
68             }
69              
70 0           my $pmv = Perl::MinimumVersion->new ($document);
71 0           my $config_above_version = $self->{'_above_version'};
72 0           my $explicit_version = _highest_explicit_perl_version($document);
73              
74 0           my @violations;
75 0           foreach my $check (sort keys %Perl::MinimumVersion::CHECKS) {
76 0 0         next if exists $skip_checks{$check};
77 0 0         next if $check eq '_constant_hash'; # better by ConstantPragmaHash
78             # next if $check =~ /_pragmas$/; # usually impossible in earlier
79 0 0         next if $check =~ /_modules$/; # wrong for dual-life stuff
80              
81 0           my $check_version = $Perl::MinimumVersion::CHECKS{$check};
82 0 0 0       next if (defined $explicit_version
83             && $check_version <= $explicit_version);
84 0 0 0       next if (defined $config_above_version
85             && $check_version <= $config_above_version);
86             ### $check
87              
88 0   0       my $elem = do {
89 40     40   264 no warnings 'redefine';
  40         81  
  40         19716  
90             local *PPI::Node::find_any = \&PPI::Node::find_first;
91             $pmv->$check
92             } || next;
93             # require Data::Dumper;
94             # print Data::Dumper::Dumper($elem);
95             # print $elem->location,"\n";
96 0           push @violations,
97             $self->violation ("$check requires $check_version",
98             '',
99             $elem);
100             }
101 0           return @violations;
102             }
103              
104             my $v5010 = version->new('5.010');
105              
106             # Some controversy:
107             # https://github.com/Perl-Critic/Perl-Critic/issues/270
108             # http://elliotlovesperl.com/2009/05/17/the-problem-with-modernperl/
109             #
110             sub _highest_explicit_perl_version {
111 0     0     my ($document) = @_;
112             ### _highest_explicit_perl_version() ...
113 0           my $ver = $document->highest_explicit_perl_version;
114 0 0 0       if ($ver < $v5010
115             && Perl::Critic::Policy::Compatibility::Gtk2Constants::_document_uses_module($document,'Modern::Perl')) {
116             ### increase to 5.010 ...
117 0           $ver = $v5010;
118             }
119 0           return $ver;
120             }
121              
122              
123             #---------------------------------------------------------------------------
124             # Crib note: $document->find_first wanted func returning undef means the
125             # element is unwanted and also don't descend into its sub-elements.
126             #
127              
128             sub _setup_extra_checks {
129              
130             # 5.12.0
131 0     0     my $v5012 = version->new('5.012');
132 0           $Perl::MinimumVersion::CHECKS{_Pulp__keys_of_array} = $v5012;
133 0           $Perl::MinimumVersion::CHECKS{_Pulp__values_of_array} = $v5012;
134 0           $Perl::MinimumVersion::CHECKS{_Pulp__each_of_array} = $v5012;
135              
136             # 5.10.0
137 0 0         unless (eval { Perl::MinimumVersion->VERSION(1.28); 1 }) {
  0            
  0            
138             # fixed in 1.28 up
139 0           $Perl::MinimumVersion::CHECKS{_Pulp__5010_magic__fix} = $v5010;
140 0           $Perl::MinimumVersion::CHECKS{_Pulp__5010_operators__fix} = $v5010;
141             }
142 0           $Perl::MinimumVersion::CHECKS{_Pulp__5010_qr_m_propagate_properly} = $v5010;
143 0           $Perl::MinimumVersion::CHECKS{_Pulp__5010_stacked_filetest} = $v5010;
144              
145             # 5.8.0
146 0           my $v5008 = version->new('5.008');
147 0           $Perl::MinimumVersion::CHECKS{_Pulp__fat_comma_across_newline} = $v5008;
148 0           $Perl::MinimumVersion::CHECKS{_Pulp__eval_line_directive_first_thing} = $v5008;
149              
150             # 5.6.0
151 0           my $v5006 = version->new('5.006');
152 0           $Perl::MinimumVersion::CHECKS{_Pulp__exists_subr} = $v5006;
153 0           $Perl::MinimumVersion::CHECKS{_Pulp__exists_array_elem} = $v5006;
154 0           $Perl::MinimumVersion::CHECKS{_Pulp__delete_array_elem} = $v5006;
155 0           $Perl::MinimumVersion::CHECKS{_Pulp__0b_number} = $v5006;
156 0           $Perl::MinimumVersion::CHECKS{_Pulp__syswrite_length_optional} = $v5006;
157 0           $Perl::MinimumVersion::CHECKS{_Pulp__open_my_filehandle} = $v5006;
158 0           $Perl::MinimumVersion::CHECKS{_Pulp__var_method_without_parens} = $v5006;
159              
160             # 5.005
161 0           my $v5005 = version->new('5.005');
162 0 0         unless (exists
163             $Perl::MinimumVersion::CHECKS{_bareword_ends_with_double_colon}) {
164             # adopted into Perl::MinimumVersion 1.28
165 0           $Perl::MinimumVersion::CHECKS{_Pulp__bareword_double_colon} = $v5005;
166             }
167 0           $Perl::MinimumVersion::CHECKS{_Pulp__my_list_with_undef} = $v5005;
168              
169             # 5.004
170 0           my $v5004 = version->new('5.004');
171 0           $Perl::MinimumVersion::CHECKS{_Pulp__special_literal__PACKAGE__} = $v5004;
172 0           $Perl::MinimumVersion::CHECKS{_Pulp__use_version_number} = $v5004;
173 0           $Perl::MinimumVersion::CHECKS{_Pulp__for_loop_variable_using_my} = $v5004;
174 0           $Perl::MinimumVersion::CHECKS{_Pulp__arrow_coderef_call} = $v5004;
175 0           $Perl::MinimumVersion::CHECKS{_Pulp__sysseek_builtin} = $v5004;
176              
177             # UNIVERSAL.pm
178 0           $Perl::MinimumVersion::CHECKS{_Pulp__UNIVERSAL_methods_5004} = $v5004;
179 0           $Perl::MinimumVersion::CHECKS{_Pulp__UNIVERSAL_methods_5010} = $v5010;
180              
181             # pack()/unpack()
182 0           $Perl::MinimumVersion::CHECKS{_Pulp__pack_format_5004} = $v5004;
183 0           $Perl::MinimumVersion::CHECKS{_Pulp__pack_format_5006} = $v5006;
184 0           $Perl::MinimumVersion::CHECKS{_Pulp__pack_format_5008} = $v5008;
185 0           $Perl::MinimumVersion::CHECKS{_Pulp__pack_format_5010} = $v5010;
186             }
187              
188             {
189             # Perl::MinimumVersion prior to 1.28 had 'PPI::Token::Operator' and
190             # 'PPI::Token::Magic' swapped between the respective operator/magic tests
191              
192             package Perl::MinimumVersion;
193 40     40   297 use vars qw(%MATCHES);
  40         92  
  40         135839  
194             sub _Pulp__5010_operators__fix {
195             shift->Document->find_first
196             (sub {
197             $_[1]->isa('PPI::Token::Operator')
198             and
199 0 0   0     $MATCHES{_perl_5010_operators}->{$_[1]->content};
200 0     0     } );
201             }
202             sub _Pulp__5010_magic__fix {
203             shift->Document->find_first
204             (sub {
205             $_[1]->isa('PPI::Token::Magic')
206             and
207 0 0   0     $MATCHES{_perl_5010_magic}->{$_[1]->content};
208 0     0     } );
209             }
210             }
211              
212             sub Perl::MinimumVersion::_Pulp__5010_qr_m_propagate_properly {
213 0     0     my ($pmv) = @_;
214             ### _Pulp__5010_qr_m_propagate_properly() check ...
215             $pmv->Document->find_first
216             (sub {
217 0     0     my ($document, $elem) = @_;
218 0 0         $elem->isa('PPI::Token::QuoteLike::Regexp') || return 0;
219 0           my %modifiers = $elem->get_modifiers;
220             ### content: $elem->content
221             ### modifiers: \%modifiers
222 0 0         return ($modifiers{'m'} ? 1 : 0);
223 0           });
224             }
225              
226             # new in 5.010 as described in perlfunc.pod
227             sub Perl::MinimumVersion::_Pulp__5010_stacked_filetest {
228 0     0     my ($pmv) = @_;
229             ### _Pulp__5010_stacked_filetest() check ...
230             $pmv->Document->find_first
231             (sub {
232 0     0     my ($document, $elem) = @_;
233 0 0 0       return (_elem_is_filetest_operator($elem) # -X
234             && ($elem = $elem->snext_sibling) # has a next sibling
235             && _elem_is_filetest_operator($elem) # -X
236             ? 1 : 0);
237 0           });
238             }
239             # $elem is a PPI::Element
240             # Return true if it's a -X operator.
241             sub _elem_is_filetest_operator {
242 0     0     my ($elem) = @_;
243 0   0       return ($elem->isa('PPI::Token::Operator')
244             && $elem =~ /^-./);
245             }
246              
247              
248             #-----------------------------------------------------------------------------
249             # foo \n => fat comma across newline new in 5.8.0
250             # extra code in 5.8 toke.c under comment "not a keyword" checking for =>
251             #
252              
253             sub Perl::MinimumVersion::_Pulp__fat_comma_across_newline {
254 0     0     my ($pmv) = @_;
255             ### _Pulp__fat_comma_across_newline() check
256             $pmv->Document->find_first
257             (sub {
258 0     0     my ($document, $elem) = @_;
259             ### elem: "$elem"
260 0 0 0       if ($elem->isa('PPI::Token::Operator')
261             && $elem->content eq '=>') {
262 0           my ($prev, $saw_newline) = sprevious_sibling_and_newline($elem);
263             ### prev: "$prev"
264             ### $saw_newline
265 0 0 0       if ($saw_newline
      0        
      0        
      0        
266             && $prev
267             && $prev->isa('PPI::Token::Word')
268             && $prev !~ /^-/ # -foo self-quotes
269             && ! Perl::Critic::Utils::is_method_call($prev)) { # ->foo
270 0           return 1; # found
271             }
272             }
273 0           return 0; # continue searching
274 0           });
275             }
276              
277             sub sprevious_sibling_and_newline {
278 0     0 0   my ($elem) = @_;
279             ### sprevious_sibling_and_newline()
280 0           my $saw_newline;
281 0           for (;;) {
282 0   0       $elem = $elem->previous_sibling || last;
283 0 0         if ($elem->isa('PPI::Token::Whitespace')) {
    0          
284 0   0       $saw_newline ||= ($elem->content =~ /\n/);
285             } elsif ($elem->isa('PPI::Token::Comment')) {
286 0           $saw_newline = 1;
287             } else {
288 0           last;
289             }
290             }
291 0           return ($elem, $saw_newline);
292             }
293              
294             #-----------------------------------------------------------------------------
295              
296             # delete $array[0] and exists $array[0] new in 5.6.0
297             # two functions so the "exists" or "delete" appears in the check name
298             #
299             sub Perl::MinimumVersion::_Pulp__exists_array_elem {
300 0     0     my ($pmv) = @_;
301             ### _Pulp__exists_array_elem() check
302 0           return _exists_or_delete_array_elem ($pmv, 'exists');
303             }
304             sub Perl::MinimumVersion::_Pulp__delete_array_elem {
305 0     0     my ($pmv) = @_;
306             ### _Pulp__delete_array_elem() check
307 0           return _exists_or_delete_array_elem ($pmv, 'delete');
308             }
309             sub _exists_or_delete_array_elem {
310 0     0     my ($pmv, $which) = @_;
311             ### _exists_or_delete_array_elem()
312             $pmv->Document->find_first
313             (sub {
314 0     0     my ($document, $elem) = @_;
315 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
316             && $elem eq $which
317             && Perl::Critic::Utils::is_function_call($elem)
318             && _arg_is_array_elem($elem->snext_sibling)) {
319 0           return 1;
320             } else {
321 0           return 0;
322             }
323 0           });
324             }
325             sub _arg_is_array_elem {
326 0     0     my ($elem) = @_;
327             ### _arg_is_array_elem: "$elem"
328              
329 0 0 0       (($elem = _descend_through_lists($elem))
      0        
      0        
330             && $elem->isa('PPI::Token::Symbol')
331             && $elem->raw_type eq '$'
332             && ($elem = $elem->snext_sibling))
333             or return 0;
334              
335 0           my $ret = 0;
336 0           for (;;) {
337 0 0 0       if ($elem->isa('PPI::Structure::Subscript')) {
    0          
    0          
338             # adjacent $x{key}[123]
339 0           $ret = ($elem->start eq '[');
340             } elsif ($elem->isa('PPI::Structure::List')) {
341             # $x[0]->() function call
342 0           return 0;
343             } elsif ($elem->isa('PPI::Token::Operator')
344             && $elem eq '->') {
345             # subscript ->, continue
346             } else {
347             # anything else below -> precedence, stop
348 0           last;
349             }
350 0   0       $elem = $elem->snext_sibling || last;
351             }
352             ### $ret
353 0           return $ret;
354             }
355              
356             sub _descend_through_lists {
357 0     0     my ($elem) = @_;
358 0   0       while ($elem
      0        
359             && ($elem->isa('PPI::Structure::List')
360             || $elem->isa('PPI::Statement::Expression')
361             || $elem->isa('PPI::Statement'))) {
362 0           $elem = $elem->schild(0);
363             }
364 0           return $elem;
365             }
366              
367             # exists(&subr) new in 5.6.0
368             #
369             sub Perl::MinimumVersion::_Pulp__exists_subr {
370 0     0     my ($pmv) = @_;
371             ### _Pulp__exists_subr() check
372             $pmv->Document->find_first
373             (sub {
374 0     0     my ($document, $elem) = @_;
375 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
      0        
376             && $elem eq 'exists'
377             && Perl::Critic::Utils::is_function_call($elem)
378             && ($elem = _symbol_or_list_symbol($elem->snext_sibling))
379             && $elem->symbol_type eq '&') {
380 0           return 1;
381             } else {
382 0           return 0;
383             }
384 0           });
385             }
386              
387             # 0b110011 binary literals new in 5.6.0
388             #
389             sub Perl::MinimumVersion::_Pulp__0b_number {
390 0     0     my ($pmv) = @_;
391             ### _Pulp__0b_number() check
392             $pmv->Document->find_first
393             (sub {
394 0     0     my ($document, $elem) = @_;
395 0 0         if ($elem->isa('PPI::Token::Number::Binary')) {
396 0           return 1;
397             } else {
398 0           return 0;
399             }
400 0           });
401             }
402              
403             # syswrite($fh,$str) length optional in 5.6.0
404             #
405             sub Perl::MinimumVersion::_Pulp__syswrite_length_optional {
406 0     0     my ($pmv) = @_;
407             ### _Pulp__syswrite_length_optional() check
408             $pmv->Document->find_first
409             (sub {
410 0     0     my ($document, $elem) = @_;
411 0           my @args;
412 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
413             && $elem eq 'syswrite'
414             && Perl::Critic::Utils::is_function_call($elem)
415             && (@args = Perl::Critic::Utils::parse_arg_list($elem)) == 2) {
416 0           return 1;
417             } else {
418 0           return 0;
419             }
420 0           });
421             }
422              
423             # open(my $fh,...) auto-creating a handle glob new in 5.6.0
424             #
425             my %open_func = (open => 1,
426             opendir => 1,
427             pipe => 2,
428             socketpair => 2,
429             sysopen => 1,
430             socket => 1,
431             accept => 1);
432             sub Perl::MinimumVersion::_Pulp__open_my_filehandle {
433 0     0     my ($pmv) = @_;
434             ### _Pulp__open_my_filehandle() check
435             $pmv->Document->find_first
436             (sub {
437 0     0     my ($document, $elem) = @_;
438 0           my ($count, $my, $fh);
439 0 0 0       unless ($elem->isa('PPI::Token::Word')
      0        
440             && ($count = $open_func{$elem})
441             && Perl::Critic::Utils::is_function_call($elem)) {
442 0           return 0;
443             }
444 0           $my = $elem->snext_sibling;
445              
446             # with parens is
447             # PPI::Token::Word 'open'
448             # PPI::Structure::List ( ... )
449             # PPI::Statement::Variable
450             # PPI::Token::Word 'my'
451             # PPI::Token::Symbol '$fh'
452             # PPI::Token::Operator ','
453             #
454 0 0         if ($my->isa('PPI::Structure::List')) {
455 0   0       $my = $my->schild(0) || return 0;
456             }
457 0 0         if ($my->isa('PPI::Statement::Variable')) {
458 0   0       $my = $my->schild(0) || return 0;
459             }
460              
461 0           foreach (1 .. $count) {
462             ### my: "$my"
463 0 0         if (_is_uninitialized_my($my)) {
464 0           return 1;
465             }
466 0   0       $my = _skip_to_next_arg($my) || last;
467             }
468 0           return 0;
469 0           });
470             }
471              
472             sub _is_uninitialized_my {
473 0     0     my ($my) = @_;
474 0           my ($fh, $after);
475 0   0       return ($my->isa('PPI::Token::Word')
476             && $my eq 'my'
477             && ($fh = $my->snext_sibling)
478             && $fh->isa('PPI::Token::Symbol')
479             && $fh->symbol_type eq '$'
480             && ! (($after = $fh->snext_sibling)
481             && $after->isa('PPI::Token::Operator')
482             && $after eq '='));
483             }
484              
485             # FIXME: is this enough for prototyped funcalls in the args?
486             sub _skip_to_next_arg {
487 0     0     my ($elem) = @_;
488 0           for (;;) {
489 0   0       my $next = $elem->snext_sibling || return undef;
490 0 0 0       if ($elem->isa('PPI::Token::Operator')
491             && $Perl::Critic::Pulp::Utils::COMMA{$elem}) {
492 0           return $next;
493             }
494 0           $elem = $next;
495             }
496             }
497              
498             # $obj->$method; omit parens new in 5.6.0
499             # previously required parens like $obj->$method();
500             #
501             sub Perl::MinimumVersion::_Pulp__var_method_without_parens {
502 0     0     my ($pmv) = @_;
503             ### _Pulp__var_method_without_parens() ...
504             $pmv->Document->find_first
505             (sub {
506 0     0     my ($document, $elem) = @_;
507 0           my $next;
508 0 0 0       if ($elem->isa('PPI::Token::Symbol')
      0        
      0        
      0        
509             && $elem->symbol_type eq '$'
510             && Perl::Critic::Utils::is_method_call($elem)
511             # must be followed by "()" for earlier perl, so if not then it
512             # means 5.6.0 required
513             && ! (($next = $elem->snext_sibling)
514             && $next->isa('PPI::Structure::List'))) {
515 0           return 1;
516             } else {
517 0           return 0;
518             }
519 0           });
520             }
521              
522             #-----------------------------------------------------------------------------
523             # Foo::Bar:: bareword new in 5.005
524             # generally a compile-time syntax error in 5.004
525             #
526             sub Perl::MinimumVersion::_Pulp__bareword_double_colon {
527 0     0     my ($pmv) = @_;
528             ### _Pulp__bareword_double_colon() check
529             $pmv->Document->find_first
530             (sub {
531 0     0     my ($document, $elem) = @_;
532 0 0 0       if ($elem->isa('PPI::Token::Word')
533             && $elem =~ /::$/) {
534 0           return 1;
535             } else {
536 0           return 0;
537             }
538 0           });
539             }
540              
541             # my ($x, undef, $y), undef in a my() list new in 5.005
542             # usually something like my (undef, $x) = @values
543             #
544             sub Perl::MinimumVersion::_Pulp__my_list_with_undef {
545 0     0     my ($pmv) = @_;
546             ### _Pulp__my_list_with_undef() check
547             $pmv->Document->find_first
548             (sub {
549 0     0     my ($document, $elem) = @_;
550 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
551             && $elem eq 'my'
552             && _list_contains_undef ($elem->snext_sibling)) {
553 0           return 1;
554             } else {
555 0           return 0;
556             }
557 0           });
558             }
559              
560             # $elem is a PPI::Element or false
561             # return true if it's a list and there's an 'undef' element in the list
562             #
563             # PPI::Structure::List ( ... )
564             # PPI::Statement::Expression
565             # PPI::Token::Symbol '$x'
566             # PPI::Token::Operator ','
567             # PPI::Token::Word 'undef'
568             # PPI::Token::Operator ','
569             # PPI::Token::Symbol '$y'
570             #
571             # Or for multi-parens: my ((undef)) with PPI::Statement in the middle
572             #
573             # PPI::Structure::List ( ... )
574             # PPI::Statement
575             # PPI::Structure::List ( ... )
576             # PPI::Statement::Expression
577             # PPI::Token::Word 'undef'
578             #
579             sub _list_contains_undef {
580 0     0     my ($elem) = @_;
581             ### _list_contains_undef: "$elem"
582 0 0         $elem or return;
583 0 0         $elem->isa('PPI::Structure::List') or return;
584 0           my @search = ($elem);
585 0           while (@search) {
586 0           $elem = pop @search;
587             ### elem: "$elem"
588 0 0 0       if ($elem->isa('PPI::Structure::List')
    0 0        
      0        
589             || $elem->isa('PPI::Statement::Expression')
590             || $elem->isa('PPI::Statement')) {
591 0           push @search, $elem->schildren;
592             } elsif ($elem->isa('PPI::Token::Word')
593             && $elem eq 'undef') {
594 0           return 1;
595             }
596             }
597             }
598              
599              
600             #-----------------------------------------------------------------------------
601             # pack() / unpack()
602             #
603             # Nothing new in 5.12, nothing new in 5.14.
604              
605             sub Perl::MinimumVersion::_Pulp__pack_format_5004 {
606 0     0     my ($pmv) = @_;
607             # w - BER integer
608 0           return _pack_format ($pmv, qr/w/);
609             }
610             sub Perl::MinimumVersion::_Pulp__pack_format_5006 {
611 0     0     my ($pmv) = @_;
612             # Z - asciz
613             # q - signed quad
614             # Q - unsigned quad
615             # ! - native size
616             # / - counted string
617             # # - comment
618 0           return _pack_format ($pmv, qr{[ZqQ!/#]});
619             }
620             sub Perl::MinimumVersion::_Pulp__pack_format_5008 {
621 0     0     my ($pmv) = @_;
622             # F - NV
623             # D - long double
624             # j - IV
625             # J - UV
626             # ( - group
627             # [ - in a repeat count like "L[20]"
628 0           return _pack_format ($pmv, qr/[FDjJ([]/);
629             }
630             sub Perl::MinimumVersion::_Pulp__pack_format_5010 {
631 0     0     my ($pmv) = @_;
632             # < - little endian
633             # > - big endian
634 0           return _pack_format ($pmv, qr/[<>]/);
635             }
636             # Think nothing new in 5012 ...
637              
638             my %pack_func = (pack => 1, unpack => 1);
639             sub _pack_format {
640 0     0     my ($pmv, $regexp) = @_;
641 0           require Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders;
642             $pmv->Document->find_first
643             (sub {
644 0     0     my ($document, $elem) = @_;
645              
646 0 0         $elem->isa ('PPI::Token::Word') || return 0;
647 0 0         $pack_func{$elem->content} || return 0;
648 0 0         Perl::Critic::Utils::is_function_call($elem) || return 0;
649              
650 0           my @args = parse_arg_list ($elem);
651 0           my $format_arg = $args[0];
652             ### format: @$format_arg
653              
654 0           my ($str, $any_vars) = Perl::Critic::Policy::Miscellanea::TextDomainPlaceholders::_arg_string ($format_arg, $document);
655             ### $str
656             ### $any_vars
657              
658 0 0         if ($any_vars) { return 0; }
  0            
659 0           return ($str =~ $regexp);
660 0           });
661             }
662              
663             # 5.004 new __PACKAGE__
664             #
665             sub Perl::MinimumVersion::_Pulp__special_literal__PACKAGE__ {
666 0     0     my ($pmv) = @_;
667             ### _Pulp__special_literal__PACKAGE__
668             $pmv->Document->find_first
669             (sub {
670 0     0     my ($document, $elem) = @_;
671 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
672             && $elem eq '__PACKAGE__'
673             && ! Perl::Critic::Utils::is_hash_key($elem)) {
674 0           return 1;
675             } else {
676 0           return 0;
677             }
678 0           });
679             }
680              
681             # 5.004 new "use VERSION"
682             #
683             # "use MODULE VERSION" is not as easy, fairly sure it depends whether the
684             # target module uses Exporter.pm or not since the VERSION part is passed to
685             # import() and Exporter.pm checks it.
686             #
687             sub Perl::MinimumVersion::_Pulp__use_version_number {
688 0     0     my ($pmv) = @_;
689             ### _Pulp__use_version_number
690             $pmv->Document->find_first
691             (sub {
692 0     0     my ($document, $elem) = @_;
693 0 0         $elem->isa('PPI::Statement::Include') or return 0;
694 0 0         $elem->type eq 'use' or return 0;
695 0 0         if ($elem->version ne '') { # empty string '' for not a "use VERSION"
696 0           return 1;
697             } else {
698 0           return 0;
699             }
700 0           });
701             }
702              
703             # 5.004 new "foreach my $i" lexical loop variable
704             #
705             sub Perl::MinimumVersion::_Pulp__for_loop_variable_using_my {
706 0     0     my ($pmv) = @_;
707             ### _Pulp__for_loop_variable_using_my
708             $pmv->Document->find_first
709             (sub {
710 0     0     my ($document, $elem) = @_;
711 0 0         $elem->isa('PPI::Statement::Compound') or return 0;
712 0 0         $elem->type eq 'foreach' or return 0;
713 0   0       my $second = $elem->schild(1) || return 0;
714 0 0         $second->isa('PPI::Token::Word') or return 0;
715 0 0         if ($second eq 'my') {
716 0           return 1;
717             } else {
718 0           return 0;
719             }
720 0           });
721             }
722              
723             # 5.004 new "$foo->(PARAMS)" coderef call
724             #
725             sub Perl::MinimumVersion::_Pulp__arrow_coderef_call {
726 0     0     my ($pmv) = @_;
727             ### _Pulp__arrow_coderef_call
728             $pmv->Document->find_first
729             (sub {
730 0     0     my ($document, $elem) = @_;
731 0 0         $elem->isa('PPI::Token::Operator') or return 0;
732             ### operator: "$elem"
733 0 0         $elem eq '->' or return 0;
734 0   0       $elem = $elem->snext_sibling || return 0;
735             ### next: "$elem"
736 0 0         if ($elem->isa('PPI::Structure::List')) {
737 0           return 1;
738             } else {
739 0           return 0;
740             }
741 0           });
742             }
743              
744             # 5.004 new sysseek() function
745             #
746             # Crib note: the prototype() function is newly documented in 5.004 but
747             # existed earlier, or something. Might have returned a trailing "\0" in
748             # 5.003.
749             #
750             sub Perl::MinimumVersion::_Pulp__sysseek_builtin {
751 0     0     my ($pmv) = @_;
752             ### _Pulp__sysseek_builtin
753             $pmv->Document->find_first
754             (sub {
755 0     0     my ($document, $elem) = @_;
756 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
757             && ($elem eq 'sysseek' || $elem eq 'CORE::sysseek')
758             && Perl::Critic::Utils::is_function_call ($elem)) {
759 0           return 1;
760             } else {
761 0           return 0;
762             }
763 0           });
764             }
765              
766              
767             #---------------------------------------------------------------------------
768             # UNIVERSAL.pm methods
769             #
770             {
771             my $methods = { VERSION => 1,
772             isa => 1,
773             can => 1 };
774             sub Perl::MinimumVersion::_Pulp__UNIVERSAL_methods_5004 {
775 0     0     my ($pmv) = @_;
776             ### _Pulp__UNIVERSAL_methods_5004() ...
777 0           return _any_method($pmv,$methods);
778             }
779             }
780             {
781             my $methods = { DOES => 1 };
782             sub Perl::MinimumVersion::_Pulp__UNIVERSAL_methods_5010 {
783 0     0     my ($pmv) = @_;
784             ### _Pulp__UNIVERSAL_methods_5010() ...
785 0           return _any_method($pmv,$methods);
786             }
787             }
788             sub _any_method {
789 0     0     my ($pmv, $hash) = @_;
790             $pmv->Document->find_first
791             (sub {
792 0     0     my ($document, $elem) = @_;
793 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
794             && $hash->{$elem}
795             && Perl::Critic::Utils::is_method_call ($elem)) {
796 0           return 1;
797             } else {
798 0           return 0;
799             }
800 0           });
801             }
802              
803              
804             #------------------------------------------------------------------------------
805             # keys @foo, values @foo, each @foo new in 5.12.0
806             #
807             sub Perl::MinimumVersion::_Pulp__keys_of_array {
808 0     0     my ($pmv) = @_;
809 0           return _keys_etc_of_array ($pmv, 'keys');
810             }
811             sub Perl::MinimumVersion::_Pulp__values_of_array {
812 0     0     my ($pmv) = @_;
813 0           return _keys_etc_of_array ($pmv, 'values');
814             }
815             sub Perl::MinimumVersion::_Pulp__each_of_array {
816 0     0     my ($pmv) = @_;
817 0           return _keys_etc_of_array ($pmv, 'each');
818             }
819             sub _keys_etc_of_array {
820 0     0     my ($pmv, $which) = @_;
821             ### _keys_etc_of_array() ...
822             $pmv->Document->find_first
823             (sub {
824 0     0     my ($document, $elem) = @_;
825 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
826             && $elem eq $which
827             && Perl::Critic::Utils::is_function_call($elem)
828             && _arg_is_array($elem->snext_sibling)) {
829 0           return 1;
830             } else {
831 0           return 0;
832             }
833 0           });
834             }
835             sub _arg_is_array {
836 0     0     my ($elem) = @_;
837             ### _arg_is_array "$elem"
838              
839 0   0       $elem = _descend_through_lists($elem) || return 0;
840              
841 0 0 0       if ($elem->isa('PPI::Token::Symbol')
842             && $elem->raw_type eq '@') {
843 0           return 1;
844             }
845 0 0 0       if ($elem->isa('PPI::Token::Cast') && $elem eq '@') {
846 0           return 1;
847             }
848 0           return 0;
849             }
850              
851              
852             #------------------------------------------------------------------------------
853             # eval '#line ...' with the #line the very first thing,
854             # the #line doesn't take effect until 5.008,
855             # in 5.006 need a blank line or something first
856              
857             {
858             my $initial_line_re = qr/^#[ \t]*line/;
859              
860             sub Perl::MinimumVersion::_Pulp__eval_line_directive_first_thing {
861 0     0     my ($pmv) = @_;
862             ### _Pulp__eval_line_directive_first_thing() ...
863             $pmv->Document->find_first
864             (sub {
865 0     0     my ($document, $elem) = @_;
866 0 0 0       if ($elem->isa('PPI::Token::Word')
      0        
      0        
      0        
867             && $elem eq 'eval'
868             && Perl::Critic::Utils::is_function_call($elem)
869             && ($elem = $elem->snext_sibling)
870             && ($elem = _descend_through_lists($elem))) {
871             ### eval of: "$elem"
872              
873 0 0         if ($elem->isa('PPI::Token::Quote')) {
    0          
874 0 0         if ($elem->string =~ $initial_line_re) {
875 0           return 1;
876             }
877             } elsif ($elem->isa('PPI::Token::HereDoc')) {
878 0           my ($str) = $elem->heredoc; # first line
879 0 0         if ($str =~ $initial_line_re) {
880 0           return 1;
881             }
882             }
883             }
884 0           return 0;
885 0           });
886             }
887             }
888              
889              
890             #---------------------------------------------------------------------------
891             # generic
892              
893             # if $elem is a symbol or a List of a symbol then return that symbol elem,
894             # otherwise return an empty list
895             #
896             sub _symbol_or_list_symbol {
897 0     0     my ($elem) = @_;
898 0 0         if ($elem->isa('PPI::Structure::List')) {
899 0   0       $elem = $elem->schild(0) || return;
900 0 0         $elem->isa('PPI::Statement::Expression') || return;
901 0   0       $elem = $elem->schild(0) || return;
902             }
903 0 0         $elem->isa('PPI::Token::Symbol') || return;
904 0           return $elem;
905             }
906              
907              
908             #---------------------------------------------------------------------------
909              
910             1;
911             __END__
912              
913             =for stopwords config MinimumVersion Pragma CPAN prereq multi-constant concats pragma endianness filehandle asciz builtin Ryde no-args parens BER lexically-scoped
914              
915             =head1 NAME
916              
917             Perl::Critic::Policy::Compatibility::PerlMinimumVersionAndWhy - explicit Perl version for features used
918              
919             =head1 DESCRIPTION
920              
921             This policy is part of the L<C<Perl::Critic::Pulp>|Perl::Critic::Pulp>
922             add-on. It requires that you have an explicit C<use 5.XXX> etc for the Perl
923             syntax features you use, as determined by
924             L<C<Perl::MinimumVersion>|Perl::MinimumVersion>.
925              
926             use 5.010; # the // operator is new in perl 5.010
927             print $x // $y; # ok
928              
929             If you don't have the C<Perl::MinimumVersion> module then nothing is
930             reported. Certain nasty hacks are used to extract reasons and locations
931             from C<Perl::MinimumVersion>.
932              
933             This policy is under the "compatibility" theme (see L<Perl::Critic/POLICY
934             THEMES>). Its best use is when it picks up things like C<//> or C<qr> which
935             are only available in a newer Perl than you meant to target.
936              
937             An explicit C<use 5.xxx> can be a little tedious, but has the advantage of
938             making it clear what's needed (or supposed to be needed) and it gives a good
939             error message if run on an older Perl.
940              
941             =head2 Disabling
942            
943             The config options below let you limit how far back to go. Or if you don't
944             care at all about this sort of thing you can always disable the policy
945             completely from your F<~/.perlcriticrc> file in the usual way (see
946             L<Perl::Critic/CONFIGURATION>),
947              
948             [-Compatibility::PerlMinimumVersionAndWhy]
949              
950             =head2 MinimumVersion Mangling
951              
952             Some mangling is applied to what C<Perl::MinimumVersion> normally reports
953             (as of its version 1.28).
954              
955             =over 4
956              
957             =item *
958              
959             A multi-constant hash with the L<C<constant>|constant> module is not
960             reported, since that's covered better by
961             L<Compatibility::ConstantPragmaHash|Perl::Critic::Policy::Compatibility::ConstantPragmaHash>.
962              
963             =item *
964              
965             Module requirements for things like C<use Errno> are dropped, since you
966             might get a back-port from CPAN etc and the need for a module is better
967             expressed in a distribution "prereq".
968              
969             But pragma modules like C<use warnings> are still reported. They're
970             normally an interface to a feature new in the Perl version it comes with and
971             can't be back-ported. (See L</OTHER NOTES> below too.)
972              
973             =back
974              
975             =head2 MinimumVersion Extras
976              
977             The following extra checks are added to C<Perl::MinimumVersion>.
978              
979             =over
980              
981             =item 5.12 for
982              
983             =over
984              
985             =item *
986              
987             new C<keys @array>, C<values @array> and C<each @array>
988              
989             =back
990              
991             =item 5.10 for
992              
993             =over
994              
995             =item *
996              
997             C<qr//m>, since "m" modifier doesn't propagate correctly on a C<qr> until
998             5.10
999              
1000             =item *
1001              
1002             C<-e -f -x> stacked filetest operators.
1003              
1004             =item *
1005              
1006             C<pack()> new C<E<lt>> and C<E<gt>> endianness.
1007              
1008             =item *
1009              
1010             new C<UNIVERSAL.pm> method C<DOES()>
1011              
1012             =back
1013              
1014             =item 5.8 for
1015              
1016             =over
1017              
1018             =item *
1019              
1020             new C<word [newline] =E<gt>> fat comma quoting across a newline
1021              
1022             For earlier Perl C<word> ended up a function call. It's presumed such code
1023             is meant to quote in the 5.8 style, and thus requires 5.8 or higher.
1024              
1025             =item *
1026              
1027             C<eval '#line ...'> with C<#line> the very first thing
1028              
1029             In earlier Perl a C<#line> as the very first thing in an C<eval> doesn't
1030             take effect. Adding a blank line so it's not first is enough.
1031              
1032             =item *
1033              
1034             C<pack()> new C<F> native NV, C<D> long double, C<i> IV, C<j> UV, C<()>
1035             group, C<[]> repeat count
1036              
1037             =back
1038              
1039             =item 5.6 for
1040              
1041             =over
1042              
1043             =item *
1044              
1045             new C<exists &subr>, C<exists $array[0]> and C<delete $array[0]>
1046              
1047             =item *
1048              
1049             new C<0b110011> binary number literals
1050              
1051             =item *
1052              
1053             new C<open(my $fh,...)> etc auto-creation of filehandle
1054              
1055             =item *
1056              
1057             C<syswrite()> length parameter optional
1058              
1059             =item *
1060              
1061             C<Foo-E<gt>$method> no-args call without parens
1062              
1063             For earlier Perl a no-args call to a method named in a variable must be
1064             C<Foo-E<gt>$method()>. The parens are optional in 5.6 up.
1065              
1066             =item *
1067              
1068             C<pack()> new C<Z> asciz, C<q>,C<Q> quads, C<!> native size, C</> counted
1069             string, C<#> comment
1070              
1071             =back
1072              
1073             =item 5.005 for
1074              
1075             =over
1076              
1077             =item *
1078              
1079             new C<Foo::Bar::> double-colon package name quoting
1080              
1081             =item *
1082              
1083             new C<my ($x, undef, $y) = @values>, using C<undef> as a dummy in a C<my>
1084             list
1085              
1086             =back
1087              
1088             =item 5.004 for
1089              
1090             =over
1091              
1092             =item *
1093              
1094             new C<use 5.xxx> Perl version check through C<use>. For earlier Perl it can
1095             be C<BEGIN { require 5.000 }> etc
1096              
1097             =item *
1098              
1099             new C<__PACKAGE__> special literal
1100              
1101             =item *
1102              
1103             new C<foreach my $foo> lexical loop variable
1104              
1105             =item *
1106              
1107             new C<$coderef-E<gt>()> call with C<-E<gt>>
1108              
1109             =item *
1110              
1111             new C<sysseek()> builtin function
1112              
1113             =item *
1114              
1115             C<pack()> new C<w> BER integer
1116              
1117             =item *
1118              
1119             new C<UNIVERSAL.pm> with C<VERSION()>, C<isa()> and C<can()> methods
1120              
1121             =back
1122              
1123             =back
1124              
1125             C<pack()> and C<unpack()> format strings are only checked if they're literal
1126             strings or here-documents without interpolations, or C<.> operator concats
1127             of those.
1128              
1129             The C<qr//m> report concerns a misfeature fixed in perl 5.10.0 (see
1130             L<perl5101delta>). In earlier versions a regexp like C<$re = qr/^x/m>
1131             within another regexp like C</zz|$re/> loses the C</m> attribute from
1132             C<$re>, changing the interpretation of the C<^> (and C<$> similarly). Forms
1133             like C<(\A|\n)> are a possible workaround, though are uncommon so may be a
1134             little obscure. C<RegularExpressions::RequireLineBoundaryMatching> asks for
1135             C</m> in all cases so if think you want that then you probably want Perl
1136             5.10 or up for the fix too.
1137              
1138             =head2 C<Modern::Perl>
1139              
1140             C<use Modern::Perl> is taken to mean Perl 5.10. This is slightly
1141             experimental and in principle the actual minimum it implies is forever
1142             rising, and even now could be more, or depends on it date argument scheme.
1143             Maybe if could say its actual current desire then an installed version could
1144             be queried.
1145              
1146             =head1 CONFIGURATION
1147              
1148             =over 4
1149              
1150             =item C<above_version> (version string, default none)
1151              
1152             Set a minimum version of Perl you always use, so that reports are only about
1153             things higher than this and higher than what the document declares. The
1154             value is anything the L<C<version.pm>|version> module can parse.
1155              
1156             [Compatibility::PerlMinimumVersionAndWhy]
1157             above_version = 5.006
1158              
1159             For example if you always use Perl 5.6 and set 5.006 like this then you can
1160             have C<our> package variables without an explicit C<use 5.006>.
1161              
1162             =item C<skip_checks> (list of check names, default none)
1163              
1164             Skip the given MinimumVersion checks (a space separated list). The check
1165             names are shown in the violation message and come from
1166             C<Perl::MinimumVersion::CHECKS>. For example,
1167              
1168             [Compatibility::PerlMinimumVersionAndWhy]
1169             skip_checks = _some_thing _another_thing
1170              
1171             This can be used for checks you believe are wrong, or where the
1172             compatibility matter only affects limited circumstances which you
1173             understand.
1174              
1175             The check names are likely to be a moving target, especially the Pulp
1176             additions. Unknown checks in the list are quietly ignored.
1177              
1178             =back
1179              
1180             =head1 OTHER NOTES
1181              
1182             C<use warnings> is reported as a Perl 5.6 feature since the lexically-scoped
1183             fine grain warnings control it gives is new in that version. If targeting
1184             earlier versions then it's often enough to drop C<use warnings>, ensure your
1185             code runs cleanly under S<< C<perl -w> >>, and leave it to applications to
1186             use C<-w> (or set C<$^W>) if they desire.
1187              
1188             C<warnings::compat> offers a C<use warnings> for earlier Perl, but it's not
1189             lexical, instead setting C<$^W> globally. In a script this might be an
1190             alternative to S<C<#!/usr/bin/perl -w>> (per L<perlrun>), but in a module
1191             it's probably not a good idea to change global settings.
1192              
1193             The C<UNIVERSAL.pm> methods C<VERSION()>, C<isa()>, C<can()> or C<DOES()>
1194             might in principle be implemented explicitly by a particular class, but it's
1195             assumed that's not so and that any call to those requires the respective
1196             minimum Perl version.
1197              
1198             =head1 SEE ALSO
1199              
1200             L<Perl::Critic::Pulp>,
1201             L<Perl::Critic>
1202              
1203             L<Perl::Critic::Policy::Modules::PerlMinimumVersion>, which is similar, but
1204             compares against a Perl version configured in your F<~/.perlcriticrc> rather
1205             than a version in the document.
1206              
1207             L<Perl::Critic::Policy::Modules::RequirePerlVersion>
1208              
1209             =head1 HOME PAGE
1210              
1211             L<http://user42.tuxfamily.org/perl-critic-pulp/index.html>
1212              
1213             =head1 COPYRIGHT
1214              
1215             Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Kevin Ryde
1216              
1217             Perl-Critic-Pulp is free software; you can redistribute it and/or modify it
1218             under the terms of the GNU General Public License as published by the Free
1219             Software Foundation; either version 3, or (at your option) any later
1220             version.
1221              
1222             Perl-Critic-Pulp is distributed in the hope that it will be useful, but
1223             WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
1224             or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
1225             more details.
1226              
1227             You should have received a copy of the GNU General Public License along with
1228             Perl-Critic-Pulp. If not, see <http://www.gnu.org/licenses/>.
1229              
1230             =cut