diff --git a/lib/astutils.cpp b/lib/astutils.cpp index f75e6408c..88ba82617 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -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; } diff --git a/lib/astutils.h b/lib/astutils.h index 3d11278a2..fc25a7efa 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -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); diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 5fd9b0e57..e97eb1b11 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -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())) { diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 84ffeba27..a459ad1b1 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3150,7 +3150,7 @@ std::vector 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); diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index e6e09243f..4b4939e0e 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -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() {