Skip to content

Commit b835744

Browse files
authored
Fix 10585: FP danglingTemporaryLifetime recent regression (#3544)
Fix 10585: FP danglingTemporaryLifetime recent regression
1 parent 0f259a5 commit b835744

2 files changed

Lines changed: 113 additions & 5 deletions

File tree

lib/checkautovariables.cpp

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,53 @@ static bool isAssignedToNonLocal(const Token* tok)
517517
return !var->isLocal() || var->isStatic();
518518
}
519519

520+
static std::vector<const Token*> getParentMembers(const Token* tok)
521+
{
522+
if (!tok)
523+
return {};
524+
if (!Token::simpleMatch(tok->astParent(), "."))
525+
return {tok};
526+
const Token* parent = tok;
527+
while (Token::simpleMatch(parent->astParent(), "."))
528+
parent = parent->astParent();
529+
std::vector<const Token*> result;
530+
for (const Token* tok2 : astFlatten(parent, ".")) {
531+
if (Token::simpleMatch(tok2, "(") && Token::simpleMatch(tok2->astOperand1(), ".")) {
532+
std::vector<const Token*> sub = getParentMembers(tok2->astOperand1());
533+
result.insert(result.end(), sub.begin(), sub.end());
534+
}
535+
result.push_back(tok2);
536+
}
537+
return result;
538+
}
539+
540+
static const Token* getParentLifetime(bool cpp, const Token* tok, const Library* library)
541+
{
542+
std::vector<const Token*> members = getParentMembers(tok);
543+
if (members.size() < 2)
544+
return tok;
545+
// Find the first local variable or temporary
546+
auto it = std::find_if(members.rbegin(), members.rend(), [&](const Token* tok2) {
547+
const Variable* var = tok2->variable();
548+
if (var) {
549+
return var->isLocal() || var->isArgument();
550+
} else {
551+
return isTemporary(cpp, tok2, library);
552+
}
553+
});
554+
if (it == members.rend())
555+
return tok;
556+
// If any of the submembers are borrowed types then stop
557+
if (std::any_of(it.base() - 1, members.end() - 1, [&](const Token* tok2) {
558+
if (astIsPointer(tok2) || astIsContainerView(tok2) || astIsIterator(tok2))
559+
return true;
560+
const Variable* var = tok2->variable();
561+
return var && var->isReference();
562+
}))
563+
return nullptr;
564+
return *it;
565+
}
566+
520567
void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token * end)
521568
{
522569
const bool printInconclusive = mSettings->certainty.isEnabled(Certainty::inconclusive);
@@ -569,13 +616,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
569616
}
570617
}
571618
const bool escape = Token::Match(tok->astParent(), "return|throw");
619+
std::unordered_set<const Token*> exprs;
572620
for (const ValueFlow::Value& val:tok->values()) {
573621
if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue())
574622
continue;
575623
if (!printInconclusive && val.isInconclusive())
576624
continue;
577-
for (const LifetimeToken& lt :
578-
getLifetimeTokens(getParentLifetime(val.tokvalue), escape || isAssignedToNonLocal(tok))) {
625+
const Token* parent = getParentLifetime(mTokenizer->isCPP(), val.tokvalue, &mSettings->library);
626+
if (!exprs.insert(parent).second)
627+
continue;
628+
for (const LifetimeToken& lt : getLifetimeTokens(parent, escape || isAssignedToNonLocal(tok))) {
579629
const Token * tokvalue = lt.token;
580630
if (val.isLocalLifetimeValue()) {
581631
if (escape) {

test/testautovariables.cpp

Lines changed: 61 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ class TestAutoVariables : public TestFixture {
150150
TEST_CASE(danglingLifetimeInitList);
151151
TEST_CASE(danglingLifetimeImplicitConversion);
152152
TEST_CASE(danglingTemporaryLifetime);
153+
TEST_CASE(danglingLifetimeBorrowedMembers);
153154
TEST_CASE(invalidLifetime);
154155
TEST_CASE(deadPointer);
155156
TEST_CASE(splitNamespaceAuto); // crash #10473
@@ -3195,11 +3196,68 @@ class TestAutoVariables : public TestFixture {
31953196
" const std::map<int, int>::iterator& m = func().a_.m_.begin();\n"
31963197
" (void)m->first;\n"
31973198
"}\n");
3198-
ASSERT_EQUALS(
3199-
"[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using object that points to member variable 'm_' that is a temporary.\n",
3200-
errout.str());
3199+
ASSERT_EQUALS("[test.cpp:9] -> [test.cpp:9] -> [test.cpp:10]: (error) Using iterator that is a temporary.\n",
3200+
errout.str());
32013201
}
32023202

3203+
void danglingLifetimeBorrowedMembers()
3204+
{
3205+
// #10585
3206+
check("struct Info { int k; };\n"
3207+
"struct MoreInfo {\n"
3208+
" int* k;\n"
3209+
" char dat;\n"
3210+
"};\n"
3211+
"struct Fields {\n"
3212+
" Info info;\n"
3213+
"};\n"
3214+
"template <typename T> void func1(T val){}\n"
3215+
"template <typename T> void func2(T val){}\n"
3216+
"Fields* get();\n"
3217+
"void doit() {\n"
3218+
" MoreInfo rech;\n"
3219+
" rech.k = &get()->info.k;\n"
3220+
" func1(&rech.dat);\n"
3221+
" func2(rech.k);\n"
3222+
"}\n");
3223+
ASSERT_EQUALS("", errout.str());
3224+
3225+
check("struct A { int x; };\n"
3226+
"A* g();\n"
3227+
"void f() {\n"
3228+
" A** ap = &g();\n"
3229+
" (*ap)->x;\n"
3230+
"}\n");
3231+
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:4] -> [test.cpp:5]: (error) Using pointer that is a temporary.\n",
3232+
errout.str());
3233+
3234+
check("struct A { int* x; };\n"
3235+
"A g();\n"
3236+
"void f() {\n"
3237+
" int* x = g().x;\n"
3238+
" (void)*x + 1;\n"
3239+
"}\n");
3240+
ASSERT_EQUALS("", errout.str());
3241+
3242+
check("struct A { int x; };\n"
3243+
"struct B { A* a; }\n"
3244+
"B g();\n"
3245+
"void f() {\n"
3246+
" int* x = &g()->a.x;\n"
3247+
" (void)*x + 1;\n"
3248+
"}\n");
3249+
ASSERT_EQUALS("", errout.str());
3250+
3251+
check("struct A { int x; };\n"
3252+
"struct B { A* g(); };\n"
3253+
"A* g();\n"
3254+
"void f(B b) {\n"
3255+
" A** ap = &b.g();\n"
3256+
" (*ap)->x;\n"
3257+
"}\n");
3258+
ASSERT_EQUALS("[test.cpp:5] -> [test.cpp:5] -> [test.cpp:6]: (error) Using pointer that is a temporary.\n",
3259+
errout.str());
3260+
}
32033261
void invalidLifetime() {
32043262
check("void foo(int a) {\n"
32053263
" std::function<void()> f;\n"

0 commit comments

Comments
 (0)