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   27415 use 5.010001;
  40         171  
4 40     40   266 use strict;
  40         108  
  40         821  
5 40     40   211 use warnings;
  40         103  
  40         975  
6 40     40   228 use Readonly;
  40         109  
  40         1819  
7              
8 40     40   282 use File::Spec;
  40         93  
  40         1446  
9 40     40   243 use English qw(-no_match_vars);
  40         114  
  40         267  
10 40     40   15702 use Carp;
  40         106  
  40         2872  
11              
12 40     40   307 use Perl::Critic::Utils qw{ :booleans :severities split_nodes_on_comma };
  40         144  
  40         2032  
13 40     40   7002 use parent 'Perl::Critic::Policy';
  40         115  
  40         325  
14              
15             our $VERSION = '1.148';
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 2495 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 424 sub default_severity { return $SEVERITY_MEDIUM }
49 86     86 1 365 sub default_themes { return qw( core pbp maintenance ) }
50 41     41 1 141 sub applies_to { return 'PPI::Statement::Sub' }
51              
52             #-----------------------------------------------------------------------------
53              
54             sub violates {
55 37     37 1 104 my ( $self, $elem, undef ) = @_;
56              
57             # forward declaration?
58 37 100       124 return if !$elem->block;
59              
60 36         874 my $num_args;
61 36 100       115 if ($elem->prototype) {
62 10         502 my $prototype = $elem->prototype();
63 10         392 $prototype =~ s/ \\ [[] .*? []] /*/smxg; # Allow for grouping
64 10         26 $num_args = $prototype =~ tr/$@%&*_+/$@%&*_+/; # RT 56627
65             } else {
66 26         1078 $num_args = _count_args($self->{_skip_object}, $elem->block->schildren);
67             }
68              
69 36 100       207 if ($self->{_max_arguments} < $num_args) {
70 12         52 return $self->violation( $DESC, $EXPL, $elem );
71             }
72 24         105 return; # OK
73             }
74              
75             sub _count_args {
76 57     57   1004 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       168 return 0 if !@statements; # no statements
83              
84 51         100 my $statement = shift @statements;
85 51         138 my @elements = $statement->schildren();
86 51         796 my $operand = pop @elements;
87 51   66     375 while ($operand && $operand->isa('PPI::Token::Structure') && q{;} eq $operand->content()) {
      66        
88 50         472 $operand = pop @elements;
89             }
90 51 50       164 return 0 if !$operand;
91              
92             #print "pulled off last, remaining: '@elements'\n";
93 51         105 my $operator = pop @elements;
94 51 50       137 return 0 if !$operator;
95 51 100       159 return 0 if !$operator->isa('PPI::Token::Operator');
96 47 50       111 return 0 if q{=} ne $operator->content();
97              
98 47 100 66     406 if ($operand->isa('PPI::Token::Magic') && $AT_ARG eq $operand->content()) {
    100 66        
99 15         95 return _count_list_elements($skip_object, @elements);
100             } elsif ($operand->isa('PPI::Token::Word') && 'shift' eq $operand->content()) {
101 31 100       182 my $count_first = $skip_object ? !_is_object_arg(pop @elements) : 1;
102 31         122 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   38 my ($skip_object, @elements) = @_;
110              
111 15         30 my $list = pop @elements;
112 15 50       50 return 0 if !$list;
113 15 50       56 return 0 if !$list->isa('PPI::Structure::List');
114 15         133 my @inner = $list->schildren;
115 15 50 33     208 if (1 == @inner && $inner[0]->isa('PPI::Statement::Expression')) {
116 15         50 @inner = $inner[0]->schildren;
117             }
118 15         301 my @args = split_nodes_on_comma(@inner);
119 15 100 66     99 return scalar @args if !$skip_object || !@args;;
120              
121             # Check if first argument is $self/$class
122 7         18 my $first_ref = $args[0];
123 7 50       14 return scalar @args if scalar @{ $first_ref } != 1; # more complex than simple scalar
  7         23  
124 7         35 return scalar @args - !!_is_object_arg($first_ref->[0]);
125             }
126              
127             sub _is_object_arg {
128 13     13   29 my ($symbol) = @_;
129 13 50       45 return 0 if !$symbol;
130 13 50       48 return 0 if !$symbol->isa('PPI::Token::Symbol');
131 13   66     36 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 :