File Coverage

blib/lib/Perl/Lint/Policy/NamingConventions/Capitalization.pm
Criterion Covered Total %
statement 243 253 96.0
branch 138 160 86.2
condition 50 60 83.3
subroutine 15 15 100.0
pod 0 1 0.0
total 446 489 91.2


line stmt bran cond sub pod time code
1             package Perl::Lint::Policy::NamingConventions::Capitalization;
2 134     134   107325 use strict;
  134         265  
  134         5165  
3 134     134   626 use warnings;
  134         209  
  134         3528  
4 134     134   1465 use B::Keywords;
  134         2360  
  134         5846  
5 134     134   66597 use String::CamelCase qw/wordsplit/;
  134         67636  
  134         8872  
6 134     134   1394 use Perl::Lint::Constants::Type;
  134         239  
  134         84788  
7 134     134   903 use parent "Perl::Lint::Policy";
  134         236  
  134         906  
8              
9             # TODO integrate error messages
10              
11             use constant {
12 134         335773 EXPL => [45, 46],
13 134     134   9507 };
  134         248  
14              
15             my %local_var_declare_token_types = (
16             &VAR_DECL => 1,
17             &STATE_DECL => 1,
18             );
19              
20             my %global_var_declare_token_types = (
21             &OUR_DECL => 1,
22             &LOCAL_DECL => 1,
23             );
24              
25             my %var_declare_token_types = (
26             %local_var_declare_token_types,
27             %global_var_declare_token_types,
28             );
29              
30             my %var_token_types = (
31             &VAR => 1,
32             &CODE_VAR => 1,
33             &ARRAY_VAR => 1,
34             &HASH_VAR => 1,
35             &GLOBAL_VAR => 1,
36             &GLOBAL_ARRAY_VAR => 1,
37             &GLOBAL_HASH_VAR => 1,
38             &LOCAL_VAR => 1,
39             &LOCAL_ARRAY_VAR => 1,
40             &LOCAL_HASH_VAR => 1,
41             );
42              
43             my %globals = map {$_ => 1} ( # TODO integrate?
44             @B::Keywords::Arrays,
45             @B::Keywords::Hashes,
46             @B::Keywords::Scalars,
47             );
48              
49             sub evaluate {
50 933     933 0 2871 my ($class, $file, $tokens, $src, $args) = @_;
51              
52 933         1981 my $packages_rule = '';
53 933         1067 my $package_exemptions = '';
54              
55 933         1506 my $subroutines_rule = '';
56 933         1588 my $subroutine_exemptions = '';
57              
58 933         1300 my $local_lexical_variables_rule = '';
59 933         1193 my $local_lexical_variable_exemptions = '';
60              
61 933         1285 my $global_variables_rule = '';
62 933         1704 my $global_variable_exemptions = '';
63              
64 933         1951 my $labels_rule = '';
65 933         1672 my $label_exemptions = '';
66              
67 933 100       4366 if (my $this_policies_rule = $args->{capitalization}) {
68 854   100     5968 $packages_rule = $this_policies_rule->{packages} || '';
69 854   100     4756 $package_exemptions = $this_policies_rule->{package_exemptions} || '';
70              
71 854   100     5231 $subroutines_rule = $this_policies_rule->{subroutines} || '';
72 854   100     4850 $subroutine_exemptions = $this_policies_rule->{subroutine_exemptions} || '';
73              
74 854   100     4624 $local_lexical_variables_rule = $this_policies_rule->{local_lexical_variables} || '';
75 854   100     4397 $local_lexical_variable_exemptions = $this_policies_rule->{local_lexical_variable_exemptions} || '';
76              
77 854   100     4104 $global_variables_rule = $this_policies_rule->{global_variables} || '';
78 854   100     4737 $global_variable_exemptions = $this_policies_rule->{global_variable_exemptions} || '';
79              
80 854   100     3996 $labels_rule = $this_policies_rule->{labels} || '';
81 854   100     4693 $label_exemptions = $this_policies_rule->{label_exemptions} || '';
82             }
83              
84 933         1356 my @violations;
85              
86 933         4366 TOP: for (my $i = 0, my $token_type, my $token_data; my $token = $tokens->[$i]; $i++) {
87 8040         9305 $token_type = $token->{type};
88 8040         8466 $token_data = $token->{data};
89              
90             # for variables name
91 8040 100       14081 if ($var_declare_token_types{$token_type}) {
92 681         981 my $exemptions;
93 681         1055 my $is_global_var = 0;
94              
95 681         1694 my $checker = \&_is_singlecase;
96 681         1025 my $error_message = '"%s" is not all lower case or all upper case';
97              
98 681 100       2474 if ($local_var_declare_token_types{$token_type}) {
99 288 100       1165 if (my $condition = $class->_choose_condition_dispenser($local_lexical_variables_rule)) {
100 272         739 $checker = $condition->{checker};
101 272         560 $error_message = $condition->{error_message};
102             }
103 288         841 $exemptions = $local_lexical_variable_exemptions;
104             }
105             else {
106 393 100       1635 if (my $condition = $class->_choose_condition_dispenser($global_variables_rule)) {
107 321         856 $checker = $condition->{checker};
108 321         669 $error_message = $condition->{error_message};
109             }
110 393         586 $exemptions = $global_variable_exemptions;
111 393         923 $is_global_var = 1;
112             }
113              
114 681 50       2408 $token = $tokens->[++$i] or last;
115 681         1335 $token_type = $token->{type};
116              
117             # when multiple variables declared
118 681 100       1830 if ($token_type == LEFT_PAREN) {
119 3         5 my $lpnum = 1;
120 3         11 for ($i++; $token = $tokens->[$i]; $i++) {
121 12         12 $token_type = $token->{type};
122 12 50       37 if ($token_type == LEFT_PAREN) {
    100          
    100          
123 0         0 $lpnum++;
124             }
125             elsif ($token_type == RIGHT_PAREN) {
126 3 50       10 last if --$lpnum <= 0 ;
127             }
128             elsif ($var_token_types{$token_type}) {
129             # To ignore variables from other packages
130             # TODO
131 6   50     13 my $next_token = $tokens->[$i+1] || {};
132 6 50 33     25 if ($next_token->{type} && $next_token->{type} == NAMESPACE_RESOLVER) {
133 0         0 next;
134             }
135              
136 6         8 $token_data = $token->{data};
137              
138 6 50 66     18 if ($is_global_var && $globals{$token_data}) {
139 0         0 next;
140             }
141              
142 6         8 $token_data = substr($token_data, 1); # to exclude sigils
143              
144 6 50       28 if ($token_data =~ /\A$exemptions\Z/) {
145 0         0 next;
146             }
147              
148 6 50       14 if (ref $checker ne 'CODE') {
149 0 0       0 if ($token_data =~ /\A$checker\Z/) {
150 0         0 next;
151             }
152              
153 0         0 push @violations, {
154             filename => $file,
155             line => $token->{line},
156             description => sprintf($error_message, $token_data),
157             explanation => EXPL,
158             policy => __PACKAGE__,
159             };
160 0         0 next;
161             }
162              
163 6         13 for my $part (wordsplit($token_data)) {
164 6 100       44 if (!$checker->($part)) { # include Upper Case
165 2         15 push @violations, {
166             filename => $file,
167             line => $token->{line},
168             description => sprintf($error_message, $token_data),
169             explanation => EXPL,
170             policy => __PACKAGE__,
171             };
172 2         8 last;
173             }
174             }
175             }
176             }
177              
178 3         10 next;
179             }
180              
181             # To ignore variables from other packages
182             # TODO
183 678   50     2716 my $next_token = $tokens->[$i+1] || {};
184 678 100 66     4480 if ($next_token->{type} && $next_token->{type} == NAMESPACE_RESOLVER) {
185 36         138 next;
186             }
187              
188 642         1395 $token_data = $token->{data};
189              
190 642 100 100     3511 if ($is_global_var && $globals{$token_data}) {
191 105         382 next;
192             }
193              
194 537         1455 $token_data = substr($token_data, 1); # to exclude sigils
195              
196 537 100       9181 if ($token_data =~ /\A$exemptions\Z/) {
197 104         475 next;
198             }
199              
200 433 100       1694 if (ref $checker ne 'CODE') {
201 45 100       352 if ($token_data =~ /\A$checker\Z/) {
202 25         91 next;
203             }
204              
205 20         207 push @violations, {
206             filename => $file,
207             line => $token->{line},
208             description => sprintf($error_message, $token_data),
209             explanation => EXPL,
210             policy => __PACKAGE__,
211             };
212 20         85 next;
213             }
214              
215 388         2153 for my $part (wordsplit($token_data)) {
216 1218 100       8474 if (!$checker->($part)) { # include Upper Case
217 139         1859 push @violations, {
218             filename => $file,
219             line => $token->{line},
220             description => sprintf($error_message, $token_data),
221             explanation => EXPL,
222             policy => __PACKAGE__,
223             };
224 139         415 last;
225             }
226             }
227 388         1743 next;
228             }
229              
230             # for subroutines name
231 7359 100       11664 if ($token_type == FUNCTION_DECL) {
232 211 50       811 $token = $tokens->[++$i] or last;
233 211         402 $token_type = $token->{type};
234              
235 211 100       1047 if ($token_type == NAMESPACE) {
    50          
236 34         85 my $last_namespace_token;
237              
238 34         147 for ($i++; $token = $tokens->[$i]; $i++) {
239 170         182 $token_type = $token->{type};
240              
241 170 100       387 if ($token_type == NAMESPACE) {
    100          
242 68         141 $last_namespace_token = $token;
243             }
244             elsif ($token_type != NAMESPACE_RESOLVER) {
245 34         69 last;
246             }
247             }
248              
249 34         63 $token = $last_namespace_token;
250             }
251             elsif ($token_type != FUNCTION) {
252 0         0 next;
253             }
254              
255 211         723 my $checker = \&_is_started_with_lower;
256 211         423 my $error_message = '"%s" does not start with a lower case letter';
257 211 100       974 if (my $condition = $class->_choose_condition_dispenser($subroutines_rule)) {
258 204         570 $checker = $condition->{checker};
259 204         397 $error_message = $condition->{error_message};
260             }
261              
262 211         401 $token_data = $token->{data};
263              
264 211 100       3282 if ($token_data =~ /\A$subroutine_exemptions\Z/) {
265 34         206 next;
266             }
267              
268 177 100       738 if (ref $checker ne 'CODE') {
269 6 100       57 if ($token_data =~ /\A$checker\Z/) {
270 4         20 next;
271             }
272              
273 2         31 push @violations, {
274             filename => $file,
275             line => $token->{line},
276             description => sprintf($error_message, $token_data),
277             explanation => EXPL,
278             policy => __PACKAGE__,
279             };
280 2         14 next;
281             }
282              
283 171         867 for my $part (wordsplit($token_data)) { # to exclude sigils
284 413 100       3422 if (!$checker->($part)) {
285 26         304 push @violations, {
286             filename => $file,
287             line => $token->{line},
288             description => sprintf($error_message, $token_data),
289             explanation => EXPL,
290             policy => __PACKAGE__,
291             };
292 26         59 last;
293             }
294             }
295              
296 171         937 next;
297             }
298              
299             # for package's name
300 7148 100       10664 if ($token_type == PACKAGE) {
301 111 50       400 $token = $tokens->[++$i] or last;
302 111         205 $token_type = $token->{type};
303 111         251 $token_data = $token->{data};
304              
305             # special case: main
306 111 100 100     543 if ($token_type == CLASS && $token_data eq 'main') {
307 1         3 next;
308             }
309              
310 110 100 66     750 if ($package_exemptions && $token_data =~ /\A$package_exemptions\Z/) {
311 34         150 next;
312             }
313              
314 76         255 my $checker = \&_is_started_with_upper;
315 76         145 my $error_message = '"%s" does not start with a upper case letter';
316 76 100       323 if (my $condition = $class->_choose_condition_dispenser($packages_rule)) {
317 68         164 $checker = $condition->{checker};
318 68         153 $error_message = $condition->{error_message};
319             }
320              
321 76         133 my $package_full_name = $token_data;
322 76 100       225 if (ref $checker eq 'CODE') {
    100          
323 70         349 for my $part (wordsplit($token_data)) {
324 201 100       1519 if (!$checker->($part)) {
325 13         162 push @violations, {
326             filename => $file,
327             line => $token->{line},
328             description => sprintf($error_message, $token_data),
329             explanation => EXPL,
330             policy => __PACKAGE__,
331             };
332              
333 13         85 next TOP;
334             }
335             }
336             }
337             elsif ($token_type == CLASS) {
338 3 100       51 if ($package_full_name =~ /\A$checker\Z/) {
339 2         10 next;
340             }
341              
342 1         15 push @violations, {
343             filename => $file,
344             line => $token->{line},
345             description => sprintf($error_message, $token_data),
346             explanation => EXPL,
347             policy => __PACKAGE__,
348             };
349 1         5 next;
350             }
351              
352 60 100       219 if ($token_type == NAMESPACE) {
353 38 100       136 if (ref $checker ne 'CODE') {
354 3         12 for ($i++; $token = $tokens->[$i]; $i++) { # TODO
355 9         45 $token_type = $token->{type};
356 9         13 $token_data = $token->{data};
357 9 100 100     38 if ($token_type == NAMESPACE || $token_type == NAMESPACE_RESOLVER) {
358 6         16 $package_full_name .= $token_data;
359             }
360             else {
361 3         9 last;
362             }
363             }
364 3 100       49 if ($package_full_name =~ /\A$checker\Z/) {
365 2         10 next;
366             }
367              
368 1         14 push @violations, {
369             filename => $file,
370             line => $token->{line},
371             description => sprintf($error_message, $token_data),
372             explanation => EXPL,
373             policy => __PACKAGE__,
374             };
375 1         6 next;
376             }
377              
378 35         127 SCAN_NAMESPACE: for ($i++; $token = $tokens->[$i]; $i++) { # TODO
379 92         133 $token_type = $token->{type};
380 92         136 $token_data = $token->{data};
381 92 100       255 if ($token_type == NAMESPACE) {
    100          
382 34         104 for my $part (wordsplit($token_data)) {
383 104 100       475 if (!$checker->($part)) {
384 12         149 push @violations, {
385             filename => $file,
386             line => $token->{line},
387             description => sprintf($error_message, $token_data),
388             explanation => EXPL,
389             policy => __PACKAGE__,
390             };
391 12         44 last SCAN_NAMESPACE;
392             }
393             }
394             }
395             elsif ($token_type != NAMESPACE_RESOLVER) {
396 23         47 last;
397             }
398             }
399             }
400              
401 57         276 next;
402             }
403              
404             # for constant variables
405             # * Readonly::Scalar
406             # * const (From Const::Fast)
407             {
408 7037         5142 my $is_const_decl = 0;
  7037         5535  
409 7037 100 100     26469 if ($token_type == NAMESPACE && $token_data eq 'Readonly') {
    100 100        
410 3         3 $i += 2;
411 3 50       10 $token = $tokens->[$i] or last;
412 3 50 33     15 if ($token->{type} == NAMESPACE && $token->{data} eq 'Scalar') {
413 3         3 $is_const_decl = 1;
414             }
415             }
416             elsif ($token_type == KEY && $token_data eq 'const') {
417 2         3 $is_const_decl = 1;
418             }
419              
420 7037 100       11043 if ($is_const_decl) {
421 5 50       12 $token = $tokens->[++$i] or last;
422 5 50       12 if (!$var_token_types{$token->{type}}) {
423 5 50       9 $token = $tokens->[++$i] or last;
424             }
425              
426 5 50       13 if ($var_token_types{$token->{type}}) {
427 5         6 $token_data = $token->{data};
428 5 100       14 if (uc $token_data ne $token_data) {
429 3         14 push @violations, {
430             filename => $file,
431             line => $token->{line},
432             description => sprintf('"%s" is not all upper case', $token_data),
433             explanation => EXPL,
434             policy => __PACKAGE__,
435             };
436             }
437             }
438              
439 5         5 next;
440             }
441             }
442              
443             # for constants
444             # * use constant
445 7037 100       10078 if ($token_type == USE_DECL) {
446 9 50       24 $token = $tokens->[++$i] or last;
447 9 100 66     57 if ($token->{type} != USED_NAME || $token->{data} ne 'constant') {
448 8         13 next;
449             }
450              
451 1         4 for ($i++; $token = $tokens->[$i]; $i++) {
452 36 100       47 if ($token->{type} != ARROW) {
453 30         39 next;
454             }
455              
456 6         8 my $key = $tokens->[$i-1]->{data};
457 6 50       14 if (uc $key ne $key) {
458 0         0 push @violations, {
459             filename => $file,
460             line => $token->{line},
461             description => sprintf('"%s" is not all upper case', $key),
462             explanation => EXPL,
463             policy => __PACKAGE__,
464             };
465             }
466             }
467              
468 1         4 next;
469             }
470              
471             # for LABELs
472 7028 100       17946 if ($token_type == KEY) {
473 207 50       595 my $next_token = $tokens->[$i+1] or last;
474 207 100       544 if ($next_token->{type} != COLON) {
475 139         318 next;
476             }
477              
478 68 100       2203 if ($token_data =~ /\A$label_exemptions\Z/) {
479 34         188 next;
480             }
481              
482 34         125 my $checker = \&_is_all_upper;
483 34         93 my $error_message = '"%s" is not all upper case';
484 34 50       198 if (my $condition = $class->_choose_condition_dispenser($labels_rule) ) {
485 34         102 $checker = $condition->{checker};
486 34         109 $error_message = $condition->{error_message};
487             }
488              
489 34 100       133 if (ref $checker ne 'CODE') {
490 3 100       30 if ($token_data =~ /\A$checker\Z/) {
491 2         10 next;
492             }
493              
494 1         9 push @violations, {
495             filename => $file,
496             line => $token->{line},
497             description => sprintf($error_message, $token_data),
498             explanation => EXPL,
499             policy => __PACKAGE__,
500             };
501 1         6 next;
502             }
503              
504 31 100       124 if (!$checker->($token_data)) {
505 11         159 push @violations, {
506             filename => $file,
507             line => $token->{line},
508             description => sprintf($error_message, $token_data),
509             explanation => EXPL,
510             policy => __PACKAGE__,
511             };
512             }
513              
514 31         170 next;
515             }
516              
517             }
518              
519 933         6193 return \@violations;
520             }
521              
522             sub _choose_condition_dispenser {
523 1002     1002   1540 my ($self, $rule) = @_;
524              
525 1002 100       7543 if ($rule eq ':single_case') {
    100          
    100          
    100          
    100          
    100          
    100          
526             return {
527 115         903 error_message => '"%s" is not all lower case or all upper case',
528             checker => \&_is_singlecase,
529             };
530             }
531             elsif ($rule eq ':all_lower') {
532             return {
533 221         1833 error_message => '"%s" is not all lower case',
534             checker => \&_is_all_lower,
535             };
536             }
537             elsif ($rule eq ':all_upper') {
538             return {
539 115         939 error_message => '"%s" is not all upper case',
540             checker => \&_is_all_upper,
541             };
542             }
543             elsif ($rule eq ':starts_with_lower') {
544             return {
545 115         824 error_message => '"%s" does not start with a lower case letter',
546             checker => \&_is_started_with_lower,
547             };
548             }
549             elsif ($rule eq ':starts_with_upper') {
550             return {
551 115         939 error_message => '"%s" does not start with a upper case letter',
552             checker => \&_is_started_with_upper,
553             };
554             }
555             elsif ($rule eq ':no_restriction') {
556             return {
557 138         1096 error_message => 'There is a bug in Perl::Critic if you are reading this',
558             checker => \&_everything_will_be_alright,
559             }
560             }
561             elsif ($rule) {
562             return {
563 80         527 error_message => qq{"%s" is not matched with $rule},
564             checker => $rule, # XXX
565             };
566             }
567              
568 103         354 return;
569             }
570              
571             sub _is_all_lower {
572 416     416   626 my ($part) = @_;
573 416         1559 return lc($part) eq $part;
574             }
575              
576             sub _is_all_upper {
577 196     196   307 my ($part) = @_;
578 196         663 return uc($part) eq $part;
579             }
580              
581             sub _is_singlecase {
582 332     332   351 my ($part) = @_;
583 332   100     1482 return uc($part) eq $part || lc($part) eq $part;
584             }
585              
586             sub _is_started_with_lower {
587 301     301   307 my ($part) = @_;
588 301         884 return lcfirst($part) eq $part;
589             }
590              
591             sub _is_started_with_upper {
592 304     304   353 my ($part) = @_;
593 304         1029 return ucfirst($part) eq $part;
594             }
595              
596             sub _everything_will_be_alright {
597 424     424   940 return 1;
598             }
599              
600             1;
601