diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 0aa9dd18a84..3b2a27c68fa 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 isAssignmentOrInit(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; - - // 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; - } - - const Token* rhs = tok->astOperand2(); - // Do not warn about assignment with 0 / NULL - if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) - continue; + if (!isAssignmentOrInit(tok)) + continue; - if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) - // todo: check references + // 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; + } - if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) - // todo: check static variables - continue; + const Token* rhs = tok->astOperand2(); + // Do not warn about assignment with 0 / NULL + if ((rhs && MathLib::isNullValue(rhs->str())) || isNullOperand(rhs)) + 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; - } - if (inconclusive && !mSettings.certainty.isEnabled(Certainty::inconclusive)) - continue; + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isReference()) + // todo: check references + continue; - FwdAnalysis fwdAnalysis(mSettings); - if (fwdAnalysis.hasOperand(tok->astOperand2(), tok->astOperand1())) - continue; + if (tok->astOperand1()->variable() && tok->astOperand1()->variable()->isStatic()) + // todo: check static variables + continue; - // Is there a redundant assignment? - const Token *start; - if (tok->isAssignmentOp()) - start = tok->astOperand2(); - else - start = tok->findExpressionStartEndTokens().second->next(); + 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; + } + 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); } } } diff --git a/test/testother.cpp b/test/testother.cpp index 27f7700bd54..64dcea0a163 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -11461,7 +11461,32 @@ 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