Fix issue 9835: False negative: Return reference to temporary with const reference

This commit is contained in:
Paul 2020-09-08 18:30:45 -05:00
parent 18e99176e5
commit 8d7088aa24
5 changed files with 47 additions and 3 deletions

View File

@ -248,6 +248,9 @@ bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknow
if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1()))
return false;
if (Token::Match(tok->previous(), ">|%name% (")) {
if (tok->valueType()) {
return tok->valueType()->reference == Reference::None;
}
const Token* ftok = nullptr;
if (tok->previous()->link())
ftok = tok->previous()->link()->previous();

View File

@ -509,6 +509,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
errorDanglingReference(tok, var, errorPath);
continue;
}
// Reference to temporary
} else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) {
for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) {
const Token * tokvalue = lt.token;
if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive);
break;
}
}
}
for (const ValueFlow::Value& val:tok->values()) {
if (!val.isLocalLifetimeValue())
@ -621,6 +631,13 @@ void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow:
reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive);
}
void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
{
errorPath.emplace_back(tok, "");
reportError(
errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive);
}
void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive)
{
errorPath.emplace_back(tok, "");

View File

@ -79,6 +79,7 @@ private:
void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val);
void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath);
void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive);
void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val);
void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname);
@ -94,6 +95,7 @@ private:
c.errorReturnReference(nullptr, errorPath, false);
c.errorDanglingReference(nullptr, nullptr, errorPath);
c.errorReturnTempReference(nullptr, errorPath, false);
c.errorDanglingTempReference(nullptr, errorPath, false);
c.errorInvalidDeallocation(nullptr, nullptr);
c.errorReturnAddressOfFunctionParameter(nullptr, "parameter");
c.errorUselessAssignmentArg(nullptr);

View File

@ -3062,7 +3062,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, bool escape, Valu
return std::vector<LifetimeToken> {};
const Token* argTok = args[n];
lt.errorPath.emplace_back(returnTok, "Return reference.");
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->str() + "'.");
lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'.");
std::vector<LifetimeToken> arglts = LifetimeToken::setInconclusive(
getLifetimeTokens(argTok, escape, std::move(lt.errorPath), depth - 1), returns.size() > 1);
result.insert(result.end(), arglts.begin(), arglts.end());
@ -3422,7 +3422,7 @@ struct LifetimeStore {
return LifetimeStore{};
}
const Token *argtok2 = args[n];
return LifetimeStore{argtok2, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object};
return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object};
}
template <class Predicate>

View File

@ -1638,7 +1638,8 @@ private:
" int& x = h();\n"
" g(&x);\n"
"}\n");
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n", errout.str());
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n"
"[test.cpp:4] -> [test.cpp:5]: (error) Using reference to dangling temporary.\n", errout.str());
check("void g(int*);\n"
"int h();\n"
@ -1675,6 +1676,27 @@ private:
ASSERT_EQUALS("", errout.str());
}
void danglingTempReference() {
check("const std::string& g(const std::string& str_cref) {\n"
" return str_cref;\n"
"}\n"
"void f() {\n"
" const auto& str_cref2 = g(std::string(\"hello\"));\n"
" std::cout << str_cref2 << std::endl;\n"
"}\n");
ASSERT_EQUALS("error", errout.str());
// Lifetime extended
check("std::string g(const std::string& str_cref) {\n"
" return str_cref;\n"
"}\n"
"void f() {\n"
" const auto& str_cref2 = g(std::string(\"hello\"));\n"
" std::cout << str_cref2 << std::endl;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void testglobalnamespace() {
check("class SharedPtrHolder\n"
"{\n"