Fix issue 9201: FP: returnDanglingLifetime on pointer to variable of static struct (#2303)

* Fix issue 9201: FP: returnDanglingLifetime on pointer to variable of static struct

* Fix capture of non-local variables in lambdas
This commit is contained in:
Paul Fultz II 2019-11-05 00:10:32 -06:00 committed by Daniel Marjamäki
parent c61880c457
commit d1f225b8ee
5 changed files with 66 additions and 6 deletions

View File

@ -311,6 +311,23 @@ Token* astParentSkipParens(Token* tok)
return astParentSkipParens(parent); return astParentSkipParens(parent);
} }
const Token* getParentMember(const Token * tok)
{
if (!tok)
return tok;
const Token * parent = tok->astParent();
if (!Token::simpleMatch(parent, "."))
return tok;
if (tok == parent->astOperand2())
return parent->astOperand1();
const Token * gparent = parent->astParent();
if (!Token::simpleMatch(gparent, ".") || gparent->astOperand2() != parent)
return tok;
if (gparent->astOperand1())
return gparent->astOperand1();
return tok;
}
static const Token * getVariableInitExpression(const Variable * var) static const Token * getVariableInitExpression(const Variable * var)
{ {
if (!var || !var->declEndToken()) if (!var || !var->declEndToken())

View File

@ -94,6 +94,8 @@ const Token * nextAfterAstRightmostLeaf(const Token * tok);
Token* astParentSkipParens(Token* tok); Token* astParentSkipParens(Token* tok);
const Token* astParentSkipParens(const Token* tok); const Token* astParentSkipParens(const Token* tok);
const Token* getParentMember(const Token * tok);
bool precedes(const Token * tok1, const Token * tok2); bool precedes(const Token * tok1, const Token * tok2);
bool exprDependsOnThis(const Token* expr, nonneg int depth = 0); bool exprDependsOnThis(const Token* expr, nonneg int depth = 0);

View File

@ -433,6 +433,22 @@ static bool isDeadScope(const Token * tok, const Scope * scope)
return false; return false;
} }
static const Token * getParentLifetime(const Token *tok)
{
if (!tok)
return tok;
const Variable * var = tok->variable();
// TODO: Call getLifetimeVariable for deeper analysis
if (!var)
return tok;
if (var->isLocal())
return tok;
const Token * parent = getParentMember(tok);
if (parent != tok)
return getParentLifetime(parent);
return tok;
}
static int getPointerDepth(const Token *tok) static int getPointerDepth(const Token *tok)
{ {
if (!tok) if (!tok)
@ -489,23 +505,24 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token
for (const ValueFlow::Value& val:tok->values()) { for (const ValueFlow::Value& val:tok->values()) {
if (!val.isLocalLifetimeValue()) if (!val.isLocalLifetimeValue())
continue; continue;
const Token * tokvalue = getParentLifetime(val.tokvalue);
if (Token::Match(tok->astParent(), "return|throw")) { if (Token::Match(tok->astParent(), "return|throw")) {
if (getPointerDepth(tok) < getPointerDepth(val.tokvalue)) if (getPointerDepth(tok) < getPointerDepth(tokvalue))
continue; continue;
if (!isLifetimeBorrowed(tok, mSettings)) if (!isLifetimeBorrowed(tok, mSettings))
continue; continue;
if ((val.tokvalue->variable() && isInScope(val.tokvalue->variable()->nameToken(), scope)) || if ((tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), scope)) ||
isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok, &mSettings->library)) { isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorReturnDanglingLifetime(tok, &val); errorReturnDanglingLifetime(tok, &val);
break; break;
} }
} else if (val.tokvalue->variable() && isDeadScope(val.tokvalue->variable()->nameToken(), tok->scope())) { } else if (tokvalue->variable() && isDeadScope(tokvalue->variable()->nameToken(), tok->scope())) {
errorInvalidLifetime(tok, &val); errorInvalidLifetime(tok, &val);
break; break;
} else if (!val.tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), val.tokvalue, tok, &mSettings->library)) { } else if (!tokvalue->variable() && isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) {
errorDanglingTemporaryLifetime(tok, &val); errorDanglingTemporaryLifetime(tok, &val);
break; break;
} else if (val.tokvalue->variable() && isInScope(val.tokvalue->variable()->nameToken(), tok->scope())) { } else if (tokvalue->variable() && isInScope(tokvalue->variable()->nameToken(), tok->scope())) {
const Variable * var = nullptr; const Variable * var = nullptr;
const Token * tok2 = tok; const Token * tok2 = tok;
if (Token::simpleMatch(tok->astParent(), "=")) { if (Token::simpleMatch(tok->astParent(), "=")) {

View File

@ -3772,6 +3772,8 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
const Variable *var = varTok->variable(); const Variable *var = varTok->variable();
if (!var) if (!var)
return false; return false;
if (!var->isLocal() && !var->isArgument())
return false;
const Scope *scope = var->scope(); const Scope *scope = var->scope();
if (!scope) if (!scope)
return false; return false;

View File

@ -2197,6 +2197,14 @@ private:
" m = msg;\n" " m = msg;\n"
"}\n"); "}\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// #9201
check("int* f() {\n"
" struct a { int m; };\n"
" static a b{0};\n"
" return &b.m;\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void danglingLifetimeFunction() { void danglingLifetimeFunction() {
@ -2502,6 +2510,20 @@ private:
check("int &a[];\n" check("int &a[];\n"
"void b(){int *c = a};\n"); "void b(){int *c = a};\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
check("struct A {\n"
" int x;\n"
"};\n"
"struct B {\n"
" std::function<void()> x;\n"
" void f() {\n"
" this->x = [&] {\n"
" B y;\n"
" return y.x;\n"
" };\n"
" }\n"
"};\n");
ASSERT_EQUALS("", errout.str());
} }
void deadPointer() { void deadPointer() {