File Coverage

blib/lib/Perl/MinimumVersion.pm
Criterion Covered Total %
statement 437 484 90.2
branch 278 394 70.5
condition 109 166 65.6
subroutine 95 102 93.1
pod 7 12 58.3
total 926 1158 79.9


line stmt bran cond sub pod time code
1             package Perl::MinimumVersion;
2             $Perl::MinimumVersion::VERSION = '1.44';
3             =pod
4              
5             =head1 NAME
6              
7             Perl::MinimumVersion - Find a minimum required version of perl for Perl code
8              
9             =head1 SYNOPSIS
10              
11             # Create the version checking object
12             $object = Perl::MinimumVersion->new( $filename );
13             $object = Perl::MinimumVersion->new( \$source );
14             $object = Perl::MinimumVersion->new( $ppi_document );
15              
16             # Find the minimum version
17             $version = $object->minimum_version;
18              
19             =head1 DESCRIPTION
20              
21             C takes Perl source code and calculates the minimum
22             version of perl required to be able to run it. Because it is based on
23             L, it can do this without having to actually load the code.
24              
25             Currently it tests both the syntax of your code, and the use of explicit
26             version dependencies such as C.
27              
28             Future plans are to also add support for tracing module dependencies.
29              
30             Using C is dead simple, the synopsis pretty much
31             covers it.
32              
33             The distribution comes with a script called L,
34             which is the easiest way to run C on your code:
35              
36             % perlver lib/Foo/Bar.pm
37              
38             See the L for more details.
39              
40             =head1 METHODS
41              
42             =cut
43              
44 21     21   2836088 use 5.006;
  21         95  
45 21     21   143 use strict;
  21         67  
  21         779  
46 21     21   151 use warnings;
  21         39  
  21         1397  
47 21     21   9174 use version 0.76 ();
  21         45002  
  21         847  
48 21     21   159 use Carp ();
  21         39  
  21         354  
49 21     21   108 use Exporter ();
  21         32  
  21         977  
50 21     21   123 use List::Util 1.20 qw(max first);
  21         400  
  21         1990  
51 21     21   10880 use Params::Util 0.25 ('_INSTANCE', '_CLASS');
  21         150541  
  21         2322  
52 21     21   9589 use PPI::Util ('_Document');
  21         17591  
  21         1530  
53 21     21   10762 use PPI 1.252 ();
  21         4300766  
  21         1148  
54 21         6031 use PPIx::Utils qw{
55             :classification
56             :traversal
57 21     21   9994 };
  21         395539  
58 21     21   14182 use PPIx::Regexp 0.051;
  21         2780776  
  21         922  
59 21     21   12657 use Perl::MinimumVersion::Reason ();
  21         80  
  21         7095  
