From db5f00a16ad5423d473623f0f825f8ecef5ba7c3 Mon Sep 17 00:00:00 2001 From: Paul Fultz II Date: Mon, 19 Apr 2021 07:20:29 -0500 Subject: [PATCH] Fix issue 10214: FP: danglingTempReference doesn't account for reference lifetime extension (#3220) * Fix issue 10214: FP: danglingTempReference doesn't account for reference lifetime extension --- lib/checkautovariables.cpp | 15 ++++++++++++++- lib/valueflow.cpp | 6 +++--- test/testautovariables.cpp | 13 +++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 109dcb475..c8a0e2ab6 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -488,6 +488,18 @@ static bool isDanglingSubFunction(const Token* tokvalue, const Token* tok) return exprDependsOnThis(parent); } +static bool isAssignedToNonLocal(const Token* tok) +{ + if (!Token::simpleMatch(tok->astParent(), "=")) + return false; + if (!Token::Match(tok->astParent()->astOperand1(), "%var%")) + return false; + const Variable* var = tok->astParent()->astOperand1()->variable(); + if (!var) + return false; + return !var->isLocal() || var->isStatic(); +} + void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end) { const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive); @@ -546,7 +558,8 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token if (!printInconclusive && val.isInconclusive()) continue; const bool escape = Token::Match(tok->astParent(), "return|throw"); - for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(val.tokvalue), escape)) { + for (const LifetimeToken& lt : + getLifetimeTokens(getParentLifetime(val.tokvalue), escape || isAssignedToNonLocal(tok))) { const Token * tokvalue = lt.token; if (val.isLocalLifetimeValue()) { if (escape) { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 69cb76120..4006a496e 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -2693,10 +2693,10 @@ std::vector getLifetimeTokens(const Token* tok, bool escape, Valu } else if (Token::simpleMatch(var->declEndToken(), "=")) { errorPath.emplace_back(var->declEndToken(), "Assigned to reference."); const Token *vartok = var->declEndToken()->astOperand2(); - const bool temporaryDefault = false; //If we can't tell then assume the value is not temporary as this will result in fewer false positives. - const bool temporary = isTemporary(true, vartok, nullptr, temporaryDefault); + const bool temporary = isTemporary(true, vartok, nullptr, true); const bool nonlocal = var->isStatic() || var->isGlobal(); - if (vartok == tok || (nonlocal && temporary) || (!escape && (var->isConst() || var->isRValueReference()) && temporary)) + if (vartok == tok || (nonlocal && temporary) || + (!escape && (var->isConst() || var->isRValueReference()) && temporary)) return {{tok, true, std::move(errorPath)}}; if (vartok) return getLifetimeTokens(vartok, escape, std::move(errorPath), depth - 1); diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index fc6af6f30..019360380 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -2597,6 +2597,19 @@ private: " return e;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + // #10214 + check("struct A {\n" + " std::string key;\n" + " const char *value;\n" + "};\n" + "const char *f(const std::string &key, const std::vector &lookup) {\n" + " const auto &entry =\n" + " std::find_if(lookup.begin(), lookup.end(),\n" + " [key](const auto &v) { return v.key == key; });\n" + " return (entry == lookup.end()) ? \"\" : entry->value;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void danglingLifetimeFunction() {