Fix 11057: FP danglingTemporaryLifetime with reference member (#4103)

* Fix 11057: FP danglingTemporaryLifetime with reference member

* Add test

* Format

* Use ast for number of arguments

* Get number of arguments using ast

* Skip aggregate constructor when there are too many arguments

* Format
This commit is contained in:
Paul Fultz II 2022-05-12 23:51:07 -05:00 committed by GitHub
parent 5f9bee9b91
commit fbba72ab5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 129 additions and 32 deletions

View File

@ -147,6 +147,17 @@ std::vector<Token*> astFlatten(Token* tok, const char* op)
return result; return result;
} }
nonneg int astCount(const Token* tok, const char* op, int depth)
{
--depth;
if (!tok || depth < 0)
return 0;
if (tok->str() == op)
return astCount(tok->astOperand1(), op, depth) + astCount(tok->astOperand2(), op, depth);
else
return 1;
}
bool astHasToken(const Token* root, const Token * tok) bool astHasToken(const Token* root, const Token * tok)
{ {
if (!root) if (!root)
@ -2583,7 +2594,24 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
return result; return result;
} }
int numberOfArguments(const Token *start) const Token* getArgumentStart(const Token* ftok)
{
const Token* tok = ftok;
if (Token::Match(tok, "%name% (|{"))
tok = ftok->next();
if (!Token::Match(tok, "(|{|["))
return nullptr;
const Token* startTok = tok->astOperand2();
if (!startTok && tok->next() != tok->link())
startTok = tok->astOperand1();
return startTok;
}
int numberOfArguments(const Token* ftok) {
return astCount(getArgumentStart(ftok), ",");
}
int numberOfArgumentsWithoutAst(const Token* start)
{ {
int arguments=0; int arguments=0;
const Token* const openBracket = start->next(); const Token* const openBracket = start->next();
@ -2597,17 +2625,8 @@ int numberOfArguments(const Token *start)
return arguments; return arguments;
} }
std::vector<const Token *> getArguments(const Token *ftok) std::vector<const Token*> getArguments(const Token* ftok) {
{ return astFlatten(getArgumentStart(ftok), ",");
const Token* tok = ftok;
if (Token::Match(tok, "%name% (|{"))
tok = ftok->next();
if (!Token::Match(tok, "(|{|["))
return std::vector<const Token *> {};
const Token *startTok = tok->astOperand2();
if (!startTok && tok->next() != tok->link())
startTok = tok->astOperand1();
return astFlatten(startTok, ",");
} }
int getArgumentPos(const Variable* var, const Function* f) int getArgumentPos(const Variable* var, const Function* f)

View File

