File Coverage

blib/lib/Perl/Lint/Policy/RegularExpressions/ProhibitCaptureWithoutTest.pm
Criterion Covered Total %
statement 94 100 94.0
branch 50 60 83.3
condition 30 43 69.7
subroutine 8 8 100.0
pod 0 1 0.0
total 182 212 85.8


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::RegularExpressions::ProhibitCaptureWithoutTest;
2 133     133   73778 use strict;
  133         175  
  133         3298  
3 133     133   409 use warnings;
  133         161  
  133         2824  
4 133     133   434 use List::Util qw/none/;
  133         150  
  133         6966  
5 133     133   824 use Perl::Lint::Constants::Type;
  133         178  
  133         61902  
6 133     133   546 use parent "Perl::Lint::Policy";
  133         146  
  133         616  
7              
8             use constant {
9 133         81825 DESC => 'Capture variable used outside conditional',
10             EXPL => [253],
11 133     133   6856 };
  133         173  
12              
13             my %transfer_of_control_stmt_token_types = (
14             &NEXT => 1,
15             &LAST => 1,
16             &REDO => 1,
17             &GOTO => 1,
18             &RETURN => 1,
19             );
20              
21             my %control_stmt_token_types = (
22             &IF_STATEMENT => 1,
23             &ELSIF_STATEMENT => 1,
24             &UNLESS_STATEMENT => 1,
25             &WHILE_STATEMENT => 1,
26             );
27              
28             sub evaluate {
29 24     24 0 40 my ($class, $file, $tokens, $src, $args) = @_;
30              
31 24         62 my %exceptions = (
32             die => 1,
33             croak => 1,
34             confess => 1,
35             );
36              
37 24 100       50 if (my $this_policies_arg = $args->{prohibit_capture_without_test}) {
38 1   50     8 for my $exception (split(/\s+/, $this_policies_arg->{exception_source} || '')) {
39 2         4 $exceptions{$exception} = 1;
40             };
41             }
42              
43 24         19 my @violations;
44             my @is_tested_by_depth;
45 24         19 my $is_in_context_to_assign = 0;
46 24         22 my $depth = 0;
47             # use Data::Dumper::Concise; warn Dumper($tokens); # TODO remove
48 24         82 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
49 589         433 $token_type = $token->{type};
50 589         394 $token_data = $token->{data};
51              
52 589 50 33     1443 if ($token_type == REG_QUOTE || $token_type == REG_DOUBLE_QUOTE) {
53             # skip reg quotes (because it is recognized as regexp)
54 0         0 $i += 2;
55 0         0 next;
56             }
57              
58 589 100 100     1246 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
59             # XXX
60 47 50 33     68 if ($depth < 0 && scalar @is_tested_by_depth < -$depth) {
61 0         0 next;
62             }
63 47 100       74 $is_tested_by_depth[$depth] = $is_in_context_to_assign ? 1 : 0;
64              
65 47         76 for ($i++; $token = $tokens->[$i]; $i++) {
66 128         95 $token_type = $token->{type};
67 128 100 100     406 if ($token_type == SEMI_COLON) {
    100          
    100          
68 10         180 goto END_OF_STATEMENT;
69             }
70             elsif (
71             $token_type == THREE_TERM_OP
72             ) {
73 7         7 $is_tested_by_depth[$depth] = 1;
74 7         5 last;
75             }
76             elsif (
77             $token_type == OR ||
78             $token_type == ALPHABET_OR
79             ) {
80 30 50       58 $token = $tokens->[++$i] or last;
81 30         27 $token_type = $token->{type};
82 30         66 $token_data = $token->{data};
83              
84 30 100 100     138 if (
      100        
      66        
85             ($exceptions{$token_data} && $token_type == KEY || $token_type == BUILTIN_FUNC) ||
86             $transfer_of_control_stmt_token_types{$token_type}
87             ) {
88 25         18 $is_tested_by_depth[$depth] = 1;
89 25         24 last;
90             }
91              
92 5 50       8 $token = $tokens->[++$i] or last;
93 5 100       9 if ($token->{type} == POINTER) {
94 2 50       5 $token = $tokens->[++$i] or last;
95 2         2 $token_type = $token->{type};
96 2         3 $token_data = $token->{data};
97 2 50 33     14 if ($exceptions{$token_data} && $token_type == METHOD) {
98 2         3 $is_tested_by_depth[$depth] = 1;
99             }
100 2         2 last;
101             }
102              
103 3         4 last;
104             }
105             }
106              
107 37         60 next;
108             }
109              
110 542 100 100     876 if ($token_type == SPECIFIC_VALUE && $token_data =~ /\A\$[1-9][0-9]*\Z/) {
111 56 100   68   280 if (none {$_} @is_tested_by_depth) {
  68         91  
112             push @violations, {
113             filename => $file,
114             line => $token->{line},
115 11         43 description => DESC,
116             explanation => EXPL,
117             policy => __PACKAGE__,
118             };
119             }
120              
121 56         141 next;
122             }
123              
124 486 100       582 if ($control_stmt_token_types{$token_type}) {
125 16         19 $token = $tokens->[++$i];
126 16         10 $token_type = $token->{type};
127 16 100       22 if ($token_type == LEFT_PAREN) {
128 10         7 my $lpnum = 1;
129 10         18 for ($i++; $token = $tokens->[$i]; $i++) {
130 53         38 $token_type = $token->{type};
131 53 50 100     161 if ($token_type == LEFT_PAREN) {
    100          
    100          
132 0         0 $lpnum++;
133             }
134             elsif ($token_type == RIGHT_PAREN) {
135 10 50       22 last if --$lpnum <= 0;
136             }
137             elsif ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
138             # XXX
139 7 50 33     18 if ($depth + 1 < 0 && scalar @is_tested_by_depth < -$depth + 1) {
140 0         0 next;
141             }
142 7         12 $is_tested_by_depth[$depth + 1] = 1;
143             }
144             }
145              
146 10         14 next;
147             }
148              
149             # for postfix
150 6         11 for ($i++; $token = $tokens->[$i]; $i++) {
151 24         19 $token_type = $token->{type};
152 24 100 66     84 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
    100          
153 6         11 $is_tested_by_depth[$depth + 1] = 1;
154             }
155             elsif ($token_type == SEMI_COLON) {
156 6         5 last;
157             }
158             }
159 6         9 next;
160             }
161              
162 470 100       548 if ($token_type == LEFT_BRACE) {
163 20         15 $depth++;
164              
165             # XXX
166 20 50 33     33 if ($depth < 0 && scalar @is_tested_by_depth < -$depth) {
167 0         0 next;
168             }
169              
170 20   100     41 $is_tested_by_depth[$depth] ||= 0;
171 20         32 next;
172             }
173              
174 450 100       458 if ($token_type == RIGHT_BRACE) {
175 23         21 pop @is_tested_by_depth;
176 23         20 $depth--;
177 23         32 next;
178             }
179              
180 427 100       456 if ($token_type == ASSIGN) {
181 12         10 $is_in_context_to_assign = 1;
182 12         19 next;
183             }
184              
185             END_OF_STATEMENT:
186 425 100       692 if ($token_type == SEMI_COLON) {
187 100         69 $is_in_context_to_assign = 0;
188 100         140 next;
189             }
190             }
191              
192 24         107 return \@violations;
193             }
194              
195             1;
196