warnings - Perl 编译指示,用于控制可选警告
use warnings;
no warnings;
# Standard warnings are enabled by use v5.35 or above
use v5.35;
use warnings "all";
no warnings "uninitialized";
# or equivalent to those last two ...
use warnings qw(all -uninitialized);
use warnings::register;
if (warnings::enabled()) {
warnings::warn("some warning");
}
if (warnings::enabled("void")) {
warnings::warn("void", "some warning");
}
if (warnings::enabled($object)) {
warnings::warn($object, "some warning");
}
warnings::warnif("some warning");
warnings::warnif("void", "some warning");
warnings::warnif($object, "some warning");
warnings
编译指示可以控制在 Perl 程序的哪些部分启用哪些警告。它是命令行标志 -w 和等效 Perl 变量 $^W
的更灵活的替代方案。
此编译指示的工作方式与 strict
编译指示相同。这意味着警告编译指示的范围仅限于封闭的代码块。这也意味着编译指示设置不会泄漏到其他文件(通过 use
、require
或 do
)。这允许作者独立定义将应用于其模块的警告检查程度。
默认情况下,可选警告被禁用,因此任何不尝试控制警告的旧代码将按原样工作。
可以通过以下两种方式在代码块中启用所有警告。
use warnings;
use warnings 'all';
类似地,可以通过以下两种方式在代码块中禁用所有警告。
no warnings;
no warnings 'all';
例如,考虑以下代码。
use warnings;
my @x;
{
no warnings;
my $y = @x[0];
}
my $z = @x[0];
封闭代码块中的代码启用了警告,但内部代码块禁用了警告。在这种情况下,这意味着对标量 $z
的赋值将触发 "Scalar value @x[0] better written as $x[0]"
警告,但对标量 $y
的赋值不会触发警告。
在 use v5.35
(或更高版本)声明的范围内,所有警告都会自动启用。
在引入词法警告之前,Perl 有两类警告:强制性警告和可选警告。
顾名思义,如果您的代码触发了强制性警告,无论您是否想要,都会收到警告。例如,以下代码将始终产生关于 "2:" 的 "isn't numeric"
警告。
my $x = "2:" + 3;
随着词法警告的引入,强制性警告现在变为默认警告。区别在于,尽管以前强制性警告仍然默认启用,但随后可以使用词法警告 pragma 启用或禁用它们。例如,在以下代码中,"isn't numeric"
警告仅针对 $x
变量报告。
my $x = "2:" + 3;
no warnings;
my $y = "2:" + 3;
请注意,-w 标志或 $^W
都不能用于禁用/启用默认警告。在这种情况下,它们仍然是强制性的。
为了方便起见,您可以(从 Perl 5.34 开始)以正负两种方式将参数传递给 import()
方法。负面警告是在其名称前加 -
符号的警告;正面警告是其他任何警告。这使您可以在一条命令中打开某些警告并关闭其他警告。因此,假设您已经打开了一堆警告,但想在某个代码块中稍微调整一下,您可以这样做
{
use warnings qw(uninitialized -redefine);
...
}
这等同于
{
use warnings qw(uninitialized);
no warnings qw(redefine);
...
}
参数列表按您指定的顺序处理。例如,如果您不想收到有关使用实验性功能的警告,但确实不喜欢 somefeature
,您可以这样说
use warnings qw(all -experimental experimental::somefeature);
这等同于
use warnings 'all';
no warnings 'experimental';
use warnings 'experimental::somefeature';
随着实验性功能成为 Perl 的常规功能,相应的警告将不再打印。它们也将在下面的 "类别层次结构" 中不再列出。
仍然可以请求打开或关闭这些警告,但这样做不会有任何效果。
$^W
有什么问题虽然非常有用,但使用命令行上的 -w 来启用警告的最大问题是它要么全部启用,要么全部禁用。以编写 Perl 程序的典型场景为例。您将自己编写代码的一部分,但很可能您将使用预先编写的 Perl 模块。在这种情况下,如果您使用 -w 标志,您最终将在您没有编写的代码部分中启用警告。
类似地,使用 $^W
来禁用或启用代码块从根本上来说是有缺陷的。首先,假设您想在代码块中禁用警告。您可能期望这样做就足够了
{
local ($^W) = 0;
my $x =+ 2;
my $y; chop $y;
}
当这段代码使用 -w 标志运行时,将为 $x
行生成一个警告:"Reversed += operator"
。
问题是 Perl 既有编译时警告,也有运行时警告。要禁用编译时警告,您需要像这样重写代码
{
BEGIN { $^W = 0 }
my $x =+ 2;
my $y; chop $y;
}
请注意,与第一个示例不同,这将永久设置 $^W
,因为它不能在编译时运行,也不能被本地化为运行时块。
$^W
的另一个大问题是,您可以在代码中意外的地方意外地更改警告设置。例如,当以下代码运行时(没有 -w 标志),对 doit
的第二次调用将触发 "Use of uninitialized value"
警告,而第一次则不会。
sub doit
{
my $y; chop $y;
}
doit();
{
local ($^W) = 1;
doit()
}
这是由于$^W
是动态作用域的副作用。
词法警告通过允许更精细地控制警告可以在哪里触发或不能触发来解决这些限制。
有三个命令行标志可用于控制何时产生(或不产生)警告。
这是现有的标志。如果您的任何代码或您使用的任何模块中都没有使用词法警告 pragma,则此标志将在所有地方启用警告。有关此标志如何与词法警告交互的详细信息,请参见"向后兼容性"。
如果在命令行中使用-W标志,它将在整个程序中启用所有警告,无论是否使用no warnings
或$^W =0
在本地禁用警告。这包括通过use
、require
或do
包含的所有文件。可以将其视为 Perl 中等效于“lint”命令。
与-W标志的作用完全相反,即它禁用所有警告。
如果您习惯于使用在引入词法作用域警告之前的 Perl 版本,或者您的代码同时使用词法警告和$^W
,则本节将描述它们如何交互。
词法警告如何与-w/$^W
交互
如果未使用控制警告的三个命令行标志(-w、-W或-X)中的任何一个,并且未使用$^W
或warnings
pragma,则将启用默认警告并禁用可选警告。这意味着不会尝试控制警告的旧代码将按原样工作。
-w标志只是设置全局$^W
变量,就像在 5.005 中一样。这意味着当前依赖于操作$^W
来控制警告行为的任何旧代码将按原样工作。
除了现在是布尔值之外,$^W
变量以完全相同的方式以可怕的、不受控制的全局方式运行,只是它不能禁用/启用默认警告。
如果一段代码受warnings
pragma 的控制,则$^W
变量和-w标志将被忽略,因为词法警告的作用域。
覆盖词法警告设置的唯一方法是使用-W或-X命令行标志。
3 和 4 的组合效果是,它将允许使用 warnings
编译指示的代码控制 $^W 类型代码的警告行为(使用 local $^W=0
),如果它确实想要这样做,但反之则不行。
已经定义了“类别”的层次结构,以允许独立地启用/禁用警告组。
当前的层次结构是
all -+
|
+- closure
|
+- deprecated ----+
| |
| +- deprecated::apostrophe_as_package_separator
| |
| +- deprecated::delimiter_will_be_paired
| |
| +- deprecated::dot_in_inc
| |
| +- deprecated::goto_construct
| |
| +- deprecated::smartmatch
| |
| +- deprecated::unicode_property_name
| |
| +- deprecated::version_downgrade
|
+- exiting
|
+- experimental --+
| |
| +- experimental::args_array_with_signatures
| |
| +- experimental::builtin
| |
| +- experimental::class
| |
| +- experimental::const_attr
| |
| +- experimental::declared_refs
| |
| +- experimental::defer
| |
| +- experimental::extra_paired_delimiters
| |
| +- experimental::for_list
| |
| +- experimental::private_use
| |
| +- experimental::re_strict
| |
| +- experimental::refaliasing
| |
| +- experimental::regex_sets
| |
| +- experimental::try
| |
| +- experimental::uniprop_wildcards
| |
| +- experimental::vlb
|
+- glob
|
+- imprecision
|
+- io ------------+
| |
| +- closed
| |
| +- exec
| |
| +- layer
| |
| +- newline
| |
| +- pipe
| |
| +- syscalls
| |
| +- unopened
|
+- locale
|
+- misc
|
+- missing
|
+- numeric
|
+- once
|
+- overflow
|
+- pack
|
+- portable
|
+- recursion
|
+- redefine
|
+- redundant
|
+- regexp
|
+- scalar
|
+- severe --------+
| |
| +- debugging
| |
| +- inplace
| |
| +- internal
| |
| +- malloc
|
+- shadow
|
+- signal
|
+- substr
|
+- syntax --------+
| |
| +- ambiguous
| |
| +- bareword
| |
| +- digit
| |
| +- illegalproto
| |
| +- parenthesis
| |
| +- precedence
| |
| +- printf
| |
| +- prototype
| |
| +- qw
| |
| +- reserved
| |
| +- semicolon
|
+- taint
|
+- threads
|
+- uninitialized
|
+- unpack
|
+- untie
|
+- utf8 ----------+
| |
| +- non_unicode
| |
| +- nonchar
| |
| +- surrogate
|
+- void
就像“strict”编译指示一样,这些类别中的任何一个都可以组合起来
use warnings qw(void redefine);
no warnings qw(io syntax untie);
同样像“strict”编译指示一样,如果在给定范围内存在多个 warnings
编译指示,则累积效果是累加的。
use warnings qw(void); # only "void" warnings enabled
...
use warnings qw(io); # only "void" & "io" warnings enabled
...
no warnings qw(void); # only "io" warnings enabled
要确定特定警告已分配到的类别,请参阅 perldiag。
注意:在 Perl 5.8.0 之前,词法警告类别“deprecated”是“syntax”类别的子类别。现在它是一个独立的顶级类别。
注意:在 5.21.0 之前,词法警告类别“missing”在内部定义为与“uninitialized”类别相同。现在它是一个独立的顶级类别。
类别列表中存在“FATAL”一词将使这些类别的警告在该词法范围内升级为致命错误。
注意: 应谨慎使用 FATAL 警告,尤其是 FATAL => 'all'
。
使用 warnings::warn 用于自定义警告类别的库通常不希望 warnings::warn 成为致命错误,因此可能会因此而处于意外状态。对于发出分类警告的 XS 模块,此类意外异常也可能暴露内存泄漏错误。
此外,Perl 解释器本身也存在与致命警告相关的严重错误。有关截至 2015 年 1 月已解决和未解决问题的摘要,请参阅 此 perl5-porters 帖子。
虽然一些开发人员发现将某些警告致命化是一种有用的防御性编程技术,但使用 FATAL => 'all'
将所有可能的警告类别(包括自定义类别)致命化是特别危险的。因此,不建议使用 FATAL => 'all'
。 不建议。
CPAN 上的 strictures 模块提供了一个警告子集的示例,该模块的作者认为该子集相对安全地致命化。
注意: FATAL 警告的用户,尤其是使用 FATAL => 'all'
的用户,应该充分意识到他们这样做会冒着程序未来可移植性的风险。Perl 绝对不承诺将来不会引入新的警告或警告类别;事实上,我们明确保留这样做权利。现在可能不会发出警告的代码在 Perl 的未来版本中可能会发出警告,如果 Perl5 开发团队认为这样做符合社区的最佳利益。如果使用 FATAL 警告的代码由于引入新的警告而中断,我们不会将其视为不兼容的更改。FATAL 警告的用户在升级时应格外小心,以检查他们的代码是否触发了任何新的警告,并且应特别注意他们使用的功能文档的细则,以确保他们没有利用那些被记录为有风险、已弃用或未指定的特性,或者文档中说“所以不要这样做”,或者任何具有相同意义和精神的特性。在 FATAL 警告下使用此类特性完全由用户自行承担风险。
以下文档描述了如何使用 FATAL 警告,但 perl5 维护者强烈建议您在使用之前了解风险,特别是对于供他人使用的库代码,因为下游用户无法更改致命类别选择。
在下面的代码中,time
、length
和 join
的使用都可能产生 "Useless use of xxx in void context"
警告。
use warnings;
time;
{
use warnings FATAL => qw(void);
length "abc";
}
join "", 1,2,3;
print "done\n";
运行时会产生以下输出
Useless use of time in void context at fatal line 3.
Useless use of length in void context at fatal line 7.
length
使用的范围已将 void
警告类别升级为致命错误,因此程序在遇到警告时会立即终止。
要显式关闭 "FATAL" 警告,只需禁用与其关联的警告。例如,要禁用上面示例中的 "void" 警告,可以使用以下任一方法
no warnings qw(void);
no warnings FATAL => qw(void);
如果要将已升级为致命错误的警告降级为普通警告,可以使用 "NONFATAL" 关键字。例如,以下代码将所有警告提升为致命错误,除了 "syntax" 类别的警告。
use warnings FATAL => 'all', NONFATAL => 'syntax';
从 Perl 5.20 开始,您可以使用以下方法代替 use warnings FATAL => 'all';
use v5.20; # Perl 5.20 or greater is required for the following
use warnings 'FATAL'; # short form of "use warnings FATAL => 'all';"
但是,您仍然应该注意本节前面关于避免使用 use warnings FATAL => 'all';
的指导。
如果您希望您的程序与 Perl 5.20 之前的版本兼容,则必须使用 use warnings FATAL => 'all';
代替。(在 Perl 的早期版本中,语句 use warnings 'FATAL';
、use warnings 'NONFATAL';
和 no warnings 'FATAL';
的行为未定义;它们的行为不像包含 => 'all'
部分一样。从 5.20 开始,它们的行为就一样了。)
warnings
编译指示提供了一些对模块作者有用的函数。当您想要向已通过 warnings
编译指示启用警告的调用模块报告模块特定的警告时,可以使用这些函数。
考虑以下 MyMod::Abc
模块。
package MyMod::Abc;
use warnings::register;
sub open {
my $path = shift;
if ($path !~ m#^/#) {
warnings::warn("changing relative path to /var/abc")
if warnings::enabled();
$path = "/var/abc/$path";
}
}
1;
对 warnings::register
的调用将创建一个名为 "MyMod::Abc" 的新警告类别,即新类别名称与当前包名匹配。模块中的 open
函数如果被赋予相对路径作为参数,将显示警告消息。只有当使用 MyMod::Abc
的代码实际上已通过 warnings
编译指示启用警告时,才会显示此警告,如下所示。
use MyMod::Abc;
use warnings 'MyMod::Abc';
...
abc::open("../fred.txt");
还可以使用 warnings::enabled
函数测试调用模块中是否设置了预定义的警告类别。考虑以下代码片段
package MyMod::Abc;
sub open {
if (warnings::enabled("deprecated")) {
warnings::warn("deprecated",
"open is deprecated, use new instead");
}
new(@_);
}
sub new
...
1;
open
函数已弃用,因此已包含代码以在调用模块启用(至少)"deprecated" 警告类别时显示警告消息。例如,像这样。
use warnings 'deprecated';
use MyMod::Abc;
...
MyMod::Abc::open($filename);
应该使用 warnings::warn
或 warnings::warnif
函数来实际显示警告消息。这是因为它们可以利用允许将警告升级为致命错误的功能。因此,在这种情况下
use MyMod::Abc;
use warnings FATAL => 'MyMod::Abc';
...
MyMod::Abc::open('../fred.txt');
warnings::warnif
函数会检测到这种情况,并在显示警告消息后退出。
三个警告函数,warnings::warn
、warnings::warnif
和 warnings::enabled
可以选择性地使用对象引用来代替类别名称。在这种情况下,函数将使用对象的类名作为警告类别。
考虑以下示例
package Original;
no warnings;
use warnings::register;
sub new
{
my $class = shift;
bless [], $class;
}
sub check
{
my $self = shift;
my $value = shift;
if ($value % 2 && warnings::enabled($self))
{ warnings::warn($self, "Odd numbers are unsafe") }
}
sub doit
{
my $self = shift;
my $value = shift;
$self->check($value);
# ...
}
1;
package Derived;
use warnings::register;
use Original;
our @ISA = qw( Original );
sub new
{
my $class = shift;
bless [], $class;
}
1;
下面的代码使用了这两个模块,但它只启用了来自 Derived
的警告。
use Original;
use Derived;
use warnings 'Derived';
my $x = Original->new();
$x->doit(1);
my $y = Derived->new();
$x->doit(1);
当运行此代码时,只有 Derived
对象 $y
会产生警告。
Odd numbers are unsafe at main.pl line 7
还要注意,警告是在对象首次使用的那一行报告的。
注册新的警告类别时,您可以向 warnings::register
提供更多名称,如下所示
package MyModule;
use warnings::register qw(format precision);
...
warnings::warnif('MyModule::format', '...');
注意:以 _at_level
结尾的函数是在 Perl 5.28 中添加的。
创建一个新的警告类别,其名称与调用 pragma 的包的名称相同。
使用与当前包名称相同的警告类别。
如果该警告类别在调用模块中启用,则返回 TRUE。否则返回 FALSE。
如果警告类别 $category
在调用模块中启用,则返回 TRUE。否则返回 FALSE。
使用对象引用 $object
的类名作为警告类别。
如果该警告类别在对象首次使用的第一个作用域中启用,则返回 TRUE。否则返回 FALSE。
与 warnings::enabled
相似,但 $level 指定了确切的调用帧,0 表示直接调用者。
如果在调用模块中将与当前包名称相同的警告类别设置为 FATAL,则返回 TRUE。否则返回 FALSE。
如果在调用模块中将警告类别 $category
设置为 FATAL,则返回 TRUE。否则返回 FALSE。
使用对象引用 $object
的类名作为警告类别。
如果该警告类别在对象首次使用时的第一个作用域中被设置为 FATAL,则返回 TRUE。否则返回 FALSE。
类似于 warnings::fatal_enabled
,但 $level 指定了确切的调用帧,0 表示直接调用者。
将 $message
打印到 STDERR。
使用与当前包名称相同的警告类别。
如果该警告类别在调用模块中被设置为 "FATAL",则退出程序。否则返回。
将 $message
打印到 STDERR。
如果警告类别 $category
在调用模块中被设置为 "FATAL",则退出程序。否则返回。
将 $message
打印到 STDERR。
使用对象引用 $object
的类名作为警告类别。
如果该警告类别在 $object
首次使用时的作用域中被设置为 "FATAL",则退出程序。否则返回。
类似于 warnings::warn
,但 $level 指定了确切的调用帧,0 表示直接调用者。
等效于
if (warnings::enabled())
{ warnings::warn($message) }
等效于
if (warnings::enabled($category))
{ warnings::warn($category, $message) }
等效于
if (warnings::enabled($object))
{ warnings::warn($object, $message) }
类似于 warnings::warnif
,但 $level 指定了确切的调用帧,0 表示直接调用者。
此函数用于注册给定名称的警告类别,主要供 warnings::register 编译指示使用。
另请参阅 "perlmodlib 中的实用模块" 和 perldiag。