Fix issue 9448: Check for temporaries from library function calls (#2312)

This commit is contained in:
Paul Fultz II 2019-11-03 15:02:10 -06:00 committed by amai2012
parent c7a23f126f
commit c38bbb75e4
5 changed files with 38 additions and 18 deletions

View File

@ -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;
}

View File

@ -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);

View File

@ -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())) {

View File

@ -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);

View File

@ -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() {