File Coverage

blib/lib/Perl/Lint/Policy/Variables/ProhibitPackageVars.pm
Criterion Covered Total %
statement 131 138 94.9
branch 88 100 88.0
condition 44 65 67.6
subroutine 10 10 100.0
pod 0 1 0.0
total 273 314 86.9


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::Variables::ProhibitPackageVars;
2 133     133   100214 use strict;
  133         272  
  133         4803  
3 133     133   609 use warnings;
  133         185  
  133         3771  
4 133     133   597 use List::Util qw/any/;
  133         223  
  133         9823  
5 133     133   1107 use Perl::Lint::Constants::Type;
  133         321  
  133         81792  
6 133     133   790 use parent "Perl::Lint::Policy";
  133         213  
  133         771  
7              
8             use constant {
9 133         209173 DESC => 'Package variable declared or used',
10             EXPL => [73, 75],
11 133     133   9789 };
  133         235  
12              
13             sub evaluate {
14 10     10 0 24 my ($class, $file, $tokens, $src, $args) = @_;
15              
16 10         26 my @allowed_packages = qw/Data::Dumper File::Find FindBin Log::Log4perl/;
17 10 100       29 if (my $this_policies_arg = $args->{prohibit_package_vars}) {
18 3 50       10 if (my $add_packages = $this_policies_arg->{add_packages}) {
19 3         10 push @allowed_packages, split /\s+/, $add_packages;
20             }
21             }
22              
23 10         12 my @violations;
24 10         39 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
25 226         221 $token_type = $token->{type};
26 226         197 $token_data = $token->{data};
27              
28 226 100 100     2368 if ($token_type == OUR_DECL) {
    100 100        
    100 100        
    100 66        
    100 66        
      66        
      100        
29 10         15 $token = $tokens->[++$i];
30 10         11 $token_type = $token->{type};
31 10 100 66     45 if ($token_type == LEFT_PAREN) {
    50 33        
      33        
      0        
      0        
32 5         5 my $violation;
33 5         7 my $left_paren_num = 1;
34 5         9 for ($i++; $token = $tokens->[$i]; $i++) {
35 20         21 $token_type = $token->{type};
36 20 50 66     129 if ($token_type == LEFT_PAREN) {
    100 100        
    100 66        
      66        
      33        
37 0         0 $left_paren_num++;
38             }
39             elsif ($token_type == RIGHT_PAREN) {
40 5 50       10 if (--$left_paren_num <= 0) {
41 5 100       11 if ($violation) {
42 4         4 push @violations, $violation;
43 4         12 undef $violation;
44             }
45 5         13 last;
46             }
47             }
48             elsif (
49             $token_type == GLOBAL_VAR ||
50             $token_type == GLOBAL_ARRAY_VAR ||
51             $token_type == GLOBAL_HASH_VAR ||
52             $token_type == VAR ||
53             $token_type == ARRAY_VAR ||
54             $token_type == HASH_VAR
55             ) {
56 10 100       35 if ($token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
57 6   100     30 $violation ||= +{
58             filename => $file,
59             line => $token->{line},
60             description => DESC,
61             explanation => EXPL,
62             policy => __PACKAGE__,
63             };
64             }
65             }
66             }
67             }
68             elsif (
69             $token_type == GLOBAL_VAR ||
70             $token_type == GLOBAL_ARRAY_VAR ||
71             $token_type == GLOBAL_HASH_VAR ||
72             $token_type == VAR ||
73             $token_type == ARRAY_VAR ||
74             $token_type == HASH_VAR
75             ) {
76 5 100       30 if ($token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
77 2         20 push @violations, {
78             filename => $file,
79             line => $token->{line},
80             description => DESC,
81             explanation => EXPL,
82             policy => __PACKAGE__,
83             };
84             }
85             }
86             }
87             elsif ($token_type == LOCAL_DECL) {
88 8         8 $token = $tokens->[++$i];
89 8         7 $token_type = $token->{type};
90 8 100       10 if ($token_type == LEFT_PAREN) {
91 2         3 my $violation;
92 2         2 my $left_paren_num = 1;
93 2         2 my $does_exist_namespace_resolver = 0;
94              
95 2         3 my @namespaces;
96              
97             my @packages;
98 0         0 my @var_names;
99 2         6 for ($i++; $token = $tokens->[$i]; $i++) {
100 20         20 $token_type = $token->{type};
101 20 50       50 if ($token_type == LEFT_PAREN) {
    100          
    100          
    100          
102 0         0 $left_paren_num++;
103             }
104             elsif ($token_type == RIGHT_PAREN) {
105 2         4 push @var_names, pop @namespaces;
106 2         5 push @packages, join '::', @namespaces;
107 2 50       6 if (--$left_paren_num <= 0) {
108 2         3 last;
109             }
110 0         0 @namespaces = ();
111             }
112             elsif ($token_type == COMMA) {
113 2         3 push @var_names, pop @namespaces;
114 2         6 push @packages, join '::', @namespaces;
115 2         5 @namespaces = ();
116             }
117             elsif ($token_type == NAMESPACE_RESOLVER) {
118 6         13 $does_exist_namespace_resolver = 1;
119             }
120             else {
121 10         27 push @namespaces, $token->{data};
122             }
123             }
124              
125 2 50       4 if ($does_exist_namespace_resolver) {
126 2         3 $token = $tokens->[++$i];
127 2 100       8 if ($token->{type} == ASSIGN) {
128 1         3 my $is_violated = 0;
129 1         3 for my $package (@packages) {
130 2 50   8   11 if (!any {$package =~ /\A[\$\@\%]$_/} @allowed_packages) {
  8         104  
131 0         0 $is_violated = 1;
132             }
133             }
134              
135             # TODO check @var_names ?
136              
137 1 50       11 if ($is_violated) {
138 0         0 push @violations, {
139             filename => $file,
140             line => $token->{line},
141             description => DESC,
142             explanation => EXPL,
143             policy => __PACKAGE__,
144             };
145             }
146             }
147             }
148             }
149             else {
150 6         5 my $does_exist_namespace_resolver = 0;
151 6         6 my $is_assigned = 0;
152 6         7 my @namespaces = ($token->{data});
153 6         12 for ($i++; $token = $tokens->[$i]; $i++) {
154 26         22 $token_type = $token->{type};
155 26         16 $token_data = $token->{data};
156 26 100       57 if ($token_type == NAMESPACE) {
    100          
    100          
    100          
157 7         12 push @namespaces, $token_data;
158             }
159             elsif ($token_type == NAMESPACE_RESOLVER) {
160 7         13 $does_exist_namespace_resolver = 1;
161             }
162             elsif ($token_type == ASSIGN) {
163 1         1 $is_assigned = 1;
164 1         2 last;
165             }
166             elsif ($token_type == SEMI_COLON) {
167 5         1 last;
168             }
169             }
170              
171 6 100 66     27 if ($does_exist_namespace_resolver && $is_assigned) {
172 1         1 pop @namespaces; # throw variable name away
173 1         3 my $package_name = join '::', @namespaces;
174 1 50   4   4 if (any {$package_name =~ /\A[\$\@\%]$_/} @allowed_packages) {
  4         39  
175 1         7 next;
176             }
177              
178             # TODO check the var name
179 0         0 push @violations, {
180             filename => $file,
181             line => $token->{line},
182             description => DESC,
183             explanation => EXPL,
184             policy => __PACKAGE__,
185             };
186             }
187             }
188             }
189             elsif (
190             $token_type == GLOBAL_VAR ||
191             $token_type == GLOBAL_ARRAY_VAR ||
192             $token_type == GLOBAL_HASH_VAR ||
193             $token_type == VAR ||
194             $token_type == ARRAY_VAR ||
195             $token_type == HASH_VAR
196             ) {
197 28         53 my @namespaces = ($token->{data});
198              
199 28 100       49 my $does_exist_namespace_resolver = $tokens->[$i+1]->{type} == NAMESPACE_RESOLVER ? 1 : 0;
200              
201 28         50 for ($i++; $token = $tokens->[$i]; $i++) {
202 110         80 $token_type = $token->{type};
203 110 100 100     361 if ($token_type == ASSIGN || $token_type == SEMI_COLON) {
    100          
204 28         27 last;
205             }
206             elsif ($token_type == NAMESPACE) {
207 35         74 push @namespaces, $token->{data};
208             }
209             }
210              
211 28 100       45 if ($does_exist_namespace_resolver) {
212 24         26 my $var_name = pop @namespaces;
213              
214 24         43 my $package_name = join '::', @namespaces;
215 24 100   92   125 if (any {$package_name =~ /\A[\$\@\%]$_/} @allowed_packages) {
  92         836  
216 7         37 next;
217             }
218              
219 17 100       82 if ($var_name !~ /\A.[A-Z0-9_]+\Z/) {
220 12         75 push @violations, {
221             filename => $file,
222             line => $token->{line},
223             description => DESC,
224             explanation => EXPL,
225             policy => __PACKAGE__,
226             };
227             }
228             }
229             }
230             elsif ($token_type == SPECIFIC_VALUE && $token_data eq '$:') {
231 3         7 $token = $tokens->[++$i];
232 3 50       6 my $does_exist_namespace_resolver = $token->{type} == COLON ? 1 : 0;
233              
234 3         7 my $var_token;
235 3         8 for ($i++; $token = $tokens->[$i]; $i++) {
236 12         11 $token_type = $token->{type};
237 12 100       29 if ($token_type == ASSIGN) {
    100          
238 3         8 $var_token = $tokens->[$i-1];
239             }
240             elsif ($token_type == SEMI_COLON) { # XXX skip to the edge
241 3         11 last;
242             }
243             }
244              
245 3 100 66     18 if ($does_exist_namespace_resolver && $var_token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
246 2         14 push @violations, {
247             filename => $file,
248             line => $token->{line},
249             description => DESC,
250             explanation => EXPL,
251             policy => __PACKAGE__,
252             };
253             }
254             }
255             elsif ($token_type == USED_NAME && $token_data eq 'vars') {
256 10         12 my $is_used_package_var = 0;
257 10         18 for ($i++; $token = $tokens->[$i]; $i++) {
258 47         41 $token_type = $token->{type};
259 47         42 $token_data = $token->{data};
260              
261 47 100 100     162 if ($token_type == REG_EXP) {
    100          
    100          
262 3         12 for my $elem (split /\s+/, $token_data) {
263 6 50       23 if ($elem =~ /\A[\$\@\%](.*)\Z/) {
264 6 100       22 if ($1 !~ /\A[A-Z0-9_]+\Z/) {
265 3         9 $is_used_package_var = 1;
266             }
267             }
268             }
269             }
270             elsif ($token_type == STRING || $token_type == RAW_STRING) {
271 13 100       36 if ($token_data =~ /\A[\$\@\%](.*)\Z/) {
272 12 100       34 if ($1 !~ /\A[A-Z0-9_]+\Z/) {
273 6         11 $is_used_package_var = 1;
274             }
275             }
276             }
277             elsif ($token_type == SEMI_COLON) {
278 10         10 last;
279             }
280             }
281 10 100       23 if ($is_used_package_var) {
282 6         28 push @violations, {
283             filename => $file,
284             line => $token->{line},
285             description => DESC,
286             explanation => EXPL,
287             policy => __PACKAGE__,
288             };
289             }
290             }
291             }
292              
293 10         77 return \@violations;
294             }
295              
296             1;
297