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   71620 use strict;
  133         185  
  133         3224  
3 133     133   410 use warnings;
  133         145  
  133         2819  
4 133     133   407 use List::Util qw/any/;
  133         151  
  133         6739  
5 133     133   897 use Perl::Lint::Constants::Type;
  133         155  
  133         59154  
6 133     133   553 use parent "Perl::Lint::Policy";
  133         160  
  133         590  
7              
8             use constant {
9 133         147999 DESC => 'Package variable declared or used',
10             EXPL => [73, 75],
11 133     133   7472 };
  133         170  
12              
13             sub evaluate {
14 10     10 0 22 my ($class, $file, $tokens, $src, $args) = @_;
15              
16 10         37 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       12 if (my $add_packages = $this_policies_arg->{add_packages}) {
19 3         12 push @allowed_packages, split /\s+/, $add_packages;
20             }
21             }
22              
23 10         11 my @violations;
24 10         34 for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
25 226         167 $token_type = $token->{type};
26 226         174 $token_data = $token->{data};
27              
28 226 100 100     2064 if ($token_type == OUR_DECL) {
    100 100        
    100 100        
    100 66        
    100 66        
      66        
      100        
29 10         13 $token = $tokens->[++$i];
30 10         7 $token_type = $token->{type};
31 10 100 66     32 if ($token_type == LEFT_PAREN) {
    50 33        
      33        
      0        
      0        
32 5         4 my $violation;
33 5         5 my $left_paren_num = 1;
34 5         11 for ($i++; $token = $tokens->[$i]; $i++) {
35 20         19 $token_type = $token->{type};
36 20 50 66     119 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       8 if (--$left_paren_num <= 0) {
41 5 100       11 if ($violation) {
42 4         5 push @violations, $violation;
43 4         7 undef $violation;
44             }
45 5         11 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       25 if ($token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
57             $violation ||= +{
58             filename => $file,
59             line => $token->{line},
60 6   100     30 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       20 if ($token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
77             push @violations, {
78             filename => $file,
79             line => $token->{line},
80 2         17 description => DESC,
81             explanation => EXPL,
82             policy => __PACKAGE__,
83             };
84             }
85             }
86             }
87             elsif ($token_type == LOCAL_DECL) {
88 8         6 $token = $tokens->[++$i];
89 8         7 $token_type = $token->{type};
90 8 100       11 if ($token_type == LEFT_PAREN) {
91 2         2 my $violation;
92 2         3 my $left_paren_num = 1;
93 2         2 my $does_exist_namespace_resolver = 0;
94              
95 2         2 my @namespaces;
96              
97             my @packages;
98 0         0 my @var_names;
99 2         5 for ($i++; $token = $tokens->[$i]; $i++) {
100 20         15 $token_type = $token->{type};
101 20 50       40 if ($token_type == LEFT_PAREN) {
    100          
    100          
    100          
102 0         0 $left_paren_num++;
103             }
104             elsif ($token_type == RIGHT_PAREN) {
105 2         3 push @var_names, pop @namespaces;
106 2         4 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         4 push @var_names, pop @namespaces;
114 2         4 push @packages, join '::', @namespaces;
115 2         4 @namespaces = ();
116             }
117             elsif ($token_type == NAMESPACE_RESOLVER) {
118 6         9 $does_exist_namespace_resolver = 1;
119             }
120             else {
121 10         18 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       6 if ($token->{type} == ASSIGN) {
128 1         2 my $is_violated = 0;
129 1         3 for my $package (@packages) {
130 2 50   8   9 if (!any {$package =~ /\A[\$\@\%]$_/} @allowed_packages) {
  8         52  
131 0         0 $is_violated = 1;
132             }
133             }
134              
135             # TODO check @var_names ?
136              
137 1 50       8 if ($is_violated) {
138             push @violations, {
139             filename => $file,
140             line => $token->{line},
141 0         0 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         3 my $is_assigned = 0;
152 6         10 my @namespaces = ($token->{data});
153 6         11 for ($i++; $token = $tokens->[$i]; $i++) {
154 26         19 $token_type = $token->{type};
155 26         18 $token_data = $token->{data};
156 26 100       54 if ($token_type == NAMESPACE) {
    100          
    100          
    100          
157 7         14 push @namespaces, $token_data;
158             }
159             elsif ($token_type == NAMESPACE_RESOLVER) {
160 7         12 $does_exist_namespace_resolver = 1;
161             }
162             elsif ($token_type == ASSIGN) {
163 1         3 $is_assigned = 1;
164 1         1 last;
165             }
166             elsif ($token_type == SEMI_COLON) {
167 5         6 last;
168             }
169             }
170              
171 6 100 66     25 if ($does_exist_namespace_resolver && $is_assigned) {
172 1         2 pop @namespaces; # throw variable name away
173 1         5 my $package_name = join '::', @namespaces;
174 1 50   4   9 if (any {$package_name =~ /\A[\$\@\%]$_/} @allowed_packages) {
  4         44  
175 1         6 next;
176             }
177              
178             # TODO check the var name
179             push @violations, {
180             filename => $file,
181             line => $token->{line},
182 0         0 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         45 my @namespaces = ($token->{data});
198              
199 28 100       56 my $does_exist_namespace_resolver = $tokens->[$i+1]->{type} == NAMESPACE_RESOLVER ? 1 : 0;
200              
201 28         53 for ($i++; $token = $tokens->[$i]; $i++) {
202 110         76 $token_type = $token->{type};
203 110 100 100     328 if ($token_type == ASSIGN || $token_type == SEMI_COLON) {
    100          
204 28         32 last;
205             }
206             elsif ($token_type == NAMESPACE) {
207 35         64 push @namespaces, $token->{data};
208             }
209             }
210              
211 28 100       39 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   114 if (any {$package_name =~ /\A[\$\@\%]$_/} @allowed_packages) {
  92         725  
216 7         54 next;
217             }
218              
219 17 100       76 if ($var_name !~ /\A.[A-Z0-9_]+\Z/) {
220             push @violations, {
221             filename => $file,
222             line => $token->{line},
223 12         62 description => DESC,
224             explanation => EXPL,
225             policy => __PACKAGE__,
226             };
227             }
228             }
229             }
230             elsif ($token_type == SPECIFIC_VALUE && $token_data eq '$:') {
231 3         6 $token = $tokens->[++$i];
232 3 50       9 my $does_exist_namespace_resolver = $token->{type} == COLON ? 1 : 0;
233              
234 3         4 my $var_token;
235 3         11 for ($i++; $token = $tokens->[$i]; $i++) {
236 12         11 $token_type = $token->{type};
237 12 100       26 if ($token_type == ASSIGN) {
    100          
238 3         7 $var_token = $tokens->[$i-1];
239             }
240             elsif ($token_type == SEMI_COLON) { # XXX skip to the edge
241 3         5 last;
242             }
243             }
244              
245 3 100 66     20 if ($does_exist_namespace_resolver && $var_token->{data} !~ /\A.[A-Z0-9_]+\Z/) {
246             push @violations, {
247             filename => $file,
248             line => $token->{line},
249 2         13 description => DESC,
250             explanation => EXPL,
251             policy => __PACKAGE__,
252             };
253             }
254             }
255             elsif ($token_type == USED_NAME && $token_data eq 'vars') {
256 10         9 my $is_used_package_var = 0;
257 10         17 for ($i++; $token = $tokens->[$i]; $i++) {
258 47         31 $token_type = $token->{type};
259 47         33 $token_data = $token->{data};
260              
261 47 100 100     149 if ($token_type == REG_EXP) {
    100          
    100          
262 3         12 for my $elem (split /\s+/, $token_data) {
263 6 50       17 if ($elem =~ /\A[\$\@\%](.*)\Z/) {
264 6 100       20 if ($1 !~ /\A[A-Z0-9_]+\Z/) {
265 3         5 $is_used_package_var = 1;
266             }
267             }
268             }
269             }
270             elsif ($token_type == STRING || $token_type == RAW_STRING) {
271 13 100       31 if ($token_data =~ /\A[\$\@\%](.*)\Z/) {
272 12 100       32 if ($1 !~ /\A[A-Z0-9_]+\Z/) {
273 6         10 $is_used_package_var = 1;
274             }
275             }
276             }
277             elsif ($token_type == SEMI_COLON) {
278 10         8 last;
279             }
280             }
281 10 100       18 if ($is_used_package_var) {
282             push @violations, {
283             filename => $file,
284             line => $token->{line},
285 6         36 description => DESC,
286             explanation => EXPL,
287             policy => __PACKAGE__,
288             };
289             }
290             }
291             }
292              
293 10         43 return \@violations;
294             }
295              
296             1;
297