File Coverage

blib/lib/Perl/Lint/Policy/Variables/ProhibitEvilVariables.pm
Criterion Covered Total %
statement 98 99 98.9
branch 37 42 88.1
condition 7 9 77.7
subroutine 9 9 100.0
pod 0 1 0.0
total 151 160 94.3


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::Variables::ProhibitEvilVariables;
2 133     133   74384 use strict;
  133         180  
  133         3059  
3 133     133   435 use warnings;
  133         142  
  133         2326  
4 133     133   416 use Carp ();
  133         128  
  133         1428  
5 133     133   775 use Perl::Lint::Constants::Type;
  133         136  
  133         59068  
6 133     133   543 use parent "Perl::Lint::Policy";
  133         148  
  133         549  
7              
8             use constant {
9 133         11502 DESC => 'The names of or patterns for variables to forbid.',
10             EXPL => 'Find an alternative variable (used: "%s")',
11 133     133   6399 };
  133         171  
12              
13 133         8880 use constant VAR_TOKENS => {
14             &VAR => 1,
15             &CODE_VAR => 1,
16             &ARRAY_VAR => 1,
17             &HASH_VAR => 1,
18              
19             &GLOBAL_VAR => 1,
20             &GLOBAL_ARRAY_VAR => 1,
21             &GLOBAL_HASH_VAR => 1,
22              
23             &LOCAL_VAR => 1,
24             &LOCAL_ARRAY_VAR => 1,
25             &LOCAL_HASH_VAR => 1,
26              
27             &SPECIFIC_VALUE => 1,
28 133     133   457 };
  133         153  
29              
30 133         100056 use constant DEREFERENCE_TOKENS => {
31             &SCALAR_DEREFERENCE => 1,
32             &HASH_DEREFERENCE => 1,
33 133     133   460 };
  133         149  
