【问题标题】:Is there a tool to check a Perl script for unnecessary use statements?是否有工具可以检查 Perl 脚本中是否存在不必要的使用语句?
【发布时间】:2012-08-30 11:51:46
【问题描述】:

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

Perl use(和require)语句是否有类似的实用程序?

【问题讨论】:

  • perlcritic 可能具有该功能。
  • 请注意,没有解决方案是 100% 准确的。 use Module; 可能是假阴性。 use strict qw( refs ); 可能是误报。 use Module qw( :ALL ); 可能是假阴性和假阳性。
  • 究竟需要检查什么? (1) 简单的use Mod; 可以导入符号——检查所有这些? (2) 还是只针对use Mod qw(...); 中列出的那些? (3)use Module qw(); 怎么样(代码中没有使用Mod subs)?这是关于可以完全删除的use 语句,还是关于与特定use 语句相关的特定(未使用)符号? (我只知道 (2) 的 perlcritic 政策。)
  • 为什么——除了美学之外还有实际问题(以及它是什么?)? (如符号污染等)
  • @zwol 我只为某些案例 (2) TooMuchCode::ProhibitUnusedImport 找到了一个批评性的“政策”。看一看。至于其余的,它会很混乱(据我所见),但有一些工具可以提供帮助。 (如果有帮助,我可以发布一些详细说明和示例的答案,但我没有全面的解决方案。)

标签: perl perl-module utility


【解决方案1】:

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

【讨论】:

  • 这似乎向我展示了正在加载的内容。我想知道正在加载但未使用的内容。
  • 我不知道如果没有一些非常粗糙的解析,您将如何获得静态分析,但对于动态分析,您可以将 TraceUse 与 NYTProf 结合使用并编写一些代码来确定所有可能的方法和子程序在所有使用的模块中可用,然后减去您调用的方法和子程序,如 NYTProf 所列。
【解决方案2】:

这是一个script I wrote 来尝试这个。它非常简单,不会为您自动执行任何操作,但它会给您一些帮助。

#!/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";
        }
    }
}

【讨论】:

    【解决方案3】:

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

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

    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 没有标记,因为它已被使用(在子中),carpcwd 也没有被标记;应该是,出于政策的目的。

    注意

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

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

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

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

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

    此外,根据明确说明的策略用法,未找到 Dumper(由 Data::Dumper 导入),也未找到来自 Path::Tinypath。该政策确实处理了一些奇怪的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 行)并且 carppp 也在子 a_func 中被调用。因此ddpath 未被使用(在所有导入的符号中,以其他方式找到,例如Devel::Symdump)。这很容易解析。

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

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


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

    【讨论】:

      猜你喜欢
      • 2010-09-18
      • 1970-01-01
      • 2013-06-20
      • 1970-01-01
      • 1970-01-01
      • 2013-05-17
      • 1970-01-01
      • 2019-08-12
      • 2018-07-28
      相关资源
      最近更新 更多