From fdd0dae44f65e01ead0315b4115b64bc5937460e Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 6 Jul 2026 15:40:15 -0500 Subject: [PATCH 1/2] Handle conditional escape --- lib/forwardanalyzer.cpp | 5 +++++ test/testcondition.cpp | 31 +++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/lib/forwardanalyzer.cpp b/lib/forwardanalyzer.cpp index 727c64b076d..ced1cfbda8d 100644 --- a/lib/forwardanalyzer.cpp +++ b/lib/forwardanalyzer.cpp @@ -395,6 +395,11 @@ namespace { bool structuralUnknown = false; const bool structuralEscape = isEscapeScope(branch.endBlock, structuralUnknown); branch.escapeUnknown = !structuralEscape || structuralUnknown; + // The traversal stopped at the escape, so the rest of the scope was not walked; a + // fall-through path could still modify the value there - include the whole scope's + // actions so isModified() sees it. + if (branch.escapeUnknown) + branch.action |= analyzeScope(branch.endBlock); } else { // Detect an escape the traversal did not flag (e.g. an unknown noreturn call); // escapeUnknown reports a possible (unknown) escape. diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 837cd4e78ba..d9d6e7927dd 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -4935,6 +4935,37 @@ class TestCondition : public TestFixture { " if (v > 0) {}\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" // a modification after a conditional escape must still be seen + "int main() {\n" + " bool datumAdded = false;\n" + " if (f()) {\n" + " if (f()) return 1;\n" + " if (f()) datumAdded = true;\n" + " }\n" + " if (datumAdded) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" + "int main() {\n" + " bool b = false;\n" + " if (f()) {\n" + " if (f()) return 1;\n" + " b = true;\n" + " }\n" + " if (b) {}\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("bool f();\n" // the branch always escapes - keep the known value + "int main() {\n" + " bool b = false;\n" + " if (f()) { b = true; return 1; }\n" + " if (b) {}\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:5:9]: (style) Condition 'b' is always false [knownConditionTrueFalse]\n", + errout_str()); } void alwaysTrueSymbolic() From 3a7412b7eebddd07ed01b949935247d8f204aae5 Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 6 Jul 2026 16:03:54 -0500 Subject: [PATCH 2/2] Move to valueflow --- test/testcondition.cpp | 31 ------------------------------- test/testvalueflow.cpp | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 31 deletions(-) diff --git a/test/testcondition.cpp b/test/testcondition.cpp index d9d6e7927dd..837cd4e78ba 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -4935,37 +4935,6 @@ class TestCondition : public TestFixture { " if (v > 0) {}\n" "}\n"); ASSERT_EQUALS("", errout_str()); - - check("bool f();\n" // a modification after a conditional escape must still be seen - "int main() {\n" - " bool datumAdded = false;\n" - " if (f()) {\n" - " if (f()) return 1;\n" - " if (f()) datumAdded = true;\n" - " }\n" - " if (datumAdded) {}\n" - "}\n"); - ASSERT_EQUALS("", errout_str()); - - check("bool f();\n" - "int main() {\n" - " bool b = false;\n" - " if (f()) {\n" - " if (f()) return 1;\n" - " b = true;\n" - " }\n" - " if (b) {}\n" - "}\n"); - ASSERT_EQUALS("", errout_str()); - - check("bool f();\n" // the branch always escapes - keep the known value - "int main() {\n" - " bool b = false;\n" - " if (f()) { b = true; return 1; }\n" - " if (b) {}\n" - "}\n"); - ASSERT_EQUALS("[test.cpp:5:9]: (style) Condition 'b' is always false [knownConditionTrueFalse]\n", - errout_str()); } void alwaysTrueSymbolic() diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 888710ce57d..8a0d181900d 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -3214,6 +3214,38 @@ class TestValueFlow : public TestFixture { " return x;\n" "}\n"; ASSERT_EQUALS(true, testValueOfXKnown(code, 3U, 0)); + + code = "bool f();\n" // a modification after a conditional escape must still be seen + "void g() {\n" + " bool x = false;\n" + " if (f()) {\n" + " if (f()) return;\n" + " if (f()) x = true;\n" + " }\n" + " if (x) {}\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 8U, 0)); + ASSERT_EQUALS(false, testValueOfXKnown(code, 8U, 0)); + + code = "bool f();\n" + "void g() {\n" + " bool x = false;\n" + " if (f()) {\n" + " if (f()) return;\n" + " x = true;\n" + " }\n" + " if (x) {}\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfX(code, 8U, 0)); + ASSERT_EQUALS(false, testValueOfXKnown(code, 8U, 0)); + + code = "bool f();\n" // the branch always escapes - keep the known value + "void g() {\n" + " bool x = false;\n" + " if (f()) { x = true; return; }\n" + " if (x) {}\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXKnown(code, 5U, 0)); } void valueFlowAfterSwap()