From 4fc115df2b831c1f429a86f84a394816a3c672a8 Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Wed, 1 Apr 2026 17:32:00 +0200 Subject: [PATCH 1/2] Handle reverse conditions in isOppositeCond function Signed-off-by: Francois Berder --- lib/astutils.cpp | 63 ++++++++++++++++++++++++++++++++---------- test/testcondition.cpp | 6 ++++ 2 files changed, 55 insertions(+), 14 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index f16144310ac..bb7060b2810 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1810,21 +1810,32 @@ bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Se return commutativeEquals; } -static bool isZeroBoundCond(const Token * const cond) +static bool isZeroBoundCond(const Token * const cond, bool reverse) { if (cond == nullptr) return false; // Assume unsigned - // TODO: Handle reverse conditions - const bool isZero = cond->astOperand2()->getValue(0); - if (cond->str() == "==" || cond->str() == ">=") - return isZero; - if (cond->str() == "<=") - return true; - if (cond->str() == "<") - return !isZero; - if (cond->str() == ">") - return false; + const bool isZero = reverse ? cond->astOperand1()->getValue(0) : cond->astOperand2()->getValue(0); + if (reverse) { + if (cond->str() == "==" || cond->str() == "<=") + return isZero; + if (cond->str() == ">=") + return true; + if (cond->str() == ">") + return !isZero; + if (cond->str() == "<") + return false; + } else { + if (cond->str() == "==" || cond->str() == ">=") + return isZero; + if (cond->str() == "<=") + return true; + if (cond->str() == "<") + return !isZero; + if (cond->str() == ">") + return false; + } + return false; } @@ -1888,7 +1899,7 @@ bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const c if (isSameExpression(true, cond1->astOperand2(), cond2->astOperand2(), settings, pure, followVar, errors)) return isDifferentKnownValues(cond1->astOperand1(), cond2->astOperand1()); } - // TODO: Handle reverse conditions + if (Library::isContainerYield(cond1, Library::Container::Yield::EMPTY, "empty") && Library::isContainerYield(cond2->astOperand1(), Library::Container::Yield::SIZE, "size") && isSameExpression(true, @@ -1898,7 +1909,19 @@ bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const c pure, followVar, errors)) { - return !isZeroBoundCond(cond2); + return !isZeroBoundCond(cond2, false); + } + + if (Library::isContainerYield(cond1, Library::Container::Yield::EMPTY, "empty") && + Library::isContainerYield(cond2->astOperand2(), Library::Container::Yield::SIZE, "size") && + isSameExpression(true, + cond1->astOperand1()->astOperand1(), + cond2->astOperand2()->astOperand1()->astOperand1(), + settings, + pure, + followVar, + errors)) { + return !isZeroBoundCond(cond2, true); } if (Library::isContainerYield(cond2, Library::Container::Yield::EMPTY, "empty") && @@ -1910,7 +1933,19 @@ bool isOppositeCond(bool isNot, const Token * const cond1, const Token * const c pure, followVar, errors)) { - return !isZeroBoundCond(cond1); + return !isZeroBoundCond(cond1, false); + } + + if (Library::isContainerYield(cond2, Library::Container::Yield::EMPTY, "empty") && + Library::isContainerYield(cond1->astOperand2(), Library::Container::Yield::SIZE, "size") && + isSameExpression(true, + cond2->astOperand1()->astOperand1(), + cond1->astOperand2()->astOperand1()->astOperand1(), + settings, + pure, + followVar, + errors)) { + return !isZeroBoundCond(cond1, true); } } diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 29d63562fa3..421fa53e2bc 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -2674,6 +2674,12 @@ class TestCondition : public TestFixture { check("void f1(const std::string &s) { if(s.size() >= 0) if(s.empty()) {}} "); // CheckOther says: Unsigned expression 's.size()' can't be negative so it is unnecessary to test it. [unsignedPositive] ASSERT_EQUALS("", errout_str()); + check("void f1(const std::string &s) { if(42 < s.size()) if(s.empty()) {}}"); + ASSERT_EQUALS("[test.cpp:1:39] -> [test.cpp:1:61]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str()); + + check("void f1(const std::string &s) { if(s.empty()) if(42 < s.size()) {}}"); + ASSERT_EQUALS("[test.cpp:1:43] -> [test.cpp:1:53]: (warning) Opposite inner 'if' condition leads to a dead code block. [oppositeInnerCondition]\n", errout_str()); + // TODO: These are identical condition since size cannot be negative check("void f1(const std::string &s) { if(s.size() <= 0) if(s.empty()) {}}"); ASSERT_EQUALS("", errout_str()); From 37efa2e35e2a773f52adba46f4852a363ac6286f Mon Sep 17 00:00:00 2001 From: Francois Berder Date: Sun, 5 Apr 2026 19:00:50 +0200 Subject: [PATCH 2/2] fixup! Handle reverse conditions in isOppositeCond function --- lib/astutils.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index bb7060b2810..8492591e3ca 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1812,10 +1812,14 @@ bool isSameExpression(bool macro, const Token *tok1, const Token *tok2, const Se static bool isZeroBoundCond(const Token * const cond, bool reverse) { - if (cond == nullptr) + if (cond == nullptr || cond->astOperand1() == nullptr || cond->astOperand2() == nullptr) return false; + + if ((reverse && !cond->astOperand1()->hasKnownIntValue()) || (!reverse && !cond->astOperand2()->hasKnownIntValue())) + return false; + // Assume unsigned - const bool isZero = reverse ? cond->astOperand1()->getValue(0) : cond->astOperand2()->getValue(0); + const bool isZero = reverse ? cond->astOperand1()->getKnownIntValue() == 0 : cond->astOperand2()->getKnownIntValue() == 0; if (reverse) { if (cond->str() == "==" || cond->str() == "<=") return isZero;