Fix 10585: FP danglingTemporaryLifetime recent regression (#3544)
Fix 10585: FP danglingTemporaryLifetime recent regression
This commit is contained in:
parent
0f259a5dc6
commit
b835744a81
|
@ -517,6 +517,53 @@ static bool isAssignedToNonLocal(const Token* tok)
|
|||
return !var->isLocal() || var->isStatic();
|
||||
}
|
||||
|
||||
static std::vector<const Token*> getParentMembers(const Token* tok)
|
||||
{
|
||||
if (!tok)
|
||||
return {};
|
||||
if (!Token::simpleMatch(tok->astParent(), "."))
|
||||
return {tok};
|
||||
const Token* parent = tok;
|
||||
while (Token::simpleMatch(parent->astParent(), "."))
|
||||
parent = parent->astParent();
|
||||
std::vector<const Token*> result;
|
||||
for (const Token* tok2 : astFlatten(parent, ".")) {
|
||||
if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->astOperand1(), ".")) {
|
||||
std::vector<const Token*> sub = getParentMembers(tok2->astOperand1());
|
||||
result.insert(result.end(), sub.begin(), sub.end());
|
||||
}
|
||||
result.push_back(tok2);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static const Token* getParentLifetime(bool cpp, const Token* tok, const Library* library)
|
||||
{
|
||||
std::vector<const Token*> members = getParentMembers(tok);
|
||||
if (members.size() < 2)
|
||||
return tok;
|
||||
// Find the first local variable or temporary
|
||||
auto it = std::find_if(members.rbegin(), members.rend(), [&](const Token* tok2) {
|
||||
const Variable* var = tok2->variable();
|
||||
if (var) {
|
||||
return var->isLocal() || var->isArgument();
|
||||
} else {
|
||||
return isTemporary(cpp, tok2, library);
|
||||
}
|
||||
});
|
||||
if (it == members.rend())
|
||||
return tok;
|
||||
// If any of the submembers are borrowed types then stop
|
||||
if (std::any_of(it.base() - 1, members.end() - 1, [&](const Token* tok2) {
|
||||
if (astIsPointer(tok2) || astIsContainerView(tok2) || astIsIterator(tok2))
|
||||
return true;
|
||||
const Variable* var = tok2->variable();
|
||||
return var && var->isReference();
|
||||
}))
|
||||
return nullptr;
|
||||
return *it;
|
||||
}
|
||||
|
||||
void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end)
|
||||
{
|
||||
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
|
||||
|
@ -569,13 +616,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
|
|||
}
|
||||
}
|
||||
const bool escape = Token::Match(tok->astParent(), "return|throw");
|
||||
std::unordered_set<const Token*> exprs;
|
||||
for (const ValueFlow::Value& val:tok->values()) {
|
||||
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
|
||||
continue;
|
||||
if (!printInconclusive && val.isInconclusive())
|
||||
continue;
|
||||
for (const LifetimeToken& lt :
|
||||
getLifetimeTokens(getParentLifetime(val.tokvalue), escape || isAssignedToNonLocal(tok))) {
|
||||
const Token* parent = getParentLifetime(mTokenizer->isCPP(), val.tokvalue, &mSettings->library);
|
||||
if (!exprs.insert(parent).second)
|
||||
continue;
|
||||
for (const LifetimeToken& lt : getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) {
|
||||
const Token * tokvalue = lt.token;
|
||||
if (val.isLocalLifetimeValue()) {
|
||||
if (escape) {
|
||||
|
|
|
@ -150,6 +150,7 @@ private:
|
|||
TEST_CASE(danglingLifetimeInitList);
|
||||
TEST_CASE(danglingLifetimeImplicitConversion);
|
||||
TEST_CASE(danglingTemporaryLifetime);
|
||||
TEST_CASE(danglingLifetimeBorrowedMembers);
|
||||
TEST_CASE(invalidLifetime);
|
||||
TEST_CASE(deadPointer);
|
||||
TEST_CASE(splitNamespaceAuto); // crash #10473
|
||||
|
@ -3195,11 +3196,68 @@ private:
|
|||
" const std::map<int, int>::iterator& m = func().a_.m_.begin();\n"
|
||||
" (void)m->first;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS(
|
||||
"[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using object that points to member variable 'm_' that is a temporary.\n",
|
||||
errout.str());
|
||||
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using iterator that is a temporary.\n",
|
||||
errout.str());
|
||||
}
|
||||
|
||||
void danglingLifetimeBorrowedMembers()
|
||||
{
|
||||
// #10585
|
||||
check("struct Info { int k; };\n"
|
||||
"struct MoreInfo {\n"
|
||||
" int* k;\n"
|
||||
" char dat;\n"
|
||||
"};\n"
|
||||
"struct Fields {\n"
|
||||
" Info info;\n"
|
||||
"};\n"
|
||||
"template <typename T> void func1(T val){}\n"
|
||||
"template <typename T> void func2(T val){}\n"
|
||||
"Fields* get();\n"
|
||||
"void doit() {\n"
|
||||
" MoreInfo rech;\n"
|
||||
" rech.k = &get()->info.k;\n"
|
||||
" func1(&rech.dat);\n"
|
||||
" func2(rech.k);\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("struct A { int x; };\n"
|
||||
"A* g();\n"
|
||||
"void f() {\n"
|
||||
" A** ap = &g();\n"
|
||||
" (*ap)->x;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n",
|
||||
errout.str());
|
||||
|
||||
check("struct A { int* x; };\n"
|
||||
"A g();\n"
|
||||
"void f() {\n"
|
||||
" int* x = g().x;\n"
|
||||
" (void)*x + 1;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("struct A { int x; };\n"
|
||||
"struct B { A* a; }\n"
|
||||
"B g();\n"
|
||||
"void f() {\n"
|
||||
" int* x = &g()->a.x;\n"
|
||||
" (void)*x + 1;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("", errout.str());
|
||||
|
||||
check("struct A { int x; };\n"
|
||||
"struct B { A* g(); };\n"
|
||||
"A* g();\n"
|
||||
"void f(B b) {\n"
|
||||
" A** ap = &b.g();\n"
|
||||
" (*ap)->x;\n"
|
||||
"}\n");
|
||||
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n",
|
||||
errout.str());
|
||||
}
|
||||
void invalidLifetime() {
|
||||
check("void foo(int a) {\n"
|
||||
" std::function<void()> f;\n"
|
||||
|
|
Loading…
Reference in New Issue