File Coverage

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


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::RegularExpressions::ProhibitCaptureWithoutTest;
2 133     133   111255 use strict;
  133         261  
  133         5399  
3 133     133   616 use warnings;
  133         220  
  133         4632  
4 133     133   618 use List::Util qw/none/;
  133         196  
  133         9355  
5 133     133   1323 use Perl::Lint::Constants::Type;
  133         240  
  133         90828  
6 133     133   794 use parent "Perl::Lint::Policy";
  133         220  
  133         879  
7              
8             use constant {
9 133         107710 DESC => 'Capture variable used outside conditional',
10             EXPL => [253],
11 133     133   9440 };
  133         250  
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 67 my ($class, $file, $tokens, $src, $args) = @_;
30              
31 24         93 my %exceptions = (
32             die => 1,
33             croak => 1,
34             confess => 1,
35             );
36              
37 24 100       138 if (my $this_policies_arg = $args->{prohibit_capture_without_test}) {
38 1   50     9 for my $exception (split(/\s+/, $this_policies_arg->{exception_source} || '')) {
39 2         6 $exceptions{$exception} = 1;
40             };
41             }
42              
43 24         28 my @violations;
44             my @is_tested_by_depth;
45 24         28 my $is_in_context_to_assign = 0;
46 24         33 my $depth = 0;
47             # use Data::Dumper::Concise; warn Dumper($tokens); # TODO remove
48 24         111 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
49 589         714 $token_type = $token->{type};
50 589         746 $token_data = $token->{data};
51              
52 589 50 33     2247 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     1912 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
59             # XXX
60 47 50 33     113 if ($depth < 0 && scalar @is_tested_by_depth < -$depth) {
61 0         0 next;
62             }
63 47 100       97 $is_tested_by_depth[$depth] = $is_in_context_to_assign ? 1 : 0;
64              
65 47         117 for ($i++; $token = $tokens->[$i]; $i++) {
66 128         165 $token_type = $token->{type};
67 128 100 100     723 if ($token_type == SEMI_COLON) {
    100          
    100          
68 10         302 goto END_OF_STATEMENT;
69             }
70             elsif (
71             $token_type == THREE_TERM_OP
72             ) {
73 7         9 $is_tested_by_depth[$depth] = 1;
74 7         9 last;
75             }
76             elsif (
77             $token_type == OR ||
78             $token_type == ALPHABET_OR
79             ) {
80 30 50       75 $token = $tokens->[++$i] or last;
81 30         47 $token_type = $token->{type};
82 30         37 $token_data = $token->{data};
83              
84 30 100 100     236 if (
      100        
      100        
85             ($exceptions{$token_data} && $token_type == KEY || $token_type == BUILTIN_FUNC) ||
86             $transfer_of_control_stmt_token_types{$token_type}
87             ) {
88 25         35 $is_tested_by_depth[$depth] = 1;
89 25         36 last;
90             }
91              
92 5 50       16 $token = $tokens->[++$i] or last;
93 5 100       13 if ($token->{type} == POINTER) {
94 2 50       8 $token = $tokens->[++$i] or last;
95 2         4 $token_type = $token->{type};
96 2         5 $token_data = $token->{data};
97 2 50 33     21 if ($exceptions{$token_data} && $token_type == METHOD) {
98 2         4 $is_tested_by_depth[$depth] = 1;
99             }
100 2         5 last;
101             }
102              
103 3         6 last;
104             }
105             }
106              
107 37         107 next;
108             }
109              
110 542 100 100     1318 if ($token_type == SPECIFIC_VALUE && $token_data =~ /\A\$[1-9][0-9]*\Z/) {
111 56 100   68   397 if (none {$_} @is_tested_by_depth) {
  68         140  
112 11         76 push @violations, {
113             filename => $file,
114             line => $token->{line},
115             description => DESC,
116             explanation => EXPL,
117             policy => __PACKAGE__,
118             };
119             }
120              
121 56         269 next;
122             }
123              
124 486 100       964 if ($control_stmt_token_types{$token_type}) {
125 16         30 $token = $tokens->[++$i];
126 16         25 $token_type = $token->{type};
127 16 100       34 if ($token_type == LEFT_PAREN) {
128 10         12 my $lpnum = 1;
129 10         32 for ($i++; $token = $tokens->[$i]; $i++) {
130 53         74 $token_type = $token->{type};
131 53 50 100     264 if ($token_type == LEFT_PAREN) {
    100          
    100          
132 0         0 $lpnum++;
133             }
134             elsif ($token_type == RIGHT_PAREN) {
135 10 50       30 last if --$lpnum <= 0;
136             }
137             elsif ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
138             # XXX
139 7 50 33     27 if ($depth + 1 < 0 && scalar @is_tested_by_depth < -$depth + 1) {
140 0         0 next;
141             }
142 7         21 $is_tested_by_depth[$depth + 1] = 1;
143             }
144             }
145              
146 10         33 next;
147             }
148              
149             # for postfix
150 6         19 for ($i++; $token = $tokens->[$i]; $i++) {
151 24         28 $token_type = $token->{type};
152 24 100 66     109 if ($token_type == REG_EXP || $token_type == REG_REPLACE_FROM) {
    100          
153 6         14 $is_tested_by_depth[$depth + 1] = 1;
154             }
155             elsif ($token_type == SEMI_COLON) {
156 6         8 last;
157             }
158             }
159 6         13 next;
160             }
161              
162 470 100       770 if ($token_type == LEFT_BRACE) {
163 20         25 $depth++;
164              
165             # XXX
166 20 50 33     55 if ($depth < 0 && scalar @is_tested_by_depth < -$depth) {
167 0         0 next;
168             }
169              
170 20   100     73 $is_tested_by_depth[$depth] ||= 0;
171 20         51 next;
172             }
173              
174 450 100       679 if ($token_type == RIGHT_BRACE) {
175 23         33 pop @is_tested_by_depth;
176 23         23 $depth--;
177 23         55 next;
178             }
179              
180 427 100       683 if ($token_type == ASSIGN) {
181 12         15 $is_in_context_to_assign = 1;
182 12         32 next;
183             }
184              
185             END_OF_STATEMENT:
186 425 100       1201 if ($token_type == SEMI_COLON) {
187 100         108 $is_in_context_to_assign = 0;
188 100         315 next;
189             }
190             }
191              
192 24         199 return \@violations;
193             }
194              
195             1;
196