我怎样才能抽出未定义的子程序?

How can I smoke out undefined subroutines?

我想扫描代码库以识别当前无法访问的所有未定义子例程实例。

举个例子:

use strict;
use warnings;

my $flag = 0;
if ( $flag ) {
  undefined_sub();
}

观察结果

你要求的至少在某种意义上是不可能的。考虑以下代码片段:

( rand()<0.5 ? *foo : *bar } = sub { say "Hello World!" };

foo();

有 50% 的几率 运行 正常,有 50% 的几率会出现 "Undefined subroutine" 错误。该决定是在 运行 时间做出的,因此无法在此之前告诉它会是什么。这当然是为了证明一点而设计的案例,但是 运行 时间(或编译时)子例程的生成在实际代码中并不少见。例如,查看 Moose 如何添加创建方法的函数。静态源代码分析永远无法完全分析这样的代码。

B::Lint 可能是 运行 之前的最佳状态。

要查找对未在编译时定义的子程序的调用,您可以使用 B::Lint,如下所示:

a.pl:

use List::Util qw( min );

sub defined_sub { }
sub defined_later;
sub undefined_sub;

defined_sub();
defined_later();
undefined_sub();
undeclared_sub();
min();
max();              # XXX Didn't import
List::Util::max();
List::Util::mac();  # XXX Typo!

sub defined_later { }

测试:

$ perl -MO=Lint,undefined-subs a.pl
Undefined subroutine 'undefined_sub' called at a.pl line 9
Nonexistent subroutine 'undeclared_sub' called at a.pl line 10
Nonexistent subroutine 'max' called at a.pl line 12
Nonexistent subroutine 'List::Util::mac' called at a.pl line 14
a.pl syntax OK

请注意,这仅适用于子调用。不检查方法调用(例如 Class->methodmethod Class)。但是你问的是子调用。

请注意 foo $x 是一个有效的方法调用(使用 间接方法调用 语法)意味着 $x->foo 如果 foo 不是一个有效的函数或子函数,所以 B::Lint 不会捕捉到它。但它会捕获 foo($x).

也许Subroutines::ProhibitCallsToUndeclaredSubs policy from Perl::Critic可以提供帮助

This Policy checks that every unqualified subroutine call has a matching subroutine declaration in the current file, or that it explicitly appears in the import list for one of the included modules.

这个"policy"是Perl::Critic::StricterSubs的一部分,需要安装。那里还有一些政策。这被认为是 4 级违规,因此您可以

perlcritic -4 script.pl

并解析 neither declared nor explicitly imported 的输出,或使用

perlcritic -4 --single-policy ProhibitCallsToUndeclaredSubs script.pl

一些合法用途仍被标记,因为它要求明确导入所有潜艇。

这是一个静态分析器,我认为应该符合您的目的。