Perl Hackers Hub

第52回 Perlで堅牢な開発―構文チェック,静的検査,型制約(1)

この記事を読むのに必要な時間:およそ 4 分

stricturesプラグマで,バランスの良い約束を入れる

構文チェックを厳しくするためにwarnings FATAL =>'all'を行うのも手ですが,現実的には,一部警告のままのほうが都合が良いです。たとえば,警告カテゴリのrecursionは無限再帰の可能性を示しているだけで,逆に意図せぬエラーを生み出すかもしれません。できる限り警告を致命的エラーに昇格し,取捨選択を行ってくれるプラグマがstricturesです。strictures2.000005を用いて説明します。

use strictures 2; # バージョン2を指定

上記は,次のコードと等価になります。

use strict;
use warnings FATAL => 'all';
use warnings NONFATAL => qw(
  exec
  recursion
  internal
  malloc
  newline
  experimental
  deprecated
  portable
);
no warnings 'once';

strictures 2を指定して,さらに,PERL_STRICTURES_EXTRA環境変数も有効にすると,次のようなプラグマも有効になります。

no indirect 'fatal'; ――(1)
no multidimensional; ――(2)
no bareword::filehandles; ――(3)

(1)new Fooといった間接オブジェクト記法を禁止し,(2)$hash{1,2}といったキーに配列を利用した呼び出しを禁止し,(3)<FH>といったBarewordによるファイルハンドルの扱いを禁止します。

構文チェックを一部外す方法

未然に問題に気付くために構文チェックは厳しいほうがよいと思いますが,一部,例外として処理したいケースもあると思います。Perlの場合,ブロックで手軽に構文チェックを緩めることができます。

use warnings FATAL => 'numeric';
{
    no warnings 'numeric';
    my $total = "foo" + 111 # NO error
}
my $total = "foo" + 111 # ERROR!!

Perl::Criticで静的検査を行う

次に,静的解析でコードを検査するPerl::Critic 1.132を紹介します。

なお,Perlの静的解析について詳しくは,本連載第27回「Perlにおける静的解析」第28回「Perlの構文解析器の作り方と応用例」で紹介しています。

perlcriticを使ってみる

Perl::Criticに同封されるperlcriticと呼ばれるコマンドラインツールを使ってみましょう。実際の用途としては,エディタで自動的にチェックするように設定することをお勧めします注2⁠。

次のコードは,strictが有効になっていないため警告が出ています。

% echo "print 'HELLO'" | perlcritic
Code before strictures are enabled at line 1, column 1. S
ee page 429 of PBP. (Severity: 5)

strictプラグマを有効にして,警告を回避します。

% echo "use strict;print 'HELLO'" | perlcritic
source OK

先ほどの警告にあった(Severity: 5)は静的検査の厳しさを指し,1~5の段階のうち,最も直すべき指摘です。以下は厳しさを3と指定した場合の例です。

% echo "use strict;print 'HELLO'" | perlcritic -3
Code not contained in explicit package at line 1, column
1. Violates encapsulation. (Severity: 4)
Module does not end with "1;" at line 1, column 12. Must
end with a recognizable true value. (Severity: 4)
Code before warnings are enabled at line 1, column 12. Se
e page 431 of PBP. (Severity: 4)

この指摘の意味はそれぞれ,このコードのパッケージが明示されてない,1;で終わっていない,warningsプラグマを利用していない,という指摘です。これらは,手軽に書きたいワンライナーでは不要な指摘でしょう。次項にて,必要十分な指摘となるようルールを指定する方法を説明します。

注2
エディタがVimであれば,https://github.com/w0rp/aleでチェックできます。
.perlcriticrcで,静的検査のルールを指定する

Perl::Criticでは,~/.perlcriticrcファイルに静的検査の設定を記述します。基本的な設定例は次のとおりです。

severity = 3 ――(1)
theme    = security || bugs ――(2)

[-Subroutines::ProhibitSubroutinePrototypes] ――(3)

(1)で静的検査の厳しさを3に指定しています。(2)で静的検査のグループを指定でき,ここでは,セキュリティに関連するルールとバグを誘発する構文のチェックをしています。(3)では,Subroutines::ProhibitSubroutinePrototypesの先頭に-を添え,この構文ルールを適用しない設定をしています。

静的検査チェックのルール一覧を確認する

適用可能な静的検査のルール一覧はperlcritic --listで取得でき,.perlcriticrcを適用したルール一覧はperlcritic --list-enabledで取得できます。各ルールの概要をつかむのであれば,Perl::Critic::PolicySummaryを読むことをお勧めします。以下,ルールを一部紹介します。

InputOutput::ProhibitTwoArgOpen
open $fh, "< $file"と書くより,open $fh, '<', $fileと書く
Modules::RequireFilenameMatchesPackage
package Foo::Barというパッケージであれば,Bar.pmというファイル名である必要がある
Subroutines::ProhibitExplicitReturnUndef
失敗時の返却は,return undefと書くより,returnと書く
Subroutines::ProhibitReturnSort
スカラコンテキストでsortが呼び出されたときの挙動は定まっていないため禁止
Variables::ProhibitConditionalDeclarations
my $foo = $bar if $baz;という変数宣言を禁止
BuiltinFunctions::ProhibitStringyEval
eval "my $foo; bar($foo);と書くより,eval { my $foo;bar($foo) }と書く

ここまでに,プラグマやPerl::Criticを用いて未然に異常を防ぐ方法を紹介しました。アプリケーションによらない汎用的な解決手段ですので,ぜひ試してください。

<続きの(2)こちら。>

WEB+DB PRESS

本誌最新号をチェック!
WEB+DB PRESS Vol.113

2019年10月24日発売
B5判/160ページ
定価(本体1,480円+税)
ISBN978-4-297-10905-9

  • 特集1
    接続エラー,性能低下,権限エラー,クラウド障害
    AWSトラブル解決
    原因調査・対応・予防のノウハウ
  • 特集2
    Ruby書き方ドリル
    要点解説と例題で身に付く!
  • 特集3
    体験
    ドメイン駆動設計
    モデリングから実装までを一気に制覇
  • 一般記事
    FigmaによるUIデザイン
    デザイナーとエンジニアがオンラインで協業できる!
  • 一般記事
    入門
    SwooleによるPHP非同期処理
    高速化のための並列実行はどのように書くのか

著者プロフィール

小林謙太(こばやしけんた)

東北大学大学院数学専攻修了後,2011年に株式会社モバイルファクトリーに新卒入社。

ソーシャルゲームの開発やプロダクトマネジメントに従事したのち,現在は,育成・採用・広報などのエンジニア組織の課題解決に取り組む。また,Japan Perl Associationのメンバーとして,YAPC::JapanやGotanda.pmなどのコミュニティを中心に活動している。