File Coverage

blib/lib/Perl/Critic/Policy/TooMuchCode/ProhibitUnusedImport.pm
Criterion Covered Total %
statement 86 87 98.8
branch 25 32 78.1
condition 18 28 64.2
subroutine 14 15 93.3
pod 3 5 60.0
total 146 167 87.4


line stmt bran cond sub pod time code
1             package Perl::Critic::Policy::TooMuchCode::ProhibitUnusedImport;
2              
3 5     5   3123 use strict;
  5         14  
  5         155  
4 5     5   29 use warnings;
  5         13  
  5         120  
5 5     5   28 use Perl::Critic::Utils;
  5         17  
  5         78  
6 5     5   4435 use parent 'Perl::Critic::Policy';
  5         24  
  5         44  
7              
8 5     5   1953 use Perl::Critic::TooMuchCode;
  5         13  
  5         145  
9 5     5   2410 use Perl::Critic::Policy::Variables::ProhibitUnusedVariables;
  5         732591  
  5         5166  
10              
11 0     0 1 0 sub default_themes { return qw( maintenance ) }
12 20     20 1 198760 sub applies_to { return 'PPI::Document' }
13             sub supported_parameters {
14             return (
15             {
16 23     23 0 458269 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 20     20 1 299 my ( $self, $elem, $doc ) = @_;
47              
48 20         60 my $moose_types = $self->{_moose_type_modules};
49              
50 20         52 my %imported;
51 20         103 $self->gather_imports_generic( \%imported, $elem, $doc );
52              
53 20         72 my %used;
54 20         46 for my $el_word (
55             @{
56             $elem->find(
57             sub {
58 534 100 100 534   8285 $_[1]->isa('PPI::Token::Word')
59             || ( $_[1]->isa('PPI::Token::Symbol')
60             && $_[1]->symbol_type eq '&' );
61             }
62             )
63 20 50       138 || []
64             }
65             ) {
66 87 100       765 if ( $el_word->isa('PPI::Token::Symbol') ) {
67 1         4 $el_word =~ s{^&}{};
68             }
69 87         209 $used{"$el_word"}++;
70             }
71              
72 20         222 __get_symbol_usage(\%used, $doc);
73              
74 20         43 my @violations;
75 20         101 my @to_report = grep { !$used{$_} } (keys %imported);
  27         133  
76              
77             # Maybe filter out Moose types.
78 20 100       88 if ( @to_report ) {
79 5         23 my %to_report = map { $_ => 1 } @to_report;
  9         35  
80              
81 5         18 for my $import ( keys %to_report ) {
82 9 100 66     64 if ( exists $used{ 'is_' . $import } || exists $used { 'to_' . $import }
      100        
83             && exists $moose_types->{$imported{$import}->[0]} ) {
84 2         12 delete $to_report{$import};
85             }
86             }
87 5         23 @to_report = keys %to_report;
88             }
89 20         103 @to_report = sort { $a cmp $b } @to_report;
  3         13  
90              
91 20         71 for my $tok (@to_report) {
92 7         697 for my $inc_mod (@{ $imported{$tok} }) {
  7         21  
93 7         75 push @violations, $self->violation( "Unused import: $tok", "A token is imported but not used in the same code.", $inc_mod );
94             }
95             }
96              
97 20         999 return @violations;
98             }
99              
100             sub gather_imports_generic {
101 20     20 0 65 my ( $self, $imported, $elem, $doc ) = @_;
102              
103 20         57 my $is_ignored = $self->{_ignored_modules};
104 20   100 534   124 my $include_statements = $elem->find(sub { $_[1]->isa('PPI::Statement::Include') && !$_[1]->pragma }) || [];
  534         7262  
105 20         333 for my $st (@$include_statements) {
106 21 50       96 next if $st->schild(0) eq 'no';
107 21 50   134   764 my $expr_qw = $st->find( sub { $_[1]->isa('PPI::Token::QuoteLike::Words'); }) or next;
  134         1568  
108              
109 21         308 my $included_module = $st->schild(1);
110 21 100       342 next if exists $is_ignored->{$included_module};
111              
112 16 50       122 if (@$expr_qw == 1) {
113 16         48 my $expr = $expr_qw->[0];
114 16         90 my @words = $expr_qw->[0]->literal;
115 16         1051 for my $w (@words) {
116 27 50       114 next if $w =~ /\A [:\-\+]/x;
117              
118 27   50     62 push @{ $imported->{$w} //=[] }, $included_module;
  27         193  
119             }
120             }
121             }
122             }
123              
124             sub __get_symbol_usage {
125 20     20   71 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 20         114 Perl::Critic::TooMuchCode::__get_terop_usage($usage, $doc);
131              
132 20         494 Perl::Critic::Policy::Variables::ProhibitUnusedVariables::_get_regexp_symbol_usage($usage, $doc);
133              
134 20 100       962 for my $e (@{ $doc->find('PPI::Token::Symbol') || [] }) {
  20         63  
135 17         384 $usage->{ $e->symbol() }++;
136             }
137              
138 20         759 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 120 100       1357 for my $e (@{ $doc->find( $class ) || [] }) {
  120         285  
147 5 50       194 my $str = PPIx::QuoteLike->new( $e ) or next;
148 5         8348 for my $var ( $str->variables() ) {
149 2         8241 $usage->{ $var }++;
150             }
151             }
152             }
153              
154             # Gather usages in the exact form of:
155             # our @EXPORT = qw( ... );
156             # our @EXPORT_OK = qw( ... );
157 20 100       225 for my $st (@{ $doc->find('PPI::Statement::Variable') || [] }) {
  20         78  
158 12 100       156 next unless $st->schildren == 5;
159              
160 5         120 my @children = $st->schildren;
161 5 50 66     93 next unless $children[0]->content() eq 'our'
      66        
      33        
      33        
      33        
162             && ($children[1]->content() eq '@EXPORT'
163             || $children[1]->content() eq '@EXPORT_OK')
164             && $children[2]->content() eq '='
165             && $children[3]->isa('PPI::Token::QuoteLike::Words')
166             && $children[4]->content() eq ';';
167              
168 3         83 for my $w ($children[3]->literal) {
169 8         140 $usage->{ $w }++;
170             }
171             }
172              
173 20         366 return;
174             }
175              
176             1;
177              
178             =encoding utf-8
179              
180             =head1 NAME
181              
182             TooMuchCode::ProhibitUnusedImport -- Find unused imports
183              
184             =head1 DESCRIPTION
185              
186             An "Unused Import" is usually a subroutine name imported by a C<use> statement.
187             For example, the word C<Dumper> in the following statement:
188              
189             use Foo qw( baz );
190              
191             The word C<baz> can be removed if it is not mentioned in the rest of this program.
192              
193             Conventionally, this policy looks only for the C<use> statement with a C<qw()>
194             operator at the end. This syntax is easier to deal with. It also works with
195             the usage of C<Importer> module -- as long as a C<qw()> is there at the end:
196              
197             use Importer 'Foo' => qw( baz );
198              
199             This may be adjusted to be a bit smarter, but it is a clear convention in the
200             beginning.
201              
202             Modules which will be ignored, generally because the args of import do not mean
203             the symbols to be imported.
204              
205             [TooMuchCode::ProhibitUnusedImport]
206             ignored_modules = Git::Sub Regexp::Common
207              
208             =head2 Moose Types
209              
210             When importing types from a Moose type library, you may run into the following
211             situation:
212              
213             use My::Type::Library::Numeric qw( PositiveInt );
214              
215             my $foo = 'bar';
216             my $ok = is_PositiveInt($foo);
217              
218             In this case, C<My::Type::Library::Numeric> exports C<is_PositiveInt> as well
219             as C<PositiveInt>. Even though C<PositiveInt> has not specifically been called
220             by the code, it should be considered as being used. In order to allow for this case,
221             you can specify class names of Moose-like type libraries which you intend to
222             import from.
223              
224             A similar case exists for coercions:
225              
226             use My::Type::Library::String qw( LowerCaseStr );
227             my $foo = 'Bar';
228             my $lower = to_LowerCaseStr($foo);
229              
230             In the above case, C<LowerCaseStr> has not specifically been called by the
231             code, but it should be considered as being used.
232              
233             The imports of C<is_*> and C<to_*> from the following modules be handled by
234             default:
235              
236             * MooseX::Types::Moose
237             * MooseX::Types::Common::Numeric
238             * MooseX::Types::Common::String
239              
240             You can configure this behaviour by adding more modules to the list:
241              
242             [TooMuchCode::ProhibitUnusedImport]
243             moose_type_modules = My::Type::Library::Numeric My::Type::Library::String
244              
245             =cut