Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
25 changes: 18 additions & 7 deletions lib/valueflow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2861,29 +2861,40 @@ 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,
settings,
args,
"Passed to constructor of '" + t->name() + "'.",
ValueFlow::Value::LifetimeKind::SubObject,
[&](LifetimeStore& ls) {
[&](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()) {
if (var.isReference() || var.isRValueReference())
{
ls.byRef(tok, tokenlist, errorLogger, settings);
} else if (ValueFlow::isLifetimeBorrowed(ls.argtok, 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
18 changes: 18 additions & 0 deletions test/testautovariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3937,6 +3937,24 @@ 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());
}

void danglingLifetimeInitList() {
Expand Down