Fix issue 9835: False negative: Return reference to temporary with const reference
This commit is contained in:
parent
18e99176e5
commit
8d7088aa24
|
@ -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();
|
||||
|
|
|
@ -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, "");
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue