File Coverage

blib/lib/Perl/Lint.pm
Criterion Covered Total %
statement 94 100 94.0
branch 25 30 83.3
condition 5 9 55.5
subroutine 10 11 90.9
pod 1 3 33.3
total 135 153 88.2


line stmt bran cond sub pod time code
1             package Perl::Lint;
2 136     136   114136 use 5.010001;
  136         314  
3 136     136   426 use strict;
  136         129  
  136         1940  
4 136     136   370 use warnings;
  136         120  
  136         2665  
5 136     136   367 use Carp ();
  136         120  
  136         1647  
6 136     136   48816 use Compiler::Lexer;
  136         332884  
  136         5499  
7 136     136   56929 use Module::Pluggable;
  136         1073844  
  136         680  
8 136     136   64641 use Module::Load;
  136         99670  
  136         690  
9              
10             our $VERSION = "0.25";
11              
12             sub new {
13 1396     1396 0 2935949 my ($class, $args) = @_;
14              
15 1396         2416 my @ignores;
16              
17 1396 100       5271 if (my $ignores = $args->{ignore}) {
18 3 100       12 if (ref $ignores ne 'ARRAY') {
19 1         154 Carp::croak "`ignore` must be array reference";
20             }
21              
22 2         5 push @ignores, map {"Perl::Lint::Policy::$_"} @$ignores;
  3         10  
23             }
24              
25 1395 100       4419 if (my $filters = $args->{filter}) {
26 3 100       13 if (ref $filters ne 'ARRAY') {
27 1         197 Carp::croak "`filter` must be array reference";
28             }
29              
30 2         3 for my $filter (@$filters) {
31 2         5 my $filter_package = "Perl::Lint::Filter::$filter";
32 2         9 load $filter_package;
33              
34 2         20 push @ignores, map {"Perl::Lint::Policy::$_"} @{$filter_package->filter};
  115         129  
  2         12  
35             }
36             }
37              
38             Module::Pluggable->import(
39 1394         10312 search_path => 'Perl::Lint::Policy',
40             require => 1,
41             inner => 0,
42             except => [@ignores],
43             );
44 1394         100467 my @site_policies = plugins(); # Exported by Module::Pluggable
45              
46             # TODO add mechanism to add extend policies
47              
48 1394         55068691 bless {
49             args => $args,
50             site_policies => \@site_policies,
51             }, $class;
52             }
53              
54             sub lint {
55 2500     2500 1 1663594 my ($self, $files) = @_;
56              
57 2500         5400 my @files = ($files); # when scalar value
58 2500 50       8621 if (my $ref = ref $files) {
59 0 0       0 if ($ref ne 'ARRAY') {
60 0         0 Carp::croak("Argument of files expects scalar value or array reference");
61             }
62 0         0 @files = @$files;
63             }
64              
65 2500         3196 my @violations;
66 2500         5067 for my $file (@files) {
67 2500 50       89895 open my $fh, '<', $file or die "Cannnot open $file: $!";
68 2500         3194 my $src = do { local $/; <$fh> };
  2500         8940  
  2500         31961  
69              
70 2500         3661 push @violations, @{$self->_lint($src, $file)};
  2500         6957  
71             }
72              
73 2500         11228 return \@violations;
74             }
75              
76             sub lint_string {
77 0     0 0 0 my ($self, $src) = @_;
78              
79 0         0 return $self->_lint($src);
80             }
81              
82             sub _lint {
83 2500     2500   4712 my ($self, $src, $file) = @_;
84              
85 2500 50 33     16681 if (!defined $src || $src eq '') {
86 0         0 return [];
87             }
88              
89 2500         4300 my $args = $self->{args};
90              
91 2500         15633 my $lexer = Compiler::Lexer->new($file);
92 2500         383380 my $tokens = $lexer->tokenize($src);
93              
94             # list `no lint` lines
95             # TODO improve performance
96 2500         7679 my %no_lint_lines = ();
97 2500         3369 my %used_no_lint_lines = ();
98 2500         2796 my $line_num = 1;
99 2500         24402 for my $line (split /\r?\n/, $src) {
100 14458         12492 $line =~ s/"(?:[^"]*?#[^"]*?)*"//g;
101 14458         12084 $line =~ s/'(?:[^']*?#[^']*?)*'//g;
102 14458         11099 $line =~ s/\[(?:[^\[]*?#[^\]]*?)*\]//g;
103 14458 100       21112 if ($line =~ /(#.+)?##\s*no\slint(?:\s+(?:qw)?([(][^)]*[)]|"[^"]*"|'[^']*'))?/) {
104 176 100       737 next if $1; # Already commented out at before
105              
106 174         342 my $annotations = {};
107 174 100       570 if ($2) {
108 20         26 my $annotation = substr $2, 1, -1;
109 20         34 $annotations = {map {$_ => 1} grep {$_} split /[, ]/, $annotation};
  17         29  
  17         22  
110             }
111 174         525 $no_lint_lines{$line_num} = $annotations;
112             }
113 14456         11565 $line_num++;
114             }
115              
116 2500         4235 my $prohibit_useless_no_lint_policy;
117              
118             my @violations;
119 2500         2683 for my $policy (@{$self->{site_policies}}) {
  2500         5973  
120 2590 100       5622 if ($policy eq 'Perl::Lint::Policy::Miscellanea::ProhibitUselessNoLint') {
121 4         5 $prohibit_useless_no_lint_policy = $policy;
122 4         5 next;
123             }
124              
125 2586 100       5172 if ($policy eq 'Perl::Lint::Policy::Miscellanea::ProhibitUnrestrictedNoLint') {
126 6         6 push @violations, @{$policy->evaluate($file, $tokens, $src, $args, \%no_lint_lines)};
  6         38  
127 6         12 next;
128             }
129              
130              
131 2580         2537 for my $violation (@{$policy->evaluate($file, $tokens, $src, $args)}) {
  2580         19303  
132 3307         2951 my $violation_line = $violation->{line};
133 3307         2943 my $no_lint = $no_lint_lines{$violation_line};
134 3307 100 66     6402 if (!$no_lint || (keys %$no_lint > 0 && !$no_lint->{(split /::/, $violation->{policy})[-1]})) {
      66        
135 3159         2870 push @violations, $violation;
136             }
137 3307         5411 $used_no_lint_lines{$violation_line} = 1;
138             }
139             }
140              
141 2500 100       4847 if ($prohibit_useless_no_lint_policy) {
142             push @violations,
143 4         5 @{$prohibit_useless_no_lint_policy->evaluate($file, \%no_lint_lines, \%used_no_lint_lines)};
  4         26  
144             }
145              
146 2500         81350 return \@violations;
147             }
148              
149             1;
150              
151             __END__