@ -89,6 +89,8 @@ const Token* findExpression(const Token* start, const nonneg int exprid);
std::vector<const Token*> astFlatten(const Token* tok, const char* op); std::vector<const Token*> astFlatten(const Token* tok, const char* op);
std::vector<Token*> astFlatten(Token* tok, const char* op); std::vector<Token*> astFlatten(Token* tok, const char* op);
nonneg int astCount(const Token* tok, const char* op, int depth = 100);
bool astHasToken(const Token* root, const Token * tok); bool astHasToken(const Token* root, const Token * tok);
bool astHasVar(const Token * tok, nonneg int varid); bool astHasVar(const Token * tok, nonneg int varid);
@ -310,11 +312,16 @@ bool isAliasOf(const Token *tok, nonneg int varid, bool* inconclusive = nullptr)
bool isAliased(const Variable *var); bool isAliased(const Variable *var);
const Token* getArgumentStart(const Token* ftok);
/** Determines the number of arguments - if token is a function call or macro /** Determines the number of arguments - if token is a function call or macro
* @param start token which is supposed to be the function/macro name. * @param start token which is supposed to be the function/macro name.
* \return Number of arguments * \return Number of arguments
*/ */
int numberOfArguments(const Token *start); int numberOfArguments(const Token* ftok);
/// Get number of arguments without using AST
int numberOfArgumentsWithoutAst(const Token* start);
/** /**
* Get arguments (AST) * Get arguments (AST)

View File

@ -1197,7 +1197,7 @@ bool Library::isNotLibraryFunction(const Token *ftok) const
bool Library::matchArguments(const Token *ftok, const std::string &functionName) const bool Library::matchArguments(const Token *ftok, const std::string &functionName) const
{ {
const int callargs = numberOfArguments(ftok); const int callargs = numberOfArgumentsWithoutAst(ftok);
const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName); const std::unordered_map<std::string, Function>::const_iterator it = functions.find(functionName);
if (it == functions.cend()) if (it == functions.cend())
return (callargs == 0); return (callargs == 0);

View File

@ -7172,6 +7172,14 @@ MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform, bool p)
return 0; return 0;
} }
bool ValueType::isTypeEqual(const ValueType* that) const
{
auto tie = [](const ValueType* vt) {
return std::tie(vt->type, vt->container, vt->pointer, vt->typeScope, vt->smartPointer);
};
return tie(this) == tie(that);
}
std::string ValueType::str() const std::string ValueType::str() const
{ {
std::string ret; std::string ret;

View File

@ -1334,6 +1334,9 @@ public:
MathLib::bigint typeSize(const cppcheck::Platform &platform, bool p=false) const; MathLib::bigint typeSize(const cppcheck::Platform &platform, bool p=false) const;
/// Check if type is the same ignoring const and references
bool isTypeEqual(const ValueType* that) const;
std::string str() const; std::string str() const;
std::string dump() const; std::string dump() const;
}; };

View File

@ -311,22 +311,37 @@ static std::vector<ValueType> getParentValueTypes(const Token* tok,
} else if (Token::Match(tok->astParent(), "(|{|,")) { } else if (Token::Match(tok->astParent(), "(|{|,")) {
int argn = -1; int argn = -1;
const Token* ftok = getTokenArgumentFunction(tok, argn); const Token* ftok = getTokenArgumentFunction(tok, argn);
if (ftok && ftok->function()) { const Token* typeTok = nullptr;
std::vector<ValueType> result; if (ftok && argn >= 0) {
std::vector<const Variable*> argsVars = getArgumentVars(ftok, argn); if (ftok->function()) {
const Token* nameTok = nullptr; std::vector<ValueType> result;
for (const Variable* var : getArgumentVars(ftok, argn)) { std::vector<const Variable*> argsVars = getArgumentVars(ftok, argn);
if (!var) const Token* nameTok = nullptr;
continue; for (const Variable* var : getArgumentVars(ftok, argn)) {
if (!var->valueType()) if (!var)
continue; continue;
nameTok = var->nameToken(); if (!var->valueType())
result.push_back(*var->valueType()); continue;
nameTok = var->nameToken();
result.push_back(*var->valueType());
}
if (result.size() == 1 && nameTok && parent) {
*parent = nameTok;
}
return result;
} else if (const Type* t = Token::typeOf(ftok, &typeTok)) {
if (astIsPointer(typeTok))
return {*typeTok->valueType()};
const Scope* scope = t->classScope;
// Check for aggregate constructors
if (scope && scope->numConstructors == 0 && t->derivedFrom.empty() &&
(t->isClassType() || t->isStructType()) && numberOfArguments(ftok) < scope->varlist.size()) {
assert(argn < scope->varlist.size());
auto it = std::next(scope->varlist.begin(), argn);
if (it->valueType())
return {*it->valueType()};
}
} }
if (result.size() == 1 && nameTok && parent) {
*parent = nameTok;
}
return result;
} }
} }
if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") && if (settings && Token::Match(tok->astParent()->tokAt(-2), ". push_back|push_front|insert|push (") &&
@ -3293,6 +3308,14 @@ static std::vector<LifetimeToken> getLifetimeTokens(const Token* tok,
return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1), return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1),
!(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "["))); !(astIsContainer(vartok) && Token::simpleMatch(vartok->astParent(), "[")));
} }
} else if (Token::simpleMatch(tok, "{") && getArgumentStart(tok) &&
!Token::simpleMatch(getArgumentStart(tok), ",") && getArgumentStart(tok)->valueType()) {
const Token* vartok = getArgumentStart(tok);
auto vts = getParentValueTypes(tok);
for (const ValueType& vt : vts) {
if (vt.isTypeEqual(vartok->valueType()))
return getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1);
}
} }
return {{tok, std::move(errorPath)}}; return {{tok, std::move(errorPath)}};
} }
@ -4216,7 +4239,15 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
} }
for (const ValueType& vt : vts) { for (const ValueType& vt : vts) {
if (vt.container && vt.type == ValueType::CONTAINER) { if (vt.pointer > 0) {
std::vector<const Token*> args = getArguments(tok);
LifetimeStore::forEach(args,
"Passed to initializer list.",
ValueFlow::Value::LifetimeKind::SubObject,
[&](const LifetimeStore& ls) {
ls.byVal(tok, tokenlist, errorLogger, settings);
});
} else if (vt.container && vt.type == ValueType::CONTAINER) {
std::vector<const Token*> args = getArguments(tok); std::vector<const Token*> args = getArguments(tok);
if (args.size() == 1 && vt.container->view && astIsContainerOwned(args.front())) { if (args.size() == 1 && vt.container->view && astIsContainerOwned(args.front())) {
LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject} LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject}
@ -4238,7 +4269,12 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error
}); });
} }
} else { } else {
valueFlowLifetimeClassConstructor(tok, Token::typeOf(tok->previous()), tokenlist, errorLogger, settings); const Type* t = nullptr;
if (vt.typeScope && vt.typeScope->definedType)
t = vt.typeScope->definedType;
else
t = Token::typeOf(tok->previous());
valueFlowLifetimeClassConstructor(tok, t, tokenlist, errorLogger, settings);
} }
} }
} }
@ -4544,7 +4580,7 @@ static void valueFlowLifetime(TokenList *tokenlist, SymbolDatabase*, ErrorLogger
valueFlowForwardLifetime(parent->tokAt(2), tokenlist, errorLogger, settings); valueFlowForwardLifetime(parent->tokAt(2), tokenlist, errorLogger, settings);
} }
// Check constructors // Check constructors
else if (Token::Match(tok, "=|return|%type%|%var% {") && !isScope(tok->next())) { else if (Token::Match(tok, "=|return|%name%|{|,|> {") && !isScope(tok->next())) {
valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings); valueFlowLifetimeConstructor(tok->next(), tokenlist, errorLogger, settings);
} }
// Check function calls // Check function calls

View File

@ -3561,6 +3561,16 @@ private:
" }\n" " }\n"
"};\n"); "};\n");
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS("", errout.str());
// #11057
check("struct S {\n"
" int& r;\n"
"};\n"
"void f(int i) {\n"
" const S a[] = { { i } };\n"
" for (const auto& s : a) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
} }
void danglingLifetimeBorrowedMembers() void danglingLifetimeBorrowedMembers()

View File

@ -6571,6 +6571,20 @@ private:
code = "void f(const char * const x) { !!system(x); }\n"; code = "void f(const char * const x) { !!system(x); }\n";
valueOfTok(code, "x"); valueOfTok(code, "x");
code = "struct struct1 {\n"
" int i1;\n"
" int i2;\n"
"};\n"
"struct struct2 {\n"
" char c1;\n"
" struct1 is1;\n"
" char c2[4];\n"
"};\n"
"void f() {\n"
" struct2 a = { 1, 2, 3, {4,5,6,7} }; \n"
"}\n";
valueOfTok(code, "a");
code = "void setDeltas(int life, int age, int multiplier) {\n" code = "void setDeltas(int life, int age, int multiplier) {\n"
" int dx = 0;\n" " int dx = 0;\n"
" int dy = 0;\n" " int dy = 0;\n"