Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions lib/astutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down
5 changes: 5 additions & 0 deletions lib/astutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down
14 changes: 10 additions & 4 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2861,6 +2861,7 @@ static void valueFlowLifetimeClassConstructor(Token* tok,
std::vector<const Token*> 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,
Expand All @@ -2870,20 +2871,25 @@ 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()) {
ls.byRef(tok, tokenlist, errorLogger, settings);
} 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);
Expand Down
36 changes: 36 additions & 0 deletions test/testautovariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down