File Coverage

blib/lib/Perl/Critic/Policy/Subroutines/ProhibitManyArgs.pm
Criterion Covered Total %
statement 75 75 100.0
branch 27 36 75.0
condition 13 21 61.9
subroutine 17 17 100.0
pod 4 5 80.0
total 136 154 88.3


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Subroutines::ProhibitManyArgs;
2              
3 40     40   27337 use 5.010001;
  40         164  
4 40     40   238 use strict;
  40         121  
  40         854  
5 40     40   207 use warnings;
  40         100  
  40         970  
6 40     40   197 use Readonly;
  40         112  
  40         1891  
7              
8 40     40   286 use File::Spec;
  40         115  
  40         1485  
9 40     40   267 use English qw(-no_match_vars);
  40         129  
  40         260  
10 40     40   15357 use Carp;
  40         142  
  40         2590  
11              
12 40     40   293 use Perl::Critic::Utils qw{ :booleans :severities split_nodes_on_comma };
  40         115  
  40         2032  
13 40     40   7233 use parent 'Perl::Critic::Policy';
  40         130  
  40         243  
14              
15             our $VERSION = '1.146';
16              
17             #-----------------------------------------------------------------------------
18              
19             Readonly::Scalar my $AT => q{@};
20             Readonly::Scalar my $AT_ARG => q{@_}; ## no critic (InterpolationOfMetachars)
21             Readonly::Scalar my $CLASS => q{$class}; ## no critic (InterpolationOfMetachars)
22             Readonly::Scalar my $SELF => q{$self}; ## no critic (InterpolationOfMetachars)
23              
24             Readonly::Scalar my $DESC => q{Too many arguments};
25             Readonly::Scalar my $EXPL => [182];
26              
27             #-----------------------------------------------------------------------------
28              
29             sub supported_parameters {
30             return (
31             {
32 102     102 0 2313 name => 'max_arguments',
33             description =>
34             'The maximum number of arguments to allow a subroutine to have.',
35             default_string => '5',
36             behavior => 'integer',
37             integer_minimum => 1,
38             },
39             {
40             name => 'skip_object',
41             description => q[Don't count $self or $class first argument], ## no critic (InterpolationOfMetachars)
42             default_string => '0',
43             behavior => 'boolean',
44             },
45             );
46             }
47              
48 86     86 1 377 sub default_severity { return $SEVERITY_MEDIUM }
49 86     86 1 363 sub default_themes { return qw( core pbp maintenance ) }
50 41     41 1 139 sub applies_to { return 'PPI::Statement::Sub' }
51              
52             #-----------------------------------------------------------------------------
53              
54             sub violates {
55 37     37 1 96 my ( $self, $elem, undef ) = @_;
56              
57             # forward declaration?
58 37 100       146 return if !$elem->block;
59              
60 36         947 my $num_args;
61 36 100       103 if ($elem->prototype) {
62 10         528 my $prototype = $elem->prototype();
63 10         384 $prototype =~ s/ \\ [[] .*? []] /*/smxg; # Allow for grouping
64 10         29 $num_args = $prototype =~ tr/$@%&*_+/$@%&*_+/; # RT 56627
65             } else {
66 26         1014 $num_args = _count_args($self->{_skip_object}, $elem->block->schildren);
67             }
68              
69 36 100       194 if ($self->{_max_arguments} < $num_args) {
70 12         58 return $self->violation( $DESC, $EXPL, $elem );
71             }
72 24         117 return; # OK
73             }
74              
75             sub _count_args {
76 57     57   961 my ($skip_object, @statements) = @_;
77              
78             # look for these patterns:
79             # " ... = @_;" => then examine previous variable list
80             # " ... = shift;" => counts as one arg, then look for more
81              
82 57 100       170 return 0 if !@statements; # no statements
83              
84 51         113 my $statement = shift @statements;
85 51         131 my @elements = $statement->schildren();
86 51         766 my $operand = pop @elements;
87 51   66     345 while ($operand && $operand->isa('PPI::Token::Structure') && q{;} eq $operand->content()) {
      66        
88 50         488 $operand = pop @elements;
89             }
90 51 50       157 return 0 if !$operand;
91              
92             #print "pulled off last, remaining: '@elements'\n";
93 51         96 my $operator = pop @elements;
94 51 50       133 return 0 if !$operator;
95 51 100       184 return 0 if !$operator->isa('PPI::Token::Operator');
96 47 50       110 return 0 if q{=} ne $operator->content();
97              
98 47 100 66     390 if ($operand->isa('PPI::Token::Magic') && $AT_ARG eq $operand->content()) {
    100 66        
99 15         99 return _count_list_elements($skip_object, @elements);
100             } elsif ($operand->isa('PPI::Token::Word') && 'shift' eq $operand->content()) {
101 31 100       175 my $count_first = $skip_object ? !_is_object_arg(pop @elements) : 1;
102 31         124 return $count_first + _count_args(0, @statements); # only check for object on first argument
103             }
104              
105 1         4 return 0;
106             }
107              
108             sub _count_list_elements {
109 15     15   51 my ($skip_object, @elements) = @_;
110              
111 15         33 my $list = pop @elements;
112 15 50       54 return 0 if !$list;
113 15 50       59 return 0 if !$list->isa('PPI::Structure::List');
114 15         140 my @inner = $list->schildren;
115 15 50 33     189 if (1 == @inner && $inner[0]->isa('PPI::Statement::Expression')) {
116 15         46 @inner = $inner[0]->schildren;
117             }
118 15         335 my @args = split_nodes_on_comma(@inner);
119 15 100 66     98 return scalar @args if !$skip_object || !@args;;
120              
121             # Check if first argument is $self/$class
122 7         16 my $first_ref = $args[0];
123 7 50       12 return scalar @args if scalar @{ $first_ref } != 1; # more complex than simple scalar
  7         24  
124 7         24 return scalar @args - !!_is_object_arg($first_ref->[0]);
125             }
126              
127             sub _is_object_arg {
128 13     13   31 my ($symbol) = @_;
129 13 50       46 return 0 if !$symbol;
130 13 50       43 return 0 if !$symbol->isa('PPI::Token::Symbol');
131 13   66     37 return $SELF eq $symbol->content() || $CLASS eq $symbol->content();
132             }
133              
134             1;
135              
136             __END__
137              
138             #-----------------------------------------------------------------------------
139              
140             =pod
141              
142             =for stopwords refactored
143              
144             =head1 NAME
145              
146             Perl::Critic::Policy::Subroutines::ProhibitManyArgs - Too many arguments.
147              
148              
149             =head1 AFFILIATION
150              
151             This Policy is part of the core L<Perl::Critic|Perl::Critic>
152             distribution.
153              
154              
155             =head1 DESCRIPTION
156              
157             Subroutines that expect large numbers of arguments are hard to use
158             because programmers routinely have to look at documentation to
159             remember the order of those arguments. Many arguments is often a sign
160             that a subroutine should be refactored or that an object should be
161             passed to the routine.
162              
163              
164             =head1 CONFIGURATION
165              
166             By default, this policy allows up to 5 arguments without warning. To
167             change this threshold, put entries in a F<.perlcriticrc> file like
168             this:
169              
170             [Subroutines::ProhibitManyArgs]
171             max_arguments = 6
172              
173             To ignore C<$self> or C<$class> in your argument count, as long as they're
174             the first argument, use:
175              
176             [Subroutines::ProhibitManyArgs]
177             skip_object = 1
178              
179              
180             =head1 CAVEATS
181              
182             PPI doesn't currently detect anonymous subroutines, so we don't check
183             those. This should just work when PPI gains that feature.
184              
185             We don't check for C<@ARG>, the alias for C<@_> from English.pm.
186             That's deprecated anyway.
187              
188              
189             =head1 CREDITS
190              
191             Initial development of this policy was supported by a grant from the
192             Perl Foundation.
193              
194              
195             =head1 AUTHOR
196              
197             Chris Dolan <cdolan@cpan.org>
198              
199              
200             =head1 COPYRIGHT
201              
202             Copyright (c) 2007-2019 Chris Dolan. Many rights reserved.
203              
204             This program is free software; you can redistribute it and/or modify
205             it under the same terms as Perl itself. The full text of this license
206             can be found in the LICENSE file included with this module
207              
208             =cut
209              
210             # Local Variables:
211             # mode: cperl
212             # cperl-indent-level: 4
213             # fill-column: 78
214             # indent-tabs-mode: nil
215             # c-indentation-style: bsd
216             # End:
217             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :