File Coverage

blib/lib/Perl/Critic/StricterSubs/Utils.pm
Criterion Covered Total %
statement 184 195 94.3
branch 81 100 81.0
condition 23 36 63.8
subroutine 29 30 96.6
pod 11 11 100.0
total 328 372 88.1


line stmt bran cond sub pod time code
1             package Perl::Critic::StricterSubs::Utils;
2              
3 1     1   4 use strict;
  1         1  
  1         27  
4 1     1   4 use warnings;
  1         1  
  1         19  
5              
6 1     1   3 use base 'Exporter';
  1         1  
  1         56  
7              
8 1     1   4 use Carp qw(croak);
  1         1  
  1         44  
9              
10 1     1   4 use List::MoreUtils qw( any );
  1         1  
  1         8  
11 1         72 use Perl::Critic::Utils qw(
12             :characters
13             :severities
14             &first_arg
15             &hashify
16             &is_function_call
17             &is_perl_builtin
18             &words_from_string
19 1     1   314 );
  1         2  
20              
21             #-----------------------------------------------------------------------------
22              
23             our $VERSION = 0.04;
24              
25             #-----------------------------------------------------------------------------
26              
27             our @EXPORT_OK = qw{
28             &find_exported_subroutine_names
29             &find_declared_subroutine_names
30             &find_declared_constant_names
31             &find_imported_subroutine_names
32             &find_subroutine_calls
33             &get_all_subs_from_list_of_symbols
34             &get_package_names_from_include_statements
35             &get_package_names_from_package_statements
36             &parse_literal_list
37             &parse_quote_words
38             &parse_simple_list
39             };
40              
41             #-----------------------------------------------------------------------------
42              
43             sub parse_simple_list {
44 12     12 1 46 my ($list_node) = @_;
45              
46             # Per RT 36783, lists may contain qw{...} strings as well as words. We
47             # don't need to look for nested lists because they are of interest only
48             # for their contents, which we get by looking for them directly.
49 20 100       3471 my @strings = map { $_->string() }
  12         41  
50 12         19 @{ $list_node->find( 'PPI::Token::Quote' ) || [] };
51 1 100       203 push @strings, map { parse_quote_words( $_ ) }
  12         28  
52 12         570 @{ $list_node->find( 'PPI::Token::QuoteLike::Words' ) || [] };
53              
54 12         2871 return @strings; #Just hoping that these are single words
55             }
56              
57             #-----------------------------------------------------------------------------
58              
59             sub parse_literal_list {
60 0     0 1 0 my (@nodes) = @_;
61 0         0 my @string_elems = grep { $_->isa('PPI::Token::Quote') } @nodes;
  0         0  
62 0 0       0 return if not @string_elems;
63              
64 0         0 my @strings = map { $_->string() } @string_elems;
  0         0  
65 0         0 return @strings; #Just hoping that these are single words
66             }
67              
68             #-----------------------------------------------------------------------------
69              
70             sub parse_quote_words {
71 16     16 1 20 my ($qw_elem) = @_;
72 16         45 my ($word_string) = ( $qw_elem =~ m{\A qw \s* . (.*) .\z}msx );
73 16   33     182 my @words = words_from_string( $word_string || $EMPTY );
74 16         128 return @words;
75             }
76              
77             #-----------------------------------------------------------------------------
78              
79             sub get_package_names_from_include_statements {
80 50     50 1 76 my $doc = shift;
81              
82 50         140 my $statements = $doc->find( \&_wanted_include_statement );
83 50 100       482 return () if not $statements;
84              
85 29         36 return map { $_->module() } @{$statements};
  53         449  
  29         62  
86             }
87              
88             #-----------------------------------------------------------------------------
89              
90             sub get_package_names_from_package_statements {
91 22     22 1 26 my $doc = shift;
92              
93 22         54 my $statements = $doc->find( 'PPI::Statement::Package' );
94 22 100       248 return () if not $statements;
95              
96 4         5 return map { $_->namespace() } @{$statements};
  4         17  
  4         7  
97             }
98              
99             #-----------------------------------------------------------------------------
100              
101             sub _wanted_include_statement {
102 2675     2675   18290 my ($doc, $element) = @_;
103              
104 2675 100       8778 return 0 if not $element->isa('PPI::Statement::Include');
105              
106             # This will block out file names, e.g. require 'Foo.pm';
107 58 100       147 return 0 if not $element->module();
108              
109             # Skip 'no' as in 'no strict'
110 53         978 my $include_type = $element->type();
111 53 50 66     734 return 0 if $include_type ne 'use' && $include_type ne 'require';
112              
113 53         74 return 1;
114             }
115              
116             #-----------------------------------------------------------------------------
117              
118             sub _find_exported_names {
119 10     10   16 my ($doc, @export_types) = @_;
120              
121 10 50       41 @export_types = @export_types ?
122             @export_types : qw{@EXPORT @EXPORT_OK};
123              
124 10         17 my @all_exports = ();
125 10         17 for my $export_type( @export_types ) {
126              
127 20         40 my $export_assignment = _find_export_assignment( $doc, $export_type );
128 20 100       51 next if not $export_assignment;
129              
130 12         25 my @exports = _parse_export_list( $export_assignment );
131 12         25 foreach (@exports) { s/ \A & //xms; } # Strip all sub sigils
  41         63  
132 12         30 push @all_exports, @exports;
133             }
134              
135 10         33 return @all_exports;
136             }
137              
138             #-----------------------------------------------------------------------------
139              
140             sub find_exported_subroutine_names {
141 10     10 1 23 my ($doc, @export_types) = @_;
142              
143 10         29 my @exports = _find_exported_names( $doc, @export_types );
144 10         29 return get_all_subs_from_list_of_symbols( @exports );
145             }
146              
147             #-----------------------------------------------------------------------------
148              
149             sub find_declared_subroutine_names {
150 34     34 1 44 my ($doc) = @_;
151 34         83 my $sub_nodes = $doc->find('PPI::Statement::Sub');
152 34 100       375 return if not $sub_nodes;
153              
154 7         13 my @sub_names = map { $_->name() } @{ $sub_nodes };
  9         76  
  7         16  
155 7         151 for (@sub_names) { s{\A .*::}{}mxs }; # Remove leading package name
  9         27  
156 7         37 return @sub_names;
157             }
158              
159             #-----------------------------------------------------------------------------
160              
161             sub find_imported_subroutine_names {
162 28     28 1 35 my ($doc) = @_;
163              
164 28         54 my $includes_ref = $doc->find('PPI::Statement::Include');
165 28 100       220 return if not $includes_ref;
166              
167 16         21 my @use_stmnts = grep { $_->type() eq 'use' } @{ $includes_ref };
  31         275  
  16         37  
168              
169 28         52 my @imported_symbols =
170 16         231 map { _get_imports_from_use_statements($_) } @use_stmnts;
171              
172 16         48 my @imported_sub_names =
173             get_all_subs_from_list_of_symbols( @imported_symbols );
174              
175 16         50 return @imported_sub_names;
176             }
177              
178             #-----------------------------------------------------------------------------
179              
180             sub _get_imports_from_use_statements {
181 28     28   31 my ($use_stmnt) = @_;
182              
183             # In a typical C<use> statement, the first child is "use", and the
184             # second child is the package name (a bareword). Everything after
185             # that (except the trailing semi-colon) is part of the import
186             # arguments.
187              
188 28         67 my @schildren = $use_stmnt->schildren();
189 28         307 my @import_args = @schildren[2 .. $#schildren - 1];
190              
191 28         39 my $first_import_arg = $import_args[0];
192 28 100       133 return if not defined $first_import_arg;
193              
194             # RT 43310 is a pathological case, which shows we can't simply look at the
195             # first token after the module name to tell what to do. So we iterate over
196             # the entire argument list, scavenging what we recognize, and hoping the
197             # rest is structure (commas and such).
198 22         24 my @result;
199 22         30 foreach my $import_rqst ( @import_args ) {
200              
201 37 50       72 defined $import_rqst
202             or next;
203              
204 37 100       206 if ( $import_rqst->isa( 'PPI::Token::QuoteLike::Words' ) ) {
    100          
    100          
205              
206 8         22 push @result, parse_quote_words( $import_rqst );
207              
208             } elsif ( $import_rqst->isa( 'PPI::Structure::List' ) ) {
209              
210 7         95 push @result, parse_simple_list ( $import_rqst );
211              
212             } elsif ( $import_rqst->isa( 'PPI::Token::Quote' ) ) {
213              
214 11         28 push @result, $import_rqst->string();
215              
216             }
217              
218             }
219              
220 22         76 return @result;
221              
222             }
223              
224             #-----------------------------------------------------------------------------
225              
226             sub find_declared_constant_names {
227 34     34 1 81 my ($doc) = @_;
228              
229 34         109 my $constant_pragmas_ref = $doc->find( \&_is_constant_pragma );
230 34 100       379 return if not $constant_pragmas_ref;
231 3         6 my @declared_constants = ();
232              
233 3         5 for my $constant_pragma ( @{$constant_pragmas_ref} ) {
  3         7  
234              
235             #######################################################
236             # Constant pragmas typically look like one of these:
237             # use constant (AVAGADRO => 6.02*10^23); # With parens
238             # use constant PI => 3.1415927; # Without parens
239             # use constant {FOO => 1, BAR => 1} # Block form
240             #######################################################
241              
242 7         25 my $pragma_bareword = $constant_pragma->schild(1);
243 7         75 my $sibling = $pragma_bareword->snext_sibling();
244              
245 7 100 66     142 if ( defined $sibling && $sibling->isa('PPI::Structure::Constructor') ) {
246             # Parse the multi-constant block form...
247 2         6 push @declared_constants, _get_keys_of_hash($sibling);
248             }
249             else {
250             # Parse the single-constant declaration
251 5   50     34 my $constant_name = first_arg( $pragma_bareword ) || next;
252 5         164 push @declared_constants, $constant_name->content();
253             }
254              
255             }
256 3         18 return @declared_constants;
257             }
258              
259             #-----------------------------------------------------------------------------
260              
261             sub _get_keys_of_hash {
262 2     2   4 my ($block_or_list_node) = @_;
263 2 50       6 return if not defined $block_or_list_node;
264              
265 2 50       10 my $fat_commas = $block_or_list_node->find( \&_is_fat_comma )
266             or return;
267              
268 2         24 my @keys = map { $_->sprevious_sibling() } @{$fat_commas};
  5         69  
  2         3  
269 2         36 return @keys;
270             }
271              
272             #-----------------------------------------------------------------------------
273              
274             sub _is_fat_comma {
275 39     39   309 my( undef, $elem) = @_;
276 39   100     140 return $elem->isa('PPI::Token::Operator')
277             && $elem eq $FATCOMMA;
278             }
279              
280             #-----------------------------------------------------------------------------
281              
282             sub _is_constant_pragma {
283 1994     1994   14837 my (undef, $elem) = @_;
284              
285 1994   66     7617 return $elem->isa('PPI::Statement::Include')
286             && $elem->pragma() eq 'constant'
287             && $elem->type() eq 'use';
288             }
289              
290             #-----------------------------------------------------------------------------
291              
292             sub find_subroutine_calls {
293 33     33 1 45 my ($doc) = @_;
294              
295 33         111 my $sub_calls_ref = $doc->find( \&_is_subroutine_call );
296 33 100       361 return if not $sub_calls_ref;
297 28         44 return @{$sub_calls_ref};
  28         101  
298             }
299              
300             #-----------------------------------------------------------------------------
301              
302             sub _is_subroutine_call {
303 2106     2106   31519 my ($doc, $elem) = @_;
304              
305 2106 100       8729 if ( $elem->isa('PPI::Token::Word') ) {
    100          
306              
307 243 100       436 return 0 if is_perl_builtin( $elem );
308 163 100       2552 return 0 if _smells_like_filehandle( $elem );
309 146 100       641 return 0 if _smells_like_label( $elem );
310 141 100       523 return 1 if is_function_call( $elem );
311              
312             }
313             elsif ($elem->isa('PPI::Token::Symbol')) {
314              
315 85 100       176 return 1 if $elem->symbol_type eq q{&};
316             }
317              
318 1897         10479 return 0;
319             }
320              
321             #-----------------------------------------------------------------------------
322              
323             my %functions_that_take_filehandles =
324             hashify( qw(print printf read write sysopen tell open close) );
325              
326             sub _smells_like_filehandle {
327 163     163   138 my ($elem) = @_;
328 163 50       316 return if not $elem;
329              
330             #--------------------------------------------------------------------
331             # This handles calls *without* parens, for example:
332             # open HANDLE, $path;
333             # print HANDLE 'Hello World';
334             # close HANDLE;
335              
336 163 100       360 if ( my $left_sib = $elem->sprevious_sibling ){
337 71   66     1346 return exists $functions_that_take_filehandles{ $left_sib }
338             && is_function_call( $left_sib );
339             }
340              
341             #--------------------------------------------------------------------
342             # This handles calls *with* parens, for example:
343             # open( HANDLE, $path );
344             # print( HANDLE 'Hello World' );
345             # close( HANDLE );
346             #
347             # Or this case (Conway-style):
348             # print {HANDLE} 'Hello World';
349              
350 92   50     1232 my $expression = $elem->parent() || return;
351 92   50     524 my $enclosing_node = $expression->parent() || return;
352              
353 92 100 100     1027 return if ! ( $enclosing_node->isa('PPI::Structure::List')
354             || $enclosing_node->isa('PPI::Structure::Block') );
355              
356 18 100       105 return if $enclosing_node->schild(0) != $expression;
357              
358 17 50       267 if ( my $left_uncle = $enclosing_node->sprevious_sibling ){
359 17   66     255 return exists $functions_that_take_filehandles{ $left_uncle }
360             && is_function_call( $left_uncle );
361             }
362              
363 0         0 return;
364             }
365              
366             #-----------------------------------------------------------------------------
367              
368             my %functions_that_take_labels =
369             hashify( qw( last next redo ) );
370              
371             # The following is cribbed shamelessly from _looks_like_filehandle. TRW
372              
373             sub _smells_like_label {
374 146     146   128 my ($elem) = @_;
375 146 50       313 return if not $elem;
376              
377             #--------------------------------------------------------------------
378             # This handles calls *without* parens, for example:
379             # next FOO
380             # last BAR
381             # redo BAZ
382              
383 146 100       239 if ( my $left_sib = $elem->sprevious_sibling ){
384 62         928 return exists $functions_that_take_labels{ $left_sib };
385             }
386              
387             #--------------------------------------------------------------------
388             # This handles calls *with* parens, for example:
389             # next ( FOO )
390             # last ( BAR )
391             # redo ( BAZ )
392             #
393             # The above actually work, at least under 5.6.2 and 5.14.2.
394             # next { FOO }
395             # does _not_ work under those Perls, so we don't check for it.
396              
397 84   50     876 my $expression = $elem->parent() || return;
398 84   50     389 my $enclosing_node = $expression->parent() || return;
399              
400 84 100       559 return if ! ( $enclosing_node->isa('PPI::Structure::List') );
401              
402 6 50       37 return if $enclosing_node->schild(0) != $expression;
403              
404 6 50       80 if ( my $left_uncle = $enclosing_node->sprevious_sibling ){
405 6         92 return exists $functions_that_take_labels{ $left_uncle };
406             }
407              
408 0         0 return;
409             }
410              
411             #-----------------------------------------------------------------------------
412              
413             sub get_all_subs_from_list_of_symbols {
414 26     26 1 50 my @symbols = @_;
415              
416 26         47 my @sub_names = grep { m/\A [&\w]/mxs } @symbols;
  73         145  
417 26         54 for (@sub_names) { s/\A &//mxs; } # Remove optional sigil
  50         71  
418              
419             return @sub_names
420 26         80 }
421              
422             #-----------------------------------------------------------------------------
423              
424             sub _find_export_assignment {
425 20     20   23 my ($doc, $export_type) = @_;
426              
427 20         39 my $wanted = _make_assignment_finder( $export_type );
428 20         55 my $export_assignments = $doc->find( $wanted );
429 20 100       213 return if not $export_assignments;
430              
431 12         28 croak qq{Found multiple $export_type lists\n}
432 12 50       11 if @{$export_assignments} > 1;
433              
434 12         57 return $export_assignments->[0];
435             }
436              
437             #-----------------------------------------------------------------------------
438              
439             sub _make_assignment_finder {
440 20     20   20 my ($wanted_symbol) = @_;
441              
442             #############################################################
443             # This function returns a callback functiaon that is suitable
444             # for use with the PPI::Node::find() method. It will find
445             # all the occurances of the $wanted_symbol where the symbol
446             # is on the immediate left-hand side of the assignment operator.
447             ##############################################################
448              
449             my $finder = sub {
450              
451 1062     1062   6685 my ($doc, $elem) = @_;
452              
453 1062 100       3513 return 0 if not $elem->isa('PPI::Token::Symbol');
454 26 100       68 return 0 if $elem ne $wanted_symbol;
455              
456             # Check if symbol is on left-hand side of assignment
457 13   50     206 my $next_sib = $elem->snext_sibling() || return 0;
458 13 100       298 return 0 if not $next_sib->isa('PPI::Token::Operator');
459 12 50       26 return 0 if $next_sib ne q{=};
460              
461 12         131 return 1;
462 20         80 };
463              
464 20         30 return $finder;
465             }
466              
467             #-----------------------------------------------------------------------------
468              
469             sub _parse_export_list {
470 12     12   15 my ($export_symbol) = @_;
471              
472             # First element after the symbol should be "="
473 12         26 my $snext_sibling = $export_symbol->snext_sibling();
474 12 50       177 return if not $snext_sibling;
475              
476              
477             # Gather up remaining elements
478 12         18 my @left_hand_side = ();
479 12         34 while ( $snext_sibling = $snext_sibling->snext_sibling() ) {
480 24         338 push @left_hand_side, $snext_sibling;
481             }
482              
483             # Did we get any?
484 12 50       201 return if not @left_hand_side;
485              
486              
487             #Now parse the rest based on type of first element
488 12         15 my $first_element = $left_hand_side[0];
489 12 100       48 return parse_quote_words( $first_element )
490             if $first_element->isa('PPI::Token::QuoteLike::Words');
491              
492 5 50       45 return parse_simple_list( $first_element )
493             if $first_element->isa('PPI::Structure::List');
494              
495 0 0         return parse_literal_list( @left_hand_side )
496             if $first_element->isa('PPI::Token::Quote');
497              
498              
499 0           return; #Don't know what do do!
500             }
501              
502             #-----------------------------------------------------------------------------
503              
504             1;
505              
506             __END__
507              
508             =pod
509              
510             =for stopwords INIT typeglob distro
511              
512             =head1 NAME
513              
514             Perl::Critic::StricterSubs::Utils
515              
516             =head1 AFFILIATION
517              
518             This module is part of L<Perl::Critic::StricterSubs|Perl::Critic::StricterSubs>.
519              
520             =head1 DESCRIPTION
521              
522             This module holds utility methods that are shared by other modules in the
523             L<Perl::Critic::StricterSubs|Perl::Critic::StricterSubs> distro. Until this distro becomes more mature,
524             I would discourage you from using these subs outside of this distro.
525              
526             =head1 IMPORTABLE SUBS
527              
528             =over
529              
530             =item C<parse_quote_words( $qw_elem )>
531              
532             Gets the words from a L<PPI::Token::Quotelike::Words|PPI::Token::Quotelike::Words>.
533              
534             =item C<parse_simple_list( $list_node )>
535              
536             Returns the string literals from a L<PPI::Structure::List|PPI::Structure::List>.
537              
538             =item C<parse_literal_list( @nodes )>
539              
540             Returns the string literals contained anywhere in a collection of
541             L<PPI::Node|PPI::Node>s.
542              
543             =item C<find_declared_subroutine_names( $doc )>
544              
545             Returns a list of the names for all the subroutines that are declared in the
546             document. The package will be stripped from the name. TODO: Give examples of
547             what this will return for a given $doc.
548              
549             =item C<find_declared_constant_names( $doc )>
550              
551             Returns a list of the names for all the constants that were declared in the
552             document using the C<constant> pragma. At the moment, only these styles of
553             declaration is supported:
554              
555             use constant 'FOO' => 42; #with quotes, no parens
556             use constant BAR => 27; #no quotes, no parens
557             use constant (BAZ => 98); #no quotes, with parens
558              
559             Multiple declarations per pragma are not supported at all:
560              
561             use constant {WANGO => 1, TANGO => 2}; #no love here.
562              
563             =item C<find_imported_subroutine_names( $doc )>
564              
565             Returns a list of the names of all subroutines that are imported into the
566             document via C<use MODULE LIST;>. The leading ampersand sigil will be
567             stripped. TODO: Give examples of what this will return for a given $doc.
568              
569             =item C<find_subroutine_calls( $doc )>
570              
571             Returns a list C<PPI::Element>s, where each is the bareword name of a static
572             subroutine invocation. If the subroutine call is fully-qualified the package
573             will still be attached to the name. In all cases, the leading sigil will be
574             removed. TODO: Give examples of what this will return for a given $doc.
575              
576             =item C<find_exported_subroutine_names( $doc )>
577              
578             Returns a list of the names of each subroutine that is marked for exportation
579             via C<@EXPORT> or C<@EXPORT_OK>. Be aware that C<%EXPORT_TAGS> are not
580             supported here. TODO: Give examples of what this will return for a given
581             $doc.
582              
583             =item C<get_package_names_from_include_statements( $doc )>
584              
585             Returns a list of module names referred to with a bareword in an
586             include statement. This covers all include statements, such as:
587              
588             use Foo;
589             require Foo;
590              
591             sub load_foo {
592             require Foo if $condition;
593             }
594              
595             eval{ require Foo };
596              
597             INIT {
598             require Foo;
599             }
600              
601             But it does not cover these:
602              
603             require "Foo.pm";
604             eval { require $foo };
605              
606             =item C<get_package_names_from_package_statements( $doc )>
607              
608             Returns a list of all the namespaces from all the packages statements
609             that appear in the document.
610              
611             =item C<find_exported_sub_names( $doc, @export_types )>
612              
613             Returns a list of subroutines which are exported via the specified export
614             types. If C<@export_types> is empty, it defaults to C<qw{ @EXPORT, @EXPORT_OK
615             }>.
616              
617             Subroutine names are returned as in
618             C<get_all_subs_from_list_of_symbols()>.
619              
620             =item C<get_all_subs_from_list_of_symbols( @symbols )>
621              
622             Returns a list of all the input symbols which could be subroutine
623             names.
624              
625             Subroutine names are considered to be those symbols that don't have
626             scalar, array, hash, or glob sigils. Any subroutine sigils are
627             stripped off; i.e. C<&foo> will be returned as "foo".
628              
629             =back
630              
631             =head1 SEE ALSO
632              
633             L<Exporter|Exporter>
634              
635             =head1 AUTHOR
636              
637             Jeffrey Ryan Thalhammer <thaljef@cpan.org>
638              
639             =head1 COPYRIGHT
640              
641             Copyright (c) 2007 Jeffrey Ryan Thalhammer. All rights reserved.
642              
643             This program is free software; you can redistribute it and/or modify
644             it under the same terms as Perl itself. The full text of this license
645             can be found in the LICENSE file included with this module.
646              
647             =cut
648              
649              
650             ##############################################################################
651             # Local Variables:
652             # mode: cperl
653             # cperl-indent-level: 4
654             # fill-column: 78
655             # indent-tabs-mode: nil
656             # c-indentation-style: bsd
657             # End:
658             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab :