From fe83217c3d5f72a170ed51682d9aae393658cf3d Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:26:52 +0200 Subject: [PATCH 1/5] Update checkother.cpp --- lib/checkother.cpp | 207 +++++++++++++++++++++++---------------------- 1 file changed, 106 insertions(+), 101 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 0aa9dd18a84..901fd096e7d 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -594,6 +594,14 @@ void CheckOtherImpl::invalidPointerCastError(const Token* tok, const std::string // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- +static bool isAssignment(const Token* tok) { + if (tok->astParent() || !tok->astOperand1()) + return false; + if (tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) + return true; + return Token::Match(tok, "[{(]") && tok->astOperand1()->variable() && tok->astOperand1() == tok->astOperand1()->variable()->nameToken(); +} + void CheckOtherImpl::checkRedundantAssignment() { if (!mSettings.severity.isEnabled(Severity::style) && @@ -614,122 +622,119 @@ void CheckOtherImpl::checkRedundantAssignment() if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); - if ((tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) && tok->astOperand1()) { - if (tok->astParent()) - continue; + if (!isAssignment(tok)) + continue; - // Do not warn about redundant initialization when rhs is trivial - // TODO : do not simplify the variable declarations - bool isInitialization = false; - if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) { - isInitialization = true; - bool trivial = true; - visitAstNodes(tok->astOperand2(), - [&](const Token *rhs) { - if (Token::simpleMatch(rhs, "{ 0 }")) - return ChildrenToVisit::none; - if (Token::Match(rhs, "%num%|%name%") && !rhs->varId()) - return ChildrenToVisit::none; - if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue()) - return ChildrenToVisit::none; - if (rhs->isCast()) - return ChildrenToVisit::op2; - trivial = false; - return ChildrenToVisit::done; - }); - if (trivial) - continue; + // Do not warn about redundant initialization when rhs is trivial + // TODO : do not simplify the variable declarations + bool isInitialization = false; + if ((Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) || Token::Match(tok, "[{(]")) { + isInitialization = true; + bool trivial = true; + visitAstNodes(tok->astOperand2(), + [&](const Token *rhs) { + if (Token::simpleMatch(rhs, "{ 0 }")) + return ChildrenToVisit::none; + if (Token::Match(rhs, "%num%|%name%") && !rhs->varId()) + return ChildrenToVisit::none; + if (Token::Match(rhs, ":: %name%") && rhs->hasKnownIntValue()) + return ChildrenToVisit::none; + if (rhs->isCast()) + return ChildrenToVisit::op2; + trivial = false; + return ChildrenToVisit::done; + }); + if (trivial) + continue; } - const Token* rhs = tok->astOperand2(); - // Do not warn about assignment with 0 / NULL - if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) - continue; + const Token* rhs = tok->astOperand2(); + // Do not warn about assignment with 0 / NULL + if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) + continue; - if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) - // todo: check references - continue; + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) + // todo: check references + continue; - if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) - // todo: check static variables - continue; + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) + // todo: check static variables + continue; - bool inconclusive = false; - if (tok->isCpp() && tok->astOperand1()->valueType()) { - // If there is a custom assignment operator => this is inconclusive - if (tok->astOperand1()->valueType()->typeScope) { - const std::string op = "operator" + tok->str(); - const std::list& fList = tok->astOperand1()->valueType()->typeScope->functionList; - inconclusive = std::any_of(fList.cbegin(), fList.cend(), [&](const Function& f) { - return f.name() == op; - }); - } - // assigning a smart pointer has side effects - if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER) - break; + bool inconclusive = false; + if (tok->isCpp() && tok->astOperand1()->valueType()) { + // If there is a custom assignment operator => this is inconclusive + if (tok->astOperand1()->valueType()->typeScope) { + const std::string op = "operator" + tok->str(); + const std::list& fList = tok->astOperand1()->valueType()->typeScope->functionList; + inconclusive = std::any_of(fList.cbegin(), fList.cend(), [&](const Function& f) { + return f.name() == op; + }); } - if (inconclusive && !mSettings.certainty.isEnabled(Certainty::inconclusive)) - continue; - - FwdAnalysis fwdAnalysis(mSettings); - if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) - continue; - - // Is there a redundant assignment? - const Token *start; - if (tok->isAssignmentOp()) - start = tok->astOperand2(); - else - start = tok->findExpressionStartEndTokens().second->next(); + // assigning a smart pointer has side effects + if (tok->astOperand1()->valueType()->type == ValueType::SMART_POINTER) + break; + } + if (inconclusive && !mSettings.certainty.isEnabled(Certainty::inconclusive)) + continue; - const Token * tokenToCheck = tok->astOperand1(); + FwdAnalysis fwdAnalysis(mSettings); + if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) + continue; - // Check if we are working with union - for (const Token* tempToken = tokenToCheck; Token::simpleMatch(tempToken, ".");) { - tempToken = tempToken->astOperand1(); - if (tempToken && tempToken->variable() && tempToken->variable()->type() && tempToken->variable()->type()->isUnionType()) - tokenToCheck = tempToken; - } + // Is there a redundant assignment? + const Token *start; + if (tok->isAssignmentOp()) + start = tok->astOperand2(); + else + start = tok->findExpressionStartEndTokens().second->next(); + const Token * tokenToCheck = tok->astOperand1(); + + // Check if we are working with union + for (const Token* tempToken = tokenToCheck; Token::simpleMatch(tempToken, ".");) { + tempToken = tempToken->astOperand1(); + if (tempToken && tempToken->variable() && tempToken->variable()->type() && tempToken->variable()->type()->isUnionType()) + tokenToCheck = tempToken; + } - if (start->hasKnownSymbolicValue(tokenToCheck) && Token::simpleMatch(start->astParent(), "=") && !diag(tok)) { - const ValueFlow::Value* val = start->getKnownValue(ValueFlow::Value::ValueType::SYMBOLIC); - if (val->intvalue == 0) // no offset - redundantAssignmentSameValueError(tokenToCheck, val, tok->astOperand1()->expressionString()); - } + if (start->hasKnownSymbolicValue(tokenToCheck) && Token::simpleMatch(start->astParent(), "=") && !diag(tok)) { + const ValueFlow::Value* val = start->getKnownValue(ValueFlow::Value::ValueType::SYMBOLIC); + if (val->intvalue == 0) // no offset + redundantAssignmentSameValueError(tokenToCheck, val, tok->astOperand1()->expressionString()); + } - // Get next assignment.. - const Token *nextAssign = fwdAnalysis.reassign(tokenToCheck, start, scope->bodyEnd); - // extra check for union - if (nextAssign && tokenToCheck != tok->astOperand1()) { - nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); - // reading another member of the same union in the rhs is a use through aliasing - if (nextAssign && fwdAnalysis.hasOperand(nextAssign->astOperand2(), tokenToCheck)) - nextAssign = nullptr; - } + // Get next assignment.. + const Token *nextAssign = fwdAnalysis.reassign(tokenToCheck, start, scope->bodyEnd); + // extra check for union + if (nextAssign && tokenToCheck != tok->astOperand1()) { + nextAssign = fwdAnalysis.reassign(tok->astOperand1(), start, scope->bodyEnd); + // reading another member of the same union in the rhs is a use through aliasing + if (nextAssign && fwdAnalysis.hasOperand(nextAssign->astOperand2(), tokenToCheck)) + nextAssign = nullptr; + } - if (!nextAssign) - continue; + if (!nextAssign) + continue; - // there is redundant assignment. Is there a case between the assignments? - bool hasCase = false; - for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { - if (tok2->str() == "break" || tok2->str() == "return") - break; - if (tok2->str() == "case") { - hasCase = true; - break; - } + // there is redundant assignment. Is there a case between the assignments? + bool hasCase = false; + for (const Token *tok2 = tok; tok2 != nextAssign; tok2 = tok2->next()) { + if (tok2->str() == "break" || tok2->str() == "return") + break; + if (tok2->str() == "case") { + hasCase = true; + break; } + } - // warn - if (hasCase) - redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); - else if (isInitialization) - redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); - else { - diag(nextAssign); - redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); - } + // warn + if (hasCase) + redundantAssignmentInSwitchError(tok, nextAssign, tok->astOperand1()->expressionString()); + else if (isInitialization) + redundantInitializationError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); + else { + diag(nextAssign); + redundantAssignmentError(tok, nextAssign, tok->astOperand1()->expressionString(), inconclusive); } } } From 2a86e5f1afd2f055a3ce85b7fda4291d462e6f1b Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:28:08 +0200 Subject: [PATCH 2/5] Update testother.cpp --- test/testother.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 27f7700bd54..e5937abd1bc 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11461,7 +11461,31 @@ class TestOther : public TestFixture { " i = std::distance(v.begin(), std::find_if(v.begin(), v.end(), [=](int j) { return i == j; }));\n" " return i;\n" "}\n"); + ASSERT_EQUALS("", errout_str()); + check("int g();\n" // #15023 + "int f1() {\n" + " int i{ g() };\n" + " i = 0;\n" + " return i;\n" + "}\n" + "int f2() {\n" + " int i(g());\n" + " i = 0;\n" + " return i;\n" + "}\n" + "int f3() {\n" + " int i{ 1 };\n" + " i = 0;\n" + " return i;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4:7]: style: Redundant initialization for 'i'. The initialized value is overwritten before it is read. [redundantInitialization]\n" + "[test.cpp:3:10]: note: i is initialized\n" + "[test.cpp:4:7]: note: i is overwritten\n" + "[test.cpp:9:7]: style: Redundant initialization for 'i'. The initialized value is overwritten before it is read. [redundantInitialization]\n" + "[test.cpp:8:10]: note: i is initialized\n" + "[test.cpp:9:7]: note: i is overwritten\n", + errout_str()); } // cppcheck-suppress unusedPrivateFunction From 8234674d4216c81764f8c1866df99b72c4b02208 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:43:57 +0200 Subject: [PATCH 3/5] Update testother.cpp [skip ci] --- test/testother.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/testother.cpp b/test/testother.cpp index e5937abd1bc..64dcea0a163 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11463,7 +11463,8 @@ class TestOther : public TestFixture { "}\n"); ASSERT_EQUALS("", errout_str()); - check("int g();\n" // #15023 + + check("int g();\n" // #15023 "int f1() {\n" " int i{ g() };\n" " i = 0;\n" From 503061140c82dc9e177e34f477e9149fb173a88e Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:46:32 +0200 Subject: [PATCH 4/5] Update checkother.cpp --- lib/checkother.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 901fd096e7d..d4016eeeb7b 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -599,7 +599,7 @@ static bool isAssignment(const Token* tok) { return false; if (tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) return true; - return Token::Match(tok, "[{(]") && tok->astOperand1()->variable() && tok->astOperand1() == tok->astOperand1()->variable()->nameToken(); + return Token::Match(tok, "[{(]") && tok->astOperand1()->variable() && tok->astOperand1() == tok->astOperand1()->variable()->nameToken(); } void CheckOtherImpl::checkRedundantAssignment() @@ -646,7 +646,7 @@ void CheckOtherImpl::checkRedundantAssignment() }); if (trivial) continue; - } + } const Token* rhs = tok->astOperand2(); // Do not warn about assignment with 0 / NULL From bb8d29c73791555789331384850d274626b153e0 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 12:49:09 +0200 Subject: [PATCH 5/5] Update checkother.cpp --- lib/checkother.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index d4016eeeb7b..3b2a27c68fa 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -594,7 +594,7 @@ void CheckOtherImpl::invalidPointerCastError(const Token* tok, const std::string // Detect redundant assignments: x = 0; x = 4; //--------------------------------------------------------------------------- -static bool isAssignment(const Token* tok) { +static bool isAssignmentOrInit(const Token* tok) { if (tok->astParent() || !tok->astOperand1()) return false; if (tok->isAssignmentOp() || tok->tokType() == Token::eIncDecOp) @@ -622,7 +622,7 @@ void CheckOtherImpl::checkRedundantAssignment() if (Token::simpleMatch(tok, "try {")) // todo: check try blocks tok = tok->linkAt(1); - if (!isAssignment(tok)) + if (!isAssignmentOrInit(tok)) continue; // Do not warn about redundant initialization when rhs is trivial