File Coverage

blib/lib/Perl/Lint.pm
Criterion Covered Total %
statement 95 101 94.0
branch 25 30 83.3
condition 5 9 55.5
subroutine 10 11 90.9
pod 1 3 33.3
total 136 154 88.3


line stmt bran cond sub pod time code
1             package Perl::Lint;
2 136     136   154321 use 5.010001;
  136         463  
  136         5669  
3 136     136   597 use strict;
  136         255  
  136         4222  
4 136     136   591 use warnings;
  136         181  
  136         3351  
5 136     136   598 use Carp ();
  136         169  
  136         2195  
6 136     136   68654 use Compiler::Lexer;
  136         453213  
  136         9388  
7 136     136   83239 use Module::Pluggable;
  136         1187994  
  136         915  
8 136     136   98501 use Module::Load;
  136         135388  
  136         951  
9              
10             our $VERSION = "0.23";
11              
12             sub new {
13 1396     1396 0 5181976 my ($class, $args) = @_;
14              
15 1396         3294 my @ignores;
16              
17 1396 100       7468 if (my $ignores = $args->{ignore}) {
18 3 100       16 if (ref $ignores ne 'ARRAY') {
19 1         237 Carp::croak "`ignore` must be array reference";
20             }
21              
22 2         6 push @ignores, map {"Perl::Lint::Policy::$_"} @$ignores;
  3         18  
23             }
24              
25 1395 100       5864 if (my $filters = $args->{filter}) {
26 3 100       17 if (ref $filters ne 'ARRAY') {
27 1         289 Carp::croak "`filter` must be array reference";
28             }
29              
30 2         9 for my $filter (@$filters) {
31 2         7 my $filter_package = "Perl::Lint::Filter::$filter";
32 2         12 load $filter_package;
33              
34 2         28 push @ignores, map {"Perl::Lint::Policy::$_"} @{$filter_package->filter};
  115         149  
  2         17  
35             }
36             }
37              
38             Module::Pluggable->import(
39 1394         16385 search_path => 'Perl::Lint::Policy',
40             require => 1,
41             inner => 0,
42             except => [@ignores],
43             );
44 1394         151651 my @site_policies = plugins(); # Exported by Module::Pluggable
45              
46             # TODO add mechanism to add extend policies
47              
48 1394         92827491 bless {
49             args => $args,
50             site_policies => \@site_policies,
51             }, $class;
52             }
53              
54             sub lint {
55 2500     2500 1 1243643 my ($self, $files) = @_;
56              
57 2500         5985 my @files = ($files); # when scalar value
58 2500 50       11427 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         4313 my @violations;
66 2500         6133 for my $file (@files) {
67 2500 50       157425 open my $fh, '<', $file or die "Cannnot open $file: $!";
68 2500         4225 my $src = do { local $/; <$fh> };
  2500         12875  
  2500         47005  
69              
70 2500         4574 push @violations, @{$self->_lint($src, $file)};
  2500         9870  
71             }
72              
73 2500         16447 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   6139 my ($self, $src, $file) = @_;
84              
85 2500 50 33     22768 if (!defined $src || $src eq '') {
86 0         0 return [];
87             }
88              
89 2500         6692 my $args = $self->{args};
90              
91 2500         21086 my $lexer = Compiler::Lexer->new($file);
92 2500         526372 my $tokens = $lexer->tokenize($src);
93              
94             # list `no lint` lines
95             # TODO improve performance
96 2500         10342 my %no_lint_lines = ();
97 2500         4594 my %used_no_lint_lines = ();
98 2500         4292 my $line_num = 1;
99 2500         38112 for my $line (split /\r?\n/, $src) {
100 14458         16266 $line =~ s/"(?:[^"]*?#[^"]*?)*"//g;
101 14458         16390 $line =~ s/'(?:[^']*?#[^']*?)*'//g;
102 14458         14485 $line =~ s/\[(?:[^\[]*?#[^\]]*?)*\]//g;
103 14458 100       27964 if ($line =~ /(#.+)?##\s*no\slint(?:\s+(?:qw)?([(][^)]*[)]|"[^"]*"|'[^']*'))?/) {
104 176 100       899 next if $1; # Already commented out at before
105              
106 174         408 my $annotations = {};
107 174 100       705 if ($2) {
108 20         45 my $annotation = substr $2, 1, -1;
109 20         58 $annotations = {map {$_ => 1} grep {$_} split /[, ]/, $annotation};
  17         55  
  17         35  
110             }
111 174         707 $no_lint_lines{$line_num} = $annotations;
112             }
113 14456         17099 $line_num++;
114             }
115              
116 2500         5738 my $prohibit_useless_no_lint_policy;
117              
118             my @violations;
119 2500         3357 for my $policy (@{$self->{site_policies}}) {
  2500         7964  
120 2590 100       8096 if ($policy eq 'Perl::Lint::Policy::Miscellanea::ProhibitUselessNoLint') {
121 4         8 $prohibit_useless_no_lint_policy = $policy;
122 4         6 next;
123             }
124              
125 2586 100       7250 if ($policy eq 'Perl::Lint::Policy::Miscellanea::ProhibitUnrestrictedNoLint') {
126 6         8 push @violations, @{$policy->evaluate($file, $tokens, $src, $args, \%no_lint_lines)};
  6         59  
127 6         18 next;
128             }
129              
130              
131 2580         3781 for my $violation (@{$policy->evaluate($file, $tokens, $src, $args)}) {
  2580         27330  
132 3316         4117 my $violation_line = $violation->{line};
133 3316         3816 my $no_lint = $no_lint_lines{$violation_line};
134 3316 100 66     7888 if (!$no_lint || (keys %$no_lint > 0 && !$no_lint->{(split /::/, $violation->{policy})[-1]})) {
      66        
135 3168         3845 push @violations, $violation;
136             }
137 3316         8097 $used_no_lint_lines{$violation_line} = 1;
138             }
139             }
140              
141 2500 100       6569 if ($prohibit_useless_no_lint_policy) {
142 4         43 push @violations,
143 4         6 @{$prohibit_useless_no_lint_policy->evaluate($file, \%no_lint_lines, \%used_no_lint_lines)};
144             }
145              
146 2500         187408 return \@violations;
147             }
148              
149             1;
150              
151             __END__