Perl 是否有系统的方法来检查“严格参考”?

Perl 是否有系统的方法来检查“严格参考”?,perl,reference,use-strict,Perl,Reference,Use Strict,在过去几年中,我们在遗留的Perl代码库中采用了使用strict。我的任务是将它添加到剩余的模块中,同时确保它不会破坏任何东西 现在对于使用严格的'vars'和使用严格的'subs'来说,这很容易,因为这些都是简单的perl-c捕获的编译时错误。但是,有没有一种系统的方法来检查由使用严格的'refs'触发的运行时错误 当然,我可以通过在所有可能的情况下调用所有函数(即完全覆盖)来引发运行时错误,但这非常麻烦,尤其是因为此代码缺少单元测试。如果您有更好的想法,我将不胜感激。因为Perl是非类型化的

在过去几年中,我们在遗留的Perl代码库中采用了
使用strict
。我的任务是将它添加到剩余的模块中,同时确保它不会破坏任何东西

现在对于
使用严格的'vars'
使用严格的'subs'
来说,这很容易,因为这些都是简单的
perl-c
捕获的编译时错误。但是,有没有一种系统的方法来检查由
使用严格的'refs'
触发的运行时错误

当然,我可以通过在所有可能的情况下调用所有函数(即完全覆盖)来引发运行时错误,但这非常麻烦,尤其是因为此代码缺少单元测试。如果您有更好的想法,我将不胜感激。

因为Perl是非类型化的(变量可能包含引用或非引用),所以只能在运行时进行检查。考虑:

use strict;

# Usage: foo(HASHREF)
sub foo {
   print $_[0]{name}, "\n" if int(rand(2));
}

my $var = int(rand(2))
   ? { name => "Hello" }
   : "World";  # oopsie!

foo($var);
这将失败约25%的时间。对非类型化语言的静态分析不能捕捉到这种东西——它只能被您的测试套件捕捉到,所以您的首要任务应该是改进它

也就是说,有一些改进代码的方法可以帮助您。例如,如果我们对传入参数进行类型检查,我们可以证明
foo
sub本身是正确编写的:

sub foo {
   my $href = $_[0];
   ref($href) eq 'HASH' or die("Expected hashref!");
   print $href->{name}, "\n" if int(rand(2));
}
这将确保
foo
行为正确性的负担从
foo
推到了
foo
的调用者身上。在
foo
中,您现在有了一个小的代码隔离区,在这里您可以确定每个变量的数据类型。逐步添加这样的断言可以让您不断扩大“信任区”

请注意,上面的小改动意味着我们的代码现在将有50%的时间失败,而不是25%的时间失败。由于
foo
现在的失败更加一致,这将帮助我们抓住错误的真正来源:

my $var = int(rand(2))
   ? { name => "Hello" }
   : "World";  # oopsie!

foo($var);

一部分是因为我写的,另一部分是因为,我建议您查看一下检查子参数的方法。下面是一个如何使用它的示例:

use feature qw(state);
use Types::Standard qw( HashRef );
use Type::Params qw( compile );

sub foo {
   state $signature = compile( HashRef );
   my ($href) = $signature->(@_);
   print $href->{name}, "\n" if int(rand(2));
}
将其展开为接受多个参数的子对象

use feature qw(state);
use Types::Standard qw( -types );
use Type::Params qw( compile );

sub query_get_rows {
   state $signature = compile( InstanceOf['DBI::db'], Str, Optional[Int] );
   my ($dbh, $query, $limit) = $signature->(@_);

   # do stuff here
}

现在我正在为
/[@%$]\s*[${]/
搜索源代码,它可以找到我知道的所有反引用语法。然后我手动检查每个出现的语法。我就是这样做的。但是要查找
/->\s*[[{]/
,尽管我会拒绝这个任务,并说必须首先进行单元测试(完全覆盖是不够的。例如,您可以获得
$r=$x?[]:“abc”的完全测试覆盖率,返回if!$y;推送@r,“def”;
运行
$x=0;$y=0;
$x=1;$y=1;
,但这不会发现
$x=0;$y=1;
的问题。“静态分析无法捕捉到这种情况。”–好吧,如果Perl有一个静态类型系统,它有点可能。它可能无法证明会有问题,但如果它不能证明没有任何问题,那就足够警告了。这里,
foo
有类型
Dict[name=>Str]->Bool
$var
的类型是一种求和类型(在大多数静态类型语言中隐式使用时是非法的)的
Dict[name=>Str]|Str
。我们可以看到,作为
foo
的参数,这可能是合法的,但在这种类型的实例中,情况并非如此–任何理智的编译器都会拒绝这一点。我的意图是“静态分析不能捕捉这种东西”是由“因为Perl是非类型化的”(在上一段中)限定的。