ClangのUndefinedAssignmentCheckerを試してみます。
1. Checkerの実行方法
$ clang -cc1 -analyzer-checker=core.uninitialized.Assign ¥
-analyze <target>.c
2. 機能
UndefinedAssignmentCheckerのヘッダ部分のコメントによれば、未定義の値を 代入していることを確認するCheckerのようです。
3. UndefinedAssignmentCheckerの実装
checkBindで代入式を補足しています。isUndefで未定義の値を判定しています。
void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, const Stmt *StoreE, CheckerContext &C) const { if (!val.isUndef()) return; <snip> }
3.1. isUndef
SValのgetRawKind()がUndefinedKindの場合にisUndefがtrueになります。
inline bool isUndef() const { return getRawKind() == UndefinedKind; }
4. checkBindとcheckLocationの違い
checkBindのコメントに"Called on binding of a value to a location" と記載されております。
valueとlocationの違いが難しい。valueは1等の変数定義を持たない値に対し、 locationはint a等の変数定義を持つものと私は理解しております。 checkBindではvalueとlocationを扱うのに対し、checkLocationではlocation しか扱わないようです。
4.1. checkBindとcheckLocationを補足するChecker
以下のようなCheckerを使用して、checkBindとcheckLocationに渡されるSVal のisUndefがどうなるかを出力してみます。
void BindChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { debug_stmt(S, C.getSourceManager()); llvm::errs() << "Loc.isUndef() = " << Loc.isUndef() << "\n"; llvm::errs() << "Val.isUndef() = " << Val.isUndef() << "\n"; } void LocationChecker::checkLocation(SVal Loc, bool IsLoad, const Stmt *S, CheckerContext &C) const { debug_stmt(S, C.getSourceManager()); llvm::errs() << "Loc.isUndef() = " << Loc.isUndef() << "\n"; }
4.2. 対象コード
初期化されていないrhsをlhsに代入します。
$ cat uninit.c #include <stdio.h> int main(void) { int lhs, rhs; lhs = rhs; return 0; }
4.3. 実行結果
checkBindの実行結果は以下の通りです。BinaryOperatorを補足していますね。 int a = 1という初期化処理のBinaryOperatorも補足するようです。 左辺のlocationに右辺のvalueを結びつけているということですね。 Loc.isUndef()はfalseになるのに対し、Val.isUndef()はtrueとなっておりま す。lhsという変数自体は未定義ではなく、rhsの中身が未定義ということだと 思います。
BinaryOperator lhs = rhs; ~~~~~~~ Loc.isUndef() = 0 Val.isUndef() = 1
checkLocationの実行結果は以下の通りです。BinaryOperatorの右辺と左辺を 補足しています。int a = 1という初期化処理は補足しないようです。 こちらはあくまでlocationに特化してます。isLoadの値でStoreなのかLoadな のかを判別できます。
Loc.isUndef()はfalseとなります。
$ ./LocationChecker.sh uninit.c ImplicitCastExpr lhs = rhs; ~ Loc.isUndef() = 0 DeclRefExpr lhs = rhs; ~ Loc.isUndef() = 0
まとめると、未初期化の変数を補足するにはvalueを補足できるcheckBindでの み可能ということのようです。
4. UndefinedAssignmentCheckerの実行結果
先ほどのuninit.cに対してUndefinedAssignmentCheckerを実行します。
uninit.c:6:7: warning: Assigned value is garbage or undefined lhs = rhs; ^ ~~~ 1 warning generated.
5. まとめ
checkBindとcheckLocationの違いはcheckBindがlocationとvalueを補足するの に対し、checkLocationはlocationのみ補足するところです。 未定義の値の代入と聞くと、未定義の関数や変数を代入しているととらえられ ますが、そもそもCheckerが動く前にコンパイルエラーになる為、本Checkerは あくまで初期化していない変数の代入のwarningに留まるようです。