Fix issue 9448: Check for temporaries from library function calls (#2312)
This commit is contained in:
parent
c7a23f126f
commit
c38bbb75e4
|
@ -220,17 +220,17 @@ const Token * astIsVariableComparison(const Token *tok, const std::string &comp,
|
|||
return ret;
|
||||
}
|
||||
|
||||
bool isTemporary(bool cpp, const Token* tok)
|
||||
bool isTemporary(bool cpp, const Token* tok, const Library* library)
|
||||
{
|
||||
if (!tok)
|
||||
return false;
|
||||
if (Token::simpleMatch(tok, "."))
|
||||
return (tok->originalName() != "->" && isTemporary(cpp, tok->astOperand1())) ||
|
||||
isTemporary(cpp, tok->astOperand2());
|
||||
return (tok->originalName() != "->" && isTemporary(cpp, tok->astOperand1(), library)) ||
|
||||
isTemporary(cpp, tok->astOperand2(), library);
|
||||
if (Token::Match(tok, ",|::"))
|
||||
return isTemporary(cpp, tok->astOperand2());
|
||||
return isTemporary(cpp, tok->astOperand2(), library);
|
||||
if (tok->isCast() || (cpp && isCPPCast(tok)))
|
||||
return isTemporary(cpp, tok->astOperand2());
|
||||
return isTemporary(cpp, tok->astOperand2(), library);
|
||||
if (Token::Match(tok, "?|.|[|++|--|%name%|%assign%"))
|
||||
return false;
|
||||
if (tok->isUnaryOp("*"))
|
||||
|
@ -238,12 +238,21 @@ bool isTemporary(bool cpp, const Token* tok)
|
|||
if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1()))
|
||||
return false;
|
||||
if (Token::Match(tok->previous(), ">|%name% (")) {
|
||||
const Function * f = nullptr;
|
||||
if (tok->previous()->function())
|
||||
f = tok->previous()->function();
|
||||
else if (tok->previous()->link())
|
||||
f = tok->previous()->link()->previous()->function();
|
||||
return f && !Function::returnsReference(tok->previous()->function(), true);
|
||||
const Token* ftok = nullptr;
|
||||
if (tok->previous()->link())
|
||||
ftok = tok->previous()->link()->previous();
|
||||
else
|
||||
ftok = tok->previous();
|
||||
if (!ftok)
|
||||
return false;
|
||||
if (const Function * f = ftok->function()) {
|
||||
return !Function::returnsReference(f, true);
|
||||
} else if (library) {
|
||||
std::string returnType = library->returnValueType(ftok);
|
||||
return !returnType.empty() && returnType.back() != '&';
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ std::string astCanonicalType(const Token *expr);
|
|||
/** Is given syntax tree a variable comparison against value */
|
||||
const Token * astIsVariableComparison(const Token *tok, const std::string &comp, const std::string &rhs, const Token **vartok=nullptr);
|
||||
|
||||
bool isTemporary(bool cpp, const Token* tok);
|
||||
bool isTemporary(bool cpp, const Token* tok, const Library* library);
|
||||
|
||||
const Token * nextAfterAstRightmostLeaf(const Token * tok);
|
||||
|
||||
|
|
|
@ -440,9 +440,9 @@ static int getPointerDepth(const Token *tok)
|
|||
return tok->valueType() ? tok->valueType()->pointer : 0;
|
||||
}
|
||||
|
||||
static bool isDeadTemporary(bool cpp, const Token* tok, const Token* expr)
|
||||
static bool isDeadTemporary(bool cpp, const Token* tok, const Token* expr, const Library* library)
|
||||
{
|
||||
if (!isTemporary(cpp, tok))
|
||||
if (!isTemporary(cpp, tok, library))
|
||||
return false;
|
||||
if (expr && !precedes(nextAfterAstRightmostLeaf(tok->astTop()), nextAfterAstRightmostLeaf(expr->astTop())))
|
||||
return false;
|
||||
|
@ -469,7 +469,7 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
isInScope(var->nameToken(), tok->scope())) {
|
||||
errorReturnReference(tok, lt.errorPath, lt.inconclusive);
|
||||
break;
|
||||
} else if (isDeadTemporary(mTokenizer->isCPP(), lt.token, nullptr)) {
|
||||
} else if (isDeadTemporary(mTokenizer->isCPP(), lt.token, nullptr, &mSettings->library)) {
|
||||
errorReturnTempReference(tok, lt.errorPath, lt.inconclusive);
|
||||
break;
|
||||
}
|
||||
|
@ -495,14 +495,14 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
if (!isLifetimeBorrowed(tok, mSettings))
|
||||
continue;
|
||||
if ((val.tokvalue->variable() && isInScope(val.tokvalue->variable()->nameToken(), scope)) ||
|
||||
isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok)) {
|
||||
isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok, &mSettings->library)) {
|
||||
errorReturnDanglingLifetime(tok, &val);
|
||||
break;
|
||||
}
|
||||
} else if (val.tokvalue->variable() && isDeadScope(val.tokvalue->variable()->nameToken(), tok->scope())) {
|
||||
errorInvalidLifetime(tok, &val);
|
||||
break;
|
||||
} else if (!val.tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok)) {
|
||||
} else if (!val.tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok, &mSettings->library)) {
|
||||
errorDanglingTemporaryLifetime(tok, &val);
|
||||
break;
|
||||
} else if (val.tokvalue->variable() && isInScope(val.tokvalue->variable()->nameToken(), tok->scope())) {
|
||||
|
|
|
@ -3150,7 +3150,7 @@ std::vector<LifetimeToken> getLifetimeTokens(const Token* tok, ValueFlow::Value:
|
|||
} else if (Token::simpleMatch(var->declEndToken(), "=")) {
|
||||
errorPath.emplace_back(var->declEndToken(), "Assigned to reference.");
|
||||
const Token *vartok = var->declEndToken()->astOperand2();
|
||||
if (vartok == tok || (var->isConst() && isTemporary(true, vartok)))
|
||||
if (vartok == tok || (var->isConst() && isTemporary(true, vartok, nullptr)))
|
||||
return {{tok, true, std::move(errorPath)}};
|
||||
if (vartok)
|
||||
return getLifetimeTokens(vartok, std::move(errorPath), depth - 1);
|
||||
|
|
|
@ -50,6 +50,7 @@ private:
|
|||
settings.addEnabled("warning");
|
||||
settings.addEnabled("style");
|
||||
LOAD_LIB_2(settings.library, "std.cfg");
|
||||
LOAD_LIB_2(settings.library, "qt.cfg");
|
||||
|
||||
TEST_CASE(testautovar1);
|
||||
TEST_CASE(testautovar2);
|
||||
|
@ -2426,6 +2427,16 @@ private:
|
|||
ASSERT_EQUALS(
|
||||
"[test.cpp:1] -> [test.cpp:2] -> [test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer to temporary.\n",
|
||||
errout.str());
|
||||
|
||||
check("QString f() {\n"
|
||||
" QString a(\"dummyValue\");\n"
|
||||
" const char* b = a.toStdString().c_str();\n"
|
||||
" QString c = b;\n"
|
||||
" return c;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS(
|
||||
"[test.cpp:3] -> [test.cpp:4]: (error) Using pointer to temporary.\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void invalidLifetime() {
|
||||
|
|
Loading…
Reference in New Issue