Fix issue 8924: Re-enable valueFlowTerminatingCondition

This commit is contained in:
Paul Fultz II 2019-08-05 07:18:06 +02:00 committed by Daniel Marjamäki
parent 996000da52
commit ffdd2dc793
8 changed files with 157 additions and 22 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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() {

View File

@ -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

View File

@ -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() {

View File

@ -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"