File Coverage

blib/lib/Perl/Critic/StricterSubs/Utils.pm
Criterion Covered Total %
statement 185 196 94.3
branch 81 100 81.0
condition 23 36 63.8
subroutine 30 31 96.7
pod 12 12 100.0
total 331 375 88.2


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