目錄

名稱

warnings - Perl pragma 用於控制選用警告

語法

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 pragma 可控制 Perl 程式中哪些警告會在哪些部分啟用。它是一個更靈活的替代方案,同時適用於命令列旗標 -w 和等效的 Perl 變數 $^W

此 pragma 的運作方式與 strict pragma 相同。這表示警告 pragma 的範圍僅限於封閉區塊。這也表示 pragma 設定不會透過檔案外洩(經由 userequiredo)。這讓作者可以獨立定義套用至模組的警告檢查程度。

預設情況下,選用警告會停用,因此任何不嘗試控制警告的舊程式碼都能不變地運作。

區塊中所有警告都可透過下列任一方式啟用

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 會觸發 "純量值 @x[0] 最好寫成 $x[0]" 警告,但指派給純量 $y 則不會。

所有警告都會在 use v5.35(或更高版本)宣告的範圍內自動啟用。

預設警告和選用警告

在詞彙警告推出之前,Perl 有兩類警告:強制性和選用性。

顧名思義,如果您的程式碼觸發強制性警告,無論您是否需要,都會收到警告。例如,以下程式碼會針對「2:」產生 "不是數字" 警告。

my $x = "2:" + 3;

隨著詞彙警告的推出,強制性警告現在變成預設警告。差別在於,雖然先前強制性的警告仍預設為啟用,但它們隨後可以使用詞彙警告實用程式啟用或停用。例如,在以下程式碼中,只會針對 $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 有什麼問題

雖然在命令列中使用 -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 具有動態範圍的副作用。

詞法警告透過允許更精細地控制警告可以或不可以觸發的位置來解決這些限制。

從命令列控制警告

有三個命令列旗標可用於控制何時產生(或不產生)警告

-w

這是現有的旗標。如果詞法警告實用指令未用於您的任何程式碼或您使用的任何模組中,此旗標將在所有地方啟用警告。請參閱 "向後相容性" 以了解此旗標如何與詞法警告互動。

-W

如果命令行中使用 -W 標記,它將啟用程式中的所有警告,無論警告是否使用 no warnings$^W =0 在本地端停用。這包括所有透過 userequiredo 包含的檔案。可以將它視為 Perl 等同於「lint」指令。

-X

執行與 -W 標記完全相反的動作,亦即停用所有警告。

向後相容性

如果您習慣於使用 Perl 在導入詞彙範圍警告之前推出的版本,或有同時使用詞彙警告和 $^W 的程式碼,本節將說明它們的互動方式。

詞彙警告如何與 -w/$^W 互動

  1. 如果未設定控制警告的三個命令行標記 (-w-W-X),且未設定 $^Wwarnings 實用程式,則預設警告將啟用,而選用警告將停用。這表示未嘗試控制警告的舊有程式碼將不變地運作。

  2. -w 標記只會設定全域 $^W 變數,就像在 5.005 中一樣。這表示目前依賴處理 $^W 來控制警告行為的任何舊有程式碼仍將照常運作。

  3. 除了現在是布林值之外,$^W 變數的運作方式與完全相同且不受控制的全域方式完全相同,只不過它無法停用/啟用預設警告。

  4. 如果程式碼片段受 warnings 實用程式控制,則 $^W 變數和 -w 標記都將忽略詞彙警告的範圍。

  5. 覆寫詞彙警告設定的唯一方法是使用 -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 之前,「遺失」的詞彙警告類別在內部定義為與「未初始化」類別相同。它現在本身就是一個頂層類別。

致命警告

類別清單中出現「FATAL」字樣會將這些類別中的警告提升為該詞彙範圍內的致命錯誤。

注意:應謹慎使用 FATAL 警告,特別是 FATAL => 'all'

使用 warnings::warn 進行自訂警告類別的函式庫通常不會預期 warnings::warn 會致命,並可能因此陷入意外狀態。對於發出分類警告的 XS 模組,此類意外例外情況也可能暴露記憶體外洩錯誤。

此外,Perl 解譯器本身也曾發生過涉及致命警告的嚴重錯誤。有關截至 2015 年 1 月已解決和未解決問題的摘要,請參閱 這篇 perl5-porters 文章

雖然有些開發人員發現將某些警告致命化是一種有用的防禦性程式設計技巧,但使用 FATAL => 'all' 將所有可能的警告類別(包括自訂類別)致命化特別危險。因此,不建議使用 FATAL => 'all'

CPAN 上的 strictures 模組提供了一個警告子集的範例,該模組的作者認為將其致命化相對安全。

