diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 03f4386f6..964690e62 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -147,6 +147,17 @@ std::vector astFlatten(Token* tok, const char* op) 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) { if (!root) @@ -2583,7 +2594,24 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end 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; const Token* const openBracket = start->next(); @@ -2597,17 +2625,8 @@ int numberOfArguments(const Token *start) return arguments; } -std::vector getArguments(const Token *ftok) -{ - const Token* tok = ftok; - if (Token::Match(tok, "%name% (|{")) - tok = ftok->next(); - if (!Token::Match(tok, "(|{|[")) - return std::vector {}; - const Token *startTok = tok->astOperand2(); - if (!startTok && tok->next() != tok->link()) - startTok = tok->astOperand1(); - return astFlatten(startTok, ","); +std::vector getArguments(const Token* ftok) { + return astFlatten(getArgumentStart(ftok), ","); } int getArgumentPos(const Variable* var, const Function* f) diff --git a/lib/astutils.h b/lib/astutils.h index a1bb11bff..184bad415 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -89,6 +89,8 @@ const Token* findExpression(const Token* start, const nonneg int exprid); std::vector astFlatten(const Token* tok, const char* op); std::vector 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 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); +const Token* getArgumentStart(const Token* ftok); + /** 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. * \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) diff --git a/lib/library.cpp b/lib/library.cpp index 793ec02bf..320ed6c4b 100644 --- a/lib/library.cpp +++ b/lib/library.cpp @@ -1197,7 +1197,7 @@ bool Library::isNotLibraryFunction(const Token *ftok) 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::const_iterator it = functions.find(functionName); if (it == functions.cend()) return (callargs == 0); diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 442de1c58..cacc3ea12 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -7172,6 +7172,14 @@ MathLib::bigint ValueType::typeSize(const cppcheck::Platform &platform, bool p) 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 ret; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index c2731b81b..c5eb887e7 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1334,6 +1334,9 @@ public: 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 dump() const; }; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 0ed20f286..39038e549 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -311,22 +311,37 @@ static std::vector getParentValueTypes(const Token* tok, } else if (Token::Match(tok->astParent(), "(|{|,")) { int argn = -1; const Token* ftok = getTokenArgumentFunction(tok, argn); - if (ftok && ftok->function()) { - std::vector result; - std::vector argsVars = getArgumentVars(ftok, argn); - const Token* nameTok = nullptr; - for (const Variable* var : getArgumentVars(ftok, argn)) { - if (!var) - continue; - if (!var->valueType()) - continue; - nameTok = var->nameToken(); - result.push_back(*var->valueType()); + const Token* typeTok = nullptr; + if (ftok && argn >= 0) { + if (ftok->function()) { + std::vector result; + std::vector argsVars = getArgumentVars(ftok, argn); + const Token* nameTok = nullptr; + for (const Variable* var : getArgumentVars(ftok, argn)) { + if (!var) + continue; + if (!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 (") && @@ -3293,6 +3308,14 @@ static std::vector getLifetimeTokens(const Token* tok, return LifetimeToken::setAddressOf(getLifetimeTokens(vartok, escape, std::move(errorPath), pred, depth - 1), !(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)}}; } @@ -4216,7 +4239,15 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error } for (const ValueType& vt : vts) { - if (vt.container && vt.type == ValueType::CONTAINER) { + if (vt.pointer > 0) { + std::vector 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 args = getArguments(tok); if (args.size() == 1 && vt.container->view && astIsContainerOwned(args.front())) { LifetimeStore{args.front(), "Passed to container view.", ValueFlow::Value::LifetimeKind::SubObject} @@ -4238,7 +4269,12 @@ static void valueFlowLifetimeConstructor(Token* tok, TokenList* tokenlist, Error }); } } 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); } // 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); } // Check function calls diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 399859a5e..3051a9932 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -3561,6 +3561,16 @@ private: " }\n" "};\n"); 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() diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index 806110e4f..99ff9adcd 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -6571,6 +6571,20 @@ private: code = "void f(const char * const x) { !!system(x); }\n"; 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" " int dx = 0;\n" " int dy = 0;\n"