File Coverage

blib/lib/Perl/Critic/Policy/Subroutines/ProhibitAmbiguousFunctionCalls.pm
Criterion Covered Total %
statement 69 71 97.1
branch 18 22 81.8
condition 6 8 75.0
subroutine 15 15 100.0
pod 1 1 100.0
total 109 117 93.1


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Subroutines::ProhibitAmbiguousFunctionCalls;
2              
3 1     1   260202 use strict;
  1         11  
  1         31  
4 1     1   6 use warnings;
  1         2  
  1         25  
5 1     1   6 use Digest::MD5;
  1         2  
  1         38  
6              
7 1     1   6 use Perl::Critic::Utils qw{ :severities };
  1         3  
  1         69  
8 1     1   135 use base 'Perl::Critic::Policy';
  1         2  
  1         551  
9              
10             our $VERSION = '1.002';
11              
12 1     1   36716 use constant DESC => q{Fully qualified functions calls should end in parens.};
  1         3  
  1         78  
13 1     1   8 use constant EXPL => q{To differentiate from class methods and function calls, use Foo:Bar::baz()->gimble};
  1         3  
  1         48  
14              
15 1     1   7 use constant default_severity => $SEVERITY_HIGH;
  1         2  
  1         46  
16 1     1   6 use constant default_themes => qw( core );
  1         2  
  1         44  
17 1     1   8 use constant applies_to => ('PPI::Statement');
  1         2  
  1         60  
18              
19 1         604 use constant supported_parameters => ({
20             name => 'methods_always_ok',
21             default_string => 'new add',
22             description => 'Names of methods which never trigger this policy',
23             behavior => 'string list',
24             },
25             {
26             name => 'uppercase_module_always_ok',
27             description => 'Indicates if Foo::Bar->baz is always ok because of the capital F',
28             default_string => 1,
29             behavior => 'boolean',
30             },
31              
32 1     1   6 );
  1         2  
33              
34             my ($docinfo, $file);
35              
36             sub violates {
37              
38 11     11 1 579425 my ($self, $elem, $doc) = @_;
39              
40             ## Workaround a slight bug in Perl::Critic
41 11 50       58 return if ref $elem eq 'PPI::Statement::Null';
42              
43             ## We never want to consider elements in the END section
44 11 50       33 return if ref $elem eq 'PPI::Statement::End';
45              
46             ## We may not have a filename, so we need some unique identifier
47 11   33     32 $file = $doc->filename() // Digest::MD5::md5_hex($doc->content());
48              
49             ## We need to walk through the whole document, but only the first time we are called per file
50 11 50       673 if (!defined $docinfo->{$file}) {
51 11         39 $docinfo->{$file} = {};
52 11         37 my $realdoc = $doc->ppi_document;
53             ## This will set elements to matched
54 11         72 $self->_kidwalk($realdoc);
55             }
56              
57 11         28 my $elemid = $self->_nodeid($elem);
58 11 50       43 if (!exists $docinfo->{$file}{$elemid}) {
59 0         0 warn "No element found for $elemid"; ## Should not happen
60 0         0 return;
61             }
62              
63             ## If we have already marked this one, return a violation
64 11 100       37 if ($docinfo->{$file}{$elemid}) {
65 4         25 return $self->violation(DESC, EXPL, $elem);
66             }
67              
68 7         25 return;
69             }
70              
71             sub _match {
72              
73             ## Given a PPI::Node, see if it matches our criteria
74             ## Returns 0 or 1
75              
76 55     55   125 my ($self, $lkid) = @_;
77              
78             ## We only care about things like Foo::Bar::Baz->gimble
79 55 100       143 return 0 unless $lkid =~ / (\w+(?: ::\w+)+) -> (.+)/x;
80              
81 10         290 my ($module, $name) = ($1, $2);
82              
83             ## Some method names are always allowed
84 10 100       49 return 0 if exists $self->{_methods_always_ok}{$name};
85              
86             ## Uppercase final part of module name may be ok
87 6 100 100     37 return 0 if $module =~ /::[A-Z]\w+$/ and $self->{_uppercase_module_always_ok};
88              
89 4         11 return 1;
90             }
91              
92             sub _kidwalk {
93              
94             ## Given a PPI::Node, recursively check all of its kids for a match
95              
96 23     23   52 my ($self, $parent) = @_;
97 23         65 my @kids = $parent->schildren();
98              
99 23         238 for my $kid (@kids) {
100              
101             ## Build a unique ID for this node
102 55         131 my $kidid = $self->_nodeid($kid);
103              
104             ## See if this chunk of code violated our Perl::Critic policy
105 55         148 my $found_match = $self->_match($kid);
106 55         306 $docinfo->{$file}{$kidid} = $found_match;
107              
108             ## If any ancestors have already found this match, remove it!
109             ## As children content is always a subset of the parent's content, this is safe
110 55 100       143 if ($found_match) {
111 4         17 my $parentid = $self->_nodeid($kid->parent);
112 4         15 $docinfo->{$file}{$parentid} = 0;
113             }
114              
115             ## Some nodes do not have children, but if they do, recurse through them
116 55 100       204 $self->_kidwalk($kid) if $kid->can('schildren');
117             }
118              
119 23         58 return;
120              
121             }
122              
123             sub _nodeid {
124              
125             ## Given a PPI::Node, generate a unique ID for it
126              
127 70     70   157 my ($self, $node) = @_;
128              
129             ## Because the location() is a little different when coming
130             ## via 'elem' vs 'doc', we replace some items
131 70         188 my $locinfo = $node->location;
132 70         957 $locinfo->[1] = ref $node;
133 70 100       257 $locinfo->[2] = $node->can('schildren') ? $node->schildren : 0;
134 70   100     510 return join ';' => map { $_ // 'NULL' } @$locinfo;
  350         1090  
135             }
136              
137             1;
138              
139             __END__
140              
141             #-----------------------------------------------------------------------------
142              
143             =pod
144              
145             =head1 NAME
146              
147             Perl::Critic::Policy::Subroutines::ProhibitAmbiguousFunctionCalls - Don't call fully qualified function methods without parens
148              
149             =head1 DESCRIPTION
150              
151             When writing code like this...
152              
153             Some::Class::Name::foo->mymethod
154              
155             ..it is not clear if 'foo' is part of the class, or a function within Some::Class::Name.
156             The better way to write it is:
157              
158             Some::Class::Name::foo()->method
159              
160             =head1 CONFIGURATION
161              
162             =over 4
163              
164             =item C<method_always_ok> (string list, default is "new add")
165              
166             A list of method names which should always be considered "ok"
167              
168             =item C<uppercase_module_always_ok> (boolean, defaults to true)
169              
170             Indicates whether module names starting with an uppercase letter are considered "ok".
171              
172             For example, Foo::Bar->pop; is considered ok by default, but Foo::bar->pop is not.
173              
174             =back