File Coverage

blib/lib/Perl/Critic/Policy/Subroutines/RequireFinalReturn.pm
Criterion Covered Total %
statement 114 122 93.4
branch 52 68 76.4
condition 44 53 83.0
subroutine 22 22 100.0
pod 4 5 80.0
total 236 270 87.4


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::Subroutines::RequireFinalReturn;
2              
3 40     40   27711 use 5.010001;
  40         163  
4 40     40   232 use strict;
  40         118  
  40         820  
5 40     40   206 use warnings;
  40         131  
  40         1103  
6 40     40   277 use Readonly;
  40         113  
  40         2199  
7              
8 40     40   303 use Perl::Critic::Exception::Fatal::Internal qw{ throw_internal };
  40         106  
  40         2287  
9 40     40   362 use Perl::Critic::Utils qw{ :characters :severities :data_conversion };
  40         124  
  40         2178  
10 40     40   13737 use parent 'Perl::Critic::Policy';
  40         99  
  40         233  
11              
12             our $VERSION = '1.146';
13              
14             #-----------------------------------------------------------------------------
15              
16             Readonly::Scalar my $EXPL => [ 197 ];
17              
18             Readonly::Hash my %CONDITIONALS => hashify( qw(if unless for foreach) );
19              
20             #-----------------------------------------------------------------------------
21              
22             sub supported_parameters {
23             return (
24             {
25 115     115 0 2976 name => 'terminal_funcs',
26             description => 'The additional subroutines to treat as terminal.',
27             default_string => $EMPTY,
28             behavior => 'string list',
29             list_always_present_values =>
30             [ qw< croak confess die exec exit throw Carp::confess Carp::croak ...> ],
31             },
32             {
33             name => 'terminal_methods',
34             description => 'The additional methods to treat as terminal.',
35             default_string => $EMPTY,
36             behavior => 'string list',
37             list_always_present_values => [],
38             },
39             );
40             }
41              
42 95     95 1 445 sub default_severity { return $SEVERITY_HIGH }
43 92     92 1 427 sub default_themes { return qw( core bugs pbp certrec ) }
44 56     56 1 200 sub applies_to { return 'PPI::Statement::Sub' }
45              
46             #-----------------------------------------------------------------------------
47              
48             sub violates {
49 59     59 1 156 my ( $self, $elem, undef ) = @_;
50              
51             # skip BEGIN{} and INIT{} and END{} etc
52 59 100       315 return if $elem->isa('PPI::Statement::Scheduled');
53              
54 55         202 my @blocks = grep {$_->isa('PPI::Structure::Block')} $elem->schildren();
  165         1218  
55 55 50       263 if (@blocks > 1) {
    100          
56             # sanity check
57 0         0 throw_internal 'Subroutine should have no more than one block';
58             }
59             elsif (@blocks == 0) {
60             #Technically, subroutines don't have to have a block at all. In
61             # that case, its just a declaration so this policy doesn't really apply
62 1         5 return; # ok!
63             }
64              
65              
66 54         129 my ($block) = @blocks;
67 54 100 100     182 if ($self->_block_is_empty($block) || $self->_block_has_return($block)) {
68 33         351 return; # OK
69             }
70              
71             # Must be a violation
72 21         48 my $desc;
73 21 50       100 if ( my $name = $elem->name() ) {
74 21         1359 $desc = qq<Subroutine "$name" does not end with "return">;
75             }
76             else {
77 0         0 $desc = q<Subroutine does not end with "return">;
78             }
79              
80 21         124 return $self->violation( $desc, $EXPL, $elem );
81             }
82              
83             #-----------------------------------------------------------------------------
84              
85             sub _block_is_empty {
86 54     54   129 my ( $self, $block ) = @_;
87 54         159 return $block->schildren() == 0;
88             }
89              
90             #-----------------------------------------------------------------------------
91              
92             sub _block_has_return {
93 84     84   1176 my ( $self, $block ) = @_;
94 84         227 my @blockparts = $block->schildren();
95 84         921 my $final = $blockparts[-1]; # always defined because we call _block_is_empty first
96 84 100       329 return if !$final;
97 83   100     237 return $self->_is_explicit_return($final)
98             || $self->_is_given_when_return($final)
99             || $self->_is_compound_return($final);
100             }
101              
102             #-----------------------------------------------------------------------------
103              
104             sub _is_explicit_return {
105 83     83   199 my ( $self, $final ) = @_;
106              
107 83 100       237 return if $self->_is_conditional_stmnt( $final );
108 62   100     197 return $self->_is_return_or_goto_stmnt( $final )
109             || $self->_is_terminal_stmnt( $final );
110             }
111              
112             #-----------------------------------------------------------------------------
113              
114             sub _is_compound_return {
115 35     35   87 my ( $self, $final ) = @_;
116              
117 35 100       163 if (!$final->isa('PPI::Statement::Compound')) {
118 20         99 return; #fail
119             }
120              
121 15         42 my $begin = $final->schild(0);
122 15 50       215 return if !$begin; #fail
123 15 100 100     80 if (!($begin->isa('PPI::Token::Word') &&
      66        
124             ($begin->content() eq 'if' || $begin->content() eq 'unless'))) {
125 1         17 return; #fail
126             }
127              
128 14   100     114 my @blocks = grep {!$_->isa('PPI::Structure::Condition') &&
  76         571  
129             !$_->isa('PPI::Token')} $final->schildren();
130             # Sanity check:
131 14 50       39 if (scalar grep {!$_->isa('PPI::Structure::Block')} @blocks) {
  30         89  
132 0         0 throw_internal
133             'Expected only conditions, blocks and tokens in the if statement';
134             }
135              
136 14         43 for my $block (@blocks) {
137 25 100       146 if (! $self->_block_has_return($block)) {
138 6         30 return; #fail
139             }
140             }
141              
142 8         87 return 1;
143             }
144              
145             #-----------------------------------------------------------------------------
146              
147             sub _is_given_when_return {
148 37     37   654 my ( $self, $final ) = @_;
149              
150 37 100       194 if ( ! $final->isa( 'PPI::Statement::Given' ) ) {
151 33         205 return; #fail
152             }
153              
154 4         17 my $begin = $final->schild(0);
155 4 50       55 return if !$begin; #fail
156 4 50 33     25 if ( ! ( $begin->isa( 'PPI::Token::Word' ) &&
157             $begin->content() eq 'given' ) ) {
158 0         0 return; #fail
159             }
160              
161 4   100     34 my @blocks = grep {!$_->isa( 'PPI::Structure::Given' ) &&
  12         118  
162             !$_->isa( 'PPI::Token' )} $final->schildren();
163             # Sanity check:
164 4 50       15 if (scalar grep {!$_->isa('PPI::Structure::Block')} @blocks) {
  4         24  
165 0         0 throw_internal
166             'Expected only givens, blocks and tokens in the given statement';
167             }
168 4 50       16 if (@blocks > 1) {
169             # sanity check
170 0         0 throw_internal 'Given statement should have no more than one block';
171             }
172 4 50       15 @blocks or return; #fail
173              
174 4         9 my $have_default; # We have to fail unless a default block is present
175              
176 4         16 foreach my $stmnt ( $blocks[0]->schildren() ) {
177              
178 7 100       97 if ( $stmnt->isa( 'PPI::Statement::When' ) ) {
179              
180             # Check for the default block.
181 6         15 my $first_token;
182 6 100 66     20 $first_token = $stmnt->schild( 0 )
183             and 'default' eq $first_token->content()
184             and $have_default = 1;
185              
186 6 100       129 $self->_is_when_stmnt_with_return( $stmnt )
187             or return; #fail
188              
189             } else {
190              
191 1 50       7 $self->_is_suffix_when_with_return( $stmnt )
192             or return; #fail
193              
194             }
195              
196             }
197              
198 3         24 return $have_default;
199             }
200              
201             #-----------------------------------------------------------------------------
202              
203             sub _is_return_or_goto_stmnt {
204 63     63   144 my ( $self, $stmnt ) = @_;
205 63 100       352 return if not $stmnt->isa('PPI::Statement::Break');
206 34   50     116 my $first_token = $stmnt->schild(0) || return;
207 34   100     565 return $first_token->content() eq 'return'
208             || $first_token->content() eq 'goto';
209             }
210              
211             #-----------------------------------------------------------------------------
212              
213             sub _is_terminal_stmnt {
214 31     31   99 my ( $self, $stmnt ) = @_;
215              
216 31 50       124 return if not $stmnt->isa('PPI::Statement');
217              
218 31   50     87 my $first_token = $stmnt->schild(0) || return;
219 31 100       466 return 1 if exists $self->{_terminal_funcs}->{$first_token};
220              
221 19   100     147 my $second_token = $stmnt->schild(1) || return;
222 18 100 100     395 return if not ( $second_token->isa('PPI::Token::Operator') && ($second_token eq q{->}) );
223              
224 6   50     112 my $third_token = $stmnt->schild(2) || return;
225 6         100 return exists $self->{_terminal_methods}->{$third_token};
226             }
227              
228             #-----------------------------------------------------------------------------
229              
230             sub _is_conditional_stmnt {
231 83     83   169 my ( $self, $stmnt ) = @_;
232 83 50       322 return if not $stmnt->isa('PPI::Statement');
233 83         220 for my $elem ( $stmnt->schildren() ) {
234             return 1 if $elem->isa('PPI::Token::Word')
235 196 100 100     2510 && exists $CONDITIONALS{$elem};
236             }
237 62         309 return;
238             }
239              
240             #-----------------------------------------------------------------------------
241              
242             sub _is_when_stmnt_with_return {
243 6     6   19 my ( $self, $stmnt ) = @_;
244              
245 6   100     23 my @inner = grep { ! $_->isa( 'PPI::Token' ) &&
  15         155  
246             ! $_->isa( 'PPI::Structure::When' ) }
247             $stmnt->schildren();
248 6 50       19 if ( scalar grep { ! $_->isa( 'PPI::Structure::Block' ) } @inner ) {
  6         31  
249 0         0 throw_internal 'When statement should contain only tokens, conditions, and blocks';
250             }
251 6 50       23 @inner > 1
252             and throw_internal 'When statement should have no more than one block';
253 6 50       20 @inner or return; #fail
254              
255 6         19 foreach my $block ( @inner ) {
256 6 100       51 if ( ! $self->_block_has_return( $block ) ) {
257 1         7 return; #fail
258             }
259             }
260              
261 5         61 return 1; #succeed
262             }
263              
264             #-----------------------------------------------------------------------------
265              
266             sub _is_suffix_when_with_return {
267 1     1   5 my ( $self, $stmnt ) = @_;
268 1 50       7 return if not $stmnt->isa('PPI::Statement');
269 1         5 foreach my $elem ( $stmnt->schildren() ) {
270 3 100 33     34 return ( $self->_is_return_or_goto_stmnt( $stmnt ) ||
      100        
271             $self->_is_terminal_stmnt( $stmnt ) )
272             if $elem->isa( 'PPI::Token::Word' )
273             && 'when' eq $elem->content();
274             }
275 0           return;
276             }
277              
278             1;
279              
280             __END__
281              
282             #-----------------------------------------------------------------------------
283              
284             =pod
285              
286             =head1 NAME
287              
288             Perl::Critic::Policy::Subroutines::RequireFinalReturn - End every path through a subroutine with an explicit C<return> statement.
289              
290             =head1 AFFILIATION
291              
292             This Policy is part of the core L<Perl::Critic|Perl::Critic>
293             distribution.
294              
295              
296             =head1 DESCRIPTION
297              
298             Require all subroutines to terminate explicitly with one of the
299             following: C<return>, C<carp>, C<croak>, C<die>, C<exec>, C<exit>,
300             C<goto>, or C<throw>.
301              
302             Subroutines without explicit return statements at their ends can be
303             confusing. It can be challenging to deduce what the return value will
304             be.
305              
306             Furthermore, if the programmer did not mean for there to be a
307             significant return value, and omits a return statement, some of the
308             subroutine's inner data can leak to the outside. Consider this case:
309              
310             package Password;
311             # every time the user guesses the password wrong, its value
312             # is rotated by one character
313             my $password;
314             sub set_password {
315             $password = shift;
316             }
317             sub check_password {
318             my $guess = shift;
319             if ($guess eq $password) {
320             unlock_secrets();
321             } else {
322             $password = (substr $password, 1).(substr $password, 0, 1);
323             }
324             }
325             1;
326              
327             In this case, the last statement in check_password() is the
328             assignment. The result of that assignment is the implicit return
329             value, so a wrong guess returns the right password! Adding a
330             C<return;> at the end of that subroutine solves the problem.
331              
332             The only exception allowed is an empty subroutine.
333              
334             Be careful when fixing problems identified by this Policy; don't
335             blindly put a C<return;> statement at the end of every subroutine.
336              
337             =head1 CONFIGURATION
338              
339             If you've created your own terminal functions that behave like C<die>
340             or C<exit>, then you can configure Perl::Critic to recognize those
341             functions as well. Just put something like this in your
342             F<.perlcriticrc>:
343              
344             [Subroutines::RequireFinalReturn]
345             terminal_funcs = quit abort bailout
346              
347             If you've created your own terminal methods, then you can configure
348             Perl::Critic to recognize those methods as well, but the class won't
349             be considered. For example if you define throw_exception as terminal,
350             then any method of that name will be terminal, regardless of class.
351             Just put something like this in your
352             F<.perlcriticrc>:
353              
354             [Subroutines::RequireFinalReturn]
355             terminal_methods = throw_exception
356              
357             =head1 BUGS
358              
359             We do not look for returns inside ternary operators. That
360             construction is too complicated to analyze right now. Besides, a
361             better form is the return outside of the ternary like this: C<return
362             foo ? 1 : bar ? 2 : 3>
363              
364             =head1 AUTHOR
365              
366             Chris Dolan <cdolan@cpan.org>
367              
368             =head1 COPYRIGHT
369              
370             Copyright (c) 2005-2011 Chris Dolan.
371              
372             This program is free software; you can redistribute it and/or modify
373             it under the same terms as Perl itself. The full text of this license
374             can be found in the LICENSE file included with this module.
375              
376             =cut
377              
378             ##############################################################################
379             # Local Variables:
380             # mode: cperl
381             # cperl-indent-level: 4
382             # fill-column: 78
383             # indent-tabs-mode: nil
384             # c-indentation-style: bsd
385             # End:
386             # ex: set ts=8 sts=4 sw=4 tw=78 ft=perl expandtab shiftround :