注意:FATAL 警告的使用者,尤其是使用 FATAL => 'all' 的使用者,應充分了解這樣做會冒著程式未來可移植性的風險。Perl 絕對不承諾未來不會引入新的警告或警告類別;事實上,我們明確保留這樣做的權利。如果 Perl5 開發團隊認為符合社群的最大利益,現在可能不會發出警告的程式碼可能會在未來的 Perl 版本中發出警告。如果使用 FATAL 警告的程式碼因引入新警告而中斷,我們不會將其視為不相容的變更。使用 FATAL 警告的使用者應在升級期間特別小心,檢查他們的程式碼是否觸發任何新警告,並應特別注意他們使用的功能的文件中的小字,以確保他們不會利用文件記載為有風險、已棄用或未指定的功能,或文件說「所以不要這樣做」,或任何具有相同意義和精神的功能。將此類功能與 FATAL 警告結合使用完全由使用者自行承擔風險。

以下文件說明如何使用 FATAL 警告,但 perl5 移植者強烈建議您在這樣做之前了解風險,特別是針對供他人使用的函式庫程式碼,因為下游使用者無法變更致命類別的選擇。

在以下程式碼中,timelengthjoin 的使用都會產生 "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 警告類別升級為致命錯誤,因此程式在遇到警告時會立即終止。

若要明確關閉「致命」警告,只需停用與其關聯的警告。因此,例如,若要停用上述範例中的「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'; 的建議。

若要讓您的程式與 5.20 之前的 Perl 版本相容,您必須改用 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 函數已被棄用,因此已包含程式碼,以便在呼叫模組啟用(至少)「已棄用」警告類別時顯示警告訊息。例如,如下所示。

use warnings 'deprecated';
use MyMod::Abc;
...
MyMod::Abc::open($filename);

應使用 warnings::warnwarnings::warnif 函數來實際顯示警告訊息。這是因為它們可以使用允許將警告提升為致命錯誤的功能。因此,在這種情況下

use MyMod::Abc;
use warnings FATAL => 'MyMod::Abc';
...
MyMod::Abc::open('../fred.txt');

warnings::warnif 函數將偵測到此情況,並在顯示警告訊息後終止。

三個警告函數 warnings::warnwarnings::warnifwarnings::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。

use warnings::register

建立一個新的警告類別,其名稱與使用 pragma 呼叫所在的套件相同。

warnings::enabled()

使用與目前套件名稱相同的警告類別。

如果呼叫模組中啟用該警告類別,則傳回 TRUE。否則傳回 FALSE。

warnings::enabled($category)

如果呼叫模組中啟用警告類別 $category,則傳回 TRUE。否則傳回 FALSE。

warnings::enabled($object)

使用物件參考 $object 的類別名稱作為警告類別。

如果在物件使用的第一個範圍中啟用該警告類別,則傳回 TRUE。否則傳回 FALSE。

warnings::enabled_at_level($category, $level)

warnings::enabled 類似,但 $level 指定確切的呼叫框架,0 為立即呼叫者。

warnings::fatal_enabled()

如果呼叫模組中與目前套件同名的警告類別已設定為 FATAL,則傳回 TRUE。否則傳回 FALSE。

warnings::fatal_enabled($category)

如果呼叫模組中已將警告類別 $category 設定為 FATAL,則傳回 TRUE。否則傳回 FALSE。

warnings::fatal_enabled($object)

使用物件參考 $object 的類別名稱作為警告類別。

如果在物件首次使用的第一個範圍中,該警告類別已設定為 FATAL,則傳回 TRUE。否則傳回 FALSE。

warnings::fatal_enabled_at_level($category, $level)

類似於 warnings::fatal_enabled,但 $level 指定確切的呼叫框架,0 為立即呼叫者。

warnings::warn($message)

$message 印出至 STDERR。

使用與目前套件名稱相同的警告類別。

如果呼叫模組中已將該警告類別設定為「FATAL」,則 die。否則傳回。

warnings::warn($category, $message)

$message 印出至 STDERR。

如果呼叫模組中已將警告類別 $category 設定為「FATAL」,則 die。否則傳回。

warnings::warn($object, $message)

$message 印出至 STDERR。

使用物件參考 $object 的類別名稱作為警告類別。

如果在 $object 首次使用的範圍中,已將該警告類別設定為「FATAL」,則 die。否則傳回。

warnings::warn_at_level($category, $level, $message)

類似於 warnings::warn,但 $level 指定確切的呼叫框架,0 為立即呼叫者。

warnings::warnif($message)

等於

if (warnings::enabled())
  { warnings::warn($message) }
warnings::warnif($category, $message)

等於

if (warnings::enabled($category))
  { warnings::warn($category, $message) }
warnings::warnif($object, $message)

等於

if (warnings::enabled($object))
  { warnings::warn($object, $message) }
warnings::warnif_at_level($category, $level, $message)

如同 warnings::warnif,但 $level 指定確切的呼叫框架,0 為立即呼叫者。

warnings::register_categories(@names)

這會為指定的 name 註冊警告類別,主要供 warnings::register pragma 使用。

另請參閱 "perlmodlib 中的「實用模組」perldiag