20

对于 Python,有一个名为importchecker的脚本,它会告诉您是否有不必要的import语句。

usePerl (and require) 语句是否有类似的实用程序?

4

3 回答 3

5

看看Devel::TraceUse,它可能会给你很多你正在寻找的东西。

于 2012-09-05T18:33:59.643 回答
4

这是我写的一个脚本来尝试这个。它非常简单,不会为你自动化任何东西,但它会给你一些东西开始。

#!/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";
        }
    }
}
于 2012-09-10T19:17:22.057 回答
4

有很多方法可以加载包和导入符号(或不)。我不知道有一个工具可以单独直接检查这些符号是否被使用。

但是对于给出明确导入列表的情况,

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;应该是,出于政策的目的。

但请注意

  • :constants找不到标签附带的任何东西

  • word verbose,它不是一个函数(并且被隐式使用),被报告为未使用

  • 如果a_func() 没有被调用,那么即使它们未被使用,它们pp仍然carp不会被报告。这可能没问题,因为它们存在于代码中,但值得注意的是

(这个故障列表可能并不详尽。)

回想一下,导入列表被传递给导入子,它可能期望并使用模块设计认为值得的任何东西;这些不必只是函数名。跟进这一切显然超出了这项政策。尽管如此,使用带有函数名称的显式导入列表加载模块是一种很好的做法,并且该策略所涵盖的内容是一个重要的用例。

此外,根据明确说明的政策用法,未找到Dumper(imported by Data::Dumper),也未找到pathfrom Path::Tiny。该政策确实处理了一些奇怪的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;看一个完整的例子。另一种选择是使用分析器,例如Devel::NYTProf


另一个需要一些工作†</sup>的选项是编译器的后端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在 sub 中也被调用了a_func。因此ddpath是未使用的(例如,在以其他方式找到的所有导入符号中Devel::Symdump)。这很容易解析。

但是,虽然path在使用时会报告,但如果使用new替代(也Path::Tiny作为传统的构造函数),那么最后一节中没有报告,其他方法也没有报告。

所以原则上†</sup> 这是一种方法来查找报告存在的哪些符号(用于函数)Devel::Symdump已在程序中使用。


†</sup> 这里的例子很简单,易于处理,但我不知道它有多完整,或者难以解析,这是考虑到使用导入 subs 的各种奇怪方式的时候。

于 2021-01-28T09:02:40.057 回答