有多种方法可以加载包和导入符号(或不导入)。我不知道有一个工具可以单独直接检查这些符号是否被使用。
但是对于给出明确导入列表的情况,
use Module qw(func1 func2 ...);
有一个Perl::Critic 政策TooMuchCode::ProhibitUnusedImport 可以帮助解决大部分问题。
一个在命令行上运行
perlcritic --single-policy TooMuchCode::ProhibitUnusedImport program.pl
并检查程序。或者在没有 --single-policy 标志的情况下运行以进行完整检查,并在输出中查找 Severity 1 违规,即。
举个例子,考虑一个程序
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();
运行上面的perlcritic 命令打印
未使用的导入:第 7 行第 5 列的 dd。导入了一个令牌,但未在同一代码中使用。 (严重性:1)
未使用的导入:在第 9 行第 5 列详细说明。导入了一个标记,但未在同一代码中使用。 (严重性:1)
我们捕获了dd,而来自同一个包的pp 没有标记,因为它已被使用(在子中),carp 和cwd 也没有被标记;应该是,出于政策的目的。
注意
(此故障列表可能并不详尽。)
回想一下,导入列表被传递给import 子,它可能期望并使用模块设计认为值得的任何东西;这些不必只是函数名。跟进这一切显然超出了这项政策。尽管如此,使用带有函数名称的显式导入列表加载模块是一种很好的做法,并且该策略所涵盖的内容是一个重要的用例。
此外,根据明确说明的策略用法,未找到 Dumper(由 Data::Dumper 导入),也未找到来自 Path::Tiny 的 path。该政策确实处理了一些奇怪的Moose 技巧。
如何做更多?一个有用的工具是Devel::Symdump,它收集符号表。它捕获上述程序中所有已导入的符号(当然,如果使用,则看不到Path::Tiny 方法)。不存在的“符号”verbose 也包含在内。添加
use Devel::Symdump;
my $syms = Devel::Symdump->new;
say for $syms->functions;
到上面的例子。为了还处理(运行时)require-ed 库,我们必须在代码中的某个位置执行此操作在它们被加载之后,可以在程序中的任何位置。然后最好在END 块中进行,例如
END {
my $ds = Devel::Symdump->new;
say for $ds->functions;
};
然后我们需要检查其中哪些是未使用的。目前我所知道的用于该工作的最佳工具是PPI;见complete example。另一种选择是使用分析器,例如Devel::NYTProf。
另一个需要一些工作†的选项是编译器的后端B::Xref,它几乎可以获取程序中使用的所有内容。它被用作
perl -MO=Xref,-oreport_Xref.txt find_unused.pl
并且(大量)输出在文件report_Xref.txt中。
输出包含每个相关文件的部分,其中包含子例程及其包的子部分。输出的最后一部分直接用于当前目的。
对于上面使用的示例程序,我得到了类似的输出文件
文件 /.../perl5/lib/perl5//Data/Dump.pm
...
(约 3,000 行)
...
文件 find_unused.pl --> 好了,这个程序的文件
子程序(定义)
...几十行...
子程序(主)
包主
&a_func &43
&cwd &27
子程序 a_func
包裹 ?
@?? 14
包主
&鲤鱼 &15
&pp &14
所以我们看到 cwd 被调用(在第 27 行)并且 carp 和 pp 也在子 a_func 中被调用。因此dd 和path 未被使用(在所有导入的符号中,以其他方式找到,例如Devel::Symdump)。这很容易解析。
然而,虽然path 在使用时会被报告,但如果使用new 代替(也在Path::Tiny 作为传统构造函数中),那么最后一节中没有报告,其他方法也没有报告。
所以原则上†这是查找Devel::Symdump报告存在的哪些符号(用于函数)已在程序中使用的一种方法。
† 这里的例子很简单,易于处理,但我不知道它有多完整,或者难以解析,这是考虑到使用导入 subs 的各种奇怪方式的时候。