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:
parent
c61880c457
commit
d1f225b8ee
|
@ -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())
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(), "=")) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue