File Coverage

blib/lib/Perl/Critic/Policy/TooMuchCode/ProhibitUnusedImport.pm
Criterion Covered Total %
statement 79 80 98.7
branch 20 26 76.9
condition 11 13 84.6
subroutine 14 15 93.3
pod 3 5 60.0
total 127 139 91.3


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::TooMuchCode::ProhibitUnusedImport;
2              
3 5     5   3583 use strict;
  5         15  
  5         155  
4 5     5   34 use warnings;
  5         11  
  5         150  
5 5     5   28 use Perl::Critic::Utils;
  5         16  
  5         81  
6 5     5   4438 use parent 'Perl::Critic::Policy';
  5         13  
  5         32  
7              
8 5     5   1927 use Perl::Critic::TooMuchCode;
  5         15  
  5         148  
9 5     5   2608 use Perl::Critic::Policy::Variables::ProhibitUnusedVariables;
  5         733256  
  5         4519  
10              
11 0     0 1 0 sub default_themes { return qw( maintenance ) }
12 16     16 1 164447 sub applies_to { return 'PPI::Document' }
13             sub supported_parameters {
14             return (
15             {
16 19     19 0 433769 name => 'ignored_modules',
17             description => 'Modules which will be ignored by this policy.',
18             behavior => 'string list',
19             list_always_present_values => [
20             'Exporter',
21             'Getopt::Long',
22             'Git::Sub',
23             'MooseX::Foreign',
24             'MouseX::Foreign',
25             'Test::Needs',
26             'Test::Requires',
27             'Test::RequiresInternet',
28             ],
29             },
30             {
31             name => 'moose_type_modules',
32             description => 'Modules which import Moose-like types.',
33             behavior => 'string list',
34             list_always_present_values => [
35             'MooseX::Types::Moose',
36             'MooseX::Types::Common::Numeric',
37             'MooseX::Types::Common::String',
38             ],
39             },
40             );
41             }
42              
43             #---------------------------------------------------------------------------
44              
45             sub violates {
46 16     16 1 242 my ( $self, $elem, $doc ) = @_;
47              
48 16         59 my $moose_types = $self->{_moose_type_modules};
49              
50 16         40 my %imported;
51 16         87 $self->gather_imports_generic( \%imported, $elem, $doc );
52              
53 16         57 my %used;
54 16         37 for my $el_word (
55             @{
56             $elem->find(
57             sub {
58 449 100 100 449   6814 $_[1]->isa('PPI::Token::Word')
59             || ( $_[1]->isa('PPI::Token::Symbol')
60             && $_[1]->symbol_type eq '&' );
61             }
62             )
63 16 50       174 || []
64             }
65             ) {
66 75 100       638 if ( $el_word->isa('PPI::Token::Symbol') ) {
67 1         3 $el_word =~ s{^&}{};
68             }
69 75         181 $used{"$el_word"}++;
70             }
71              
72 16         164 __get_symbol_usage(\%used, $doc);
73              
74 16         40 my @violations;
75 16         64 my @to_report = grep { !$used{$_} } (keys %imported);
  16         76  
76              
77             # Maybe filter out Moose types.
78 16 100       178 if ( @to_report ) {
79 4         10 my %to_report = map { $_ => 1 } @to_report;
  6         26  
80              
81 4         17 for my $import ( keys %to_report ) {
82 6 100 66     45 if ( exists $used{ 'is_' . $import } || exists $used { 'to_' . $import }
      100        
83             && exists $moose_types->{$imported{$import}->[0]} ) {
84 2         11 delete $to_report{$import};
85             }
86             }
87 4         17 @to_report = keys %to_report;
88             }
89 16         79 @to_report = sort { $a cmp $b } @to_report;
  1         6  
90              
91 16         48 for my $tok (@to_report) {
92 4         284 for my $inc_mod (@{ $imported{$tok} }) {
  4         12  
93 4         28 push @violations, $self->violation( "Unused import: $tok", "A token is imported but not used in the same code.", $inc_mod );
94             }
95             }
96              
97 16         889 return @violations;
98             }
99              
100             sub gather_imports_generic {
101 16     16 0 92 my ( $self, $imported, $elem, $doc ) = @_;
102              
103 16         48 my $is_ignored = $self->{_ignored_modules};
104 16   100 449   120 my $include_statements = $elem->find(sub { $_[1]->isa('PPI::Statement::Include') && !$_[1]->pragma }) || [];
  449         6286  
105 16         279 for my $st (@$include_statements) {
106 17 50       65 next if $st->schild(0) eq 'no';
107 17 50   110   650 my $expr_qw = $st->find( sub { $_[1]->isa('PPI::Token::QuoteLike::Words'); }) or next;
  110         1262  
108              
109 17         253 my $included_module = $st->schild(1);
110 17 100       268 next if exists $is_ignored->{$included_module};
111              
112 12 50       95 if (@$expr_qw == 1) {
113 12         29 my $expr = $expr_qw->[0];
114 12         64 my @words = $expr_qw->[0]->literal;
115 12         859 for my $w (@words) {
116 16 50       99 next if $w =~ /\A [:\-\+]/x;
117              
118 16   50     36 push @{ $imported->{$w} //=[] }, $included_module;
  16         136  
119             }
120             }
121             }
122             }
123              
124             sub __get_symbol_usage {
125 16     16   56 my ($usage, $doc) = @_;
126              
127             ## Look for the signature of misparsed ternary operator.
128             ## https://github.com/adamkennedy/PPI/issues/62
129             ## Once PPI is fixed, this workaround can be eliminated.
130 16         92 Perl::Critic::TooMuchCode::__get_terop_usage($usage, $doc);
131              
132 16         332 Perl::Critic::Policy::Variables::ProhibitUnusedVariables::_get_regexp_symbol_usage($usage, $doc);
133              
134 16 100       751 for my $e (@{ $doc->find('PPI::Token::Symbol') || [] }) {
  16         57  
135 13         339 $usage->{ $e->symbol() }++;
136             }
137              
138 16         466 for my $class (qw{
139             PPI::Token::Quote::Double
140             PPI::Token::Quote::Interpolate
141             PPI::Token::QuoteLike::Backtick
142             PPI::Token::QuoteLike::Command
143             PPI::Token::QuoteLike::Readline
144             PPI::Token::HereDoc
145             }) {
146 96 100       1148 for my $e (@{ $doc->find( $class ) || [] }) {
  96         245  
147 5 50       95 my $str = PPIx::QuoteLike->new( $e ) or next;
148 5         8194 for my $var ( $str->variables() ) {
149 2         8090 $usage->{ $var }++;
150             }
151             }
152             }
153              
154 16         189 return;
155             }
156              
157             1;
158              
159             =encoding utf-8
160              
161             =head1 NAME
162              
163             TooMuchCode::ProhibitUnusedImport -- Find unused imports
164              
165             =head1 DESCRIPTION
166              
167             An "Unused Import" is usually a subroutine name imported by a C<use> statement.
168             For example, the word C<Dumper> in the following statement:
169              
170             use Foo qw( baz );
171              
172             The word C<baz> can be removed if it is not mentioned in the rest of this program.
173              
174             Conventionally, this policy looks only for the C<use> statement with a C<qw()>
175             operator at the end. This syntax is easier to deal with. It also works with
176             the usage of C<Importer> module -- as long as a C<qw()> is there at the end:
177              
178             use Importer 'Foo' => qw( baz );
179              
180             This may be adjusted to be a bit smarter, but it is a clear convention in the
181             beginning.
182              
183             Modules which will be ignored, generally because the args of import do not mean
184             the symbols to be imported.
185              
186             [TooMuchCode::ProhibitUnusedImport]
187             ignored_modules = Git::Sub Regexp::Common
188              
189             =head2 Moose Types
190              
191             When importing types from a Moose type library, you may run into the following
192             situation:
193              
194             use My::Type::Library::Numeric qw( PositiveInt );
195              
196             my $foo = 'bar';
197             my $ok = is_PositiveInt($foo);
198              
199             In this case, C<My::Type::Library::Numeric> exports C<is_PositiveInt> as well
200             as C<PositiveInt>. Even though C<PositiveInt> has not specifically been called
201             by the code, the import has clearly been used. In order to allow for this case,
202             you can specify class names of Moose-like type libraries which you intend to
203             import from.
204              
205             A similar case exists for coercions:
206              
207             use My::Type::Library::String qw( LowerCaseStr );
208             my $foo = 'Bar';
209             my $lower = to_LowerCaseStr($foo);
210              
211             In the above case, C<to_LowerCaseStr> has not specifically been called by the
212             code, but the import has clearly been used.
213              
214             The imports of C<is_*> and C<to_*> from the following modules be handled by
215             default:
216              
217             * MooseX::Types::Moose
218             * MooseX::Types::Common::Numeric
219             * MooseX::Types::Common::String
220              
221             You can configure this behaviour by adding more modules to the list:
222              
223             [TooMuchCode::ProhibitUnusedImport]
224             moose_type_modules = My::Type::Library::Numeric My::Type::Library::String
225              
226             =cut