Fix issue 8924: Re-enable valueFlowTerminatingCondition
This commit is contained in:
parent
996000da52
commit
ffdd2dc793
|
@ -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<std::string> cpp20keywords = {
|
||||
"alignas",
|
||||
"alignof",
|
||||
"axiom",
|
||||
"co_await",
|
||||
"co_return",
|
||||
"co_yield",
|
||||
"concept",
|
||||
"synchronized",
|
||||
"consteval",
|
||||
"reflexpr",
|
||||
"requires",
|
||||
};
|
||||
const std::set<std::string> 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<Token *>(tok)->isIncompleteVar(true);
|
||||
}
|
||||
}
|
||||
|
||||
void SymbolDatabase::setArrayDimensionsUsingValueFlow()
|
||||
{
|
||||
// set all unknown array dimensions
|
||||
|
|
|
@ -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);
|
||||
|
|
11
lib/token.h
11
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;
|
||||
|
|
|
@ -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<const Token*, const Scope*> Condition;
|
||||
for (const Scope * scope : symboldatabase->functionScopes) {
|
||||
bool skipFunction = false;
|
||||
std::vector<Condition> 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);
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue