diff --git a/lib/astutils.cpp b/lib/astutils.cpp index b0497ac70..8fbc2d618 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1668,7 +1668,8 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn) return nullptr; if (!Token::Match(tok, "{|(")) return nullptr; - tok = tok->astOperand1(); + if (tok->astOperand2()) + tok = tok->astOperand1(); while (tok && (tok->isUnaryOp("*") || tok->str() == "[")) tok = tok->astOperand1(); while (Token::simpleMatch(tok, ".")) @@ -1690,21 +1691,31 @@ const Token * getTokenArgumentFunction(const Token * tok, int& argn) return tok; } -static std::vector getArgumentVars(const Token* tok, int argnr) +std::vector getArgumentVars(const Token* tok, int argnr) { std::vector result; if (!tok) return result; - if (tok->function()) - return {tok->function()->getArgumentVar(argnr)}; - if (Token::Match(tok->previous(), "%type% (|{") || tok->variable()) { - const bool constructor = tok->variable() && tok->variable()->nameToken() == tok; + if (tok->function()) { + const Variable* argvar = tok->function()->getArgumentVar(argnr); + if (argvar) + return {argvar}; + else + return result; + } + if (Token::Match(tok->previous(), "%type% (|{") || Token::simpleMatch(tok, "{") || tok->variable()) { + const bool constructor = Token::simpleMatch(tok, "{") || (tok->variable() && tok->variable()->nameToken() == tok); const Type* type = Token::typeOf(tok); if (!type) return result; const Scope* typeScope = type->classScope; if (!typeScope) return result; + // Aggregate constructor + if (Token::simpleMatch(tok, "{") && typeScope->numConstructors == 0 && argnr < typeScope->varlist.size()) { + auto it = std::next(typeScope->varlist.begin(), argnr); + return {&*it}; + } const int argCount = numberOfArguments(tok); for (const Function &function : typeScope->functionList) { if (function.argCount() < argCount) @@ -1713,7 +1724,9 @@ static std::vector getArgumentVars(const Token* tok, int argnr) continue; if (!constructor && !Token::simpleMatch(function.token, "operator()")) continue; - result.push_back(function.getArgumentVar(argnr)); + const Variable* argvar = function.getArgumentVar(argnr); + if (argvar) + result.push_back(argvar); } } return result; @@ -1726,6 +1739,17 @@ static bool isCPPCastKeyword(const Token* tok) return endsWith(tok->str(), "_cast", 5); } +static bool isTrivialConstructor(const Token* tok) +{ + const Token* typeTok = nullptr; + const Type* t = Token::typeOf(tok, &typeTok); + if (t) + return false; + if (typeTok->valueType() && typeTok->valueType()->isPrimitive()) + return true; + return false; +} + bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Settings *settings, bool *inconclusive) { if (!tok) @@ -1743,6 +1767,8 @@ bool isVariableChangedByFunctionCall(const Token *tok, int indirect, const Setti tok = getTokenArgumentFunction(tok, argnr); if (!tok) return false; // not a function => variable not changed + if (Token::simpleMatch(tok, "{") && isTrivialConstructor(tok)) + return false; if (tok->isKeyword() && !isCPPCastKeyword(tok)) return false; const Token * parenTok = tok->next(); @@ -2303,19 +2329,19 @@ std::vector getLHSVariables(const Token* tok) return result; } -static const Variable *getLHSVariableRecursive(const Token *tok) +static const Token* getLHSVariableRecursive(const Token* tok) { if (!tok) return nullptr; if (Token::Match(tok, "*|&|&&|[")) { - const Variable *var = getLHSVariableRecursive(tok->astOperand1()); - if (var || Token::simpleMatch(tok, "[")) - return var; + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if ((vartok && vartok->variable()) || Token::simpleMatch(tok, "[")) + return vartok; return getLHSVariableRecursive(tok->astOperand2()); } if (Token::Match(tok->previous(), "this . %var%")) - return tok->next()->variable(); - return tok->variable(); + return tok->next(); + return tok; } const Variable *getLHSVariable(const Token *tok) @@ -2326,7 +2352,24 @@ const Variable *getLHSVariable(const Token *tok) return nullptr; if (tok->astOperand1()->varId() > 0 && tok->astOperand1()->variable()) return tok->astOperand1()->variable(); - return getLHSVariableRecursive(tok->astOperand1()); + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if (!vartok) + return nullptr; + return vartok->variable(); +} + +const Token* getLHSVariableToken(const Token* tok) +{ + if (!Token::Match(tok, "%assign%")) + return nullptr; + if (!tok->astOperand1()) + return nullptr; + if (tok->astOperand1()->varId() > 0) + return tok->astOperand1(); + const Token* vartok = getLHSVariableRecursive(tok->astOperand1()); + if (!vartok) + return tok->astOperand1(); + return vartok; } const Token* findAllocFuncCallToken(const Token *expr, const Library &library) diff --git a/lib/astutils.h b/lib/astutils.h index 6b39d5930..a2598e05c 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -188,6 +188,8 @@ bool isReturnScope(const Token* const endToken, /// Return the token to the function and the argument number const Token * getTokenArgumentFunction(const Token * tok, int& argn); +std::vector getArgumentVars(const Token* tok, int argnr); + /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true @@ -279,6 +281,8 @@ bool isConstVarExpression(const Token *tok, const char * skipMatch = nullptr); const Variable *getLHSVariable(const Token *tok); +const Token* getLHSVariableToken(const Token* tok); + std::vector getLHSVariables(const Token* tok); /** Find a allocation function call in expression, so result of expression is allocated memory/resource. */ diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 13fc03c67..78ae0d63e 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1273,6 +1273,8 @@ public: static MatchResult matchParameter(const ValueType *call, const ValueType *func); static MatchResult matchParameter(const ValueType *call, const Variable *callVar, const Variable *funcVar); + bool isPrimitive() const { return (type >= ValueType::Type::BOOL); } + bool isIntegral() const { return (type >= ValueType::Type::BOOL && type <= ValueType::Type::UNKNOWN_INT); } diff --git a/lib/token.cpp b/lib/token.cpp index dbecf7001..3c1ecd0fa 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -2257,10 +2257,12 @@ void Token::type(const ::Type *t) tokType(eName); } -const ::Type *Token::typeOf(const Token *tok) +const ::Type* Token::typeOf(const Token* tok, const Token** typeTok) { if (!tok) return nullptr; + if (typeTok != nullptr) + *typeTok = tok; if (Token::simpleMatch(tok, "return")) { const Scope *scope = tok->scope(); if (!scope) @@ -2282,14 +2284,30 @@ const ::Type *Token::typeOf(const Token *tok) return nullptr; return function->retType; } else if (Token::Match(tok->previous(), "%type%|= (|{")) { - return typeOf(tok->previous()); + return typeOf(tok->previous(), typeTok); } else if (Token::simpleMatch(tok, "=")) { - return Token::typeOf(tok->astOperand1()); + return Token::typeOf(getLHSVariableToken(tok), typeTok); } else if (Token::simpleMatch(tok, ".")) { - return Token::typeOf(tok->astOperand2()); + return Token::typeOf(tok->astOperand2(), typeTok); } else if (Token::simpleMatch(tok, "[")) { - return Token::typeOf(tok->astOperand1()); + return Token::typeOf(tok->astOperand1(), typeTok); + } else if (Token::simpleMatch(tok, "{")) { + int argnr; + const Token* ftok = getTokenArgumentFunction(tok, argnr); + if (argnr < 0) + return nullptr; + if (!ftok) + return nullptr; + if (ftok == tok) + return nullptr; + std::vector vars = getArgumentVars(ftok, argnr); + if (vars.empty()) + return nullptr; + if (std::all_of( + vars.begin(), vars.end(), [&](const Variable* var) { return var->type() == vars.front()->type(); })) + return vars.front()->type(); } + return nullptr; } diff --git a/lib/token.h b/lib/token.h index 1d8ae6c50..0cc3e1f32 100644 --- a/lib/token.h +++ b/lib/token.h @@ -1010,7 +1010,7 @@ public: return mTokType == eType ? mImpl->mType : nullptr; } - static const ::Type *typeOf(const Token *tok); + static const ::Type* typeOf(const Token* tok, const Token** typeTok = nullptr); static std::pair typeDecl(const Token * tok); diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 18590b8a8..c38575bb1 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -723,6 +723,8 @@ static void setTokenValue(Token* tok, const ValueFlow::Value &value, const Setti for (const ValueFlow::Value &val : tok->values()) { if (!val.isIntValue()) continue; + if (val.isImpossible() && val.intvalue != 0) + continue; ValueFlow::Value v(val); v.intvalue = !v.intvalue; setTokenValue(parent, v, settings); diff --git a/test/teststl.cpp b/test/teststl.cpp index 5059e6162..ac10d4f08 100644 --- a/test/teststl.cpp +++ b/test/teststl.cpp @@ -381,6 +381,86 @@ private: "}\n", true); ASSERT_EQUALS("", errout.str()); + check("struct T {\n" + " std::vector* v;\n" + "};\n" + "struct S {\n" + " std::vector t;\n" + "};\n" + "long g(S& s);\n" + "int f() {\n" + " std::vector ArrS;\n" + " S s = { { { &ArrS } } };\n" + " g(s);\n" + " return ArrS[0];\n" + "}\n", + true); + ASSERT_EQUALS("", errout.str()); + + check("struct T {\n" + " std::vector* v;\n" + "};\n" + "struct S {\n" + " std::vector> t;\n" + "};\n" + "long g(S& s);\n" + "int f() {\n" + " std::vector ArrS;\n" + " S s = { { { { &ArrS } } } };\n" + " g(s);\n" + " return ArrS[0];\n" + "}\n", + true); + ASSERT_EQUALS("", errout.str()); + + check("struct T {\n" + " std::vector* v;\n" + "};\n" + "struct S {\n" + " T t;\n" + "};\n" + "long g(S& s);\n" + "int f() {\n" + " std::vector ArrS;\n" + " S s { { &ArrS } };\n" + " g(s);\n" + " return ArrS[0];\n" + "}\n", + true); + ASSERT_EQUALS("", errout.str()); + + check("struct T {\n" + " std::vector* v;\n" + "};\n" + "struct S {\n" + " std::vector t;\n" + "};\n" + "long g(S& s);\n" + "int f() {\n" + " std::vector ArrS;\n" + " S s { { { &ArrS } } };\n" + " g(s);\n" + " return ArrS[0];\n" + "}\n", + true); + ASSERT_EQUALS("", errout.str()); + + check("struct T {\n" + " std::vector* v;\n" + "};\n" + "struct S {\n" + " std::vector> t;\n" + "};\n" + "long g(S& s);\n" + "int f() {\n" + " std::vector ArrS;\n" + " S s { { { { &ArrS } } } };\n" + " g(s);\n" + " return ArrS[0];\n" + "}\n", + true); + ASSERT_EQUALS("", errout.str()); + checkNormal("extern void Bar(const double, const double);\n" "void f(std::vector &r, const double ) {\n" " std::vector result;\n" diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index a9e80dabc..772b4e85a 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -141,6 +141,7 @@ private: TEST_CASE(valueFlowUnknownMixedOperators); TEST_CASE(valueFlowIdempotent); TEST_CASE(valueFlowUnsigned); + TEST_CASE(valueFlowMod); } static bool isNotTokValue(const ValueFlow::Value &val) { @@ -2643,6 +2644,20 @@ private: ASSERT_EQUALS(false, testValueOfXKnown(code, 5U, 1)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 0)); ASSERT_EQUALS(false, testValueOfXImpossible(code, 5U, 1)); + + code = "auto f(int i) {\n" + " if (i == 0) return;\n" + " auto x = !i;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, 1)); + + code = "auto f(int i) {\n" + " if (i == 1) return;\n" + " auto x = !i;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0)); } void valueFlowAfterConditionExpr() { @@ -5638,6 +5653,23 @@ private: ASSERT_EQUALS(false, testValueOfXImpossible(code, 4U, 0)); ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, -1)); } + + void valueFlowMod() { + const char *code; + + code = "auto f(int i) {\n" + " auto x = i % 2;\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, 2)); + + code = "auto f(int i) {\n" + " auto x = !(i % 2);\n" + " return x;\n" + "}\n"; + ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 0)); + ASSERT_EQUALS(false, testValueOfXImpossible(code, 3U, 1)); + } }; REGISTER_TEST(TestValueFlow)