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         30  
4 1     1   4 use warnings;
  1         1  
  1         22  
5              
6 1     1   3 use base 'Exporter';
  1         1  
  1         62  
7              
8 1     1   5 use Carp qw(croak);
  1         1  
  1         43  
9              
10 1     1   4 use List::MoreUtils qw( any );
  1         1  
  1         9  
11 1         47 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   294 );
  1         1  
20              
21             #-----------------------------------------------------------------------------
22              
23             our $VERSION = 0.05;
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 48 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       3665 my @strings = map { $_->string() }
  12         45  
50 12         20 @{ $list_node->find( 'PPI::Token::Quote' ) || [] };
51 1 100       219 push @strings, map { parse_quote_words( $_ ) }
  12         34  
52 12         579 @{ $list_node->find( 'PPI::Token::QuoteLike::Words' ) || [] };
53              
54 12         3094 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 27 my ($qw_elem) = @_;
72 16         51 my ($word_string) = ( $qw_elem =~ m{\A qw \s* . (.*) .\z}msx );
73 16   33     187 my @words = words_from_string( $word_string || $EMPTY );
74 16         143 return @words;
75             }
76              
77             #-----------------------------------------------------------------------------
78              
79             sub get_package_names_from_include_statements {
80 50     50 1 64 my $doc = shift;
81              
82 50         163 my $statements = $doc->find( \&_wanted_include_statement );
83 50 100       564 return () if not $statements;
84              
85 29         46 return map { $_->module() } @{$statements};
  53         480  
  29         58  
86             }
87              
88             #-----------------------------------------------------------------------------
89              
90             sub get_package_names_from_package_statements {
91 22     22 1 39 my $doc = shift;
92              
93 22         48 my $statements = $doc->find( 'PPI::Statement::Package' );
94 22 100       255 return () if not $statements;
95              
96 4         8 return map { $_->namespace() } @{$statements};
  4         13  
  4         5  
97             }
98              
99             #-----------------------------------------------------------------------------
100              
101             sub _wanted_include_statement {
102 2675     2675   19224 my ($doc, $element) = @_;
103              
104 2675 100       9326 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       149 return 0 if not $element->module();
108              
109             # Skip 'no' as in 'no strict'
110 53         1010 my $include_type = $element->type();
111 53 50 66     888 return 0 if $include_type ne 'use' && $include_type ne 'require';
112              
113 53         83 return 1;
114             }
115              
116             #-----------------------------------------------------------------------------
117              
118             sub _find_exported_names {
119 10     10   23 my ($doc, @export_types) = @_;
120              
121 10 50       48 @export_types = @export_types ?
122             @export_types : qw{@EXPORT @EXPORT_OK};
123              
124 10         24 my @all_exports = ();
125 10         21 for my $export_type( @export_types ) {
126              
127 20         54 my $export_assignment = _find_export_assignment( $doc, $export_type );
128 20 100       54 next if not $export_assignment;
129              
130 12         30 my @exports = _parse_export_list( $export_assignment );
131 12         27 foreach (@exports) { s/ \A & //xms; } # Strip all sub sigils
  41         68  
132 12         30 push @all_exports, @exports;
133             }
134              
135 10         30 return @all_exports;
136             }
137              
138             #-----------------------------------------------------------------------------
139              
140             sub find_exported_subroutine_names {
141 10     10 1 21 my ($doc, @export_types) = @_;
142              
143 10         28 my @exports = _find_exported_names( $doc, @export_types );
144 10         30 return get_all_subs_from_list_of_symbols( @exports );
145             }
146              
147             #-----------------------------------------------------------------------------
148              
149             sub find_declared_subroutine_names {
150 34     34 1 47 my ($doc) = @_;
151 34         111 my $sub_nodes = $doc->find('PPI::Statement::Sub');
152 34 100       421 return if not $sub_nodes;
153              
154 7         15 my @sub_names = map { $_->name() } @{ $sub_nodes };
  9         79  
  7         21  
155 7         195 for (@sub_names) { s{\A .*::}{}mxs }; # Remove leading package name
  9         51  
156 7         30 return @sub_names;
157             }
158              
159             #-----------------------------------------------------------------------------
160              
161             sub find_imported_subroutine_names {
162 28     28 1 46 my ($doc) = @_;
163              
164 28         96 my $includes_ref = $doc->find('PPI::Statement::Include');
165 28 100       262 return if not $includes_ref;
166              
167 16         24 my @use_stmnts = grep { $_->type() eq 'use' } @{ $includes_ref };
  31         297  
  16         35  
168              
169 28         54 my @imported_symbols =
170 16         254 map { _get_imports_from_use_statements($_) } @use_stmnts;
171              
172 16         58 my @imported_sub_names =
173             get_all_subs_from_list_of_symbols( @imported_symbols );
174              
175 16         62 return @imported_sub_names;
176             }
177              
178             #-----------------------------------------------------------------------------
179              
180             sub _get_imports_from_use_statements {
181 28     28   42 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         80 my @schildren = $use_stmnt->schildren();
189 28         403 my @import_args = @schildren[2 .. $#schildren - 1];
190              
191 28         37 my $first_import_arg = $import_args[0];
192 28 100       66 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         22 my @result;
199 22         34 foreach my $import_rqst ( @import_args ) {
200              
201 37 50       94 defined $import_rqst
202             or next;
203              
204 37 100       231 if ( $import_rqst->isa( 'PPI::Token::QuoteLike::Words' ) ) {
    100          
    100          
205              
206 8         23 push @result, parse_quote_words( $import_rqst );
207              
208             } elsif ( $import_rqst->isa( 'PPI::Structure::List' ) ) {
209              
210 7         113 push @result, parse_simple_list ( $import_rqst );
211              
212             } elsif ( $import_rqst->isa( 'PPI::Token::Quote' ) ) {
213              
214 11         34 push @result, $import_rqst->string();
215              
216             }
217              
218             }
219              
220 22         114 return @result;
221              
222             }
223              
224             #-----------------------------------------------------------------------------
225              
226             sub find_declared_constant_names {
227 34     34 1 48 my ($doc) = @_;
228              
229 34         121 my $constant_pragmas_ref = $doc->find( \&_is_constant_pragma );
230 34 100       458 return if not $constant_pragmas_ref;
231 3         6 my @declared_constants = ();
232              
233 3         4 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         22 my $pragma_bareword = $constant_pragma->schild(1);
243 7         75 my $sibling = $pragma_bareword->snext_sibling();
244              
245 7 100 66     148 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     35 my $constant_name = first_arg( $pragma_bareword ) || next;
252 5         172 push @declared_constants, $constant_name->content();
253             }
254              
255             }
256 3         19 return @declared_constants;
257             }
258              
259             #-----------------------------------------------------------------------------
260              
261             sub _get_keys_of_hash {
262 2     2   5 my ($block_or_list_node) = @_;
263 2 50       5 return if not defined $block_or_list_node;
264              
265 2 50       9 my $fat_commas = $block_or_list_node->find( \&_is_fat_comma )
266             or return;
267              
268 2         21 my @keys = map { $_->sprevious_sibling() } @{$fat_commas};
  5         72  
  2         5  
269 2         39 return @keys;
270             }
271              
272             #-----------------------------------------------------------------------------
273              
274             sub _is_fat_comma {
275 39     39   329 my( undef, $elem) = @_;
276 39   100     141 return $elem->isa('PPI::Token::Operator')
277             && $elem eq $FATCOMMA;
278             }
279              
280             #-----------------------------------------------------------------------------
281              
282             sub _is_constant_pragma {
283 1994     1994   16259 my (undef, $elem) = @_;
284              
285 1994   66     8322 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 41 my ($doc) = @_;
294              
295 33         126 my $sub_calls_ref = $doc->find( \&_is_subroutine_call );
296 33 100       373 return if not $sub_calls_ref;
297 28         37 return @{$sub_calls_ref};
  28         119  
298             }
299              
300             #-----------------------------------------------------------------------------
301              
302             sub _is_subroutine_call {
303 2106     2106   35892 my ($doc, $elem) = @_;
304              
305 2106 100       9383 if ( $elem->isa('PPI::Token::Word') ) {
    100          
306              
307 243 100       466 return 0 if is_perl_builtin( $elem );
308 163 100       2988 return 0 if _smells_like_filehandle( $elem );
309 146 100       689 return 0 if _smells_like_label( $elem );
310 141 100       591 return 1 if is_function_call( $elem );
311              
312             }
313             elsif ($elem->isa('PPI::Token::Symbol')) {
314              
315 85 100       201 return 1 if $elem->symbol_type eq q{&};
316             }
317              
318 1897         11837 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   188 my ($elem) = @_;
328 163 50       421 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       438 if ( my $left_sib = $elem->sprevious_sibling ){
337 71   66     1592 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     1491 my $expression = $elem->parent() || return;
351 92   50     526 my $enclosing_node = $expression->parent() || return;
352              
353 92 100 100     1174 return if ! ( $enclosing_node->isa('PPI::Structure::List')
354             || $enclosing_node->isa('PPI::Structure::Block') );
355              
356 18 100       135 return if $enclosing_node->schild(0) != $expression;
357              
358 17 50       351 if ( my $left_uncle = $enclosing_node->sprevious_sibling ){
359 17   66     329 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   145 my ($elem) = @_;
375 146 50       292 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       256 if ( my $left_sib = $elem->sprevious_sibling ){
384 62         1012 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     1075 my $expression = $elem->parent() || return;
398 84   50     420 my $enclosing_node = $expression->parent() || return;
399              
400 84 100       702 return if ! ( $enclosing_node->isa('PPI::Structure::List') );
401              
402 6 50       36 return if $enclosing_node->schild(0) != $expression;
403              
404 6 50       83 if ( my $left_uncle = $enclosing_node->sprevious_sibling ){
405 6         102 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 55 my @symbols = @_;
415              
416 26         57 my @sub_names = grep { m/\A [&\w]/mxs } @symbols;
  73         140  
417 26         69 for (@sub_names) { s/\A &//mxs; } # Remove optional sigil
  50         70  
418              
419             return @sub_names
420 26         87 }
421              
422             #-----------------------------------------------------------------------------
423              
424             sub _find_export_assignment {
425 20     20   27 my ($doc, $export_type) = @_;
426              
427 20         40 my $wanted = _make_assignment_finder( $export_type );
428 20         87 my $export_assignments = $doc->find( $wanted );
429 20 100       215 return if not $export_assignments;
430              
431 12         32 croak qq{Found multiple $export_type lists\n}
432 12 50       17 if @{$export_assignments} > 1;
433              
434 12         58 return $export_assignments->[0];
435             }
436              
437             #-----------------------------------------------------------------------------
438              
439             sub _make_assignment_finder {
440 20     20   29 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   6866 my ($doc, $elem) = @_;
452              
453 1062 100       3556 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     214 my $next_sib = $elem->snext_sibling() || return 0;
458 13 100       318 return 0 if not $next_sib->isa('PPI::Token::Operator');
459 12 50       28 return 0 if $next_sib ne q{=};
460              
461 12         129 return 1;
462 20         81 };
463              
464 20         33 return $finder;
465             }
466              
467             #-----------------------------------------------------------------------------
468              
469             sub _parse_export_list {
470 12     12   16 my ($export_symbol) = @_;
471              
472             # First element after the symbol should be "="
473 12         29 my $snext_sibling = $export_symbol->snext_sibling();
474 12 50       201 return if not $snext_sibling;
475              
476              
477             # Gather up remaining elements
478 12         19 my @left_hand_side = ();
479 12         31 while ( $snext_sibling = $snext_sibling->snext_sibling() ) {
480 24         364 push @left_hand_side, $snext_sibling;
481             }
482              
483             # Did we get any?
484 12 50       220 return if not @left_hand_side;
485              
486              
487             #Now parse the rest based on type of first element
488 12         18 my $first_element = $left_hand_side[0];
489 12 100       45 return parse_quote_words( $first_element )
490             if $first_element->isa('PPI::Token::QuoteLike::Words');
491              
492 5 50       52 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 :