Cppcheck は静的解析ツールですが、指摘のルールを作成することができます。指摘ルールにマッチしたソースコードは、指摘ルールによって指定されていた文言が表示されるようになります。このルールは、正規表現で定義するのですが、実は、Cppcheck がソースコードを一部処理してから正規表現でチェックするしくみになっています。
そのため、処理後のソースコードを作成してから正規表現を作成しないとうまくマッチしないのです。
そのために指摘ルールを作成するには次のようにします。
1. 指摘したいソースコードを作成して処理済みコードを生成する
2. 処理コードから(PCREの)正規表現を作成してcppcheckでマッチするか試す
3. xml形式でルールを指定する。
ソースコードから処理済みコード生成
freeはほとんどの実装で、NULLポインタをわたしても問題はないのでこのような表記を見つけたいとします。
指摘したいコードを適当な関数fに包んだソースコードを作成します。
void f(){ if (p) free(p); }
このファイルを dealloc.cpp として保存し、それから、cppcheck --rule=".+" dealloc.cpp を実行します。
cppcheck --rule=".+" dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:1]: (style) found ' void f ( ) { if ( p ) { free ( p ) ; } }'
このルールは、".+" はどのようなものにもマッチする正規表現のため、マッチした文字列が標準出力に出力されます。
この出力から、単純化したソースコードがわかります。これです。
void f ( ) { if ( p ) { free ( p ) ; } }
正規表現を作成して試す
ではそのまま正規表現にしてみましょう
cppcheck --rule="if \( p \) { free \( p \) ; }" dealloc.cpp
マッチしました。
Checking dealloc.cpp...
[dealloc.cpp:1]: (style) found 'if ( p ) { free ( p ) ; }'
さて、ポインタの名前はいつでもpでしょうか?違いますね。ここで正規表現の後方参照を使って対応してみましょう。
ポインタの名前を空白ではない文字列と仮定します。これは"\S+"でいいでしょう。そうすると、後方参照を "\1" 前方の指定正規表現を"()"で包むので
正規表現を"if \( (\S+) \) { free \( \1 \) ; }"にしましょう。
では正規表現を試しましょう。
cppcheck --rule="if \( (\S+) \) { free \( \1 \) ; }" dealloc.cpp
マッチしましたね。
Checking dealloc.cpp...
[dealloc.cpp:1]: (style) found 'if ( p ) { free ( p ) ; }'
XMLを作成する
XMLを作成します。
<?xml version="1.0"?> <rule version="1"> <pattern>if \( (\S+) \) { free \( \1 \) ; }</pattern> <message> <id>nullPointerIsFreeAble</id> <severity>style</severity> <summary>Redundant condition. It is valid to free a NULL pointer.</summary> </message> </rule>
patternに正規表現をいれます。
idには適当な英単語を連結して文字列を作成しましょう。
ヌルポインタは解放しても大丈夫ということでnullPointerIsFreeAbleにしました。
severityはerror,warning,styleなどから選びます。こんなかんじ。
severity | 意味 |
---|---|
error | バグがみつかった |
warning | 防衛的プログラミングによる提案 |
style | コードを清潔に保つ上でのスタイル上の指摘 |
performance | コードの高速化のための提案(一般的なものに限る) |
portability | 移植時に問題が発生しないようにするための指摘 |
information | 設定上の問題。設定変更時にのみ有効にする |
summaryの文字列は指摘の際に表示される文字列。
さて作成したxmlをdealloc.rule として保存して、チェックします。
cppcheck --rule-file=dealloc.rule dealloc.cpp
Checking dealloc.cpp...
[dealloc.cpp:2]: (style) Redundant condition. It is valid to free a NULL pointer.