File Coverage

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


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