警告 C26430
シンボルは null 性に関してすべてのパスでテストされていません。
C++ Core Guidelines: F.23: not_null<T> を使用して、"null" が有効な値ではないことを示します
コードがどこかで null 値に関してポインター変数をチェックしている場合、一貫してそれを行いすべてのパスでポインターを検証する必要があります。 過剰な null 値のチェックの方が、複雑な分岐の 1 つでハード クラッシュが発生する可能性よりは良い場合が多々あります。 理想的には、そのようなコードはより単純にして、gsl::not_null
などのマーカーを利用するようにリファクタリングする必要があります。 これらのマーカーによって、コードは有効なポインター値に関する安全な推論を行うことができるアルゴリズムの一部分を分離できます。 ルール TEST_ON_ALL_PATHS
は、null チェックに一貫性がない (つまり推論にレビューが必要かもしれない) 場所を見つけるのに役立ちます。 または、これによって null 値が一部のコード パスで null チェックをバイパスする可能性がある実際のバグが検出されます。
解説
このルールは、コードがポインター変数を逆参照しており、null チェック (または null 以外の値の強制) に意味があることを想定しています。 逆参照がない場合、このルールは一時停止されます。
現在の実装で扱えるのはプレーン ポインター (またはその別名) だけであり、スマート ポインターは検出されません。ただし null チェックはスマート ポインターにも適用可能です。
変数が以下のコンテキストで使用される場合、その変数は null チェック済みとマークされます。
- 分岐条件 (たとえば、
if (p) { ... }
) 内のシンボル式として。 - ビットごとではない論理演算内。
- 1 つのオペランドが 0 に評価される定数式である比較演算内。
ポインター値が以下のものから割り当てられる場合は、暗黙的な null チェックが想定されます。
operator new
をスローして実行される割り当て。gsl::not_null
でマークされた型から取得されたポインター。
例
不整合テストにより論理エラーが明らかになる
void merge_states(const state *left, const state *right) // C26430
{
if (*left && *right)
converge(left, right);
else
{
// ...
if (!left && !right) // Logic error!
discard(left, right);
}
}
不整合テストにより論理エラーが明らかになる - 修正後
void merge_states(gsl::not_null<const state *> left, gsl::not_null<const state *> right)
{
if (*left && *right)
converge(left, right);
else
{
// ...
if (*left && *right)
discard(left, right);
}
}
ヒューリスティック
ポインターの逆参照が null 値でないことを保証するにあたって、このルールは "すべて" の逆参照に事前の null チェックが存在することは要求しません。 そうではなく、ポインターの "最初の" 逆参照の前の null チェックを要求します。 次の関数は C26430 をトリガーしません。
void f(int* p)
{
if (p)
*p = 1;
*p = 2;
}
次の関数は、null チェックなしで *p
を割り当てるパスが存在するため、C26430 を生成します。
void f(bool b, int* p)
{
if (b && p)
*p = 1;
*p = 2;
}
ルール C26822 と C26823 は null (の可能性がある) ポインターの逆参照に適用されます。
このルールでは、データ フローの完全な追跡は行われません。 間接チェックが使用されている場合 (中間変数が null 値を保持し、後の比較で使用される場合など) には、不適切な結果を生み出す可能性があります。