From cdd6b4df9ff7dd62ab534116515951efb2ca89cf Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:41:10 +0200 Subject: [PATCH 1/2] Update checkother.cpp --- lib/checkother.cpp | 87 +++++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 40 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 0aa9dd18a84..647c9113568 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -682,53 +682,60 @@ void CheckOtherImpl::checkRedundantAssignment() else start = tok->findExpressionStartEndTokens().second->next(); - const Token * tokenToCheck = tok->astOperand1(); + std::vector tokensToCheck{ tok->astOperand1() }; + if (Token::simpleMatch(tok->astOperand1(), "[") && Token::simpleMatch(tok->astOperand1()->astOperand1(), "auto")) // structured binding + tokensToCheck = astFlatten(tok->astOperand1()->astOperand2(), ","); - // 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; - } + for (const Token* tokenToCheck : tokensToCheck) { - 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()); - } + // 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; + } - // 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 (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 (!nextAssign) - continue; + // Get next assignment.. + const Token* nextAssign = fwdAnalysis.reassign(tokenToCheck, start, scope->bodyEnd); + // extra check for union + const bool isUnion = tokenToCheck != tok->astOperand1() && !isStructuredBindingVariable(tokenToCheck->variable()); + if (nextAssign && isUnion) { + 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; + } - // 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; + 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; + } } - } - // 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 + const Token* exprTok = isUnion ? tok->astOperand1() : tokenToCheck; + if (hasCase) + redundantAssignmentInSwitchError(tok, nextAssign, exprTok->expressionString()); + else if (isInitialization) + redundantInitializationError(tok, nextAssign, exprTok->expressionString(), inconclusive); + else { + diag(nextAssign); + redundantAssignmentError(tok, nextAssign, exprTok->expressionString(), inconclusive); + } } } } From bb44bf423f0ba398678244813674cc9a5a9b3b32 Mon Sep 17 00:00:00 2001 From: chrchr-github <78114321+chrchr-github@users.noreply.github.com> Date: Fri, 11 Sep 2026 11:42:22 +0200 Subject: [PATCH 2/2] Update testother.cpp --- test/testother.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 27f7700bd54..6cf4e2c3b43 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11462,6 +11462,30 @@ class TestOther : public TestFixture { " return i;\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + check("struct S { int a, b; };\n" // #15021 + "int f(S s) {\n" + " auto [x, y] = s;\n" + " x = 1;\n" + " y = 2;\n" + " return x + y;\n" + "}\n" + "struct T { int c; };\n" + "int g(T t) {\n" + " auto [z] = t;\n" + " z = 0;\n" + " return z;\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:4:7]: style: Variable 'x' is reassigned a value before the old one has been used. [redundantAssignment]\n" + "[test.cpp:3:17]: note: x is assigned\n" + "[test.cpp:4:7]: note: x is overwritten\n" + "[test.cpp:5:7]: style: Variable 'y' is reassigned a value before the old one has been used. [redundantAssignment]\n" + "[test.cpp:3:17]: note: y is assigned\n" + "[test.cpp:5:7]: note: y is overwritten\n" + "[test.cpp:11:7]: style: Variable 'z' is reassigned a value before the old one has been used. [redundantAssignment]\n" + "[test.cpp:10:14]: note: z is assigned\n" + "[test.cpp:11:7]: note: z is overwritten\n", + errout_str()); } // cppcheck-suppress unusedPrivateFunction