perllexwarn - Perl のレキシカルな警告
use warnings プラグマは、Perl プログラムのある部分に対してどの警告を
有効にするかを精密に制御できるようにします。
これはコマンドラインオプション -w および等価な Perl 変数 $^W よりも
より柔軟な代替案です。
このプラグマはちょうど strict プラグマと同様に動作します。
つまり、警告プラグマのスコープは閉じたブロック内に限定されます。
また、プラグマ設定は (use, require, do を通して)ファイルを超えて
漏洩することはありません。
これにより、モジュール作者は警告チェックの度合いを独立に
設定できるようになります。
デフォルトでは、オプションの警告は無効なので、警告を制御しようとしない レガシーコードは変更なしで動作します。
あるブロック内で全ての警告を有効にするには以下のどちらかのようにします:
use warnings;
use warnings 'all';
同様に、あるブロック内で全ての警告を無効にするには以下のどちらかのように します:
no warnings;
no warnings 'all';
例えば、以下のコードを考えます:
use warnings;
my @a;
{
no warnings;
my $b = @a[0];
}
my $c = @a[0];
外側のブロックでは警告は有効ですが、内側のブロックでは無効です。
この場合、スカラ $c への代入では
"Scalar value @a[0] better written as $a[0]" 警告が出ますが、
スカラ $b への代入では出ません。
レキシカル警告の説明の前に、Perl は二つの警告クラスがあります: 強制的(mandatory)とオプション(optional)です。
名前が示しているように、コードが強制的な警告に引っかかると、望むと
望まな意図にかかわらず警告を出力します。
例えば、以下のコードは "2:" の部分に対して常に "isn't numeric" 警告を
出力します。
my $a = "2:" + 3;
レキシカルな警告の導入によって、強制的な警告は デフォルトの 警告と
なりました。
違いは、以前の強制的な警告は今でもデフォルトで有効ですが、引き続く
レキシカルな警告プラグマで有効/無効にに出来ることです。
例えば、以下のコードでは、"isn't numeric" 警告は $a 変数に対してだけ
報告されます。
my $a = "2:" + 3;
no warnings;
my $b = "2:" + 3;
-w オプションや $^W はデフォルトの警告を無効/有効にするのには
使えないことに注意してください。
この場合は強制的なままです。
$^W の何が悪いの?警告を有効にするのにコマンドラインで -w を使うというのはとても 便利ですが、オールオアナッシングであるという問題があります。 Perl のプログラムを書いているときのよくある状況を考えます。 コードの一部はあなた自身が書きますが、かなり確実に既に書かれている Perl モジュールを利用します。 このような場合に -w フラグを使うと、あなたが書いていないコードに 対しても警告を有効にすることになります。
同様に、コードブロックで有効または無効にするために $^W を使うことにも
本質的な欠点があります。
まず、コードブロックで警告を無効にしたいとします。
以下のようにすれば十分だと考えるかもしれません:
{
local ($^W) = 0;
my $a =+ 2;
my $b; chop $b;
}
このコードが -w フラグ付きで実行されると、$a の行で警告が
出ます -- "Reversed += operator"。
問題は、Perl にはコンパイル時警告と実行時警告があると言うことです。 コンパイル時警告を無効にするには、以下のようにコードを書き直す必要が あります:
{
BEGIN { $^W = 0 }
my $a =+ 2;
my $b; chop $b;
}
$^W に関するもう一つの問題は、コード中の予想外の位置の設定で不用意に
警告設定が変わるということです。
例えば、以下のコードが(-w フラグなしで)実行されると、doit の
2 回目の呼び出しで "Use of uninitialized value" 警告が出ますが、
1 回目では出ません。
sub doit
{
my $b; chop $b;
}
doit();
{
local ($^W) = 1;
doit()
}
これは $^W が動的スコープを持つことの副作用です。
レキシカルな警告は、どこで警告に引っかかるか引っかからないかに関して より精度の高い制御をすることで、これらの制限を回避します。
いつ警告が発生する(あるいは発生しない)かを制御するために使われる 三つのコマンドラインフラグがあります:
This is the existing flag. If the lexical warnings pragma is not used in any of you code, or any of the modules that you use, this flag will enable warnings everywhere. See 後方互換性 for details of how this flag interacts with lexical warnings. (TBT)
コマンドラインで -W フラグが使われると、プログラム中で
no warnings や $^W =0 を使って警告を無効にしていても無視して、全ての
警告を有効にします。
これは use, require, do 経由で読み込まれる全てのファイルにも
適用されます。
Perl 用の "lint" コマンドの等価物と考えられます。
正確に -W フラグの逆を行います; つまり、全ての警告を無効にします。
If you are used with working with a version of Perl prior to the
introduction of lexically scoped warnings, or have code that uses both
lexical warnings and $^W, this section will describe how they interact.
(TBT)
レキシカル警告と -w/$^W の相互作用:
If none of the three command line flags (-w, -W or -X) that
control warnings is used and neither $^W or the warnings pragma
are used, then default warnings will be enabled and optional warnings
disabled.
This means that legacy code that doesn't attempt to control the warnings
will work unchanged.
(TBT)
The -w flag just sets the global $^W variable as in 5.005 -- this
means that any legacy code that currently relies on manipulating $^W
to control warning behavior will still work as is.
(TBT)
Apart from now being a boolean, the $^W variable operates in exactly
the same horrible uncontrolled global way, except that it cannot
disable/enable default warnings.
(TBT)
If a piece of code is under the control of the warnings pragma,
both the $^W variable and the -w flag will be ignored for the
scope of the lexical warning.
(TBT)
The only way to override a lexical warnings setting is with the -W or -X command line flags. (TBT)
The combined effect of 3 & 4 is that it will allow code which uses
the warnings pragma to control the warning behavior of $^W-type
code (using a local $^W=0) if it really wants to, but not vice-versa.
(TBT)
「カテゴリ」の階層は、警告のグループを分離して警告を有効/無効にできるように するために定義されています。
現在の階層は:
all -+
|
+- closure
|
+- deprecated
|
+- exiting
|
+- glob
|
+- io -----------+
| |
| +- closed
| |
| +- exec
| |
| +- layer
| |
| +- newline
| |
| +- pipe
| |
| +- unopened
|
+- misc
|
+- numeric
|
+- once
|
+- overflow
|
+- pack
|
+- portable
|
+- recursion
|
+- redefine
|
+- regexp
|
+- severe -------+
| |
| +- debugging
| |
| +- inplace
| |
| +- internal
| |
| +- malloc
|
+- signal
|
+- substr
|
+- syntax -------+
| |
| +- ambiguous
| |
| +- bareword
| |
| +- digit
| |
| +- parenthesis
| |
| +- precedence
| |
| +- printf
| |
| +- prototype
| |
| +- qw
| |
| +- reserved
| |
| +- semicolon
|
+- taint
|
+- threads
|
+- uninitialized
|
+- unpack
|
+- untie
|
+- utf8
|
+- 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.6.1 では、レキシカル警告カテゴリ "deprecated" は "syntax" カテゴリの副カテゴリでした。 今ではそれ自体でトップレベルカテゴリです。
The presence of the word "FATAL" in the category list will escalate any
warnings detected from the categories specified in the lexical scope
into fatal errors. In the code below, the use of time, length
and join can all produce a "Useless use of xxx in void context"
warning.
(TBT)
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.
The scope where length is used has escalated the void warnings
category into a fatal error, so the program terminates immediately it
encounters the warning.
(TBT)
To explicitly turn off a "FATAL" warning you just disable the warning it is associated with. So, for example, to disable the "void" warning in the example above, either of these will do the trick: (TBT)
no warnings qw(void);
no warnings FATAL => qw(void);
If you want to downgrade a warning that has been escalated into a fatal error back to a normal warning, you can use the "NONFATAL" keyword. For example, the code below will promote all warnings into fatal errors, except for those in the "syntax" category. (TBT)
use warnings FATAL => 'all', NONFATAL => 'syntax';
The warnings pragma provides a number of functions that are useful for
module authors. These are used when you want to report a module-specific
warning to a calling module has enabled warnings via the warnings
pragma.
(TBT)
以下の 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;
The call to warnings::register will create a new warnings category
called "MyMod::abc", i.e. the new category name matches the current
package name. The open function in the module will display a warning
message if it gets given a relative path as a parameter. This warnings
will only be displayed if the code that uses MyMod::Abc has actually
enabled them with the warnings pragma like below.
(TBT)
use MyMod::Abc;
use warnings 'MyMod::Abc';
...
abc::open("../fred.txt");
It is also possible to test whether the pre-defined warnings categories are
set in the calling module with the warnings::enabled function. Consider
this snippet of code:
(TBT)
package MyMod::Abc;
sub open {
warnings::warnif("deprecated",
"open is deprecated, use new instead");
new(@_);
}
sub new
...
1;
The function open has been deprecated, so code has been included to
display a warning message whenever the calling module has (at least) the
"deprecated" warnings category enabled. Something like this, say.
(TBT)
use warnings 'deprecated';
use MyMod::Abc;
...
MyMod::Abc::open($filename);
Either the warnings::warn or warnings::warnif function should be
used to actually display the warnings message. This is because they can
make use of the feature that allows warnings to be escalated into fatal
errors. So in this case
(TBT)
use MyMod::Abc;
use warnings FATAL => 'MyMod::Abc';
...
MyMod::Abc::open('../fred.txt');
the warnings::warnif function will detect this and die after
displaying the warning message.
(TBT)
The three warnings functions, warnings::warn, warnings::warnif
and warnings::enabled can optionally take an object reference in place
of a category name. In this case the functions will use the class name
of the object as the warnings category.
(TBT)
この例を考えます:
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;
The code below makes use of both modules, but it only enables warnings from
Derived.
(TBT)
use Original;
use Derived;
use warnings 'Derived';
my $a = Original->new();
$a->doit(1);
my $b = Derived->new();
$a->doit(1);
When this code is run only the Derived object, $b, will generate
a warning.
(TBT)
Odd numbers are unsafe at main.pl line 7
Notice also that the warning is reported at the line where the object is first used. (TBT)
warnings, perldiag.
Paul Marquess