是否有工具检查Perl脚本是否有不必要的使用语句?

时间:2012-09-05 17:37:57

标签: perl perl-module utility

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

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

3 个答案:

答案 0 :(得分:5)

看一下Devel::TraceUse它可能会给你一大堆你正在寻找的东西。

答案 1 :(得分:3)

这是尝试此操作的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";
        }
    }
}

答案 2 :(得分: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 命令打印

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)

我们捕获了 dd,而来自同一个包的 pp 没有被标记,因为它被使用了(在子中),而且 carpcwd 都没有被标记也被使用;应该是,出于政策的目标。

注意

  • 未找到带有 :constants 标签的任何内容

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

  • 如果 a_func() 没有被调用,那么其中的那些 ppcarp 仍然被报告甚至尽管它们随后未使用。这可能没问题,因为它们出现在代码中,但值得注意

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

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

此外,根据明确规定的政策用法,未找到 Dumper(由 Data::Dumper 导入),也未找到来自 pathPath::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(见 complete example)。另一种选择是使用分析器,例如 Devel::NYTProf


另一个需要大量工作的选项,是编译器的后端 B::Xref,它几​​乎可以获取所有内容。它用作

perl -MO=Xref,-oreport.txt find_unused.pl

并且(大量)输出在文件 report.txt 中。

输出包含每个相关文件的部分,其中包含子程序及其包的子部分。输出的最后一部分直接用于当前目的。

对于上面使用的示例程序,我得到的输出文件如下

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

所以我们看到 cwd 被调用(在第 27 行)并且 carppp 在子 a_func 中也被调用。因此 ddpath 未使用。这很容易解析。

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

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


这里的例子简单且易于处理,但我不知道如何完整或难以解析,这是考虑到使用导入子的各种奇怪方式的情况。