@@ -2988,10 +2988,9 @@ class TestNullPointer : public TestFixture {
29882988 errout_str ());
29892989 }
29902990
2991- void nullpointer107 () // #13682 - don't flow past a condition that doesn't reference the pointer
2991+ void nullpointer107 () // #13682 - guards that depend on the pointer indirectly
29922992 {
2993- // The pointer's null-ness is cached in a boolean; the guard 'if (!ok)' makes the deref safe but
2994- // cppcheck cannot follow that correlation during forward analysis -> must not warn (no false positive)
2993+ // cached null-check 'ok'; guard 'if (!ok)' is safe -> no FP
29952994 check (" struct S { void g(); bool f() const; };\n "
29962995 " void f(S* p) {\n "
29972996 " bool ok = (p != nullptr);\n "
@@ -3003,8 +3002,7 @@ class TestNullPointer : public TestFixture {
30033002 " }\n " );
30043003 ASSERT_EQUALS (" " , errout_str ());
30053004
3006- // A guard on an unrelated boolean (that the caller may use to imply validity) is not something
3007- // cppcheck can follow -> stay conservative and do not warn
3005+ // unrelated bool guard -> conservative, no FP
30083006 check (" struct S { void g(); bool f() const; };\n "
30093007 " void f(S* p, bool valid) {\n "
30103008 " S* p1 = p;\n "
@@ -3016,7 +3014,7 @@ class TestNullPointer : public TestFixture {
30163014 " }\n " );
30173015 ASSERT_EQUALS (" " , errout_str ());
30183016
3019- // A guard on a different pointer must not be assumed to constrain this one
3017+ // guard on a different pointer -> no FP
30203018 check (" struct S { void g(); bool f() const; };\n "
30213019 " void f(S* p, S* q) {\n "
30223020 " S* p1 = p;\n "
@@ -3028,7 +3026,7 @@ class TestNullPointer : public TestFixture {
30283026 " }\n " );
30293027 ASSERT_EQUALS (" " , errout_str ());
30303028
3031- // A direct null guard on the pointer (or its alias) is still handled and must not warn
3029+ // direct null guard on the alias -> no FP
30323030 check (" struct S { void g(); bool f() const; };\n "
30333031 " void f(S* p) {\n "
30343032 " S* p1 = p;\n "
@@ -3040,10 +3038,7 @@ class TestNullPointer : public TestFixture {
30403038 " }\n " );
30413039 ASSERT_EQUALS (" " , errout_str ());
30423040
3043- // 'if (ok) return;' means the surviving path has ok==false, i.e. p==nullptr, so 'p->g()'
3044- // dereferences a null pointer. ProgramMemory cannot evaluate the cached 'ok' (== (p != nullptr))
3045- // during forward analysis, so the conditionReferencesValue() guard stops the analysis here and the
3046- // definite null dereference is missed. This should warn once ProgramMemory can follow 'ok'.
3041+ // FN: 'if (ok)' => survivor has p==nullptr, but the cached 'ok' is not followed -> should warn
30473042 check (" struct S { void g(); bool f() const; };\n "
30483043 " void f(S* p) {\n "
30493044 " bool ok = (p != nullptr);\n "
@@ -3058,10 +3053,7 @@ class TestNullPointer : public TestFixture {
30583053 " " ,
30593054 errout_str ());
30603055
3061- // 'q' aliases 'p' (symbolic q==p), so the guard 'if (q)' constrains 'p'. Modifying 'q' via sink()
3062- // drops the symbolic relationship; conditionReferencesValue() only inspects symbolic values, so it
3063- // no longer sees that the guard relates to 'p', stops the analysis, and the possible null
3064- // dereference of 'p' is missed. A more robust dependency check (not symbolic-only) should warn.
3056+ // FN: sink(q) drops the q==p symbolic, so guard 'if (q)' is no longer seen to relate to p -> should warn
30653057 check (" struct S { void g(); bool f() const; };\n "
30663058 " void sink(S*&);\n "
30673059 " void f(S* p) {\n "
@@ -3078,13 +3070,8 @@ class TestNullPointer : public TestFixture {
30783070 " " ,
30793071 errout_str ());
30803072
3081- // These are dereferences that are actually safe, but the dependency that makes them safe is hidden
3082- // behind a conditional modification. ProgramMemory tracks 'q'/'ok' and would normally evaluate the
3083- // guard, but a conditional modification ('if (c) ...') makes it stop tracking the value, so the guard
3084- // can no longer be evaluated during forward analysis. Without conditionReferencesValue() the possible
3085- // null value would flow past the guard and produce a false positive; these must stay warning-free.
3086-
3087- // symbolic substitution: 'q == p', conditionally re-assigned to 'p' (value preserved)
3073+ // a conditional modification makes ProgramMemory drop the guard (FP-prone) -> must stay quiet:
3074+ // alias 'q==p' re-assigned to p under a condition
30883075 check (" struct S { void g(); bool f() const; };\n "
30893076 " void f(S* p, bool c) {\n "
30903077 " S* q = p;\n "
@@ -3098,7 +3085,7 @@ class TestNullPointer : public TestFixture {
30983085 " }\n " );
30993086 ASSERT_EQUALS (" " , errout_str ());
31003087
3101- // conditional assignment of a cached condition: 'ok == (p != nullptr)', conditionally refreshed
3088+ // cached 'ok' refreshed under a condition
31023089 check (" struct S { void g(); bool f() const; };\n "
31033090 " void f(S* p, bool c) {\n "
31043091 " bool ok = (p != nullptr);\n "
0 commit comments