60              
61             our (@ISA, @EXPORT_OK, %CHECKS, @CHECKS_RV ,%MATCHES);
62             BEGIN {
63             # Export the PMV convenience constant
64 21     21   463 @ISA = 'Exporter';
65 21         67 @EXPORT_OK = 'PMV';
66              
67             # The primary list of version checks
68 21         2533 %CHECKS = (
69             _heredoc_indent => version->new('5.025.007'),
70             _double_diamond_operator => version->new('5.021.005'),
71             _postfix_deref => version->new('5.020'),
72              
73             # _stacked_labels => version->new('5.014'),
74              
75             _yada_yada_yada => version->new('5.012'),
76             _pkg_name_version => version->new('5.012'),
77             _postfix_when => version->new('5.012'),
78             _perl_5012_pragmas => version->new('5.012'),
79             _while_readdir => version->new('5.012'),
80              
81             _perl_5010_pragmas => version->new('5.010'),
82             _perl_5010_operators => version->new('5.010'),
83             _perl_5010_magic => version->new('5.010'),
84             _state_declaration => version->new('5.010'),
85              
86             # Various small things
87             _bugfix_magic_errno => version->new('5.008.003'),
88             _is_utf8 => version->new('5.008.001'),
89             _unquoted_versions => version->new('5.008.001'),
90             _perl_5008_pragmas => version->new('5.008'),
91             _constant_hash => version->new('5.008'),
92             _local_soft_reference => version->new('5.008'),
93             _use_carp_version => version->new('5.008'),
94             _open_temp => version->new('5.008'),
95             _open_scalar => version->new('5.008'),
96             _internals_svreadonly => version->new('5.008'),
97              
98             # Included in 5.6. Broken until 5.8
99             _pragma_utf8 => version->new('5.008'),
100             );
101 21         121 @CHECKS_RV = ( #subs that return version
102             '_feature_bundle', '_regex', '_re_flags', '_each_argument', '_binmode_2_arg',
103             '_scheduled_blocks', '_experimental_bundle',
104             );
105              
106             # Predefine some indexes needed by various check methods
107 21         200502 %MATCHES = (
108             _perl_5012_pragmas => {
109             deprecate => 1,
110             },
111             _perl_5010_pragmas => {
112             mro => 1,
113             feature => 1,
114             },
115             _perl_5010_operators => {
116             '//' => 1,
117             '//=' => 1,
118             '~~' => 1,
119             },
120             _perl_5010_magic => {
121             '%+' => 1,
122             '%-' => 1,
123             },
124             _perl_5008_pragmas => {
125             threads => 1,
126             'threads::shared' => 1,
127             sort => 1,
128             encoding => 1,
129             },
130             );
131             }
132              
133             sub PMV () { 'Perl::MinimumVersion' }
134              
135              
136              
137              
138              
139             #####################################################################
140             # Constructor
141              
142             =pod
143              
144             =head2 new
145              
146             # Create the version checking object
147             $object = Perl::MinimumVersion->new( $filename );
148             $object = Perl::MinimumVersion->new( \$source );
149             $object = Perl::MinimumVersion->new( $ppi_document );
150              
151             The C constructor creates a new version checking object for a
152             L. You can also provide the document to be read as a
153             file name, or as a C reference containing the code.
154              
155             Returns a new C object, or C on error.
156              
157             =cut
158              
159             sub new {
160 205 50   205 1 6956066 my $class = ref $_[0] ? ref shift : shift;
161 205 100       1015 my $Document = _Document(shift) or return undef;
162 202   33     1567474 my $default = _INSTANCE(shift, 'version') || version->new('5.006');
163              
164             # Create the object
165 202         1734 my $self = bless {
166             Document => $Document,
167              
168             # Checking limit and default minimum version.
169             # Explicitly don't check below this version.
170             default => $default,
171              
172             # Caches for resolved versions
173             explicit => undef,
174             syntax => undef,
175             external => undef,
176             }, $class;
177              
178 202         808 $self;
179             }
180              
181             =pod
182              
183             =head2 Document
184              
185             The C accessor can be used to get the L object
186             back out of the version checker.
187              
188             =cut
189              
190             sub Document {
191             $_[0]->{Document}
192 1401     1401 1 9431 }
193              
194              
195              
196              
197              
198             #####################################################################
199             # Main Methods
200              
201             =pod
202              
203             =head2 minimum_version
204              
205             The C method is the primary method for finding the
206             minimum perl version required based on C factors in the document.
207              
208             At the present time, this is just syntax and explicit version checks,
209             as L is not yet completed.
210              
211             Returns a L object, or C on error.
212              
213             =cut
214              
215             sub minimum_version {
216 56 50   56 1 7506 my $self = _SELF(\@_) or return undef;
217 56         127 my $minimum = $self->{default}; # Sensible default
218              
219             # Is the explicit version greater?
220 56         197 my $explicit = $self->minimum_explicit_version;
221 56 50       155 return undef unless defined $explicit;
222 56 100 100     196 if ( $explicit and $explicit > $minimum ) {
223 1         2 $minimum = $explicit;
224             }
225              
226             # Is the syntax version greater?
227             # Since this is the most expensive operation (for this file),
228             # we need to be careful we don't run things we don't need to.
229 56         147 my $syntax = $self->minimum_syntax_version;
230 56 50       150 return undef unless defined $syntax;
231 56 100 66     585 if ( $syntax and $syntax > $minimum ) {
232 40         80 $minimum = $syntax;
233             }
234              
235             ### FIXME - Disabled until minimum_external_version completed
236             # Is the external version greater?
237             #my $external = $self->minimum_external_version;
238             #return undef unless defined $external;
239             #if ( $external and $external > $minimum ) {
240             # $minimum = $external;
241             #}
242              
243 56         318 $minimum;
244             }
245              
246             sub minimum_reason {
247 0 0   0 0 0 my $self = _SELF(\@_) or return undef;
248 0         0 my $minimum = $self->default_reason; # Sensible default
249              
250             # Is the explicit version greater?
251 0         0 my $explicit = $self->minimum_explicit_version;
252 0 0       0 return undef unless defined $explicit;
253 0 0 0     0 if ( $explicit and $explicit > $minimum ) {
254 0         0 $minimum = $explicit;
255             }
256              
257             }
258              
259             sub default_reason {
260             Perl::MinimumVersion::Reason->new(
261             rule => 'default',
262             version => $_[0]->{default},
263 0     0 0 0 element => undef,
264             );
265             }
266              
267             =pod
268              
269             =head2 minimum_explicit_version
270              
271             The C method checks through Perl code for the
272             use of explicit version dependencies such as.
273              
274             use 5.006;
275             require 5.005_03;
276              
277             Although there is almost always only one of these in a file, if more than
278             one are found, the highest version dependency will be returned.
279              
280             Returns a L object, false if no dependencies could be found,
281             or C on error.
282              
283             =cut
284              
285             sub minimum_explicit_version {
286 57 50   57 1 160 my $self = _SELF(\@_) or return undef;
287 57         189 my $reason = $self->minimum_explicit_reason(@_);
288 57 100       261 return $reason ? $reason->version : $reason;
289             }
290              
291             sub minimum_explicit_reason {
292 57 50   57 0 137 my $self = _SELF(\@_) or return undef;
293 57 50       182 unless ( defined $self->{explicit} ) {
294 57         167 $self->{explicit} = $self->_minimum_explicit_version;
295             }
296 57         235 return $self->{explicit};
297             }
298              
299             sub _minimum_explicit_version {
300 57 50   57   150 my $self = shift or return undef;
301             my $explicit = $self->Document->find( sub {
302 591 100   591   8103 $_[1]->isa('PPI::Statement::Include') or return '';
303 25 100       118 $_[1]->version or return '';
304 4         107 1;
305 57         153 } );
306 57 100       955 return $explicit unless $explicit;
307              
308             # Find the highest version
309 3         5 my $max = undef;
310 3         7 my $element = undef;
311 3         8 foreach my $include ( @$explicit ) {
312 4         14 my $version = version->new($include->version);
313 4 50 66     185 if ( not $element or $version > $max ) {
314 4         10 $max = $version;
315 4         14 $element = $include;
316             }
317             }
318              
319 3         27 return Perl::MinimumVersion::Reason->new(
320             rule => 'explicit',
321             version => $max,
322             element => $element,
323             );
324             }
325              
326             =pod
327              
328             =head2 minimum_syntax_version $limit
329              
330             The C method will explicitly test only the
331             Document's syntax to determine it's minimum version, to the extent
332             that this is possible.
333              
334             It takes an optional parameter of a L object defining
335             the lowest known current value. For example, if it is already known
336             that it must be 5.006 or higher, then you can provide a param of
337             qv(5.006) and the method will not run any of the tests below this
338             version. This should provide dramatic speed improvements for
339             large and/or complex documents.
340              
341             The limitations of parsing Perl mean that this method may provide
342             artificially low results, but should not artificially high results.
343              
344             For example, if C returned 5.006, you can be
345             confident it will not run on anything lower, although there is a chance
346             that during actual execution it may use some untestable feature that
347             creates a dependency on a higher version.
348              
349             Returns a L object, false if no dependencies could be found,
350             or C on error.
351              
352             =cut
353              
354             sub minimum_syntax_version {
355 62 50   62 1 175 my $self = _SELF(\@_) or return undef;
356 62         219 my $reason = $self->minimum_syntax_reason(@_);
357 62 100       312 return $reason ? $reason->version : $reason;
358             }
359              
360             sub minimum_syntax_reason {
361 65 50   65 0 749 my $self = _SELF(\@_) or return undef;
362 65         133 my $limit = shift;
363 65 100 100     243 if ( defined $limit and not _INSTANCE($limit, 'version') ) {
364 1         10 $limit = version->new("$limit");
365             }
366 65 100       172 if ( defined $self->{syntax} ) {
367 5 100 100     22 if ( !defined($limit) or $self->{syntax}->version >= $limit ) {
368             # Previously discovered minimum is what they want
369 4         16 return $self->{syntax};
370             }
371              
372             # Rather than return a value BELOW their filter,
373             # which they would not be expecting, return false.
374 1         3 return '';
375             }
376              
377             # Look for the value
378 60         188 my $syntax = $self->_minimum_syntax_version( $limit );
379              
380             # If we found a value, it will be stable, cache it.
381             # If we did NOT, don't cache as subsequent runs without
382             # the filter may find a version.
383 60 100       234 if ( $syntax ) {
384 42         93 $self->{syntax} = $syntax;
385 42         127 return $self->{syntax};
386             }
387              
388 18         61 return '';
389             }
390              
391             #for Perl::Critic::Policy::Compatibility::PerlMinimumVersionAndWhy
392             sub _set_checks2skip {
393 1     1   7 my $self = shift;
394 1         3 my $list = shift;
395 1         5 $self->{_checks2skip} = $list;
396             }
397             sub _set_collect_all_reasons {
398 1     1   7 my $self = shift;
399 1         3 my $value = shift;
400 1 50       5 $value = 1 unless defined $value;
401 1         5 $self->{_collect_all_reasons} = $value;
402             }
403              
404             sub _minimum_syntax_version {
405 60     60   98 my $self = shift;
406 60   66     561 my $filter = shift || $self->{default};
407              
408 60         172 my %checks2skip;
409 60 100       109 @checks2skip{ @{ $self->{_checks2skip} || [] } } = ();
  60         365  
410              
411 60         198 my %rv_result;
412             my $current_reason;
413 60         176 foreach my $rule ( @CHECKS_RV ) {
414 420 100       958 next if exists $checks2skip{$rule};
415 419         1460 my ($v, $obj) = $self->$rule();
416 419         2334 $v = version->new($v);
417 419 100       2752 if ( $v > $filter ) {
418 19         237 $current_reason = Perl::MinimumVersion::Reason->new(
419             rule => $rule,
420             version => $v,
421             element => _INSTANCE($obj, 'PPI::Element'),
422             );
423 19 100       95 if ($self->{_collect_all_reasons}) {
424 1         4 push @{ $self->{_all_reasons} }, $current_reason;
  1         5  
425             } else {
426 18         50 $filter = $v;
427             }
428             }
429             }
430              
431              
432             # Always check in descending version order.
433             # By doing it this way, the version of the first check that matches
434             # is also the version of the document as a whole.
435             my @rules = sort {
436 3435         7984 $CHECKS{$b} <=> $CHECKS{$a}
437             } grep {
438 60 50       488 not(exists $checks2skip{$_}) and $CHECKS{$_} > $filter
  1380         5900  
439             } keys %CHECKS;
440              
441 60         232 foreach my $rule ( @rules ) {
442 692 100       11714 my $result = $self->$rule() or next;
443              
444             # Create the result object
445             my $reason = Perl::MinimumVersion::Reason->new(
446             rule => $rule,
447 25         983 version => $CHECKS{$rule},
448             element => _INSTANCE($result, 'PPI::Element'),
449             );
450 25 50       74 if ($self->{_collect_all_reasons}) {
451 0         0 push @{ $self->{_all_reasons} }, $current_reason;
  0         0  
452             } else {
453 25         111 return $reason;
454             }
455              
456             }
457              
458             # Found nothing of interest
459 35   100     724 return $current_reason || '';
460             }
461              
462             =pod
463              
464             =head2 minimum_external_version
465              
466             B
467             an exception>
468              
469             The C examines code for dependencies on other
470             external files, and recursively traverses the dependency tree applying the
471             same tests to those files as it does to the original.
472              
473             Returns a C object, false if no dependencies could be found, or
474             C on error.
475              
476             =cut
477              
478             sub minimum_external_version {
479 0 0   0 1 0 my $self = _SELF(\@_) or return undef;
480 0         0 my $reason = $self->minimum_explicit_reason(@_);
481 0 0       0 return $reason ? $reason->version : $reason;
482             }
483              
484             sub minimum_external_reason {
485 0 0   0 0 0 my $self = _SELF(\@_) or return undef;
486 0 0       0 unless ( defined $self->{external} ) {
487 0         0 $self->{external} = $self->_minimum_external_version;
488             }
489 0         0 $self->{external};
490             }
491              
492             sub _minimum_external_version {
493 0     0   0 Carp::croak("Perl::MinimumVersion::minimum_external_version is not implemented");
494             }
495              
496             =pod
497              
498             =head2 version_markers
499              
500             This method returns a list of pairs in the form:
501              
502             ($version, \@markers)
503              
504             Each pair represents all the markers that could be found indicating that the
505             version was the minimum needed version. C<@markers> is an array of strings.
506             Currently, these strings are not as clear as they might be, but this may be
507             changed in the future. In other words: don't rely on them as specific
508             identifiers.
509              
510             =cut
511              
512             sub version_markers {
513 1 50   1 1 1701 my $self = _SELF(\@_) or return undef;
514              
515 1         4 my %markers;
516              
517 1 50       5 if ( my $explicit = $self->minimum_explicit_version ) {
518 1         7 $markers{ $explicit } = [ 'explicit' ];
519             }
520              
521 1         12 foreach my $check ( keys %CHECKS ) {
522 23 100       469 next unless $self->$check();
523 1   50     69 my $markers = $markers{ $CHECKS{$check} } ||= [];
524 1         5 push @$markers, $check;
525             }
526              
527 1         25 my @rv;
528 1         5 my %marker_ver = map { $_ => version->new($_) } keys %markers;
  2         18  
529              
530 1         8 foreach my $ver ( sort { $marker_ver{$b} <=> $marker_ver{$a} } keys %markers ) {
  1         10  
531 2         7 push @rv, $marker_ver{$ver} => $markers{$ver};
532             }
533              
534 1         10 return @rv;
535             }
536              
537              
538              
539              
540             #####################################################################
541             # Version Check Methods
542              
543             my %feature =
544             (
545             'say' => '5.10',
546             'smartmatch' => '5.10',
547             'state' => '5.10',
548             'switch' => '5.10',
549             'unicode_strings' => '5.14',
550             'unicode_eval' => '5.16',
551             'evalbytes' => '5.16',
552             'current_sub' => '5.16',
553             'array_base' => '5.16', #defined only in 5.16
554             'fc' => '5.16',
555             'lexical_subs' => '5.18',
556             'postderef' => '5.20',
557             'postderef_qq' => '5.20',
558             'signatures' => '5.20',
559             'refaliasing' => '5.22',
560             'bitwise' => '5.22',
561             'declared_refs' => '5.26',
562             'isa' => '5.32',
563             'indirect' => '5.32', #defined only in 5.32
564             );
565             my $feature_regexp = join('|', keys %feature);
566              
567             #:5.14 means same as :5.12, but :5.14 is not defined in feature.pm in perl 5.12.
568             sub _feature_bundle {
569 84     84   261 my @versions;
570 84         134 my ($version, $obj);
571             shift->Document->find( sub {
572 772 100   772   10460 $_[1]->isa('PPI::Statement::Include') or return '';
573 49 100       167 $_[1]->pragma eq 'feature' or return '';
574 31         1564 my @child = $_[1]->schildren;
575 31         636 my @args = @child[1..$#child]; # skip 'use', 'feature' and ';'
576 31         85 foreach my $arg (@args) {
577 92         147 my $v = 0;
578 92 100       241 $v = $1 if ($arg->content =~ /:(5\.\d+)(?:\.\d+)?/);
579 92 100       644 $v = max($v, $feature{$1}) if ($arg->content =~ /\b($feature_regexp)\b/);
580             #
581 92 100 100     1551 if ($v and $v > ($version || 0) ) {
      100        
582 29         59 $version = $v;
583 29         69 $obj = $_[1];
584             }
585             }
586 31         117 return '';
587 84         231 } );
588 84 100       1847 return (defined($version)?"$version.0":undef, $obj);
589             }
590              
591             # list copied from experimental.pm v0.021 itself
592             my %experimental =
593             (
594             array_base => '5',
595             autoderef => '5.14',
596             bitwise => '5.22',
597             const_attr => '5.22',
598             current_sub => '5.16',
599             declared_refs => '5.26',
600             evalbytes => '5.16',
601             fc => '5.16',
602             isa => '5.32',
603             lexical_topic => '5.10',
604             lexical_subs => '5.18',
605             postderef => '5.20',
606             postderef_qq => '5.20',
607             refaliasing => '5.22',
608             regex_sets => '5.18',
609             say => '5.10',
610             smartmatch => '5.10',
611             signatures => '5.20',
612             state => '5.10',
613             switch => '5.10',
614             unicode_eval => '5.16',
615             unicode_strings => '5.12',
616             );
617             my $experimental_regexp = join('|', keys %experimental);
618             sub _experimental_bundle {
619 60     60   103 my ($version, $obj);
620              
621             shift->Document->find( sub {
622 591 100 100 591   7654 return '' unless $_[1]->isa('PPI::Statement::Include')
623             and $_[1]->pragma eq 'experimental';
624              
625 2         103 my @child = $_[1]->schildren;
626 2         41 my @args = @child[1..$#child]; # skip 'use', 'experimental' and ';'
627 2         27 foreach my $arg (@args) {
628 5         11 my $v = 0;
629 5 50       14 $v = $1 if ($arg->content =~ /:(5\.\d+)(?:\.\d+)?/);
630 5 100       33 $v = max($v, $experimental{$1}) if ($arg->content =~ /\b($experimental_regexp)\b/);
631              
632 5 100 50     241 if ($v and $v > ($version || 0) ) {
      66        
633 1         4 $version = $v;
634 1         3 $obj = $_[1];
635             }
636             }
637 2         9 return '';
638 60         133 } );
639              
640 60 100       1146 return (defined($version)?"$version.0":undef, $obj);
641             }
642              
643             my %SCHEDULED_BLOCK =
644             (
645             'INIT' => '5.006',
646             'CHECK' => '5.006002',
647             'UNITCHECK' => '5.010',
648             );
649              
650             sub _scheduled_blocks
651             {
652 60     60   96 my @versions;
653 60         102 my ($version, $obj);
654              
655             shift->Document->find( sub {
656 591 100   591   6818 $_[1]->isa('PPI::Statement::Scheduled') or return '';
657 4 50       16 ($_[1]->children)[0]->isa('PPI::Token::Word') or return '';
658 4         67 my $function = (($_[1]->children)[0])->content;
659 4 100       41 exists( $SCHEDULED_BLOCK{ $function }) or return '';
660              
661 3         11 my $v = $SCHEDULED_BLOCK{ ($_[1]->children)[0]->content };
662 3 50 50     56 if ($v and $v > ($version || 0) ) {
      33        
663 3         7 $version = $v;
664 3         6 $obj = $_[1];
665             }
666              
667 3         7 return '';
668 60         170 } );
669 60 100       1023 return (defined($version) ? $version : undef, $obj);
670             }
671              
672             sub _regex {
673 68     68   205 my $self = shift;
674 68         102 my @versions;
675 68         117 my ($version, $obj);
676             $self->Document->find( sub {
677             return '' unless
678 606 100   606   7044 grep { $_[1]->isa($_) }
  1818         5404  
679             qw/PPI::Token::QuoteLike::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute/;
680 13         110 my $re = PPIx::Regexp->new( $_[1] );
681 13         59842 my $v = $re->perl_version_introduced;
682 13 50 50     4864 if ($v and $v > ($version || 0) ) {
      33        
683 13         31 $version = $v;
684 13         29 $obj = $_[1];
685             }
686 13         82 return '';
687 68         170 } );
688 68         2498 my $tr_r_version = version->new('5.013.007');
689             $self->Document->find( sub {
690 606 100   606   7151 return '' unless
691             $_[1]->isa(q/PPI::Token::Regexp::Transliterate/);
692 2 50 50     63 if( exists $_[1]->get_modifiers->{r}
      33        
693             && $tr_r_version > ( $version || 0 )
694             ) {
695 2         95 $version = $tr_r_version;
696 2         4 $obj = $_[1];
697             }
698 2         10 return '';
699 68         201 } );
700 68 100 100     1073 $version = undef if ($version and $version eq '5.000');
701 68         349 return ($version, $obj);
702             }
703              
704             # Check for use re "/flags";
705             sub _re_flags {
706 60     60   105 my ($version, $obj);
707             shift->Document->find( sub {
708 586 100 66 586   7957 return '' unless $_[1]->isa('PPI::Statement::Include')
      66        
709             and ($_[1]->module eq 're' or $_[1]->pragma eq 're');
710 9         224 my $included = $_[1]->schild(2);
711 9 100       274 my @literal = $included->can('literal') ? $included->literal() : $included->string();
712 8         342 my $v = "5.005";
713 8         21 my @flags = grep {index($_, '/') == 0} @literal;
  11         53  
714 8 100       27 $v = '5.014' if @flags;
715             $v = max $v, map {
716 8         25 my $empty_regex_w_flag = "/$_";
  8         1898  
717 8         67 PPIx::Regexp->new( $empty_regex_w_flag )->perl_version_introduced;
718             } @flags;
719 8 50 50     13706 if ($v and $v > ($version || 0) ) {
      33        
720 8         15 $version = $v;
721 8         59 $obj = $_[1];
722             }
723              
724 60         215 } );
725 60 50 66     1234 $version = undef if ($version and $version eq '5.000');
726 60         182 return ($version, $obj);
727             }
728              
729             sub _each_argument {
730 102     102   384 my ($version, $obj);
731             shift->Document->find( sub {
732 899 100   899   13496 $_[1]->isa('PPI::Token::Word') or return '';
733 128 100       390 $_[1]->content =~ '^(each|keys|values)$' or return '';
734 44 100       499 return '' if is_method_call($_[1]);
735 42         1929 my $next = $_[1]->snext_sibling;
736 42 100       1293 $next = $next->schild(0)->schild(0) if $next->isa('PPI::Structure::List');
737 41 100       811 if($next->isa('PPI::Token::Cast')) {
    100          
    100          
    100          
738 4 100 50     14 if($next->content eq '@' && 5.012 > ($version || 0)) {
    50 66        
      0        
      33        
739 3         53 $version = 5.012;
740 3         11 $obj = $_[1]->parent;
741             } elsif($next->content eq '$' && 5.014 > ($version || 0)) {
742 0         0 $version = 5.014;
743 0         0 $obj = $_[1]->parent;
744             }
745             } elsif($next->isa('PPI::Token::Symbol')) {
746 28 100 100     105 if($next->raw_type eq '@' && 5.012 > ($version || 0)) {
    100 100        
      100        
      66        
747 7         92 $version = 5.012;
748 7         52 $obj = $_[1]->parent;
749             } elsif($next->raw_type eq '$' && 5.014 > ($version || 0)) {
750 14         343 $version = 5.014;
751 14         42 $obj = $_[1]->parent;
752             }
753             } elsif($next->isa('PPI::Token::Operator')) { # % $a
754 1         3 return '';
755             } elsif($_[1]->parent->isa('PPI::Statement::Sub')) { # sub each|keys|values
756 2         16 return '';
757             } else { # function call or other should be reference
758 6 50 50     67 if(5.014 > ($version || 0)) {
759 6         11 $version = 5.014;
760 6         17 $obj = $_[1]->parent;
761             }
762             }
763 38 100 100     381 return 1 if ($version and $version == 5.014);
764 17         51 return '';
765 102         280 } );
766 102 100       2770 return (defined($version)?"$version":undef, $obj);
767             }
768              
769             #Is string (first argument) in list (other arguments)
770             sub _str_in_list {
771 7     7   18 my $str = shift;
772 7         20 foreach my $s (@_) {
773 11 100       43 return 1 if $s eq $str;
774             }
775 4         32 return 0;
776             }
777              
778              
779             sub _binmode_2_arg {
780 69     69   193 my ($version, $obj);
781             shift->Document->find_first( sub {
782 663     663   7463 my $main_element=$_[1];
783 663 100       2171 $main_element->isa('PPI::Token::Word') or return '';
784 82 100       222 $main_element->content eq 'binmode' or return '';
785 10 50       86 return '' if is_hash_key($main_element);
786 10 50       580 return '' if is_method_call($main_element);
787 10 50       381 return '' if is_subroutine_name($main_element);
788 10 50       363 return '' if is_included_module_name($main_element);
789 10 50       3846 return '' if is_package_declaration($main_element);
790 10         286 my @arguments = parse_arg_list($main_element);
791 10 100       2827 if ( scalar @arguments == 2 ) {
792 8         20 my $arg2=$arguments[1][0];
793 8 100       38 if ( $arg2->isa('PPI::Token::Quote')) { #check second argument
794 7         35 my $str = $arg2->string;
795 7         174 $str =~ s/^\s+//s;
796 7         32 $str =~ s/\s+$//s;
797 7         28 $str =~ s/:\s+/:/g;
798 7 100 100     22 if ( !_str_in_list( $str => qw/:raw :crlf/) and $str !~ /[\$\@\%]/) {
799 3         7 $version = 5.008;
800 3         7 $obj = $main_element;
801 3         35 return 1;
802             }
803             }
804 5 50       17 if (!$version) {
805 5         8 $version = 5.006;
806 5         11 $obj = $main_element;
807             }
808             }
809 7         32 return '';
810 69         223 } );
811 69         1452 return ($version, $obj);
812             }
813              
814              
815              
816             #http://perldoc.perl.org/functions/readdir.html
817             #while(readdir $dh) requires perl 5.12
818             sub _while_readdir {
819             shift->Document->find_first( sub {
820 474 100   474   6380 $_[1]->isa('PPI::Token::Word') or return '';
821 59 100       174 $_[1]->content eq 'while' or return '';
822 8 50       86 return '' if is_hash_key($_[1]);
823 8 50       782 return '' if is_method_call($_[1]);
824 8 100       304 my $e1 = $_[1]->next_sibling or return '';
825 7 50       188 if ($e1->isa('PPI::Structure::Condition')) { #while ()
826 7         29 my @children = $e1->children;
827 7         55 $e1 = $children[0];
828             }
829 7 50       26 $e1->isa('PPI::Statement::Expression') or return '';
830 7         30 my @children = $e1->schildren;
831 7         141 $e1 = $children[0];
832              
833 7 100       28 $e1->isa('PPI::Token::Word') or return '';
834 6 100       19 $e1->content eq 'readdir' or return '';
835 5 50       34 return 1 if @children == 1; #incorrect call
836 5 100       20 return '' if @children > 2; #not only readdir
837 3         24 $e1 = $children[1];
838 3 50 66     23 $e1->isa('PPI::Structure::List') or $e1->isa('PPI::Token::Symbol') or return '';
839             #readdir($dh) or readdir $dh
840              
841 3         16 return 1;
842 44     44   188 } );
843             }
844              
845             sub _perl_5012_pragmas {
846             shift->Document->find_first( sub {
847             $_[1]->isa('PPI::Statement::Include')
848             and
849 312 100   312   4549 $MATCHES{_perl_5012_pragmas}->{$_[1]->pragma}
850 36     36   103 } );
851             }
852              
853             sub _open_temp {
854             shift->Document->find_first( sub {
855 293 100   293   3984 $_[1]->isa('PPI::Statement') or return '';
856 43         232 my @children = $_[1]->children;
857             #@children >= 7 or return '';
858 43         284 my $main_element = $children[0];
859 43 100       246 $main_element->isa('PPI::Token::Word') or return '';
860 25 100       143 $main_element->content eq 'open' or return '';
861 7         60 my @arguments = parse_arg_list($main_element);
862 7 100 66     2116 if ( scalar @arguments == 3 and scalar(@{$arguments[2]}) == 1) {
  5         26  
863 5         10 my $arg3 = $arguments[2][0];
864 5 100 66     31 if ($arg3->isa('PPI::Token::Word') and $arg3->content eq 'undef') {
865 3         30 return 1;
866             }
867             }
868 4         21 return '';
869 27     27   116 } );
870             }
871              
872             sub _open_scalar {
873             shift->Document->find_first( sub {
874 307 100   307   4245 $_[1]->isa('PPI::Statement') or return '';
875 46         155 my @children = $_[1]->children;
876             #@children >= 7 or return '';
877 46         320 my $main_element = $children[0];
878 46 100       218 $main_element->isa('PPI::Token::Word') or return '';
879 27 100       82 $main_element->content eq 'open' or return '';
880 8         66 my @arguments = parse_arg_list($main_element);
881 8 100       2782 if ( scalar @arguments == 3) {
882 6         14 my $arg3 = $arguments[2][0];
883 6 100 66     45 if ($arg3->isa('PPI::Token::Cast') and $arg3->content eq '\\') {
884 4         63 return 1;
885             }
886             }
887 4         19 return '';
888 29     29   123 } );
889             }
890              
891             sub _get_resulting_sigil {
892 10     10   10 my $elem = shift;
893 10 50       47 if ($elem->isa('PPI::Token::Cast')) {
    50          
894 0         0 return $elem->content;
895             } elsif ($elem->isa('PPI::Token::Symbol')) {
896 10         28 return $elem->symbol_type;
897             } else {
898 0         0 return undef;
899             }
900             }
901              
902             sub _heredoc_indent {
903             shift->Document->find_first( sub {
904 570     570   6052 my $main_element = $_[1];
905 570 100       2130 $main_element->isa('PPI::Token::HereDoc') or return '';
906 2 100       9 $main_element->content =~ /^\Q<<~\E/ or return '';
907 1         10 return 1;
908 57     57   168 });
909             }
910              
911             # Postfix dereference new (and experimental) in 5.20, mainstream in 5.24.
912             # THIS CODE ASSUMES PPI 1.237_001 OR ABOVE -- i.e. support for postfix
913             # dereferencing.
914             #
915             my %postfix_deref = (
916             '$*' => \&_postfix_deref_entire,
917             '@*' => \&_postfix_deref_entire,
918             '$#*' => \&_postfix_deref_entire,
919             '%*' => \&_postfix_deref_entire,
920             '&*' => \&_postfix_deref_entire,
921             '**' => \&_postfix_deref_entire,
922             '@' => \&_postfix_deref_slice,
923             '%' => \&_postfix_deref_slice,
924             );
925              
926             sub _postfix_deref_slice {
927 4     4   10 my ( $elem ) = @_;
928 4 50       12 my $next = $elem->snext_sibling()
929             or return;
930 4         69 return $next->isa( 'PPI::Structure::Subscript' );
931             }
932              
933             sub _postfix_deref_entire {
934 6     6   14 return 1;
935             }
936              
937             sub _postfix_deref {
938             shift->Document->find_first( sub {
939 509     509   5024 my $main_element=$_[1];
940 509 100       1732 $main_element->isa('PPI::Token::Cast') or return '';
941 11 50       29 my $prev = $main_element->sprevious_sibling()
942             or return '';
943 11 100 66     342 return '' unless $prev->isa('PPI::Token::Operator') &&
944             $prev->content() eq '->';
945 10 50       71 $prev = $prev->sprevious_sibling()
946             or return '';
947 10 50 50     222 return '' unless $prev->isa('PPI::Token::Symbol') &&
      33        
948             (_get_resulting_sigil($prev) || '') eq '$';
949 10 50       475 my $code = $postfix_deref{ $main_element->content() }
950             or return '';
951 10   50     56 return $code->( $main_element ) || '';
952 54     54   153 } );
953             }
954              
955             sub _postfix_when {
956             shift->Document->find_first( sub {
957 465     465   4749 my $main_element=$_[1];
958 465 100       1564 $main_element->isa('PPI::Token::Word') or return '';
959 57 100       161 $main_element->content eq 'when' or return '';
960 5 50       57 return '' if is_hash_key($main_element);
961 5 50       434 return '' if is_method_call($main_element);
962 5 50       169 return '' if is_subroutine_name($main_element);
963 5 50       184 return '' if is_included_module_name($main_element);
964 5 50       119 return '' if is_package_declaration($main_element);
965 5         134 my $stmnt = $main_element->statement();
966 5 50       48 return '' if !$stmnt;
967 5 100       27 return '' if $stmnt->isa('PPI::Statement::When');
968 3         8 return 1;
969 42     42   159 } );
970             }
971              
972             sub _yada_yada_yada {
973             shift->Document->find_first( sub {
974 469 100 100 469   9875 $_[1]->isa('PPI::Token::Operator')
975             and $_[1]->content eq '...' or return '';
976 8         71 my @child = $_[1]->parent->schildren;
977 8 100       155 @child == 1 and return 1;
978 3 100       16 if (@child == 2) {
979 1         7 $child[1]->isa('PPI::Token::Structure')
980             }
981 46     46   155 } );
982             }
983              
984             sub _state_declaration {
985             shift->Document->find_first( sub {
986 300 100 66 300   3747 $_[1]->isa('PPI::Statement::Variable')
987             and ($_[1]->children)[0]->isa('PPI::Token::Word')
988             and ($_[1]->children)[0]->content eq 'state'
989 31     31   94 } );
990             }
991              
992             sub _stacked_labels {
993             shift->Document->find_first( sub {
994 0 0   0   0 $_[1]->isa('PPI::Statement::Compound') || return '';
995 0 0       0 $_[1]->schild(0)->isa('PPI::Token::Label') || return '';
996              
997 0   0     0 my $next = $_[1]->snext_sibling || return '';
998              
999 0 0 0     0 if ( $next->isa('PPI::Statement::Compound')
1000             && $next->schild(0)->isa('PPI::Token::Label')) {
1001 0         0 return 1;
1002             }
1003              
1004 0         0 0;
1005 0     0   0 } );
1006             }
1007              
1008             sub _internals_svreadonly {
1009             shift->Document->find_first( sub {
1010 236 100 100 236   3638 $_[1]->isa('PPI::Statement')
1011             and ($_[1]->children)[0]->isa('PPI::Token::Word')
1012             and ($_[1]->children)[0]->content eq 'Internals::SvREADONLY'
1013 25     25   92 } );
1014             }
1015              
1016             sub _pkg_name_version {
1017             shift->Document->find_first( sub {
1018 420 100   420   5277 $_[1]->isa('PPI::Statement::Package') or return '';
1019 9         31 my @child = $_[1]->schildren();
1020 9 50       190 $child[0]->isa('PPI::Token::Word') or return '';
1021 9 50       31 $child[0]->content eq 'package' or return '';
1022 9 50       56 $child[1]->isa('PPI::Token::Word') or return '';
1023 9 100       56 $child[2]->isa('PPI::Token::Number') or return '';
1024 6         18 return 1;
1025 46     46   167 } );
1026             }
1027              
1028             sub _perl_5010_pragmas {
1029             shift->Document->find_first( sub {
1030             $_[1]->isa('PPI::Statement::Include')
1031             and
1032 244 100   244   3210 $MATCHES{_perl_5010_pragmas}->{$_[1]->pragma}
1033 30     30   661 } );
1034             }
1035              
1036             sub _perl_5010_operators {
1037             shift->Document->find_first( sub {
1038             $_[1]->isa('PPI::Token::Operator')
1039             and
1040 323 100   323   4054 $MATCHES{_perl_5010_operators}->{$_[1]->content}
1041 30     30   88 } );
1042             }
1043              
1044             sub _perl_5010_magic {
1045             shift->Document->find_first( sub {
1046             $_[1]->isa('PPI::Token::Magic')
1047             and
1048 349 100   349   4119 $MATCHES{_perl_5010_magic}->{$_[1]->symbol}
1049 34     34   135 } );
1050             }
1051              
1052             sub _perl_5008_pragmas {
1053             shift->Document->find_first( sub {
1054             $_[1]->isa('PPI::Statement::Include')
1055             and
1056 180 100   180   2496 $MATCHES{_perl_5008_pragmas}->{$_[1]->pragma}
1057 21     21   62 } );
1058             }
1059              
1060             # 5.8.3: Reading $^E now preserves $!. Previously, the C code implementing $^E did not preserve errno, so reading $^E could cause errno and therefore $! to change unexpectedly.
1061             sub _bugfix_magic_errno {
1062 26     26   711 my $Document = shift->Document;
1063             my $element = $Document->find_first( sub {
1064 291     291   3279 $_[1]->isa('PPI::Token::Magic')
1065             and
1066             $_[1]->symbol eq '$^E'
1067 26   100     127 } ) || return undef;
1068             #$^E is more rare than $!, so search for it first and return it
1069             $Document->find_any( sub {
1070 4 100   4   52 $_[1]->isa('PPI::Token::Magic')
1071             and
1072             $_[1]->symbol eq '$!'
1073 2 50       70 } ) || return '';
1074 2         68 return $element;
1075             }
1076              
1077             # utf8::is_utf requires 5.8.1 unlike the rest of utf8
1078             sub _is_utf8 {
1079             shift->Document->find_first( sub {
1080 279 100   279   3863 $_[1]->isa('PPI::Token::Word') or return '';
1081 31 50       119 $_[1] eq 'utf8::is_utf' or return '';
1082 0         0 return 1;
1083 24     24   68 } );
1084             }
1085              
1086             # version->new(5.005.004);
1087             sub _unquoted_versions {
1088             shift->Document->find_first( sub {
1089 279 100   279   3267 $_[1]->isa('PPI::Token::Number') or return '';
1090 17 50       44 $_[1]->{_subtype} or return '';
1091 0 0       0 $_[1]->{_subtype} eq 'base256' or return '';
1092 0 0       0 my $stmt = $_[1]->parent or return '';
1093 0 0       0 my $braces = $stmt->parent or return '';
1094 0 0       0 $braces->isa('PPI::Structure') or return '';
1095 0 0       0 $braces->braces eq '()' or return '';
1096 0 0       0 my $new = $braces->previous_sibling or return '';
1097 0 0       0 $new->isa('PPI::Token::Word') or return '';
1098 0 0       0 $new->content eq 'new' or return '';
1099 0 0       0 my $method = $new->previous_sibling or return '';
1100 0 0       0 $method->isa('PPI::Token::Operator') or return '';
1101 0 0       0 $method->content eq '->' or return '';
1102 0 0       0 my $_class = $method->previous_sibling or return '';
1103 0 0       0 $_class->isa('PPI::Token::Word') or return '';
1104 0 0       0 $_class->content eq 'version' or return '';
1105 0         0 1;
1106 24     24   74 } );
1107             }
1108              
1109             sub _pragma_utf8 {
1110             shift->Document->find_first( sub {
1111 217 100 100 217   3414 $_[1]->isa('PPI::Statement::Include')
      66        
      66        
1112             and
1113             (
1114             ($_[1]->module and $_[1]->module eq 'utf8')
1115             or
1116             ($_[1]->pragma and $_[1]->pragma eq 'utf8')
1117             )
1118             # This used to be just pragma(), but that was buggy in PPI v1.118
1119 21     21   72 } );
1120             }
1121              
1122             # Check for the use of 'use constant { ... }'
1123             sub _constant_hash {
1124             shift->Document->find_first( sub {
1125 244 100 66 244   3555 $_[1]->isa('PPI::Statement::Include')
      100        
      100        
1126             and
1127             $_[1]->type
1128             and
1129             $_[1]->type eq 'use'
1130             and
1131             $_[1]->module eq 'constant'
1132             and
1133             $_[1]->schild(2)->isa('PPI::Structure')
1134 23     23   68 } );
1135             }
1136              
1137             # You can't localize a soft reference
1138             sub _local_soft_reference {
1139             shift->Document->find_first( sub {
1140 228 100   228   2753 $_[1]->isa('PPI::Statement::Variable') or return '';
1141 3 100       14 $_[1]->type eq 'local' or return '';
1142              
1143             # The second child should be a '$' cast.
1144 2         82 my @child = $_[1]->schildren;
1145 2 50       30 scalar(@child) >= 2 or return '';
1146 2 50       5 $child[1]->isa('PPI::Token::Cast') or return '';
1147 2 50       5 $child[1]->content eq '$' or return '';
1148              
1149             # The third child should be a block
1150 2 50       10 $child[2]->isa('PPI::Structure::Block') or return '';
1151              
1152             # Inside the block should be a string in a statement
1153 2 50       4 my $statement = $child[2]->schild(0) or return '';
1154 2 50       29 $statement->isa('PPI::Statement') or return '';
1155 2 50       5 my $inside = $statement->schild(0) or return '';
1156 2 50       19 $inside->isa('PPI::Token::Quote') or return '';
1157              
1158             # This is indeed a localized soft reference
1159 2         4 return 1;
1160 23     23   656 } );
1161             }
1162              
1163             # Carp.pm did not have a $VERSION in 5.6.2
1164             # Therefore, even "use Carp 0" imposes a 5.8.0 dependency.
1165             sub _use_carp_version {
1166             shift->Document->find_first( sub {
1167 226 100   226   3001 $_[1]->isa('PPI::Statement::Include') or return '';
1168 9 50       63 $_[1]->module eq 'Carp' or return '';
1169              
1170 0         0 my $version = $_[1]->module_version;
1171 0   0     0 return !! ( defined $version and length "$version" );
1172 21     21   61 } );
1173             }
1174              
1175             # Double-diamond operator.
1176             # Detecting this requires at least PPI 1.252
1177             sub _double_diamond_operator {
1178             shift->Document->find_first( sub {
1179 561 100   561   6718 $_[1]->isa('PPI::Token::QuoteLike::Readline') or return '';
1180 2 100       10 $_[1]->content eq '<<>>' or return '';
1181 1         9 return 1;
1182 59     59   178 } );
1183             }
1184              
1185             #####################################################################
1186             # Support Functions
1187              
1188             # Let sub be a function, object method, and static method
1189             sub _SELF {
1190 298     298   468 my $param = shift;
1191 298 100       1408 if ( _INSTANCE($param->[0], 'Perl::MinimumVersion') ) {
1192 296         893 return shift @$param;
1193             }
1194 2 50 33     14 if (
1195             _CLASS($param->[0])
1196             and
1197             $param->[0]->isa('Perl::MinimumVersion')
1198             ) {
1199 2         42 my $class = shift @$param;
1200 2         5 my $options = shift @$param;
1201 2         7 return $class->new($options);
1202             }
1203 0         0 Perl::MinimumVersion->new(shift @$param);
1204             }
1205              
1206             # Find the maximum version, ignoring problems
1207             sub _max {
1208 18 100 100 18   505159 defined $_[0] and "$_[0]" eq PMV and shift;
1209              
1210             # Filter and prepare for a Schwartian maximum
1211             my @valid = map {
1212 26 50       89 [ $_, $_->isa('Perl::MinimumVersion::Reason') ? $_->version : $_ ]
1213             } grep {
1214 18 50       48 _INSTANCE($_, 'Perl::MinimumVersion::Reason')
  36 100       432  
1215             or
1216             _INSTANCE($_, 'version')
1217             } @_ or return '';
1218              
1219             # Find the maximum
1220 12         16 my $max = shift @valid;
1221 12         22 foreach my $it ( @valid ) {
1222 14 100       49 $max = $it if $it->[1] > $max->[1];
1223             }
1224              
1225 12         95 return $max->[0];
1226             }
1227              
1228             1;
1229              
1230             =pod
1231              
1232             =head1 BUGS
1233              
1234             B does a reasonable job of catching the best-known
1235             explicit version dependencies.
1236              
1237             B it is exceedingly easy to add a new syntax check, so if you
1238             find something this is missing, copy and paste one of the existing
1239             5 line checking functions, modify it to find what you want, and report it
1240             to rt.cpan.org, along with the version needed.
1241              
1242             I don't even need an entire diff... just the function and version.
1243              
1244             =head1 TO DO
1245              
1246             B
1247              
1248             - Perl 5.10 operators and language structures
1249              
1250             - Three-argument open
1251              
1252             B
1253              
1254             B
1255              
1256             _while_readdir for postfix while without brackets
1257              
1258             B
1259             C, C<...>, and C)>
1260              
1261             =head1 SUPPORT
1262              
1263             All bugs should be filed via the CPAN bug tracker at
1264              
1265             L
1266              
1267             For other issues, or commercial enhancement or support, contact the author.
1268              
1269             =head1 AUTHORS
1270              
1271             Adam Kennedy Eadamk@cpan.orgE
1272              
1273             =head1 SEE ALSO
1274              
1275             L - the command-line script for running C
1276             on your code.
1277              
1278             L - another module which does the same thing.
1279             It's a lot faster, but only supports Perl 5.8.1+.
1280              
1281             L, L, L
1282              
1283             =head1 REPOSITORY
1284              
1285             L
1286              
1287             =head1 COPYRIGHT
1288              
1289             Copyright 2005 - 2014 Adam Kennedy.
1290              
1291             This program is free software; you can redistribute
1292             it and/or modify it under the same terms as Perl itself.
1293              
1294             The full text of the license can be found in the
1295             LICENSE file included with this module.
1296              
1297             =cut