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   74477 use strict;
  133         180  
  133         3199  
3 133     133   412 use warnings;
  133         155  
  133         2369  
4 133     133   423 use Carp ();
  133         131  
  133         1429  
5 133     133   752 use Perl::Lint::Constants::Type;
  133         144  
  133         59445  
6 133     133   561 use parent "Perl::Lint::Policy";
  133         149  
  133         571  
7              
8             use constant {
9 133         11531 DESC => 'The names of or patterns for variables to forbid.',
10             EXPL => 'Find an alternative variable (used: "%s")',
11 133     133   6524 };
  133         171  
12              
13 133         9051 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   463 };
  133         147  
29              
30 133         99262 use constant DEREFERENCE_TOKENS => {
31             &SCALAR_DEREFERENCE => 1,
32             &HASH_DEREFERENCE => 1,
33 133     133   445 };
  133         156  
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 34 my ($class, $file, $tokens, $src, $args) = @_;
69              
70 16         19 my @evil_variables;
71             my @evil_variables_regex;
72 16 50       55 if (my $this_policies_arg = $args->{prohibit_evil_variables}) {
73 16         49 my $variable_specifications = $this_policies_arg->{variables};
74 16 100       51 if ($variable_specifications) {
75 14         271 while (my ($variable, $regex_string, @descrs) = $variable_specifications =~ m/ $variables_regex /xms) {
76 28         97 substr $variable_specifications, 0, $+[0], '';
77              
78 28 100       72 if ($variable) {
79 17         100 push @evil_variables, $variable;
80             }
81             else {
82 11         76 push @evil_variables_regex, $regex_string;
83             }
84             }
85             }
86              
87 16         35 my $variable_specification_files = $this_policies_arg->{variables_file};
88 16 100       43 if ($variable_specification_files) {
89 2 50       86 open my $fh, '<', $variable_specification_files or die "Cannot open file: $!";
90 2         38 while (my $line = <$fh>) {
91 14         26 $line =~ s< [#] .* \z ><>xms;
92 14         37 $line =~ s< \s+ \z ><>xms;
93 14         17 $line =~ s< \A \s+ ><>xms;
94              
95 14 100       30 next if not $line;
96              
97 6 50       102 if (my ($variable, $regex_string, $description) =
98             $line =~ m< $variables_file_line_regex >xms) {
99              
100 6 100       14 if ($variable) {
101 3         49 push @evil_variables, $variable;
102             }
103             else {
104 3         52 push @evil_variables_regex, $regex_string;
105             }
106             }
107             }
108             }
109             }
110              
111 16         25 my %used_var_with_line_num;
112 16         60 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
113 275         220 $token_type = $token->{type};
114 275         205 $token_data = $token->{data};
115              
116 275 100 66     801 if (VAR_TOKENS->{$token_type} || DEREFERENCE_TOKENS->{$token_type}) {
117 43         44 my $var = $token_data;
118 43         45 my $line = $token->{line};
119              
120 43         46 my $opener;
121             my $closer;
122 43 100 100     151 if (DEREFERENCE_TOKENS->{$token_type}) { # XXX workaround
    100          
123 3         5 $opener = LEFT_BRACE;
124 3         5 $closer = RIGHT_BRACE;
125             }
126             elsif ($token_type == SPECIFIC_VALUE && $token_data eq '$^') { # XXX ad hoc
127 6         9 $token = $tokens->[++$i];
128 6         8 $var .= $token->{data};
129 6         9 $used_var_with_line_num{$var} = $line;
130 6         10 next;
131             }
132             else {
133 34         45 $token = $tokens->[++$i];
134 34         38 $token_type = $token->{type};
135              
136 34 100       76 if ($token_type == LEFT_BRACE) {
    100          
137 12         12 $opener = LEFT_BRACE;
138 12         17 $closer = RIGHT_BRACE;
139             }
140             elsif ($token_type == LEFT_BRACKET) {
141 6         9 $opener = LEFT_BRACKET;
142 6         7 $closer = RIGHT_BRACKET;
143             }
144             else {
145 16         33 $used_var_with_line_num{$var} = $line;
146 16         35 next;
147             }
148              
149 18         42 $var .= $token->{data}; # data of opener
150             }
151              
152 21         19 my $left_bracket_num = 1;
153 21         56 for ($i++; $token = $tokens->[$i]; $i++) {
154 45         37 $token_type = $token->{type};
155              
156 45         46 $var .= $token->{data};
157              
158 45 50       132 if ($token_type == $opener) {
    100          
159 0         0 $left_bracket_num++;
160             }
161             elsif ($token_type == $closer) {
162 21 50       54 last if --$left_bracket_num <= 0;
163             }
164             }
165 21         59 $used_var_with_line_num{$var} = $line;
166             }
167             }
168              
169 16         22 my @violations;
170 16         45 for my $evil_var (@evil_variables) {
171 20         57 (my $alt_evil_var = $evil_var) =~ s/\A[\%\@]/\$/;
172              
173 20         31 my $line = $used_var_with_line_num{$evil_var};
174 20         28 my $used_var = $evil_var;
175 20 100 66     53 if (! $line && $alt_evil_var) {
176 5         7 $line = $used_var_with_line_num{$alt_evil_var};
177 5         7 $used_var = $alt_evil_var;
178              
179 5 100       13 if (! $line) {
180 4         10 for my $_used_var (keys %used_var_with_line_num) {
181 7 100       95 if ($line = $_used_var =~ /\A\Q$alt_evil_var\E [\[\{]/x) {
182 2         4 $used_var = $_used_var;
183 2         4 last;
184             }
185             }
186             }
187             }
188              
189 20 100       44 if ($line) {
190 18         151 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         39 for my $regex (@evil_variables_regex) {
201 14         27 for my $used_var (keys %used_var_with_line_num) {
202 43 100       303 if ($used_var =~ /$regex/) {
203             push @violations, {
204             filename => $file,
205 20         149 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         93 return \@violations;
215             }
216              
217             1;
218