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   70061 use strict;
  133         199  
  133         3217  
3 133     133   430 use warnings;
  133         163  
  133         2537  
4 133     133   855 use Perl::Lint::Constants::Type;
  133         177  
  133         59167  
5 133     133   932 use Perl::Lint::Constants::Kind;
  133         172  
  133         6904  
6 133     133   533 use List::Util qw/any/;
  133         161  
  133         6189  
7 133     133   503 use parent "Perl::Lint::Policy";
  133         161  
  133         627  
8              
9             use constant {
10 133         83581 DESC => 'The additional subroutines to treat as terminal',
11             EXPL => [197],
12 133     133   6560 };
  133         187  
13              
14             sub evaluate {
15 16     16 0 37 my ($class, $file, $tokens, $src, $args) = @_;
16              
17 16   100     98 my @terminal_funcs = split(/ /, $args->{require_final_return}->{terminal_funcs} || '');
18              
19 16         18 my @violations;
20 16         18 my $is_in_sub = 0;
21 16         23 my $left_brace_num = 0;
22 16         58 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
23 42         34 my $token_type = $token->{type};
24 42 100       78 if ($token_type == FUNCTION_DECL) {
25 17         45 for ($i++; $token = $tokens->[$i]; $i++) {
26 98         66 $token_type = $token->{type};
27 98 100       202 if ($token_type == LEFT_BRACE) {
    100          
28 37 100       74 if ($tokens->[$i+1]->{type} == RIGHT_BRACE) {
29 1         4 last;
30             }
31              
32 36         30 my $left_brace_num = 1;
33 36         33 my $is_returned = 0;
34 36         29 my $is_returned_in_cond = undef;
35 36         29 my %constant_loop;
36 36         68 for ($i++; $token = $tokens->[$i]; $i++) {
37 169         127 $token_type = $token->{type};
38 169         138 my $token_data = $token->{data};
39              
40 169 100 100     1544 if ($token_type == LEFT_BRACE) {
    100 100        
    100 100        
    100 66        
    100 100        
    100 66        
    100 100        
    100 100        
41 3         7 $left_brace_num++;
42             }
43             elsif ($token_type == RIGHT_BRACE) {
44 39         39 delete $constant_loop{$left_brace_num};
45 39 100       70 if (--$left_brace_num <= 0) {
46 36 100 66     77 if (!$is_returned && !$is_returned_in_cond) {
47             push @violations, {
48             filename => $file,
49             line => $token->{line},
50 15         57 description => DESC,
51             explanation => EXPL,
52             policy => __PACKAGE__,
53             };
54             }
55 36         102 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     46 $is_returned_in_cond //= 1; # at once
65              
66 24         17 my $left_brace_num = 0;
67 24         16 my $is_returned_in_cond_locally = 0;
68 24         38 for ($i++; $token = $tokens->[$i]; $i++) {
69 142         101 $token_type = $token->{type};
70 142         96 $token_data = $token->{data};
71 142 100 66     462 if ($token_type == LEFT_BRACE) {
    100 66        
    100          
    100          
72 24         38 $left_brace_num++;
73             }
74             elsif ($token_type == RIGHT_BRACE) {
75 24 100       32 if (!$is_returned_in_cond_locally) {
76 6         4 $is_returned_in_cond = 0;
77             }
78 24         39 last;
79             }
80             elsif ($token_type == RETURN || $token_type == GOTO) {
81 16         27 $is_returned_in_cond_locally = 1;
82             }
83 3     3   8 elsif ($token_type == KEY && any {$_ eq $token_data} @terminal_funcs) {
84 2         8 $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       16 if ($constant_loop{$left_brace_num}) {
98 1         3 next;
99             }
100              
101 8         15 $is_returned = 1;
102             }
103             elsif ($token_type == BUILTIN_FUNC) {
104 6 50       13 if ($constant_loop{$left_brace_num}) {
105 0         0 next;
106             }
107              
108 6 50 100     25 if (
      66        
109             $token_data eq 'die' ||
110             $token_data eq 'exec' ||
111             $token_data eq 'exit'
112             ) {
113 6         5 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         7 $is_returned = 1;
119             }
120             }
121             elsif ($token_type == KEY) {
122 8 50       18 if ($constant_loop{$left_brace_num}) {
123 0         0 next;
124             }
125              
126 8 100 100     91 if (
    100 100        
127             $token_data eq 'croak' ||
128             $token_data eq 'confess' ||
129 3     3   6 any {$_ eq $token_data} @terminal_funcs
130             ) {
131 4         8 my $next_token = $tokens->[$i+1];
132 4 100 50     11 if (($next_token->{kind} || -1) == KIND_STMT) {
133 1         2 $i++;
134 1         2 next;
135             }
136             else {
137 3         5 my $next_token = $tokens->[$i+2];
138 3 50 100     13 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         7 $is_returned = 1;
152             }
153             }
154             elsif ($token_type == NAMESPACE && $token_data eq 'Carp') {
155 2 50       5 if ($constant_loop{$left_brace_num}) {
156 0         0 next;
157             }
158              
159 2         3 my $target_token = $tokens->[$i+2];
160 2 50       9 if ($target_token->{type} == NAMESPACE) {
161 2         3 my $target_token_data = $target_token->{data};
162 2 50 66     8 if ($target_token_data eq 'croak' || $target_token_data eq 'confess') {
163 2         3 $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         71 return \@violations;
177             }
178              
179             1;
180