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
が動的スコープを持つことの副作用です。
レキシカルな警告は、どこで警告に引っかかるか引っかからないかに関して より精度の高い制御をすることで、これらの制限を回避します。
いつ警告が発生する(あるいは発生しない)かを制御するために使われる 三つのコマンドラインフラグがあります:
これは既存のフラグです。 レキシカル警告プラグマがあなたのコードやあなたが使っているモジュールの どこでも 使われていない なら、このフラグは全ての場所で警告を 有効にします。 このフラグがレキシカル警告とどのように相互作用するかに関する詳細については "Backward Compatibility" を参照してください。
コマンドラインで -W フラグが使われると、プログラム中で no warnings
や $^W =0
を使って警告を無効にしていても無視して、全ての 警告を有効にします。 これは use
, require
, do
経由で読み込まれる全てのファイルにも 適用されます。 Perl 用の "lint" コマンドの等価物と考えられます。
正確に -W フラグの逆を行います; つまり、全ての警告を無効にします。
レキシカルスコープ警告が導入される前のバージョンの Perl で動作させていたり、 レキシカル警告と $^W
の両方のコードがある場合、この節はこれらが どのように相互作用するかを記述しています。
レキシカル警告と -w/$^W
の相互作用:
警告を制御する三つのコマンドラインフラグ (-w, -W or -X) の どれも使われておらず、$^W
や the warnings
プラグマも使われていない 場合、デフォルトの警告が有効になり、オプションの警告は無効になります。 これにより、警告を制御しようとしないレガシーコードは無変更で動作します。
5.005 から -w フラグはグローバルな $^W
変数を設定します。 これにより、警告の振る舞いを制御するために $^W
を操作することに 依存しているレガシーコードはそのままで動作します。
真偽値になったことは別として、$^W
変数は正確に同じ恐ろしく 制御不能なグローバルな方法で操作しますが、デフォルトの警告を有効化/ 無効化することは出来ません。
コード片が warnings
プラグマの制御下にある場合、$^W
変数と -w フラグの両方はレキシカル警告のスコープで無視されます。
レキシカル警告設定を上書きする唯一の方法は -W または -X コマンドラインフラグを使うことです。
3 & 4 の組み合わせの効果により、本当に警告したいときに $^W 型のコードの 警告の振る舞いを (local $^W=0
を使って) 制御するために warnings
プラグマを使えますが、逆はできません。
「カテゴリ」の階層は、警告のグループを分離して警告を有効/無効にできるように するために定義されています。
現在の階層は:
all -+
|
+- closure
|
+- deprecated
|
+- exiting
|
+- glob
|
+- io -----------+
| |
| +- closed
| |
| +- exec
| |
| +- layer
| |
| +- newline
| |
| +- pipe
| |
| +- unopened
|
+- imprecision
|
+- misc
|
+- numeric
|
+- once
|
+- overflow
|
+- pack
|
+- portable
|
+- recursion
|
+- redefine
|
+- regexp
|
+- severe -------+
| |
| +- debugging
| |
| +- inplace
| |
| +- internal
| |
| +- malloc
|
+- signal
|
+- substr
|
+- syntax -------+
| |
| +- ambiguous
| |
| +- bareword
| |
| +- digit
| |
| +- illegalproto
| |
| +- 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" カテゴリの副カテゴリでした。 今ではそれ自体でトップレベルカテゴリです。
カテゴリ一覧中に "FATAL" の文字があると、レキシカルスコープで 指定されたカテゴリで検出された全ての警告を致命的エラーに昇格させます。 以下のコードでは、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';
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 {
warnings::warnif("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
関数はこれを検出して、警告を出力した後 die します。
三つの警告関数 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 $a = Original->new();
$a->doit(1);
my $b = Derived->new();
$a->doit(1);
このコードが Derived
オブジェクトからのみ実行されているとき、 $b
は警告を出力します。
Odd numbers are unsafe at main.pl line 7
オブジェクトが最初に使われた行で警告が報告されることにも注意してください。
Paul Marquess