Reputation: 360015
For Python, there is a script called importchecker which tells you if you have unnecessary import
statements.
Is there a similar utility for Perl use
(and require
) statements?
Upvotes: 22
Views: 1497
Reputation: 66883
There is a number of ways to load packages and import symbols (or not). I am not aware of a tool which single-handedly and directly checks whether those symbols are used or not.
But for cases where an explicit import list is given,
use Module qw(func1 func2 ...);
there is a Perl::Critic policy TooMuchCode::ProhibitUnusedImport that helps with much of that.
One runs on the command line
perlcritic --single-policy TooMuchCode::ProhibitUnusedImport program.pl
and the program is checked. Or run without --single-policy
flag for a complete check and seek Severity 1
violations in the output, which this is.
For an example, consider a program
use warnings;
use strict;
use feature 'say';
use Path::Tiny; # a class; but it imports 'path'
use Data::Dumper; # imports 'Dumper'
use Data::Dump qw(dd pp); # imports 'dd' and 'pp'
use Cwd qw(cwd); # imports only 'cwd'
use Carp qw(carp verbose); # imports 'carp'; 'verbose' isn't a symbol
use Term::ANSIColor qw(:constants); # imports a lot of symbols
sub a_func {
say "\tSome data: ", pp [ 2..5 ];
carp "\tA warning";
}
say "Current working directory: ", cwd;
a_func();
Running the above perlcritic
command prints
Unused import: dd at line 7, column 5. A token is imported but not used in the same code. (Severity: 1) Unused import: verbose at line 9, column 5. A token is imported but not used in the same code. (Severity: 1)
We got dd
caught, while pp
from the same package isn't flagged since it's used (in the sub), and neither are carp
and cwd
which are also used; as it should be, out of what the policy aims for.
But note
whatever comes with :constants
tag isn't found
word verbose
, which isn't a function (and is used implicitly), is reported as unused
if a_func()
isn't called then those pp
and carp
in it are still not reported even though they are then unused. This may be OK, since they are present in code, but it is worth noting
(This glitch-list is likely not exhaustive.)
Recall that the import list is passed to an import sub, which may expect and make use of whatever the module's design deemed worthy; these need not be only function names. It is apparently beyond this policy to follow up on all that. Still, loading modules with the explicit import list with function names is good practice and what this policy does cover is an important use case.
Also, per the clearly stated policy's usage, the Dumper
(imported by Data::Dumper
) isn't found, nor is path
from Path::Tiny
. The policy does deal with some curious Moose
tricks.
How does one do more? One useful tool is Devel::Symdump, which harvests the symbol tables. It catches all symbols in the above program that have been imported (no Path::Tiny
methods can be seen if used, of course). The non-existing "symbol" verbose
is included as well though. Add
use Devel::Symdump;
my $syms = Devel::Symdump->new;
say for $syms->functions;
to the above example. To also deal with (runtime) require
-ed libraries we have to do this at a place in code after they have been loaded, what can be anywhere in the program. Then best do it in an END
block, like
END {
my $ds = Devel::Symdump->new;
say for $ds->functions;
};
Then we need to check which of these are unused. At this time the best tool I'm aware of for that job is PPI; see a complete example. Another option is to use a profiler, like Devel::NYTProf.
Another option, which requires some legwork†, is the compiler's backend B::Xref, which gets practically everything that is used in the program. It is used as
perl -MO=Xref,-oreport_Xref.txt find_unused.pl
and the (voluminous) output is in the file report_Xref.txt
.
The output has sections for each involved file, which have subsections for subroutines and their packages. The last section of the output is directly useful for the present purpose.
For the example program used above I get the output file like
File /.../perl5/lib/perl5//Data/Dump.pm ... (some 3,000 lines) ... File find_unused.pl --> there we go, this program's file Subroutine (definitions) ... dozens of lines ... Subroutine (main) Package main &a_func &43 &cwd &27 Subroutine a_func Package ? @?? 14 Package main &carp &15 &pp &14
So we see that cwd
gets called (on line 27) and that carp
and pp
are also called in the sub a_func
. Thus dd
and path
are unused (out of all imported symbols found otherwise, by Devel::Symdump
for example). This is easy to parse.
However, while path
is reported when used, if one uses new
instead (also in Path::Tiny
as a traditional constructor) then that isn't reported in this last section, nor are other methods.
So in principle† this is one way to find which of the symbols (for functions) reported to exist by Devel::Symdump
have been used in the program.
† The example here is simple and easy to process but I have no idea how complete, or hard to parse, this is when all kinds of weird ways for using imported subs are taken into account.
Upvotes: 5
Reputation: 495
Here is a script I wrote to attempt this. It is very simplistic and will not automate anything for you but it will give you something to start with.
#!/usr/bin/perl
use strict;
use v5.14;
use PPI::Document;
use PPI::Dumper;
use PPI::Find;
use Data::Dumper;
my %import;
my $doc = PPI::Document->new($ARGV[0]);
my $use = $doc->find( sub { $_[1]->isa('PPI::Statement::Include') } );
foreach my $u (@$use) {
my $node = $u->find_first('PPI::Token::QuoteLike::Words');
next unless $node;
$import{$u->module} //= [];
push $import{$u->module}, $node->literal;
}
my $words = $doc->find( sub { $_[1]->isa('PPI::Token::Word') } );
my @words = map { $_->content } @$words;
my %words;
@words{ @words } = 1;
foreach my $u (keys %import) {
say $u;
foreach my $w (@{$import{$u}}) {
if (exists $words{$w}) {
say "\t- Found $w";
}
else {
say "\t- Can't find $w";
}
}
}
Upvotes: 4
Reputation: 3484
Take a look at Devel::TraceUse it might give you a chunk of what you're looking for.
Upvotes: 4