diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index f04392d51..c2bec76ef 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -63,6 +63,7 @@ SymbolDatabase::SymbolDatabase(const Tokenizer *tokenizer, const Settings *setti createSymbolDatabaseSetFunctionPointers(true); createSymbolDatabaseSetTypePointers(); createSymbolDatabaseEnums(); + createSymbolDatabaseIncompleteVars(); } static const Token* skipScopeIdentifiers(const Token* tok) @@ -1280,6 +1281,105 @@ void SymbolDatabase::createSymbolDatabaseEnums() } } +void SymbolDatabase::createSymbolDatabaseIncompleteVars() +{ + const std::set cpp20keywords = { + "alignas", + "alignof", + "axiom", + "co_await", + "co_return", + "co_yield", + "concept", + "synchronized", + "consteval", + "reflexpr", + "requires", + }; + const std::set cppkeywords = { + "asm", + "auto", + "catch", + "char", + "class", + "const", + "constexpr", + "decltype", + "default", + "do", + "enum", + "explicit", + "export", + "extern", + "final", + "friend", + "inline", + "mutable", + "namespace", + "new", + "noexcept", + "override", + "private", + "protected", + "public", + "register", + "sizeof", + "static", + "static_assert", + "struct", + "template", + "this", + "thread_local", + "throw", + "try", + "typedef", + "typeid", + "typename", + "union", + "using", + "virtual", + "void", + "volatile", + }; + for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) { + const Scope * scope = tok->scope(); + if (!scope) + continue; + if (!scope->isExecutable()) + continue; + if (!Token::Match(tok, "%name%")) + continue; + if (!tok->isNameOnly()) + continue; + if (Token::Match(tok, "%var%")) + continue; + if (tok->type()) + continue; + if (Token::Match(tok->next(), "::|.|(|:|%var%")) + continue; + if (Token::Match(tok->next(), "&|&&|* )|%var%")) + continue; + if (Token::simpleMatch(tok->next(), ")") && Token::simpleMatch(tok->next()->link()->previous(), "catch (")) + continue; + // Very likely a typelist + if (Token::Match(tok->tokAt(-2), "%type% ,")) + continue; + // Inside template brackets + if (Token::Match(tok->next(), "<|>") && tok->next()->link()) + continue; + if (Token::simpleMatch(tok->previous(), "<") && tok->previous()->link()) + continue; + // Skip goto labels + if (Token::simpleMatch(tok->previous(), "goto")) + continue; + if (cppkeywords.count(tok->str()) > 0) + continue; + if (mSettings->standards.cpp >= Standards::CPP20 && cpp20keywords.count(tok->str()) > 0) + continue; + const_cast(tok)->isIncompleteVar(true); + } +} + void SymbolDatabase::setArrayDimensionsUsingValueFlow() { // set all unknown array dimensions diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 5197a6344..cb72325f7 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1269,6 +1269,7 @@ private: void createSymbolDatabaseSetVariablePointers(); void createSymbolDatabaseSetTypePointers(); void createSymbolDatabaseEnums(); + void createSymbolDatabaseIncompleteVars(); void addClassFunction(Scope **scope, const Token **tok, const Token *argStart); Function *addGlobalFunctionDecl(Scope*& scope, const Token* tok, const Token *argStart, const Token* funcStart); diff --git a/lib/token.h b/lib/token.h index 9005b1dba..6019f796f 100644 --- a/lib/token.h +++ b/lib/token.h @@ -370,6 +370,9 @@ public: bool isName() const { return getFlag(fIsName); } + bool isNameOnly() const { + return mFlags == fIsName && mTokType == eName; + } bool isUpperCaseName() const; bool isLiteral() const { return getFlag(fIsLiteral); @@ -556,6 +559,13 @@ public: void isAtAddress(bool b) { setFlag(fAtAddress, b); } + bool isIncompleteVar() const { + return getFlag(fIncompleteVar); + } + void isIncompleteVar(bool b) { + setFlag(fIncompleteVar, b); + } + bool isBitfield() const { return mImpl->mBits > 0; @@ -1089,6 +1099,7 @@ private: fIsTemplateArg = (1 << 22), fIsAttributeNodiscard = (1 << 23), // __attribute__ ((warn_unused_result)), [[nodiscard]] fAtAddress = (1 << 24), // @ 0x4000 + fIncompleteVar = (1 << 25), }; Token::Type mTokType; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 309fb5f39..4eaa571c4 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -1222,17 +1222,20 @@ static void valueFlowSameExpressions(TokenList *tokenlist) } } -static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, const Settings *settings) +static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* symboldatabase, ErrorLogger *errorLogger, const Settings *settings) { - (void)tokenlist; - (void)symboldatabase; - (void)settings; - /* TODO : this is commented out until #8924 is fixed (There is a test case with the comment #8924) const bool cpp = symboldatabase->isCPP(); typedef std::pair Condition; for (const Scope * scope : symboldatabase->functionScopes) { + bool skipFunction = false; std::vector conds; for (const Token* tok = scope->bodyStart; tok != scope->bodyEnd; tok = tok->next()) { + if (tok->isIncompleteVar()) { + if (settings->debugwarnings) + bailout(tokenlist, errorLogger, tok, "Skipping function due to incomplete variable " + tok->str()); + skipFunction = true; + break; + } if (!Token::simpleMatch(tok, "if (")) continue; // Skip known values @@ -1282,8 +1285,9 @@ static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* } } conds.emplace_back(condTok->astOperand2(), condScope); - } + if (skipFunction) + break; for (Condition cond:conds) { if (!cond.first) continue; @@ -1324,7 +1328,6 @@ static void valueFlowTerminatingCondition(TokenList *tokenlist, SymbolDatabase* } } } - */ } static bool getExpressionRange(const Token *expr, MathLib::bigint *minvalue, MathLib::bigint *maxvalue) @@ -5686,7 +5689,7 @@ void ValueFlow::setValues(TokenList *tokenlist, SymbolDatabase* symboldatabase, valueFlowArrayBool(tokenlist); valueFlowRightShift(tokenlist, settings); valueFlowOppositeCondition(symboldatabase, settings); - valueFlowTerminatingCondition(tokenlist, symboldatabase, settings); + valueFlowTerminatingCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowBeforeCondition(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterMove(tokenlist, symboldatabase, errorLogger, settings); valueFlowAfterAssign(tokenlist, symboldatabase, errorLogger, settings); diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 5fd73318b..519097f0a 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -2477,7 +2477,7 @@ private: " if (x > 100) {}\n" "}"); // TODO: we should probably throw unknownMacro InternalError. Complain that the macro X must be defined. We can't check the code well without the definition. - TODO_ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", "", errout.str()); + ASSERT_EQUALS("[test.cpp:2] -> [test.cpp:4]: (style) Condition 'x>100' is always false\n", errout.str()); check("void f(const int *i) {\n" " if (!i) return;\n" @@ -2540,6 +2540,17 @@ private: " if (ch != '|') {}\n" "}\n"); ASSERT_EQUALS("", errout.str()); + + // #8924 + check("struct A {\n" + " void f() {\n" + " if (this->FileIndex >= 0) return;\n" + " this->FileIndex = 1 ;\n" + " if (this->FileIndex < 0) return;\n" + " }\n" + " int FileIndex; \n" + "};\n"); + ASSERT_EQUALS("", errout.str()); } void innerConditionModified() { diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index 9a0f28afb..e17647a9f 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -1657,7 +1657,7 @@ private: "( ( int ( * * ( * ) ( char * , char * , int , int ) ) ( ) ) global [ 6 ] ) ( \"assoc\" , \"eggdrop\" , 106 , 0 ) ; " "}"; ASSERT_EQUALS(expected, tok(code)); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp:1319:valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable global\n", errout.str()); } void simplifyTypedef68() { // ticket #2355 @@ -1864,7 +1864,7 @@ private: " B * b = new B;\n" " b->f = new A::F * [ 10 ];\n" "}"); - ASSERT_EQUALS("", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:12]: (debug) valueflow.cpp:1319:valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable idx\n", errout.str()); } void simplifyTypedef83() { // ticket #2620 diff --git a/test/testsymboldatabase.cpp b/test/testsymboldatabase.cpp index a292028ed..7f8b13843 100644 --- a/test/testsymboldatabase.cpp +++ b/test/testsymboldatabase.cpp @@ -2525,7 +2525,8 @@ private: // ticket #2991 - segmentation fault check("::y(){x}"); - ASSERT_EQUALS("[test.cpp:1]: (debug) Executable scope 'y' with unknown function.\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:1]: (debug) Executable scope 'y' with unknown function.\n" + "[test.cpp:1]: (debug) valueflow.cpp:1321:valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable x\n", errout.str()); } void symboldatabase20() { diff --git a/test/testvalueflow.cpp b/test/testvalueflow.cpp index bba2e27fd..8efde00cd 100644 --- a/test/testvalueflow.cpp +++ b/test/testvalueflow.cpp @@ -1119,7 +1119,8 @@ private: " x = y;\n" " if (x == 123) {}\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1035:valueFlowReverse bailout: assignment of x\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n" + "[test.cpp:2]: (debug) valueflow.cpp::valueFlowReverse bailout: assignment of x\n", errout.str()); } void valueFlowBeforeConditionAndAndOrOrGuard() { // guarding by && @@ -1234,7 +1235,8 @@ private: bailout("void f(int x) {\n" " y = ((x<0) ? x : ((x==2)?3:4));\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable y\n" + "[test.cpp:2]: (debug) valueflow.cpp:1113:valueFlowReverse bailout: no simplification of x within ?: expression\n", errout.str()); bailout("int f(int x) {\n" " int r = x ? 1 / x : 0;\n" @@ -1298,7 +1300,8 @@ private: " if (x != 123) { b = x; }\n" " if (x == 123) {}\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:2]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable b\n" + "[test.cpp:2]: (debug) valueflow.cpp:1144:valueFlowReverse bailout: variable x stopping on }\n", errout.str()); code = "void f(int x) {\n" " a = x;\n" @@ -1338,7 +1341,8 @@ private: " case 2: if (x==5) {} break;\n" " };\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n" + "[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on break\n", errout.str()); bailout("void f(int x, int y) {\n" " switch (y) {\n" @@ -1346,7 +1350,8 @@ private: " case 2: if (x==5) {} break;\n" " };\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n" + "[test.cpp:3]: (debug) valueflow.cpp:1180:valueFlowReverse bailout: variable x stopping on return\n", errout.str()); } void valueFlowBeforeConditionMacro() { @@ -1356,14 +1361,16 @@ private: " a = x;\n" " M;\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n" + "[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str()); bailout("#define FREE(obj) ((obj) ? (free((char *) (obj)), (obj) = 0) : 0)\n" // #8349 "void f(int *x) {\n" " a = x;\n" " FREE(x);\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str()); + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n" + "[test.cpp:4]: (debug) valueflow.cpp:1260:valueFlowBeforeCondition bailout: variable x, condition is defined in macro\n", errout.str()); } void valueFlowBeforeConditionGoto() { @@ -1374,7 +1381,8 @@ private: "out:" " if (x==123){}\n" "}"); - ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:4]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable x stopping on goto label\n" + ASSERT_EQUALS_WITHOUT_LINENUMBERS("[test.cpp:3]: (debug) valueflow.cpp::valueFlowTerminatingCondition bailout: Skipping function due to incomplete variable a\n" + "[test.cpp:4]: (debug) valueflow.cpp:1131:valueFlowReverse bailout: variable x stopping on goto label\n" "[test.cpp:2]: (debug) valueflow.cpp:1813:valueFlowForward bailout: variable x. noreturn conditional scope.\n" , errout.str()); @@ -3576,7 +3584,7 @@ private: " if (i == j) return;\n" " if(i != j) {}\n" "}\n"; - TODO_ASSERT_EQUALS(true, false, valueOfTok(code, "!=").intvalue == 1); + ASSERT_EQUALS(true, valueOfTok(code, "!=").intvalue == 1); code = "void f(int i, int j) {\n" " if (i == j) return;\n" @@ -3605,7 +3613,7 @@ private: " bool x = (i != j);\n" " bool b = x;\n" "}\n"; - TODO_ASSERT_EQUALS(true, false, testValueOfXKnown(code, 4U, 0)); + ASSERT_EQUALS(true, testValueOfXKnown(code, 4U, 0)); code = "void f(int i, int j) {\n" " if (i != j) return;\n"