File Coverage

blib/lib/Perl/Lint/Policy/Variables/ProhibitPunctuationVars.pm
Criterion Covered Total %
statement 104 113 92.0
branch 61 72 84.7
condition 15 16 93.7
subroutine 8 8 100.0
pod 0 1 0.0
total 188 210 89.5


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::Variables::ProhibitPunctuationVars;
2 134     134   77524 use strict;
  134         170  
  134         3249  
3 134     134   413 use warnings;
  134         138  
  134         2485  
4 134     134   744 use Compiler::Lexer;
  134         4930  
  134         4336  
5 134     134   478 use List::Util qw/any/;
  134         147  
  134         9601  
6 134     134   1240 use Perl::Lint::Constants::Type;
  134         196  
  134         59702  
7 134     134   550 use parent "Perl::Lint::Policy";
  134         147  
  134         564  
8              
9             use constant {
10 134         99351 DESC => 'Magic punctuation variable %s used',
11             EXPL => [79],
12 134     134   6802 };
  134         152  
13              
14             my %var_token_types = (
15             &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              
24             my %expands_regexp_token_types = (
25             ®_EXEC => 1,
26             ®_DECL => 1,
27             ®_DOUBLE_QUOTE => 1,
28             );
29              
30             my %special_variable_token_types = (
31             &SPECIFIC_VALUE => 1,
32             &ARRAY_SIZE => 1,
33             );
34              
35             my %magic_variables = (
36             '$1' => 1, '$2' => 1, '$3' => 1,
37             '$4' => 1, '$5' => 1, '$6' => 1,
38             '$7' => 1, '$8' => 1, '$9' => 1,
39             '$_' => 1, '$&' => 1, '$`' => 1,
40             '$+' => 1, '@+' => 1, '@*' => 1,
41             '%+' => 1, '$*' => 1, '$.' => 1,
42             '$/' => 1, '$|' => 1, '$(' => 1,
43             '$"' => 1, '$;' => 1, '$%' => 1,
44             '$=' => 1, '$-' => 1, '@-' => 1,
45             '%-' => 1, '$)' => 1, '$~' => 1,
46             '$^' => 1, '$:' => 1, '$?' => 1,
47             '$!' => 1, '%!' => 1, '$@' => 1,
48             '$$' => 1, '$<' => 1, '$>' => 1,
49             '$0' => 1, '$[' => 1, '$]' => 1,
50             '@_' => 1,
51              
52             q{$'} => 1,
53              
54             '$^L' => 1, '$^A' => 1, '$^E' => 1,
55             '$^C' => 1, '$^D' => 1, '$^F' => 1,
56             '$^H' => 1, '$^I' => 1, '$^M' => 1,
57             '$^N' => 1, '$^O' => 1, '$^P' => 1,
58             '$^R' => 1, '$^S' => 1, '$^T' => 1,
59             '$^V' => 1, '$^W' => 1, '$^X' => 1,
60             '%^H' => 1,
61              
62             '$\\' => 1,
63             '$::|' => 1,
64             '$}' => 1,
65             '$,' => 1,
66             '$#' => 1,
67             '$#+' => 1,
68             '$#-' => 1,
69             );
70              
71             my %ignore_for_interpolation = (
72             q{$'} => 1,
73             q{$$} => 1,
74             q{$#} => 1,
75             q{$:} => 1,
76             );
77              
78             sub evaluate {
79 29     29 0 40 my ($class, $file, $tokens, $src, $args) = @_;
80              
81 29         25 my $string_mode = '';
82 29         136 my %exempt_vars = (
83             '$_' => 1, '@_' => 1, '$]' => 1,
84             '$1' => 1, '$2' => 1, '$3' => 1,
85             '$4' => 1, '$5' => 1, '$6' => 1,
86             '$7' => 1, '$8' => 1, '$9' => 1,
87             );
88              
89 29 100       59 if (my $this_policies_arg = $args->{prohibit_punctuation_vars}) {
90 9   100     25 $string_mode = $this_policies_arg->{string_mode} || '';
91 9 100       17 if ($string_mode eq 'thorough') {
92 3         5 %exempt_vars = ();
93             }
94              
95 9   100     38 for my $exempt_var (split(/\s+/, $this_policies_arg->{allow} || '')) {
96 6         7 $exempt_vars{$exempt_var} = 1;
97             }
98             }
99              
100 29         66 my $lexer_for_str = Compiler::Lexer->new;
101              
102 29         296 my @violations;
103 29         78 for (
104             my $i = 0, my $token_type, my $token_data, my $is_ref = 0, my $is_raw_heredoc_tag = 0;
105             my $token = $tokens->[$i];
106             $i++
107             ) {
108 514         369 $token_type = $token->{type};
109 514         386 $token_data = $token->{data};
110              
111 514 100       654 if ($special_variable_token_types{$token_type}) {
112 15 100       22 if ($is_ref) {
113 1         2 $is_ref = 0;
114 1         2 next;
115             }
116              
117 14 100       23 if ($exempt_vars{$token_data}) {
118 7         10 next;
119             }
120              
121 7 50       14 if (! $magic_variables{$token_data}) {
122 0         0 next;
123             }
124              
125             push @violations, {
126             filename => $file,
127             line => $token->{line},
128 7         35 description => sprintf(DESC, $token_data),
129             explanation => EXPL,
130             policy => __PACKAGE__,
131             };
132 7         12 next;
133             }
134              
135 499 100       545 if ($var_token_types{$token_type}) {
136 14 50       20 if ($is_ref) {
137 0         0 $is_ref = 0;
138 0         0 next;
139             }
140              
141 14 50       21 if ($exempt_vars{$token_data}) {
142 0         0 next;
143             }
144              
145 14 100       20 if (! $magic_variables{$token_data}) {
146 13         23 next;
147             }
148              
149 1 50       6 if (substr($token_data, 1, 1) =~ /\A[^a-zA-Z]\Z/) {
150             push @violations, {
151             filename => $file,
152             line => $token->{line},
153 1         7 description => sprintf(DESC, $token_data),
154             explanation => EXPL,
155             policy => __PACKAGE__,
156             };
157             }
158 1         3 next;
159             }
160              
161 485 100       534 if ($token_type == REF) {
162 1         3 $is_ref = 1;
163 1         2 next;
164             }
165              
166 484 50       487 if ($token_type == HERE_DOCUMENT_RAW_TAG) {
167 0         0 $is_raw_heredoc_tag = 1;
168 0         0 next;
169             }
170              
171 484 50       511 if ($token_type == HERE_DOCUMENT_END) {
172 0         0 $is_raw_heredoc_tag = 0;
173 0         0 next;
174             }
175              
176 484 100       526 if ($expands_regexp_token_types{$token_type}) {
177 11         9 $i += 2;
178 11         11 $token = $tokens->[$i];
179 11 50       21 if ($token->{type} != REG_EXP) { # when content is empty
180 0         0 next;
181             }
182 11         27 $token_data = $token->data;
183 11         48 $token_type = STRING;
184             } # fall through
185              
186 484 100 100     1356 if (
187             $token_type == STRING ||
188             $token_type == EXEC_STRING
189             # ($token_type == HERE_DOCUMENT && $is_raw_heredoc_tag)
190             ) {
191 114 100       158 if ($string_mode eq 'disable') {
192 1         2 next;
193             }
194              
195 113         4611 my $parts = $lexer_for_str->tokenize($token_data);
196 113         117 my $ref_count = 0;
197 113         180 for (my $j = 0, my $part_type, my $used_var; my $part = $parts->[$j]; $j++) {
198 266         199 $part_type = $part->{type};
199 266         155 $used_var = $part->{data};
200              
201 266 100       314 if ($part_type == REF) {
202 26         15 $ref_count++;
203 26         40 next;
204             }
205              
206 240 100       301 if ($ref_count % 2 != 0) {
207 17         13 $ref_count = 0;
208 17         23 next;
209             }
210              
211 223 100       296 if ($part_type == SPECIFIC_VALUE) {
    100          
212 98 100       124 if ($used_var eq '$:') {
213 13         17 $part = $parts->[$j+1];
214              
215 13 100 100     42 if ($part && $part->{type} == COLON) {
216 7         14 $part = $parts->[$j+2];
217 7 100 100     19 if ($part && $part->{type} == BIT_OR) {
218 1         2 $used_var = '$::|';
219             }
220             else {
221 6         12 next;
222             }
223             }
224             }
225             # TODO
226             # elsif ($used_var eq q{$'}) {
227             # $part = $parts->[$j+1];
228             # if ($part && $part->{type} == KEY) {
229             # # next;
230             # }
231             # }
232             }
233             elsif ($part_type != ARRAY_SIZE) {
234 120 100       144 if (!$var_token_types{$part_type}) {
235 113         165 next;
236             }
237              
238 7         9 $part = $parts->[++$j];
239 7 100       9 if ($part) {
240 3 100       18 if ($used_var eq '$') {
    100          
    50          
241 1 50       3 if ($part->{type} == RIGHT_BRACE) {
242 1         2 $used_var = '$}';
243             }
244             }
245             elsif ($used_var eq '@') {
246 1 50       3 if ($part->{type} == MUL) {
247 1         12 $used_var = '@*';
248             }
249             }
250             elsif ($used_var eq '%-') {
251 1 50       3 if ($part->{type} == INT) { # for formatting. e.g. "%-04f"
252 1         3 next;
253             }
254             }
255             }
256             }
257              
258 103 100       126 if ($exempt_vars{$used_var}) {
259 4         8 next;
260             }
261              
262 99 100 66     125 if ($string_mode eq 'simple' && $ignore_for_interpolation{$used_var}) {
263 4         8 next;
264             }
265              
266 95 100       122 if ($magic_variables{$used_var}) {
267             push @violations, {
268             filename => $file,
269             line => $token->{line},
270 93         469 description => sprintf(DESC, $used_var),
271             explanation => EXPL,
272             policy => __PACKAGE__,
273             };
274             }
275             }
276              
277 113         320 next;
278             }
279             }
280              
281 29         219 return \@violations;
282             }
283              
284             1;
285