diff --git a/lib/astutils.cpp b/lib/astutils.cpp index c648e79a5b2..7ca319ef8ba 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1022,6 +1022,11 @@ const Token* isInLoopCondition(const Token* tok) return Token::Match(top->previous(), "for|while (") ? top : nullptr; } +bool isDesignatedInitializer(const Token* tok) +{ + return tok && tok->isUnaryOp("."); +} + /// If tok2 comes after tok1 bool precedes(const Token * tok1, const Token * tok2) { diff --git a/lib/astutils.h b/lib/astutils.h index bcb1f696e65..06f01acab11 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -265,6 +265,11 @@ bool isStructuredBindingVariable(const Variable* var); const Token* isInLoopCondition(const Token* tok); +/** + * Is token the dot of a designated initializer? + */ +bool isDesignatedInitializer(const Token* tok); + /** * Is token used as boolean, that is to say cast to a bool, or used as a condition in a if/while/for */ diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 632b2329add..7598d7349b0 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -2861,6 +2861,7 @@ static void valueFlowLifetimeClassConstructor(Token* tok, std::vector args = getArguments(tok); if (scope->numConstructors == 0) { auto it = scope->varlist.cbegin(); + const bool hasDesignatedInitializers = !args.empty() && isDesignatedInitializer(args[0]->astOperand1()); LifetimeStore::forEach( tokenlist, errorLogger, @@ -2870,12 +2871,14 @@ static void valueFlowLifetimeClassConstructor(Token* tok, ValueFlow::Value::LifetimeKind::SubObject, [&](LifetimeStore& ls) { // Skip static variable - it = std::find_if(it, scope->varlist.cend(), [](const Variable& var) { - return !var.isStatic(); + it = std::find_if(it, scope->varlist.cend(), [&](const Variable &var) { + return !var.isStatic() && (!hasDesignatedInitializers || var.name() == ls.argtok->astOperand1()->astOperand1()->str()); }); if (it == scope->varlist.cend()) return; - const Variable& var = *it; + if (hasDesignatedInitializers) + ls.argtok = ls.argtok->astOperand2(); + const Variable &var = *it; if (var.valueType() && var.valueType()->container && var.valueType()->container->stdStringLike && !var.valueType()->container->view) return; // TODO: check in isLifetimeBorrowed()? if (var.isReference() || var.isRValueReference()) { @@ -2883,7 +2886,10 @@ static void valueFlowLifetimeClassConstructor(Token* tok, } else if (ValueFlow::isLifetimeBorrowed(ls.argtok, settings)) { ls.byVal(tok, tokenlist, errorLogger, settings); } - it++; + if (hasDesignatedInitializers) + it = scope->varlist.cbegin(); + else + it++; }); } else { const Function* constructor = findConstructor(scope, tok, args); diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index a56e65ba5cd..c12261b1b1a 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -3937,6 +3937,42 @@ class TestAutoVariables : public TestFixture { ASSERT_EQUALS( "[test.cpp:6:30] -> [test.cpp:6:30] -> [test.cpp:6:21] -> [test.cpp:5:21] -> [test.cpp:8:12]: (error) Returning object that points to local variable 'a' that will be invalid when returning. [returnDanglingLifetime]\n", errout_str()); + + check("struct A { int& x; };\n" // #14247 + "A f() {\n" + " int x = 0;\n" + " A a{.x = x};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4:14] -> [test.cpp:3:9] -> [test.cpp:5:12]: (error) Returning object that points to local variable 'x' that will be invalid when returning. [returnDanglingLifetime]\n", + errout_str()); + + check("struct A { int x; int& r};\n" + "A f(int& r) {\n" + " int x = 0;\n" + " A a{.x = x, .r = r};\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct A { int& x; };\n" + "A f() {\n" + " int x = 0;\n" + " A a{ .x{x} };\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS( + "[test.cpp:4:13] -> [test.cpp:3:9] -> [test.cpp:5:12]: (error) Returning object that points to local variable 'x' that will be invalid when returning. [returnDanglingLifetime]\n", + errout_str()); + + check("struct A { int x; int& r};\n" + "A f(int& r) {\n" + " int x = 0;\n" + " A a{ .x{x}, .r{r} };\n" + " return a;\n" + "}\n"); + ASSERT_EQUALS("", errout_str()); } void danglingLifetimeInitList() {