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   92484 use strict;
  133         276  
  133         5003  
3 133     133   653 use warnings;
  133         228  
  133         3542  
4 133     133   1126 use Perl::Lint::Constants::Type;
  133         233  
  133         80621  
5 133     133   1505 use Perl::Lint::Constants::Kind;
  133         331  
  133         9182  
6 133     133   707 use List::Util qw/any/;
  133         261  
  133         8754  
7 133     133   690 use parent "Perl::Lint::Policy";
  133         246  
  133         745  
8              
9             use constant {
10 133         111401 DESC => 'The additional subroutines to treat as terminal',
11             EXPL => [197],
12 133     133   8876 };
  133         304  
13              
14             sub evaluate {
15 16     16 0 41 my ($class, $file, $tokens, $src, $args) = @_;
16              
17 16   100     127 my @terminal_funcs = split(/ /, $args->{require_final_return}->{terminal_funcs} || '');
18              
19 16         60 my @violations;
20 16         27 my $is_in_sub = 0;
21 16         21 my $left_brace_num = 0;
22 16         61 for (my $i = 0; my $token = $tokens->[$i]; $i++) {
23 42         51 my $token_type = $token->{type};
24 42 100       102 if ($token_type == FUNCTION_DECL) {
25 17         57 for ($i++; $token = $tokens->[$i]; $i++) {
26 98         85 $token_type = $token->{type};
27 98 100       223 if ($token_type == LEFT_BRACE) {
    100          
28 37 100       88 if ($tokens->[$i+1]->{type} == RIGHT_BRACE) {
29 1         4 last;
30             }
31              
32 36         38 my $left_brace_num = 1;
33 36         46 my $is_returned = 0;
34 36         41 my $is_returned_in_cond = undef;
35 36         33 my %constant_loop;
36 36         91 for ($i++; $token = $tokens->[$i]; $i++) {
37 169         154 $token_type = $token->{type};
38 169         160 my $token_data = $token->{data};
39              
40 169 100 100     1843 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         53 delete $constant_loop{$left_brace_num};
45 39 100       87 if (--$left_brace_num <= 0) {
46 36 100 66     98 if (!$is_returned && !$is_returned_in_cond) {
47 15         76 push @violations, {
48             filename => $file,
49             line => $token->{line},
50             description => DESC,
51             explanation => EXPL,
52             policy => __PACKAGE__,
53             };
54             }
55 36         121 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     56 $is_returned_in_cond //= 1; # at once
65              
66 24         23 my $left_brace_num = 0;
67 24         18 my $is_returned_in_cond_locally = 0;
68 24         44 for ($i++; $token = $tokens->[$i]; $i++) {
69 142         129 $token_type = $token->{type};
70 142         122 $token_data = $token->{data};
71 142 100 66     541 if ($token_type == LEFT_BRACE) {
    100 66        
    100          
    100          
72 24         42 $left_brace_num++;
73             }
74             elsif ($token_type == RIGHT_BRACE) {
75 24 100       36 if (!$is_returned_in_cond_locally) {
76 6         7 $is_returned_in_cond = 0;
77             }
78 24         60 last;
79             }
80 3     3   10 elsif ($token_type == RETURN || $token_type == GOTO) {
81 16         28 $is_returned_in_cond_locally = 1;
82             }
83             elsif ($token_type == KEY && any {$_ eq $token_data} @terminal_funcs) {
84 2         9 $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         9 $constant_loop{$left_brace_num+1} = 1;
95             }
96             elsif ($token_type == RETURN || $token_type == GOTO) {
97 9 100       34 if ($constant_loop{$left_brace_num}) {
98 1         4 next;
99             }
100              
101 8         17 $is_returned = 1;
102             }
103             elsif ($token_type == BUILTIN_FUNC) {
104 6 50       12 if ($constant_loop{$left_brace_num}) {
105 0         0 next;
106             }
107              
108 6 50 100     28 if (
      66        
109             $token_data eq 'die' ||
110             $token_data eq 'exec' ||
111             $token_data eq 'exit'
112             ) {
113 6         8 my $next_token = $tokens->[$i+1];
114 6 100       10 if ($next_token->{kind} == KIND_STMT) {
115 3         4 $i++;
116 3         8 next;
117             }
118 3         8 $is_returned = 1;
119             }
120             }
121             elsif ($token_type == KEY) {
122 8 50       17 if ($constant_loop{$left_brace_num}) {
123 0         0 next;
124             }
125              
126 8 100 100     109 if (
    100 100        
127 3     3   6 $token_data eq 'croak' ||
128             $token_data eq 'confess' ||
129             any {$_ eq $token_data} @terminal_funcs
130             ) {
131 4         8 my $next_token = $tokens->[$i+1];
132 4 100 50     14 if (($next_token->{kind} || -1) == KIND_STMT) {
133 1         2 $i++;
134 1         2 next;
135             }
136             else {
137 3         4 my $next_token = $tokens->[$i+2];
138 3 50 100     14 if (($next_token->{kind} || -1) == KIND_STMT) {
139 0         0 $i += 2;
140 0         0 next;
141             }
142             }
143 3         8 $is_returned = 1;
144             }
145             elsif ($token_data eq 'throw') {
146 2         4 my $target_token = $tokens->[$i+2];
147 2 100       7 if ($target_token->{kind} == KIND_STMT) {
148 1         1 $i += 2;
149 1         5 next;
150             }
151 1         12 $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         2 my $target_token = $tokens->[$i+2];
160 2 50       10 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         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         83 return \@violations;
177             }
178              
179             1;
180