File Coverage

blib/lib/Perl/Lint/Policy/Subroutines/RequireFinalReturn.pm
Criterion Covered Total %
statement 96 101 95.0
branch 57 64 89.0
condition 48 56 85.7
subroutine 10 10 100.0
pod 0 1 0.0
total 211 232 90.9


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::Subroutines::RequireFinalReturn;
2 133     133   72177 use strict;
  133         201  
  133         3129  
3 133     133   430 use warnings;
  133         167  
  133         2484  
4 133     133   805 use Perl::Lint::Constants::Type;
  133         174  
  133         59727  
5 133     133   1092 use Perl::Lint::Constants::Kind;
  133         194  
  133         6931  
6 133     133   499 use List::Util qw/any/;
  133         169  
  133         6107  
7 133     133   502 use parent "Perl::Lint::Policy";
  133         171  
  133         611  
8              
9             use constant {
10 133         82392 DESC => 'The additional subroutines to treat as terminal',
11             EXPL => [197],
12 133     133   6562 };
  133         201  
13              
14             sub evaluate {
15 16     16 0 23 my ($class, $file, $tokens, $src, $args) = @_;
16              
17 16   100     82 my @terminal_funcs = split(/ /, $args->{require_final_return}->{terminal_funcs} || '');
18              
19 16         13 my @violations;
20 16         16 my $is_in_sub = 0;
21 16         19 my $left_brace_num = 0;
22 16         37 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
23 42         31 my $token_type = $token->{type};
24 42 100       69 if ($token_type == FUNCTION_DECL) {
25 17         33 for ($i++; $token = $tokens->[$i]; $i++) {
26 98         70 $token_type = $token->{type};
27 98 100       176 if ($token_type == LEFT_BRACE) {
    100          
28 37 100       63 if ($tokens->[$i+1]->{type} == RIGHT_BRACE) {
29 1         3 last;
30             }
31              
32 36         27 my $left_brace_num = 1;
33 36         21 my $is_returned = 0;
34 36         26 my $is_returned_in_cond = undef;
35 36         25 my %constant_loop;
36 36         53 for ($i++; $token = $tokens->[$i]; $i++) {
37 169         127 $token_type = $token->{type};
38 169         118 my $token_data = $token->{data};
39              
40 169 100 100     1472 if ($token_type == LEFT_BRACE) {
    100 100        
    100 100        
    100 66        
    100 100        
    100 66        
    100 100        
    100 100        
41 3         5 $left_brace_num++;
42             }
43             elsif ($token_type == RIGHT_BRACE) {
44 39         32 delete $constant_loop{$left_brace_num};
45 39 100       56 if (--$left_brace_num <= 0) {
46 36 100 66     69 if (!$is_returned && !$is_returned_in_cond) {
47             push @violations, {
48             filename => $file,
49             line => $token->{line},
50 15         47 description => DESC,
51             explanation => EXPL,
52             policy => __PACKAGE__,
53             };
54             }
55 36         88 last;
56             }
57             }
58             elsif (
59             $token_type == IF_STATEMENT ||
60             $token_type == ELSIF_STATEMENT ||
61             $token_type == ELSE_STATEMENT ||
62             $token_type == UNLESS_STATEMENT
63             ) {
64 24   100     41 $is_returned_in_cond //= 1; # at once
65              
66 24         13 my $left_brace_num = 0;
67 24         18 my $is_returned_in_cond_locally = 0;
68 24         35 for ($i++; $token = $tokens->[$i]; $i++) {
69 142         100 $token_type = $token->{type};
70 142         104 $token_data = $token->{data};
71 142 100 66     438 if ($token_type == LEFT_BRACE) {
    100 66        
    100          
    100          
72 24         31 $left_brace_num++;
73             }
74             elsif ($token_type == RIGHT_BRACE) {
75 24 100       28 if (!$is_returned_in_cond_locally) {
76 6         7 $is_returned_in_cond = 0;
77             }
78 24         37 last;
79             }
80             elsif ($token_type == RETURN || $token_type == GOTO) {
81 16         24 $is_returned_in_cond_locally = 1;
82             }
83 3     3   6 elsif ($token_type == KEY && any {$_ eq $token_data} @terminal_funcs) {
84 2         6 $is_returned_in_cond_locally = 1;
85             }
86             }
87             }
88             elsif (
89             $token_type == FOR_STATEMENT ||
90             $token_type == FOREACH_STATEMENT ||
91             $token_type == WHILE_STATEMENT ||
92             $token_type == UNTIL_STATEMENT
93             ) {
94 2         7 $constant_loop{$left_brace_num+1} = 1;
95             }
96             elsif ($token_type == RETURN || $token_type == GOTO) {
97 9 100       17 if ($constant_loop{$left_brace_num}) {
98 1         2 next;
99             }
100              
101 8         17 $is_returned = 1;
102             }
103             elsif ($token_type == BUILTIN_FUNC) {
104 6 50       9 if ($constant_loop{$left_brace_num}) {
105 0         0 next;
106             }
107              
108 6 50 100     44 if (
      66        
109             $token_data eq 'die' ||
110             $token_data eq 'exec' ||
111             $token_data eq 'exit'
112             ) {
113 6         7 my $next_token = $tokens->[$i+1];
114 6 100       11 if ($next_token->{kind} == KIND_STMT) {
115 3         3 $i++;
116 3         6 next;
117             }
118 3         6 $is_returned = 1;
119             }
120             }
121             elsif ($token_type == KEY) {
122 8 50       12 if ($constant_loop{$left_brace_num}) {
123 0         0 next;
124             }
125              
126 8 100 100     79 if (
    100 100        
127             $token_data eq 'croak' ||
128             $token_data eq 'confess' ||
129 3     3   5 any {$_ eq $token_data} @terminal_funcs
130             ) {
131 4         4 my $next_token = $tokens->[$i+1];
132 4 100 50     11 if (($next_token->{kind} || -1) == KIND_STMT) {
133 1         1 $i++;
134 1         2 next;
135             }
136             else {
137 3         5 my $next_token = $tokens->[$i+2];
138 3 50 100     12 if (($next_token->{kind} || -1) == KIND_STMT) {
139 0         0 $i += 2;
140 0         0 next;
141             }
142             }
143 3         7 $is_returned = 1;
144             }
145             elsif ($token_data eq 'throw') {
146 2         4 my $target_token = $tokens->[$i+2];
147 2 100       6 if ($target_token->{kind} == KIND_STMT) {
148 1         2 $i += 2;
149 1         5 next;
150             }
151 1         5 $is_returned = 1;
152             }
153             }
154             elsif ($token_type == NAMESPACE && $token_data eq 'Carp') {
155 2 50       4 if ($constant_loop{$left_brace_num}) {
156 0         0 next;
157             }
158              
159 2         4 my $target_token = $tokens->[$i+2];
160 2 50       14 if ($target_token->{type} == NAMESPACE) {
161 2         3 my $target_token_data = $target_token->{data};
162 2 50 66     7 if ($target_token_data eq 'croak' || $target_token_data eq 'confess') {
163 2         5 $is_returned = 1;
164             }
165             }
166             }
167             }
168             }
169             elsif ($token_type == SEMI_COLON) {
170 1         2 last;
171             }
172             }
173             }
174             }
175              
176 16         59 return \@violations;
177             }
178              
179             1;
180