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.42'; # TRIAL
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   1621345 use 5.006;
  21         303  
45 21     21   110 use strict;
  21         41  
  21         446  
46 21     21   101 use warnings;
  21         38  
  21         960  
47 21     21   9274 use version 0.76 ();
  21         41485  
  21         603  
48 21     21   136 use Carp ();
  21         53  
  21         278  
49 21     21   125 use Exporter ();
  21         46  
  21         548  
50 21     21   119 use List::Util 1.20 qw(max first);
  21         501  
  21         2775  
51 21     21   10618 use Params::Util 0.25 ('_INSTANCE', '_CLASS');
  21         134023  
  21         1571  
52 21     21   9409 use PPI::Util ('_Document');
  21         16435  
  21         1254  
53 21     21   10844 use PPI 1.252 ();
  21         2295974  
  21         912  
54 21         5334 use PPIx::Utils qw{
55             :classification
56             :traversal
57 21     21   9443 };
  21         325975  
58 21     21   13759 use PPIx::Regexp 0.051;
  21         2684354  
  21         807  
59 21     21   10312 use Perl::MinimumVersion::Reason ();
  21         89  
  21         5772  
60              
61             our (@ISA, @EXPORT_OK, %CHECKS, @CHECKS_RV ,%MATCHES);
62             BEGIN {
63             # Export the PMV convenience constant
64 21     21   596 @ISA = 'Exporter';
65 21         94 @EXPORT_OK = 'PMV';
66              
67             # The primary list of version checks
68 21         1384 %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         113 @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         151809 %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 200941 my $class = ref $_[0] ? ref shift : shift;
161 205 100       693 my $Document = _Document(shift) or return undef;
162 202   33     562333 my $default = _INSTANCE(shift, 'version') || version->new('5.006');
163              
164             # Create the object
165 202         1257 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         607 $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 1416     1416 1 8409 }
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 6356 my $self = _SELF(\@_) or return undef;
217 56         123 my $minimum = $self->{default}; # Sensible default
218              
219             # Is the explicit version greater?
220 56         150 my $explicit = $self->minimum_explicit_version;
221 56 50       147 return undef unless defined $explicit;
222 56 100 100     180 if ( $explicit and $explicit > $minimum ) {
223 1         3 $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       159 return undef unless defined $syntax;
231 56 100 66     558 if ( $syntax and $syntax > $minimum ) {
232 40         96 $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         227 $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 126 my $self = _SELF(\@_) or return undef;
287 57         166 my $reason = $self->minimum_explicit_reason(@_);
288 57 100       178 return $reason ? $reason->version : $reason;
289             }
290              
291             sub minimum_explicit_reason {
292 57 50   57 0 130 my $self = _SELF(\@_) or return undef;
293 57 50       202 unless ( defined $self->{explicit} ) {
294 57         150 $self->{explicit} = $self->_minimum_explicit_version;
295             }
296 57         139 return $self->{explicit};
297             }
298              
299             sub _minimum_explicit_version {
300 57 50   57   156 my $self = shift or return undef;
301             my $explicit = $self->Document->find( sub {
302 591 100   591   8172 $_[1]->isa('PPI::Statement::Include') or return '';
303 25 100       82 $_[1]->version or return '';
304 4         110 1;
305 57         144 } );
306 57 100       885 return $explicit unless $explicit;
307              
308             # Find the highest version
309 3         7 my $max = undef;
310 3         7 my $element = undef;
311 3         11 foreach my $include ( @$explicit ) {
312 4         10 my $version = version->new($include->version);
313 4 50 66     121 if ( not $element or $version > $max ) {
314 4         9 $max = $version;
315 4         12 $element = $include;
316             }
317             }
318              
319 3         20 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 174 my $self = _SELF(\@_) or return undef;
356 62         177 my $reason = $self->minimum_syntax_reason(@_);
357 62 100       233 return $reason ? $reason->version : $reason;
358             }
359              
360             sub minimum_syntax_reason {
361 65 50   65 0 161 my $self = _SELF(\@_) or return undef;
362 65         131 my $limit = shift;
363 65 100 100     259 if ( defined $limit and not _INSTANCE($limit, 'version') ) {
364 1         17 $limit = version->new("$limit");
365             }
366 65 100       174 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         12 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         4 return '';
375             }
376              
377             # Look for the value
378 60         158 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       179 if ( $syntax ) {
384 42         96 $self->{syntax} = $syntax;
385 42         99 return $self->{syntax};
386             }
387              
388 18         48 return '';
389             }
390              
391             #for Perl::Critic::Policy::Compatibility::PerlMinimumVersionAndWhy
392             sub _set_checks2skip {
393 1     1   8 my $self = shift;
394 1         2 my $list = shift;
395 1         5 $self->{_checks2skip} = $list;
396             }
397             sub _set_collect_all_reasons {
398 1     1   8 my $self = shift;
399 1         3 my $value = shift;
400 1 50       5 $value = 1 unless defined $value;
401 1         4 $self->{_collect_all_reasons} = $value;
402             }
403              
404             sub _minimum_syntax_version {
405 60     60   136 my $self = shift;
406 60   66     484 my $filter = shift || $self->{default};
407              
408 60         177 my %checks2skip;
409 60 100       102 @checks2skip{ @{ $self->{_checks2skip} || [] } } = ();
  60         264  
410              
411 60         126 my %rv_result;
412             my $current_reason;
413 60         150 foreach my $rule ( @CHECKS_RV ) {
414 420 100       957 next if exists $checks2skip{$rule};
415 419         1185 my ($v, $obj) = $self->$rule();
416 419         2156 $v = version->new($v);
417 419 100       2276 if ( $v > $filter ) {
418 19         206 $current_reason = Perl::MinimumVersion::Reason->new(
419             rule => $rule,
420             version => $v,
421             element => _INSTANCE($obj, 'PPI::Element'),
422             );
423 19 100       65 if ($self->{_collect_all_reasons}) {
424 1         3 push @{ $self->{_all_reasons} }, $current_reason;
  1         5  
425             } else {
426 18         56 $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 3386         7440 $CHECKS{$b} <=> $CHECKS{$a}
437             } grep {
438 60 50       385 not(exists $checks2skip{$_}) and $CHECKS{$_} > $filter
  1380         5788  
439             } keys %CHECKS;
440              
441 60         205 foreach my $rule ( @rules ) {
442 707 100       10917 my $result = $self->$rule() or next;
443              
444             # Create the result object
445             my $reason = Perl::MinimumVersion::Reason->new(
446             rule => $rule,
447 25         988 version => $CHECKS{$rule},
448             element => _INSTANCE($result, 'PPI::Element'),
449             );
450 25 50       92 if ($self->{_collect_all_reasons}) {
451 0         0 push @{ $self->{_all_reasons} }, $current_reason;
  0         0  
452             } else {
453 25         100 return $reason;
454             }
455              
456             }
457              
458             # Found nothing of interest
459 35   100     630 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 717 my $self = _SELF(\@_) or return undef;
514              
515 1         3 my %markers;
516              
517 1 50       6 if ( my $explicit = $self->minimum_explicit_version ) {
518 1         9 $markers{ $explicit } = [ 'explicit' ];
519             }
520              
521 1         10 foreach my $check ( keys %CHECKS ) {
522 23 100       355 next unless $self->$check();
523 1   50     54 my $markers = $markers{ $CHECKS{$check} } ||= [];
524 1         4 push @$markers, $check;
525             }
526              
527 1         18 my @rv;
528 1         5 my %marker_ver = map { $_ => version->new($_) } keys %markers;
  2         15  
529              
530 1         6 foreach my $ver ( sort { $marker_ver{$b} <=> $marker_ver{$a} } keys %markers ) {
  1         8  
531 2         7 push @rv, $marker_ver{$ver} => $markers{$ver};
532             }
533              
534 1         9 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   197 my @versions;
570 84         155 my ($version, $obj);
571             shift->Document->find( sub {
572 772 100   772   10215 $_[1]->isa('PPI::Statement::Include') or return '';
573 49 100       152 $_[1]->pragma eq 'feature' or return '';
574 31         1089 my @child = $_[1]->schildren;
575 31         493 my @args = @child[1..$#child]; # skip 'use', 'feature' and ';'
576 31         65 foreach my $arg (@args) {
577 92         663 my $v = 0;
578 92 100       198 $v = $1 if ($arg->content =~ /:(5\.\d+)(?:\.\d+)?/);
579 92 100       747 $v = max($v, $feature{$1}) if ($arg->content =~ /\b($feature_regexp)\b/);
580             #
581 92 100 100     1147 if ($v and $v > ($version || 0) ) {
      100        
582 29         56 $version = $v;
583 29         62 $obj = $_[1];
584             }
585             }
586 31         86 return '';
587 84         173 } );
588 84 100       1577 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   131 my ($version, $obj);
620              
621             shift->Document->find( sub {
622 591 100 100 591   7944 return '' unless $_[1]->isa('PPI::Statement::Include')
623             and $_[1]->pragma eq 'experimental';
624              
625 2         82 my @child = $_[1]->schildren;
626 2         39 my @args = @child[1..$#child]; # skip 'use', 'experimental' and ';'
627 2         6 foreach my $arg (@args) {
628 5         9 my $v = 0;
629 5 50       22 $v = $1 if ($arg->content =~ /:(5\.\d+)(?:\.\d+)?/);
630 5 100       31 $v = max($v, $experimental{$1}) if ($arg->content =~ /\b($experimental_regexp)\b/);
631              
632 5 100 50     189 if ($v and $v > ($version || 0) ) {
      66        
633 1         4 $version = $v;
634 1         3 $obj = $_[1];
635             }
636             }
637 2         6 return '';
638 60         150 } );
639              
640 60 100       1053 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   109 my @versions;
653 60         99 my ($version, $obj);
654              
655             shift->Document->find( sub {
656 591 100   591   7016 $_[1]->isa('PPI::Statement::Scheduled') or return '';
657 4 50       16 ($_[1]->children)[0]->isa('PPI::Token::Word') or return '';
658 4         43 my $function = (($_[1]->children)[0])->content;
659 4 100       42 exists( $SCHEDULED_BLOCK{ $function }) or return '';
660              
661 3         9 my $v = $SCHEDULED_BLOCK{ ($_[1]->children)[0]->content };
662 3 50 50     39 if ($v and $v > ($version || 0) ) {
      33        
663 3         7 $version = $v;
664 3         16 $obj = $_[1];
665             }
666              
667 3         8 return '';
668 60         146 } );
669 60 100       980 return (defined($version) ? $version : undef, $obj);
670             }
671              
672             sub _regex {
673 68     68   142 my $self = shift;
674 68         120 my @versions;
675 68         122 my ($version, $obj);
676             $self->Document->find( sub {
677             return '' unless
678 606 100   606   8099 grep { $_[1]->isa($_) }
  1818         5580  
679             qw/PPI::Token::QuoteLike::Regexp PPI::Token::Regexp::Match PPI::Token::Regexp::Substitute/;
680 13         67 my $re = PPIx::Regexp->new( $_[1] );
681 13         51191 my $v = $re->perl_version_introduced;
682 13 50 50     3933 if ($v and $v > ($version || 0) ) {
      33        
683 13         26 $version = $v;
684 13         27 $obj = $_[1];
685             }
686 13         59 return '';
687 68         156 } );
688 68         2111 my $tr_r_version = version->new('5.013.007');
689             $self->Document->find( sub {
690 606 100   606   7303 return '' unless
691             $_[1]->isa(q/PPI::Token::Regexp::Transliterate/);
692 2 50 50     17 if( exists $_[1]->get_modifiers->{r}
      33        
693             && $tr_r_version > ( $version || 0 )
694             ) {
695 2         57 $version = $tr_r_version;
696 2         3 $obj = $_[1];
697             }
698 2         8 return '';
699 68         213 } );
700 68 100 100     998 $version = undef if ($version and $version eq '5.000');
701 68         278 return ($version, $obj);
702             }
703              
704             # Check for use re "/flags";
705             sub _re_flags {
706 60     60   107 my ($version, $obj);
707             shift->Document->find( sub {
708 586 100 66 586   8014 return '' unless $_[1]->isa('PPI::Statement::Include')
      66        
709             and ($_[1]->module eq 're' or $_[1]->pragma eq 're');
710 9         228 my $included = $_[1]->schild(2);
711 9 100       231 my @literal = $included->can('literal') ? $included->literal() : $included->string();
712 8         267 my $v = "5.005";
713 8         16 my @flags = grep {index($_, '/') == 0} @literal;
  11         38  
714 8 100       22 $v = '5.014' if @flags;
715             $v = max $v, map {
716 8         20 my $empty_regex_w_flag = "/$_";
  8         1637  
717 8         29 PPIx::Regexp->new( $empty_regex_w_flag )->perl_version_introduced;
718             } @flags;
719 8 50 50     12523 if ($v and $v > ($version || 0) ) {
      33        
720 8         16 $version = $v;
721 8         45 $obj = $_[1];
722             }
723              
724 60         144 } );
725 60 50 66     1087 $version = undef if ($version and $version eq '5.000');
726 60         159 return ($version, $obj);
727             }
728              
729             sub _each_argument {
730 102     102   264 my ($version, $obj);
731             shift->Document->find( sub {
732 899 100   899   12449 $_[1]->isa('PPI::Token::Word') or return '';
733 128 100       329 $_[1]->content =~ '^(each|keys|values)$' or return '';
734 44 100       387 return '' if is_method_call($_[1]);
735 42         1336 my $next = $_[1]->snext_sibling;
736 42 100       867 $next = $next->schild(0)->schild(0) if $next->isa('PPI::Structure::List');
737 41 100       609 if($next->isa('PPI::Token::Cast')) {
    100          
    100          
    100          
738 4 100 50     11 if($next->content eq '@' && 5.012 > ($version || 0)) {
    50 66        
      0        
      33        
739 3         27 $version = 5.012;
740 3         10 $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     74 if($next->raw_type eq '@' && 5.012 > ($version || 0)) {
    100 100        
      100        
      66        
747 7         74 $version = 5.012;
748 7         19 $obj = $_[1]->parent;
749             } elsif($next->raw_type eq '$' && 5.014 > ($version || 0)) {
750 14         216 $version = 5.014;
751 14         38 $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         13 return '';
757             } else { # function call or other should be reference
758 6 50 50     51 if(5.014 > ($version || 0)) {
759 6         11 $version = 5.014;
760 6         13 $obj = $_[1]->parent;
761             }
762             }
763 38 100 100     312 return 1 if ($version and $version == 5.014);
764 17         38 return '';
765 102         231 } );
766 102 100       2230 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   16 my $str = shift;
772 7         18 foreach my $s (@_) {
773 11 100       34 return 1 if $s eq $str;
774             }
775 4         26 return 0;
776             }
777              
778              
779             sub _binmode_2_arg {
780 69     69   145 my ($version, $obj);
781             shift->Document->find_first( sub {
782 663     663   7630 my $main_element=$_[1];
783 663 100       2144 $main_element->isa('PPI::Token::Word') or return '';
784 82 100       195 $main_element->content eq 'binmode' or return '';
785 10 50       65 return '' if is_hash_key($main_element);
786 10 50       451 return '' if is_method_call($main_element);
787 10 50       299 return '' if is_subroutine_name($main_element);
788 10 50       238 return '' if is_included_module_name($main_element);
789 10 50       276 return '' if is_package_declaration($main_element);
790 10         225 my @arguments = parse_arg_list($main_element);
791 10 100       1152 if ( scalar @arguments == 2 ) {
792 8         29 my $arg2=$arguments[1][0];
793 8 100       33 if ( $arg2->isa('PPI::Token::Quote')) { #check second argument
794 7         27 my $str = $arg2->string;
795 7         84 $str =~ s/^\s+//s;
796 7         24 $str =~ s/\s+$//s;
797 7         56 $str =~ s/:\s+/:/g;
798 7 100 100     23 if ( !_str_in_list( $str => qw/:raw :crlf/) and $str !~ /[\$\@\%]/) {
799 3         5 $version = 5.008;
800 3         7 $obj = $main_element;
801 3         13 return 1;
802             }
803             }
804 5 50       13 if (!$version) {
805 5         8 $version = 5.006;
806 5         9 $obj = $main_element;
807             }
808             }
809 7         20 return '';
810 69         189 } );
811 69         1245 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   6122 $_[1]->isa('PPI::Token::Word') or return '';
821 59 100       188 $_[1]->content eq 'while' or return '';
822 8 50       60 return '' if is_hash_key($_[1]);
823 8 50       610 return '' if is_method_call($_[1]);
824 8 100       232 my $e1 = $_[1]->next_sibling or return '';
825 7 50       144 if ($e1->isa('PPI::Structure::Condition')) { #while ()
826 7         25 my @children = $e1->children;
827 7         45 $e1 = $children[0];
828             }
829 7 50       36 $e1->isa('PPI::Statement::Expression') or return '';
830 7         21 my @children = $e1->schildren;
831 7         95 $e1 = $children[0];
832              
833 7 100       34 $e1->isa('PPI::Token::Word') or return '';
834 6 100       13 $e1->content eq 'readdir' or return '';
835 5 50       26 return 1 if @children == 1; #incorrect call
836 5 100       14 return '' if @children > 2; #not only readdir
837 3         11 $e1 = $children[1];
838 3 50 66     25 $e1->isa('PPI::Structure::List') or $e1->isa('PPI::Token::Symbol') or return '';
839             #readdir($dh) or readdir $dh
840              
841 3         12 return 1;
842 44     44   176 } );
843             }
844              
845             sub _perl_5012_pragmas {
846             shift->Document->find_first( sub {
847             $_[1]->isa('PPI::Statement::Include')
848             and
849 329 100   329   4489 $MATCHES{_perl_5012_pragmas}->{$_[1]->pragma}
850 37     37   93 } );
851             }
852              
853             sub _open_temp {
854             shift->Document->find_first( sub {
855 344 100   344   4305 $_[1]->isa('PPI::Statement') or return '';
856 50         136 my @children = $_[1]->children;
857             #@children >= 7 or return '';
858 50         279 my $main_element = $children[0];
859 50 100       198 $main_element->isa('PPI::Token::Word') or return '';
860 29 100       91 $main_element->content eq 'open' or return '';
861 7         60 my @arguments = parse_arg_list($main_element);
862 7 100 66     1672 if ( scalar @arguments == 3 and scalar(@{$arguments[2]}) == 1) {
  5         22  
863 5         11 my $arg3 = $arguments[2][0];
864 5 100 66     22 if ($arg3->isa('PPI::Token::Word') and $arg3->content eq 'undef') {
865 3         29 return 1;
866             }
867             }
868 4         18 return '';
869 30     30   122 } );
870             }
871              
872             sub _open_scalar {
873             shift->Document->find_first( sub {
874 348 100   348   4254 $_[1]->isa('PPI::Statement') or return '';
875 51         152 my @children = $_[1]->children;
876             #@children >= 7 or return '';
877 51         284 my $main_element = $children[0];
878 51 100       185 $main_element->isa('PPI::Token::Word') or return '';
879 30 100       83 $main_element->content eq 'open' or return '';
880 8         67 my @arguments = parse_arg_list($main_element);
881 8 100       2026 if ( scalar @arguments == 3) {
882 6         16 my $arg3 = $arguments[2][0];
883 6 100 66     57 if ($arg3->isa('PPI::Token::Cast') and $arg3->content eq '\\') {
884 4         30 return 1;
885             }
886             }
887 4         18 return '';
888 31     31   160 } );
889             }
890              
891             sub _get_resulting_sigil {
892 10     10   18 my $elem = shift;
893 10 50       68 if ($elem->isa('PPI::Token::Cast')) {
    50          
894 0         0 return $elem->content;
895             } elsif ($elem->isa('PPI::Token::Symbol')) {
896 10         31 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   5874 my $main_element = $_[1];
905 570 100       1850 $main_element->isa('PPI::Token::HereDoc') or return '';
906 2 100       5 $main_element->content =~ /^\Q<<~\E/ or return '';
907 1         10 return 1;
908 57     57   138 });
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   12 my ( $elem ) = @_;
928 4 50       32 my $next = $elem->snext_sibling()
929             or return;
930 4         112 return $next->isa( 'PPI::Structure::Subscript' );
931             }
932              
933             sub _postfix_deref_entire {
934 6     6   19 return 1;
935             }
936              
937             sub _postfix_deref {
938             shift->Document->find_first( sub {
939 509     509   5171 my $main_element=$_[1];
940 509 100       1696 $main_element->isa('PPI::Token::Cast') or return '';
941 11 50       96 my $prev = $main_element->sprevious_sibling()
942             or return '';
943 11 100 66     355 return '' unless $prev->isa('PPI::Token::Operator') &&
944             $prev->content() eq '->';
945 10 50       66 $prev = $prev->sprevious_sibling()
946             or return '';
947 10 50 50     276 return '' unless $prev->isa('PPI::Token::Symbol') &&
      33        
948             (_get_resulting_sigil($prev) || '') eq '$';
949 10 50       538 my $code = $postfix_deref{ $main_element->content() }
950             or return '';
951 10   50     68 return $code->( $main_element ) || '';
952 54     54   182 } );
953             }
954              
955             sub _postfix_when {
956             shift->Document->find_first( sub {
957 442     442   4664 my $main_element=$_[1];
958 442 100       1563 $main_element->isa('PPI::Token::Word') or return '';
959 53 100       167 $main_element->content eq 'when' or return '';
960 5 50       30 return '' if is_hash_key($main_element);
961 5 50       409 return '' if is_method_call($main_element);
962 5 50       175 return '' if is_subroutine_name($main_element);
963 5 50       216 return '' if is_included_module_name($main_element);
964 5 50       134 return '' if is_package_declaration($main_element);
965 5         110 my $stmnt = $main_element->statement();
966 5 50       58 return '' if !$stmnt;
967 5 100       19 return '' if $stmnt->isa('PPI::Statement::When');
968 3         7 return 1;
969 41     41   133 } );
970             }
971              
972             sub _yada_yada_yada {
973             shift->Document->find_first( sub {
974 469 100 100 469   6033 $_[1]->isa('PPI::Token::Operator')
975             and $_[1]->content eq '...' or return '';
976 8         72 my @child = $_[1]->parent->schildren;
977 8 100       122 @child == 1 and return 1;
978 3 100       12 if (@child == 2) {
979 1         10 $child[1]->isa('PPI::Token::Structure')
980             }
981 46     46   156 } );
982             }
983              
984             sub _state_declaration {
985             shift->Document->find_first( sub {
986 331 100 66 331   4204 $_[1]->isa('PPI::Statement::Variable')
987             and ($_[1]->children)[0]->isa('PPI::Token::Word')
988             and ($_[1]->children)[0]->content eq 'state'
989 34     34   89 } );
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 277 100 100 277   3997 $_[1]->isa('PPI::Statement')
1011             and ($_[1]->children)[0]->isa('PPI::Token::Word')
1012             and ($_[1]->children)[0]->content eq 'Internals::SvREADONLY'
1013 27     27   97 } );
1014             }
1015              
1016             sub _pkg_name_version {
1017             shift->Document->find_first( sub {
1018 420 100   420   5207 $_[1]->isa('PPI::Statement::Package') or return '';
1019 9         24 my @child = $_[1]->schildren();
1020 9 50       129 $child[0]->isa('PPI::Token::Word') or return '';
1021 9 50       20 $child[0]->content eq 'package' or return '';
1022 9 50       51 $child[1]->isa('PPI::Token::Word') or return '';
1023 9 100       35 $child[2]->isa('PPI::Token::Number') or return '';
1024 6         15 return 1;
1025 46     46   169 } );
1026             }
1027              
1028             sub _perl_5010_pragmas {
1029             shift->Document->find_first( sub {
1030             $_[1]->isa('PPI::Statement::Include')
1031             and
1032 255 100   255   3433 $MATCHES{_perl_5010_pragmas}->{$_[1]->pragma}
1033 30     30   634 } );
1034             }
1035              
1036             sub _perl_5010_operators {
1037             shift->Document->find_first( sub {
1038             $_[1]->isa('PPI::Token::Operator')
1039             and
1040 364 100   364   4783 $MATCHES{_perl_5010_operators}->{$_[1]->content}
1041 34     34   94 } );
1042             }
1043              
1044             sub _perl_5010_magic {
1045             shift->Document->find_first( sub {
1046             $_[1]->isa('PPI::Token::Magic')
1047             and
1048 339 100   339   4317 $MATCHES{_perl_5010_magic}->{$_[1]->symbol}
1049 33     33   92 } );
1050             }
1051              
1052             sub _perl_5008_pragmas {
1053             shift->Document->find_first( sub {
1054             $_[1]->isa('PPI::Statement::Include')
1055             and
1056 203 100   203   2782 $MATCHES{_perl_5008_pragmas}->{$_[1]->pragma}
1057 23     23   71 } );
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   651 my $Document = shift->Document;
1063             my $element = $Document->find_first( sub {
1064 291     291   3565 $_[1]->isa('PPI::Token::Magic')
1065             and
1066             $_[1]->symbol eq '$^E'
1067 26   100     121 } ) || 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   81 $_[1]->isa('PPI::Token::Magic')
1071             and
1072             $_[1]->symbol eq '$!'
1073 2 50       109 } ) || return '';
1074 2         101 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   3766 $_[1]->isa('PPI::Token::Word') or return '';
1081 31 50       146 $_[1] eq 'utf8::is_utf' or return '';
1082 0         0 return 1;
1083 24     24   73 } );
1084             }
1085              
1086             # version->new(5.005.004);
1087             sub _unquoted_versions {
1088             shift->Document->find_first( sub {
1089 279 100   279   3359 $_[1]->isa('PPI::Token::Number') or return '';
1090 17 50       51 $_[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   72 } );
1107             }
1108              
1109             sub _pragma_utf8 {
1110             shift->Document->find_first( sub {
1111 217 100 100 217   3343 $_[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   75 } );
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   3626 $_[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   75 } );
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   2833 $_[1]->isa('PPI::Statement::Variable') or return '';
1141 3 100       27 $_[1]->type eq 'local' or return '';
1142              
1143             # The second child should be a '$' cast.
1144 2         108 my @child = $_[1]->schildren;
1145 2 50       40 scalar(@child) >= 2 or return '';
1146 2 50       9 $child[1]->isa('PPI::Token::Cast') or return '';
1147 2 50       8 $child[1]->content eq '$' or return '';
1148              
1149             # The third child should be a block
1150 2 50       15 $child[2]->isa('PPI::Structure::Block') or return '';
1151              
1152             # Inside the block should be a string in a statement
1153 2 50       14 my $statement = $child[2]->schild(0) or return '';
1154 2 50       44 $statement->isa('PPI::Statement') or return '';
1155 2 50       5 my $inside = $statement->schild(0) or return '';
1156 2 50       29 $inside->isa('PPI::Token::Quote') or return '';
1157              
1158             # This is indeed a localized soft reference
1159 2         5 return 1;
1160 23     23   678 } );
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   2872 $_[1]->isa('PPI::Statement::Include') or return '';
1168 9 50       36 $_[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   95 } );
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   6817 $_[1]->isa('PPI::Token::QuoteLike::Readline') or return '';
1180 2 100       9 $_[1]->content eq '<<>>' or return '';
1181 1         10 return 1;
1182 59     59   190 } );
1183             }
1184              
1185             #####################################################################
1186             # Support Functions
1187              
1188             # Let sub be a function, object method, and static method
1189             sub _SELF {
1190 298     298   460 my $param = shift;
1191 298 100       1981 if ( _INSTANCE($param->[0], 'Perl::MinimumVersion') ) {
1192 296         1001 return shift @$param;
1193             }
1194 2 50 33     16 if (
1195             _CLASS($param->[0])
1196             and
1197             $param->[0]->isa('Perl::MinimumVersion')
1198             ) {
1199 2         47 my $class = shift @$param;
1200 2         6 my $options = shift @$param;
1201 2         9 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   1135 defined $_[0] and "$_[0]" eq PMV and shift;
1209              
1210             # Filter and prepare for a Schwartian maximum
1211             my @valid = map {
1212 26 50       117 [ $_, $_->isa('Perl::MinimumVersion::Reason') ? $_->version : $_ ]
1213             } grep {
1214 18 50       61 _INSTANCE($_, 'Perl::MinimumVersion::Reason')
  36 100       560  
1215             or
1216             _INSTANCE($_, 'version')
1217             } @_ or return '';
1218              
1219             # Find the maximum
1220 12         30 my $max = shift @valid;
1221 12         25 foreach my $it ( @valid ) {
1222 14 100       62 $max = $it if $it->[1] > $max->[1];
1223             }
1224              
1225 12         130 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