ValueFlow: Improve the starting point for uninitialized variables to find more uninitialized usages after many conditionals (#4930)
This commit is contained in:
parent
16a9f54977
commit
115f17cfe6
|
@ -7692,6 +7692,52 @@ static void addToErrorPath(ValueFlow::Value& value, const ValueFlow::Value& from
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::vector<Token*> findAllUsages(const Variable* var, Token* start)
|
||||||
|
{
|
||||||
|
std::vector<Token*> result;
|
||||||
|
const Scope* scope = var->scope();
|
||||||
|
if (!scope)
|
||||||
|
return result;
|
||||||
|
Token* tok2 = Token::findmatch(start, "%varid%", scope->bodyEnd, var->declarationId());
|
||||||
|
while (tok2) {
|
||||||
|
result.push_back(tok2);
|
||||||
|
tok2 = Token::findmatch(tok2->next(), "%varid%", scope->bodyEnd, var->declarationId());
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Token* findStartToken(const Variable* var, Token* start)
|
||||||
|
{
|
||||||
|
std::vector<Token*> uses = findAllUsages(var, start);
|
||||||
|
if (uses.empty())
|
||||||
|
return start;
|
||||||
|
Token* first = uses.front();
|
||||||
|
if (Token::findmatch(start, "goto|asm|setjmp|longjmp", first))
|
||||||
|
return start;
|
||||||
|
const Scope* scope = first->scope();
|
||||||
|
// If there is only one usage or the first usage is in the same scope
|
||||||
|
if (uses.size() == 1 || scope == var->scope())
|
||||||
|
return first->previous();
|
||||||
|
// If all uses are in the same scope
|
||||||
|
if (std::all_of(uses.begin() + 1, uses.end(), [&](const Token* tok) {
|
||||||
|
return tok->scope() == scope;
|
||||||
|
}))
|
||||||
|
return first->previous();
|
||||||
|
// Compute the outer scope
|
||||||
|
while (scope && scope->nestedIn != var->scope())
|
||||||
|
scope = scope->nestedIn;
|
||||||
|
if (!scope)
|
||||||
|
return start;
|
||||||
|
Token* tok = const_cast<Token*>(scope->bodyStart);
|
||||||
|
if (!tok)
|
||||||
|
return start;
|
||||||
|
if (Token::simpleMatch(tok->tokAt(-2), "} else {"))
|
||||||
|
tok = tok->linkAt(-2);
|
||||||
|
if (Token::simpleMatch(tok->previous(), ") {"))
|
||||||
|
return tok->linkAt(-1)->previous();
|
||||||
|
return tok;
|
||||||
|
}
|
||||||
|
|
||||||
static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDatabase*/, const Settings* settings)
|
static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDatabase*/, const Settings* settings)
|
||||||
{
|
{
|
||||||
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
for (Token *tok = tokenlist->front(); tok; tok = tok->next()) {
|
||||||
|
@ -7718,6 +7764,8 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
|
||||||
|
|
||||||
bool partial = false;
|
bool partial = false;
|
||||||
|
|
||||||
|
Token* start = findStartToken(var, tok->next());
|
||||||
|
|
||||||
std::map<Token*, ValueFlow::Value> partialReads;
|
std::map<Token*, ValueFlow::Value> partialReads;
|
||||||
if (const Scope* scope = var->typeScope()) {
|
if (const Scope* scope = var->typeScope()) {
|
||||||
if (Token::findsimplematch(scope->bodyStart, "union", scope->bodyEnd))
|
if (Token::findsimplematch(scope->bodyStart, "union", scope->bodyEnd))
|
||||||
|
@ -7733,7 +7781,7 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, tokenlist, settings);
|
MemberExpressionAnalyzer analyzer(memVar.nameToken()->str(), tok, uninitValue, tokenlist, settings);
|
||||||
valueFlowGenericForward(tok->next(), tok->scope()->bodyEnd, analyzer, *settings);
|
valueFlowGenericForward(start, tok->scope()->bodyEnd, analyzer, *settings);
|
||||||
|
|
||||||
for (auto&& p : *analyzer.partialReads) {
|
for (auto&& p : *analyzer.partialReads) {
|
||||||
Token* tok2 = p.first;
|
Token* tok2 = p.first;
|
||||||
|
@ -7763,7 +7811,7 @@ static void valueFlowUninit(TokenList* tokenlist, SymbolDatabase* /*symbolDataba
|
||||||
if (partial)
|
if (partial)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
valueFlowForward(tok->next(), tok->scope()->bodyEnd, var->nameToken(), uninitValue, tokenlist, settings);
|
valueFlowForward(start, tok->scope()->bodyEnd, var->nameToken(), uninitValue, tokenlist, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3499,7 +3499,7 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
"}",
|
"}",
|
||||||
"test.cpp");
|
"test.cpp");
|
||||||
ASSERT_EQUALS("", errout.str());
|
TODO_ASSERT_EQUALS("", "[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("void f() {\n"
|
valueFlowUninit("void f() {\n"
|
||||||
" int i, y;\n"
|
" int i, y;\n"
|
||||||
|
@ -3510,7 +3510,7 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
"}",
|
"}",
|
||||||
"test.cpp");
|
"test.cpp");
|
||||||
ASSERT_EQUALS("", errout.str());
|
TODO_ASSERT_EQUALS("", "[test.cpp:6]: (error) Uninitialized variable: i\n", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("void f() {\n"
|
valueFlowUninit("void f() {\n"
|
||||||
" int i, y;\n"
|
" int i, y;\n"
|
||||||
|
@ -3838,7 +3838,7 @@ private:
|
||||||
" if (y == 1) { return; }\n"
|
" if (y == 1) { return; }\n"
|
||||||
" return x;\n"
|
" return x;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:3] -> [test.cpp:4]: (error) Uninitialized variable: x\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: x\n", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("int f(int x) {\n"
|
valueFlowUninit("int f(int x) {\n"
|
||||||
" int ret;\n"
|
" int ret;\n"
|
||||||
|
@ -3871,7 +3871,7 @@ private:
|
||||||
" if (foo) break;\n"
|
" if (foo) break;\n"
|
||||||
" return x;\n"
|
" return x;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Uninitialized variable: x\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("int f() {\n"
|
valueFlowUninit("int f() {\n"
|
||||||
" int x;\n"
|
" int x;\n"
|
||||||
|
@ -3879,7 +3879,7 @@ private:
|
||||||
" if (bar) break;\n"
|
" if (bar) break;\n"
|
||||||
" return x;\n"
|
" return x;\n"
|
||||||
"}");
|
"}");
|
||||||
ASSERT_EQUALS("[test.cpp:4] -> [test.cpp:5]: (error) Uninitialized variable: x\n", errout.str());
|
ASSERT_EQUALS("[test.cpp:5]: (error) Uninitialized variable: x\n", errout.str());
|
||||||
|
|
||||||
// try/catch : don't warn about exception variable
|
// try/catch : don't warn about exception variable
|
||||||
valueFlowUninit("void f() {\n"
|
valueFlowUninit("void f() {\n"
|
||||||
|
@ -6662,7 +6662,7 @@ private:
|
||||||
" struct AB ab;\n"
|
" struct AB ab;\n"
|
||||||
" while (x) { ab.a = ab.a + 1; }\n"
|
" while (x) { ab.a = ab.a + 1; }\n"
|
||||||
"}");
|
"}");
|
||||||
TODO_ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: ab.a\n", "", errout.str());
|
ASSERT_EQUALS("[test.cpp:4]: (error) Uninitialized variable: ab.a\n", errout.str());
|
||||||
|
|
||||||
valueFlowUninit("struct AB { int a; };\n"
|
valueFlowUninit("struct AB { int a; };\n"
|
||||||
"void f() {\n"
|
"void f() {\n"
|
||||||
|
|
|
@ -162,6 +162,7 @@ private:
|
||||||
TEST_CASE(valueFlowSymbolicStrlen);
|
TEST_CASE(valueFlowSymbolicStrlen);
|
||||||
TEST_CASE(valueFlowSmartPointer);
|
TEST_CASE(valueFlowSmartPointer);
|
||||||
TEST_CASE(valueFlowImpossibleMinMax);
|
TEST_CASE(valueFlowImpossibleMinMax);
|
||||||
|
TEST_CASE(valueFlowImpossibleUnknownConstant);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isNotTokValue(const ValueFlow::Value &val) {
|
static bool isNotTokValue(const ValueFlow::Value &val) {
|
||||||
|
@ -5433,18 +5434,19 @@ private:
|
||||||
" return x;\n"
|
" return x;\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT);
|
values = tokenValues(code, "x ; }", ValueFlow::Value::ValueType::UNINIT);
|
||||||
ASSERT_EQUALS(0, values.size());
|
ASSERT_EQUALS(1, values.size());
|
||||||
|
ASSERT_EQUALS(true, values.front().isUninitValue());
|
||||||
|
|
||||||
code = "void f() {\n"
|
code = "void f(int x) {\n"
|
||||||
" int i;\n"
|
" int i;\n"
|
||||||
" if (x) {\n"
|
" if (x > 0) {\n"
|
||||||
" int y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated
|
" int y = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated
|
||||||
" if (y != 0) return;\n"
|
" if (y != 0) return;\n"
|
||||||
" i++;\n"
|
" i++;\n"
|
||||||
" }\n"
|
" }\n"
|
||||||
"}\n";
|
"}\n";
|
||||||
values = tokenValues(code, "i ++", ValueFlow::Value::ValueType::UNINIT);
|
values = tokenValues(code, "i ++", ValueFlow::Value::ValueType::UNINIT);
|
||||||
ASSERT_EQUALS(0, values.size());
|
TODO_ASSERT_EQUALS(0, 1, values.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
void valueFlowConditionExpressions() {
|
void valueFlowConditionExpressions() {
|
||||||
|
@ -7874,6 +7876,19 @@ private:
|
||||||
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "a", -1));
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, "a", -1));
|
||||||
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, -1));
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 3U, -1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void valueFlowImpossibleUnknownConstant()
|
||||||
|
{
|
||||||
|
const char* code;
|
||||||
|
|
||||||
|
code = "void f(bool b) {\n"
|
||||||
|
" if (b) {\n"
|
||||||
|
" int x = -ENOMEM;\n" // assume constant ENOMEM is nonzero since it's negated
|
||||||
|
" if (x != 0) return;\n"
|
||||||
|
" }\n"
|
||||||
|
"}\n";
|
||||||
|
ASSERT_EQUALS(true, testValueOfXImpossible(code, 4U, 0));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
REGISTER_TEST(TestValueFlow)
|
REGISTER_TEST(TestValueFlow)
|
||||||
|
|
Loading…
Reference in New Issue