34              
35             my $variable_name_regex = qr< [\$\@%] \S+ >xms;
36             my $regular_expression_regex = qr< [/] ( [^/]+ ) [/] >xms;
37             my @description_regexes = (
38             qr< [{] ( [^}]+ ) [}] >xms,
39             qr{ < ( [^>]+ ) > }xms,
40             qr{ [[] ( [^]]+ ) []] }xms,
41             qr{ [(] ( [^)]+ ) [)] }xms,
42             );
43             my $description_regex = qr< @{[join '|', @description_regexes]} >xms;
44             my $variables_regex = qr<
45             \A
46             \s*
47             (?:
48             ( $variable_name_regex )
49             | $regular_expression_regex
50             )
51             (?: \s* $description_regex )?
52             \s*
53             >xms;
54             my $variables_file_line_regex = qr<
55             \A
56             \s*
57             (?:
58             ( $variable_name_regex )
59             | $regular_expression_regex
60             )
61             \s*
62             ( \S (?: .* \S )? )?
63             \s*
64             \z
65             >xms;
66              
67             sub evaluate {
68 16     16 0 40 my ($class, $file, $tokens, $src, $args) = @_;
69              
70 16         24 my @evil_variables;
71             my @evil_variables_regex;
72 16 50       52 if (my $this_policies_arg = $args->{prohibit_evil_variables}) {
73 16         31 my $variable_specifications = $this_policies_arg->{variables};
74 16 100       37 if ($variable_specifications) {
75 14         230 while (my ($variable, $regex_string, @descrs) = $variable_specifications =~ m/ $variables_regex /xms) {
76 28         73 substr $variable_specifications, 0, $+[0], '';
77              
78 28 100       63 if ($variable) {
79 17         132 push @evil_variables, $variable;
80             }
81             else {
82 11         67 push @evil_variables_regex, $regex_string;
83             }
84             }
85             }
86              
87 16         28 my $variable_specification_files = $this_policies_arg->{variables_file};
88 16 100       37 if ($variable_specification_files) {
89 2 50       69 open my $fh, '<', $variable_specification_files or die "Cannot open file: $!";
90 2         22 while (my $line = <$fh>) {
91 14         21 $line =~ s< [#] .* \z ><>xms;
92 14         31 $line =~ s< \s+ \z ><>xms;
93 14         16 $line =~ s< \A \s+ ><>xms;
94              
95 14 100       31 next if not $line;
96              
97 6 50       88 if (my ($variable, $regex_string, $description) =
98             $line =~ m< $variables_file_line_regex >xms) {
99              
100 6 100       10 if ($variable) {
101 3         21 push @evil_variables, $variable;
102             }
103             else {
104 3         24 push @evil_variables_regex, $regex_string;
105             }
106             }
107             }
108             }
109             }
110              
111 16         25 my %used_var_with_line_num;
112 16         56 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
113 275         192 $token_type = $token->{type};
114 275         210 $token_data = $token->{data};
115              
116 275 100 66     749 if (VAR_TOKENS->{$token_type} || DEREFERENCE_TOKENS->{$token_type}) {
117 43         45 my $var = $token_data;
118 43         39 my $line = $token->{line};
119              
120 43         31 my $opener;
121             my $closer;
122 43 100 100     133 if (DEREFERENCE_TOKENS->{$token_type}) { # XXX workaround
    100          
123 3         6 $opener = LEFT_BRACE;
124 3         3 $closer = RIGHT_BRACE;
125             }
126             elsif ($token_type == SPECIFIC_VALUE && $token_data eq '$^') { # XXX ad hoc
127 6         7 $token = $tokens->[++$i];
128 6         7 $var .= $token->{data};
129 6         8 $used_var_with_line_num{$var} = $line;
130 6         11 next;
131             }
132             else {
133 34         45 $token = $tokens->[++$i];
134 34         33 $token_type = $token->{type};
135              
136 34 100       53 if ($token_type == LEFT_BRACE) {
    100          
137 12         13 $opener = LEFT_BRACE;
138 12         14 $closer = RIGHT_BRACE;
139             }
140             elsif ($token_type == LEFT_BRACKET) {
141 6         6 $opener = LEFT_BRACKET;
142 6         7 $closer = RIGHT_BRACKET;
143             }
144             else {
145 16         25 $used_var_with_line_num{$var} = $line;
146 16         35 next;
147             }
148              
149 18         19 $var .= $token->{data}; # data of opener
150             }
151              
152 21         18 my $left_bracket_num = 1;
153 21         50 for ($i++; $token = $tokens->[$i]; $i++) {
154 45         44 $token_type = $token->{type};
155              
156 45         45 $var .= $token->{data};
157              
158 45 50       121 if ($token_type == $opener) {
    100          
159 0         0 $left_bracket_num++;
160             }
161             elsif ($token_type == $closer) {
162 21 50       44 last if --$left_bracket_num <= 0;
163             }
164             }
165 21         51 $used_var_with_line_num{$var} = $line;
166             }
167             }
168              
169 16         19 my @violations;
170 16         33 for my $evil_var (@evil_variables) {
171 20         44 (my $alt_evil_var = $evil_var) =~ s/\A[\%\@]/\$/;
172              
173 20         27 my $line = $used_var_with_line_num{$evil_var};
174 20         17 my $used_var = $evil_var;
175 20 100 66     50 if (! $line && $alt_evil_var) {
176 5         5 $line = $used_var_with_line_num{$alt_evil_var};
177 5         7 $used_var = $alt_evil_var;
178              
179 5 100       7 if (! $line) {
180 4         8 for my $_used_var (keys %used_var_with_line_num) {
181 7 100       82 if ($line = $_used_var =~ /\A\Q$alt_evil_var\E [\[\{]/x) {
182 2         3 $used_var = $_used_var;
183 2         3 last;
184             }
185             }
186             }
187             }
188              
189 20 100       38 if ($line) {
190 18         116 push @violations, {
191             filename => $file,
192             line => $line,
193             description => DESC,
194             explanation => sprintf(EXPL, $used_var),
195             policy => __PACKAGE__,
196             };
197             }
198             }
199              
200 16         35 for my $regex (@evil_variables_regex) {
201 14         24 for my $used_var (keys %used_var_with_line_num) {
202 43 100       249 if ($used_var =~ /$regex/) {
203             push @violations, {
204             filename => $file,
205 20         110 line => $used_var_with_line_num{$used_var},
206             description => DESC,
207             explanation => sprintf(EXPL, $used_var),
208             policy => __PACKAGE__,
209             };
210             }
211             }
212             }
213              
214 16         82 return \@violations;
215             }
216              
217             1;
218