diff --git a/addons/cppcheckdata.py b/addons/cppcheckdata.py index efbe86a41..55c27c448 100755 --- a/addons/cppcheckdata.py +++ b/addons/cppcheckdata.py @@ -137,7 +137,8 @@ class Token: isUnsigned Is this token a unsigned type isSigned Is this token a signed type isExpandedMacro Is this token a expanded macro token - isSplittedVarDecl Is this token a splitted variable declaration. "int a,b; => int a; int b;" + isSplittedVarDeclComma Is this a comma changed to semicolon in a splitted variable declaration ('int a,b;' => 'int a; int b;') + isSplittedVarDeclEq Is this a '=' changed to semicolon in a splitted variable declaration ('int a=5;' => 'int a; a=5;') varId varId for token, each variable has a unique non-zero id variable Variable information for this token. See the Variable class. function If this token points at a function call, this attribute has the Function @@ -186,7 +187,8 @@ class Token: isUnsigned = False isSigned = False isExpandedMacro = False - isSplittedVarDecl = False + isSplittedVarDeclComma = False + isSplittedVarDeclEq = False varId = None variableId = None variable = None @@ -247,8 +249,10 @@ class Token: self.isLogicalOp = True if element.get('isExpandedMacro'): self.isExpandedMacro = True - if element.get('isSplittedVarDecl'): - self.isSplittedVarDecl = True + if element.get('isSplittedVarDeclComma'): + self.isSplittedVarDeclComma = True + if element.get('isSplittedVarDeclEq'): + self.isSplittedVarDeclEq = True self.linkId = element.get('link') self.link = None if element.get('varId'): @@ -279,10 +283,10 @@ class Token: attrs = ["Id", "str", "scopeId", "isName", "isUnsigned", "isSigned", "isNumber", "isInt", "isFloat", "isString", "strlen", "isChar", "isOp", "isArithmeticalOp", "isComparisonOp", - "isLogicalOp", "isExpandedMacro", "isSplittedVarDecl", - "linkId", "varId", "variableId", "functionId", "valuesId", - "valueType", "typeScopeId", "astParentId", "astOperand1Id", - "file", "linenr", "column"] + "isLogicalOp", "isExpandedMacro", "isSplittedVarDeclComma", + "isSplittedVarDeclEq","linkId", "varId", "variableId", + "functionId", "valuesId", "valueType", "typeScopeId", + "astParentId", "astOperand1Id", "file", "linenr", "column"] return "{}({})".format( "Token", ", ".join(("{}={}".format(a, repr(getattr(self, a))) for a in attrs)) diff --git a/addons/misra.py b/addons/misra.py index bb8274368..4ee33b739 100755 --- a/addons/misra.py +++ b/addons/misra.py @@ -1640,7 +1640,7 @@ class MisraChecker: def misra_12_3(self, data): for token in data.tokenlist: - if token.str == ';' and (token.isSplittedVarDecl is True): + if token.str == ';' and (token.isSplittedVarDeclComma is True): self.reportError(token, 12, 3) if token.str == ',' and token.astParent and token.astParent.str == ';': self.reportError(token, 12, 3) diff --git a/cfg/wxwidgets.cfg b/cfg/wxwidgets.cfg index 883be5969..c860016d4 100644 --- a/cfg/wxwidgets.cfg +++ b/cfg/wxwidgets.cfg @@ -14381,4 +14381,17 @@ wxItemKind kind = wxITEM_NORMAL) --> + + + false + + + + + + + false + + + diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 1f75dd775..80dde21d6 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -248,6 +248,9 @@ bool isTemporary(bool cpp, const Token* tok, const Library* library, bool unknow if (Token::Match(tok, "&|<<|>>") && isLikelyStream(cpp, tok->astOperand1())) return false; if (Token::Match(tok->previous(), ">|%name% (")) { + if (tok->valueType()) { + return tok->valueType()->reference == Reference::None; + } const Token* ftok = nullptr; if (tok->previous()->link()) ftok = tok->previous()->link()->previous(); @@ -956,7 +959,7 @@ bool isOppositeCond(bool isNot, bool cpp, const Token * const cond1, const Token if (!cond1 || !cond2) return false; - if (cond1->str() == "&&" && cond2->str() == "&&") { + if (!isNot && cond1->str() == "&&" && cond2->str() == "&&") { for (const Token* tok1: { cond1->astOperand1(), cond1->astOperand2() }) { diff --git a/lib/bughuntingchecks.cpp b/lib/bughuntingchecks.cpp index ba78a4cfc..85e3eec24 100644 --- a/lib/bughuntingchecks.cpp +++ b/lib/bughuntingchecks.cpp @@ -306,7 +306,7 @@ static void uninit(const Token *tok, const ExprEngine::Value &value, ExprEngine: if (!var->isLocal() || var->isStatic()) return; } - if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || Token::Match(var->nameToken(), "%varid% ; %varid% =", var->declarationId()))) + if (var && (Token::Match(var->nameToken(), "%name% [=:({)]") || var->isInit())) return; if (var && var->nameToken() == tok) return; diff --git a/lib/checkautovariables.cpp b/lib/checkautovariables.cpp index 9402faaf3..a1d99f1d7 100644 --- a/lib/checkautovariables.cpp +++ b/lib/checkautovariables.cpp @@ -528,6 +528,16 @@ void CheckAutoVariables::checkVarLifetimeScope(const Token * start, const Token errorDanglingReference(tok, var, errorPath); continue; } + // Reference to temporary + } else if (tok->variable() && (tok->variable()->isReference() || tok->variable()->isRValueReference())) { + for (const LifetimeToken& lt : getLifetimeTokens(getParentLifetime(tok))) { + const Token * tokvalue = lt.token; + if (isDeadTemporary(mTokenizer->isCPP(), tokvalue, tok, &mSettings->library)) { + errorDanglingTempReference(tok, lt.errorPath, lt.inconclusive); + break; + } + } + } for (const ValueFlow::Value& val:tok->values()) { if (!val.isLocalLifetimeValue() && !val.isSubFunctionLifetimeValue()) @@ -644,6 +654,13 @@ void CheckAutoVariables::errorDanglngLifetime(const Token *tok, const ValueFlow: reportError(errorPath, Severity::error, "danglingLifetime", msg + ".", CWE562, inconclusive); } +void CheckAutoVariables::errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive) +{ + errorPath.emplace_back(tok, ""); + reportError( + errorPath, Severity::error, "danglingTempReference", "Using reference to dangling temporary.", CWE562, inconclusive); +} + void CheckAutoVariables::errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive) { errorPath.emplace_back(tok, ""); diff --git a/lib/checkautovariables.h b/lib/checkautovariables.h index 7087aacef..99356ca56 100644 --- a/lib/checkautovariables.h +++ b/lib/checkautovariables.h @@ -79,6 +79,7 @@ private: void errorDanglingTemporaryLifetime(const Token* tok, const ValueFlow::Value* val); void errorReturnReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorDanglingReference(const Token *tok, const Variable *var, ErrorPath errorPath); + void errorDanglingTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorReturnTempReference(const Token* tok, ErrorPath errorPath, bool inconclusive); void errorInvalidDeallocation(const Token *tok, const ValueFlow::Value *val); void errorReturnAddressOfFunctionParameter(const Token *tok, const std::string &varname); @@ -94,6 +95,7 @@ private: c.errorReturnReference(nullptr, errorPath, false); c.errorDanglingReference(nullptr, nullptr, errorPath); c.errorReturnTempReference(nullptr, errorPath, false); + c.errorDanglingTempReference(nullptr, errorPath, false); c.errorInvalidDeallocation(nullptr, nullptr); c.errorReturnAddressOfFunctionParameter(nullptr, "parameter"); c.errorUselessAssignmentArg(nullptr); diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index f513a3285..2c45f8e9a 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -111,11 +111,7 @@ void CheckClass::constructors() if (scope->numConstructors == 0 && printStyle && !usedInUnion) { // If there is a private variable, there should be a constructor.. for (const Variable &var : scope->varlist) { - const Token *initTok = var.nameToken(); - while (Token::simpleMatch(initTok->next(), "[")) - initTok = initTok->linkAt(1); - if (var.isPrivate() && !var.isStatic() && !Token::Match(var.nameToken(), "%varid% ; %varid% =", var.declarationId()) && - !Token::Match(initTok, "%var%|] {|=") && + if (var.isPrivate() && !var.isStatic() && !var.isInit() && (!var.isClass() || (var.type() && var.type()->needInitialization == Type::NeedInitialization::True))) { noConstructorError(scope->classDef, scope->className, scope->classDef->str() == "struct"); break; @@ -947,9 +943,15 @@ void CheckClass::initializationListUsage() for (const Scope *scope : mSymbolDatabase->functionScopes) { // Check every constructor - if (!scope->function || (!scope->function->isConstructor())) + if (!scope->function || !scope->function->isConstructor()) continue; + // Do not warn when a delegate constructor is called + if (const Token *initList = scope->function->constructorMemberInitialization()) { + if (Token::Match(initList, ": %name% {|(") && initList->strAt(1) == scope->className) + continue; + } + const Scope* owner = scope->functionOf; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "%name% (")) // Assignments might depend on this function call or if/for/while/switch statement from now on. diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 5d395ccd7..89ba23d8c 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -432,7 +432,7 @@ void CheckOther::checkRedundantAssignment() // Do not warn about redundant initialization when rhs is trivial // TODO : do not simplify the variable declarations bool isInitialization = false; - if (Token::Match(tok->tokAt(-3), "%var% ; %var% =") && tok->previous()->variable() && tok->previous()->variable()->nameToken() == tok->tokAt(-3) && tok->tokAt(-3)->linenr() == tok->previous()->linenr()) { + if (Token::Match(tok->tokAt(-2), "; %var% =") && tok->tokAt(-2)->isSplittedVarDeclEq()) { isInitialization = true; bool trivial = true; visitAstNodes(tok->astOperand2(), diff --git a/lib/checkstl.cpp b/lib/checkstl.cpp index 9787d203c..914753f6a 100644 --- a/lib/checkstl.cpp +++ b/lib/checkstl.cpp @@ -59,6 +59,11 @@ static const struct CWE CWE825(825U); // Expired Pointer Dereference static const struct CWE CWE833(833U); // Deadlock static const struct CWE CWE834(834U); // Excessive Iteration +static bool isElementAccessYield(const Library::Container::Yield& yield) +{ + return yield == Library::Container::Yield::ITEM || yield == Library::Container::Yield::AT_INDEX; +} + void CheckStl::outOfBounds() { for (const Scope *function : mTokenizer->getSymbolDatabase()->functionScopes) { @@ -76,9 +81,28 @@ void CheckStl::outOfBounds() continue; if (!value.errorSeverity() && !mSettings->isEnabled(Settings::WARNING)) continue; - if (value.intvalue == 0 && Token::Match(parent, ". %name% (") && container->getYield(parent->strAt(1)) == Library::Container::Yield::ITEM) { - outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr); - continue; + if (Token::Match(parent, ". %name% (") && isElementAccessYield(container->getYield(parent->strAt(1)))) { + if (value.intvalue == 0) { + outOfBoundsError(parent->tokAt(2), tok->expressionString(), &value, parent->strAt(1), nullptr); + continue; + } + const Token* indexTok = parent->tokAt(2)->astOperand2(); + if (!indexTok) + continue; + const ValueFlow::Value* indexValue = indexTok->getMaxValue(false); + if (indexValue && indexValue->intvalue >= value.intvalue) { + outOfBoundsError( + parent, tok->expressionString(), &value, indexTok->expressionString(), indexValue); + continue; + } + if (mSettings->isEnabled(Settings::WARNING)) { + indexValue = indexTok->getMaxValue(true); + if (indexValue && indexValue->intvalue >= value.intvalue) { + outOfBoundsError( + parent, tok->expressionString(), &value, indexTok->expressionString(), indexValue); + continue; + } + } } if (Token::Match(tok, "%name% . %name% (") && container->getYield(tok->strAt(2)) == Library::Container::Yield::START_ITERATOR) { const Token *fparent = tok->tokAt(3)->astParent(); diff --git a/lib/exprengine.cpp b/lib/exprengine.cpp index 9050c2c1a..a624a124d 100644 --- a/lib/exprengine.cpp +++ b/lib/exprengine.cpp @@ -2512,7 +2512,7 @@ static ExprEngine::ValuePtr createStructVal(const Scope *structScope, bool unini std::shared_ptr structValue = std::make_shared(data.getNewSymbolName()); auto uninitValue = std::make_shared(); for (const Variable &member : structScope->varlist) { - if (uninitData) { + if (uninitData && !member.isInit()) { if (member.isPointer()) { structValue->member[member.name()] = uninitValue; continue; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 5f4c5b900..8b6e5219a 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -21,6 +21,7 @@ #include "astutils.h" #include "errorlogger.h" +#include "library.h" #include "mathlib.h" #include "platform.h" #include "settings.h" @@ -1869,6 +1870,13 @@ Variable::Variable(const Token *name_, const std::string &clangType, const Token ++pos; } while (pos < clangType.size() && clangType[pos] == '['); } + + // Is there initialization in variable declaration + const Token *initTok = mNameToken ? mNameToken->next() : nullptr; + while (initTok && initTok->str() == "[") + initTok = initTok->link()->next(); + if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) + setFlag(fIsInit, true); } Variable::~Variable() @@ -1899,6 +1907,13 @@ const Token * Variable::declEndToken() const void Variable::evaluate(const Settings* settings) { + // Is there initialization in variable declaration + const Token *initTok = mNameToken ? mNameToken->next() : nullptr; + while (initTok && initTok->str() == "[") + initTok = initTok->link()->next(); + if (Token::Match(initTok, "=|{") || (initTok && initTok->isSplittedVarDeclEq())) + setFlag(fIsInit, true); + if (!settings) return; @@ -5968,7 +5983,8 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to valuetype.sign = ValueType::Sign::SIGNED; } setValueType(tok, valuetype); - } else if (tok->link() && tok->str() == "(") { + } else if (tok->link() && Token::Match(tok, "(|{")) { + const Token* start = tok->astOperand1() ? tok->astOperand1()->findExpressionStartEndTokens().first : nullptr; // cast if (tok->isCast() && !tok->astOperand2() && Token::Match(tok, "( %name%")) { ValueType valuetype; @@ -5983,6 +5999,16 @@ void SymbolDatabase::setValueTypeInTokenList(bool reportDebugWarnings, Token *to setValueType(tok, valuetype); } + // Construct smart pointer + else if (mSettings->library.isSmartPointer(start)) { + ValueType valuetype; + if (parsedecl(start, &valuetype, mDefaultSignedness, mSettings)) { + setValueType(tok, valuetype); + setValueType(tok->astOperand1(), valuetype); + } + + } + // function else if (tok->previous() && tok->previous()->function() && tok->previous()->function()->retDef) { ValueType valuetype; diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 7485883ac..df9f1aab9 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -199,6 +199,7 @@ class CPPCHECKLIB Variable { fIsVolatile = (1 << 13), /** @brief volatile */ fIsSmartPointer = (1 << 14),/** @brief std::shared_ptr|unique_ptr */ fIsMaybeUnused = (1 << 15), /** @brief marked [[maybe_unused]] */ + fIsInit = (1 << 16), /** @brief Is variable initialized in declaration */ }; /** @@ -501,6 +502,14 @@ public: return getFlag(fHasDefault); } + /** + * Is variable initialized in its declaration + * @return true if variable declaration contains initialization + */ + bool isInit() const { + return getFlag(fIsInit); + } + /** * Get Type pointer of known type. * @return pointer to type if known, NULL if not known diff --git a/lib/token.h b/lib/token.h index 981f49e4a..a93e67258 100644 --- a/lib/token.h +++ b/lib/token.h @@ -612,11 +612,18 @@ public: setFlag(fExternC, b); } - bool isSplittedVarDecl() const { - return getFlag(fIsSplitVarDecl); + bool isSplittedVarDeclComma() const { + return getFlag(fIsSplitVarDeclComma); } - void isSplittedVarDecl(bool b) { - setFlag(fIsSplitVarDecl, b); + void isSplittedVarDeclComma(bool b) { + setFlag(fIsSplitVarDeclComma, b); + } + + bool isSplittedVarDeclEq() const { + return getFlag(fIsSplitVarDeclEq); + } + void isSplittedVarDeclEq(bool b) { + setFlag(fIsSplitVarDeclEq, b); } bool isBitfield() const { @@ -1201,7 +1208,8 @@ private: fIncompleteVar = (1 << 26), fConstexpr = (1 << 27), fExternC = (1 << 28), - fIsSplitVarDecl = (1 << 29), // int a,b; <-- vardecl is split up + fIsSplitVarDeclComma = (1 << 29), // set to true when variable declarations are split up ('int a,b;' => 'int a; int b;') + fIsSplitVarDeclEq = (1 << 30) // set to true when variable declaration with initialization is split up ('int a=5;' => 'int a; a=5;') }; Token::Type mTokType; diff --git a/lib/tokenize.cpp b/lib/tokenize.cpp index fd9c7e2b5..7c73e6331 100644 --- a/lib/tokenize.cpp +++ b/lib/tokenize.cpp @@ -4974,8 +4974,10 @@ void Tokenizer::dump(std::ostream &out) const } if (tok->isExpandedMacro()) out << " isExpandedMacro=\"true\""; - if (tok->isSplittedVarDecl()) - out << " isSplittedVarDecl=\"true\""; + if (tok->isSplittedVarDeclComma()) + out << " isSplittedVarDeclComma=\"true\""; + if (tok->isSplittedVarDeclEq()) + out << " isSplittedVarDeclEq=\"true\""; if (tok->link()) out << " link=\"" << tok->link() << '\"'; if (tok->varId() > 0) @@ -6859,7 +6861,7 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co if (tok2->str() == ",") { tok2->str(";"); - tok2->isSplittedVarDecl(true); + tok2->isSplittedVarDeclComma(true); //TODO: should we have to add also template '<>' links? TokenList::insertTokens(tok2, type0, typelen); } @@ -6883,11 +6885,12 @@ void Tokenizer::simplifyVarDecl(Token * tokBegin, const Token * const tokEnd, co syntaxError(tok2); // invalid code TokenList::insertTokens(eq, varTok, 2); eq->str(";"); + eq->isSplittedVarDeclEq(true); // "= x, " => "= x; type " if (tok2->str() == ",") { tok2->str(";"); - tok2->isSplittedVarDecl(true); + tok2->isSplittedVarDeclComma(true); TokenList::insertTokens(tok2, type0, typelen); } break; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index c48410770..a030003aa 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -3062,7 +3062,7 @@ std::vector getLifetimeTokens(const Token* tok, bool escape, Valu return std::vector {}; const Token* argTok = args[n]; lt.errorPath.emplace_back(returnTok, "Return reference."); - lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->str() + "'."); + lt.errorPath.emplace_back(tok->previous(), "Called function passing '" + argTok->expressionString() + "'."); std::vector arglts = LifetimeToken::setInconclusive( getLifetimeTokens(argTok, escape, std::move(lt.errorPath), depth - 1), returns.size() > 1); result.insert(result.end(), arglts.begin(), arglts.end()); @@ -3422,7 +3422,7 @@ struct LifetimeStore { return LifetimeStore{}; } const Token *argtok2 = args[n]; - return LifetimeStore{argtok2, "Passed to '" + tok->str() + "'.", ValueFlow::Value::LifetimeKind::Object}; + return LifetimeStore{argtok2, "Passed to '" + tok->expressionString() + "'.", ValueFlow::Value::LifetimeKind::Object}; } template @@ -5650,10 +5650,8 @@ static void valueFlowUninit(TokenList *tokenlist, SymbolDatabase * /*symbolDatab // continue; if (!Token::Match(vardecl, "%var% ;")) continue; - if (Token::Match(vardecl, "%varid% ; %varid% =", vardecl->varId())) - continue; const Variable *var = vardecl->variable(); - if (!var || var->nameToken() != vardecl) + if (!var || var->nameToken() != vardecl || var->isInit()) continue; if ((!var->isPointer() && var->type() && var->type()->needInitialization != Type::NeedInitialization::True) || !var->isLocal() || var->isStatic() || var->isExtern() || var->isReference() || var->isThrow()) @@ -5957,47 +5955,55 @@ static void valueFlowSmartPointer(TokenList *tokenlist, ErrorLogger * errorLogge continue; if (!tok->scope()->isExecutable()) continue; - if (!tok->variable()) - continue; - const Variable * var = tok->variable(); - if (!var->isSmartPointer()) - continue; - if (var->nameToken() == tok) { - if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && tok->next()->astOperand2()->str() != ",") { - Token * inTok = tok->next()->astOperand2(); - std::list values = inTok->values(); - const bool constValue = inTok->isNumber(); - valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings); + if (tok->variable()) { + const Variable* var = tok->variable(); + if (!var->isSmartPointer()) + continue; + if (var->nameToken() == tok) { + if (Token::Match(tok, "%var% (|{") && tok->next()->astOperand2() && + tok->next()->astOperand2()->str() != ",") { + Token* inTok = tok->next()->astOperand2(); + std::list values = inTok->values(); + const bool constValue = inTok->isNumber(); + valueFlowForwardAssign(inTok, var, values, constValue, true, tokenlist, errorLogger, settings); - } else if (Token::Match(tok, "%var% ;")) { - std::list values; - ValueFlow::Value v(0); - v.setKnown(); - values.push_back(v); - valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings); - } - } else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") { - if (Token::simpleMatch(tok->tokAt(3), "( )")) { + } else if (Token::Match(tok, "%var% ;")) { + std::list values; + ValueFlow::Value v(0); + v.setKnown(); + values.push_back(v); + valueFlowForwardAssign(tok, var, values, false, true, tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok, "%var% . reset (") && tok->next()->originalName() != "->") { + if (Token::simpleMatch(tok->tokAt(3), "( )")) { + std::list values; + ValueFlow::Value v(0); + v.setKnown(); + values.push_back(v); + valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings); + } else { + tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue)); + Token* inTok = tok->tokAt(3)->astOperand2(); + if (!inTok) + continue; + std::list values = inTok->values(); + const bool constValue = inTok->isNumber(); + valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings); + } + } else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") { std::list values; ValueFlow::Value v(0); v.setKnown(); values.push_back(v); valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings); - } else { - tok->removeValues(std::mem_fn(&ValueFlow::Value::isIntValue)); - Token * inTok = tok->tokAt(3)->astOperand2(); - if (!inTok) - continue; - std::list values = inTok->values(); - const bool constValue = inTok->isNumber(); - valueFlowForwardAssign(inTok, var, values, constValue, false, tokenlist, errorLogger, settings); } - } else if (Token::Match(tok, "%var% . release ( )") && tok->next()->originalName() != "->") { - std::list values; - ValueFlow::Value v(0); - v.setKnown(); - values.push_back(v); - valueFlowForwardAssign(tok->tokAt(4), var, values, false, false, tokenlist, errorLogger, settings); + } else if (Token::Match(tok->previous(), "%name%|> (|{") && astIsSmartPointer(tok) && + astIsSmartPointer(tok->astOperand1())) { + std::vector args = getArguments(tok); + if (args.empty()) + continue; + for (const ValueFlow::Value& v : args.front()->values()) + setTokenValue(tok, v, settings); } } } @@ -6686,7 +6692,6 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, valueFlowGlobalStaticVar(tokenlist, settings); valueFlowPointerAlias(tokenlist); valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); - valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowBitAnd(tokenlist); valueFlowSameExpressions(tokenlist); valueFlowFwdAnalysis(tokenlist, settings); @@ -6708,6 +6713,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, valueFlowSwitchVariable(tokenlist, symboldatabase, errorLogger, settings); valueFlowForLoop(tokenlist, symboldatabase, errorLogger, settings); valueFlowSubFunction(tokenlist, symboldatabase, errorLogger, settings); + valueFlowFunctionReturn(tokenlist, errorLogger); valueFlowLifetime(tokenlist, symboldatabase, errorLogger, settings); valueFlowFunctionDefaultParameter(tokenlist, symboldatabase, errorLogger, settings); valueFlowUninit(tokenlist, symboldatabase, errorLogger, settings); diff --git a/test/testautovariables.cpp b/test/testautovariables.cpp index 6841f5f51..2b5668ff0 100644 --- a/test/testautovariables.cpp +++ b/test/testautovariables.cpp @@ -1442,7 +1442,7 @@ private: check("auto& f() {\n" " return std::vector{1}.front();\n" "}\n"); - TODO_ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:3]: (error) Reference to temporary returned.\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:2]: (error) Reference to temporary returned.\n", errout.str()); check("struct A { int foo; };\n" "int& f(std::vector v) {\n" @@ -1638,7 +1638,8 @@ private: " int& x = h();\n" " g(&x);\n" "}\n"); - ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n", errout.str()); + ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5] -> [test.cpp:5]: (error) Using pointer to temporary.\n" + "[test.cpp:4] -> [test.cpp:5]: (error) Using reference to dangling temporary.\n", errout.str()); check("void g(int*);\n" "int h();\n" @@ -1675,6 +1676,27 @@ private: ASSERT_EQUALS("", errout.str()); } + void danglingTempReference() { + check("const std::string& g(const std::string& str_cref) {\n" + " return str_cref;\n" + "}\n" + "void f() {\n" + " const auto& str_cref2 = g(std::string(\"hello\"));\n" + " std::cout << str_cref2 << std::endl;\n" + "}\n"); + ASSERT_EQUALS("error", errout.str()); + + // Lifetime extended + check("std::string g(const std::string& str_cref) {\n" + " return str_cref;\n" + "}\n" + "void f() {\n" + " const auto& str_cref2 = g(std::string(\"hello\"));\n" + " std::cout << str_cref2 << std::endl;\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); + } + void testglobalnamespace() { check("class SharedPtrHolder\n" "{\n" diff --git a/test/testbughuntingchecks.cpp b/test/testbughuntingchecks.cpp index f4aba5233..5229224b4 100644 --- a/test/testbughuntingchecks.cpp +++ b/test/testbughuntingchecks.cpp @@ -44,6 +44,7 @@ private: TEST_CASE(uninit_bailout); TEST_CASE(uninit_fp_smartptr); TEST_CASE(uninit_fp_struct); + TEST_CASE(uninit_fp_struct_member_init_2); TEST_CASE(uninit_fp_template_var); TEST_CASE(ctu); #endif @@ -202,6 +203,16 @@ private: ASSERT_EQUALS("", errout.str()); } + void uninit_fp_struct_member_init_2() { + check("struct A {\n" + " int x {0}; int y {0};\n" + "};\n" + "void foo(const A& a) {\n" + " bar(a);\n" + "}"); + ASSERT_EQUALS("", errout.str()); + } + void uninit_fp_template_var() { check("void foo() {\n" " X*x = DYNAMIC_CAST(X, p);\n" diff --git a/test/testclass.cpp b/test/testclass.cpp index eaa197725..34a1d6104 100644 --- a/test/testclass.cpp +++ b/test/testclass.cpp @@ -6567,6 +6567,19 @@ private: " Foo m_i;\n" "};"); ASSERT_EQUALS("", errout.str()); + + checkInitializationListUsage("class A {\n" // #9821 - delegate constructor + "public:\n" + " A() : st{} {}\n" + "\n" + " explicit A(const std::string &input): A() {\n" + " st = input;\n" + " }\n" + "\n" + "private:\n" + " std::string st;\n" + "};"); + ASSERT_EQUALS("", errout.str()); } diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 823947033..5b41aa0e9 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -3658,6 +3658,15 @@ private: " if(b[1] == 2) {}\n" "}\n"); ASSERT_EQUALS("[test.cpp:3]: (style) Condition 'b[1]==2' is always false\n", errout.str()); + + // #9878 + check("void f(bool a, bool b) {\n" + " if (a && b){;}\n" + " else if (!a && b){;}\n" + " else if (!a && !b){;}\n" + " else {;}\n" + "}\n"); + ASSERT_EQUALS("", errout.str()); } void duplicateCondition() { diff --git a/test/testnullpointer.cpp b/test/testnullpointer.cpp index 3c7c1421e..11b77f6b0 100644 --- a/test/testnullpointer.cpp +++ b/test/testnullpointer.cpp @@ -2859,6 +2859,16 @@ private: " return x.release();\n" "}\n", true); ASSERT_EQUALS("", errout.str()); + + // #9496 + check("std::shared_ptr f() {\n" + " return std::shared_ptr(nullptr);\n" + "}\n" + "void g() {\n" + " int a = *f();\n" + "}\n", + true); + ASSERT_EQUALS("[test.cpp:5]: (error) Null pointer dereference: f()\n", errout.str()); } void functioncall() { // #3443 - function calls diff --git a/test/testother.cpp b/test/testother.cpp index 99f688320..22b5f7c76 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -5310,7 +5310,7 @@ private: check("void f(int* x, bool b) {\n" " if ((!x && b) || (x != 0 && b)) {}\n" "}\n"); - ASSERT_EQUALS("[test.cpp:2]: (style) Opposite expression on both sides of '||'.\n", errout.str()); + ASSERT_EQUALS("", errout.str()); } void oppositeExpression() { diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 2e4984f56..1a177dc68 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -1152,8 +1152,9 @@ private: ASSERT_EQUALS(expected, tok(code, false)); ASSERT_EQUALS_WITHOUT_LINENUMBERS( - "[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n" - , errout.str()); + "[test.cpp:31]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n" + "[test.cpp:28]: (debug) valueflow.cpp:3109:valueFlowFunctionReturn bailout: function return; nontrivial function body\n", + errout.str()); } void simplifyTypedef36() { diff --git a/test/teststl.cpp b/test/teststl.cpp index 4fc0226d1..af953104f 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -347,6 +347,24 @@ private: " x[0] = 2;\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + checkNormal("void f(bool b) {\n" + " std::vector v;\n" + " if(v.at(b?42:0)) {}\n" + "}\n"); + ASSERT_EQUALS( + "test.cpp:3:error:Out of bounds access in expression 'v.at(b?42:0)' because 'v' is empty and 'at' may be non-zero.\n", + errout.str()); + + checkNormal("void f(std::vector v, bool b){\n" + " if (v.size() == 1)\n" + " if(v.at(b?42:0)) {}\n" + "}\n"); + ASSERT_EQUALS( + "test.cpp:3:warning:Either the condition 'v.size()==1' is redundant or v size can be 1. Expression 'v.at' cause access out of bounds.\n" + "test.cpp:2:note:condition 'v.size()==1'\n" + "test.cpp:3:note:Access out of bounds\n", + errout.str()); } void outOfBoundsIndexExpression() {