Fix 11006: FP uninitvar with unknown constant (#4056)

This commit is contained in:
Paul Fultz II 2022-04-28 03:48:37 -05:00 committed by GitHub
parent 7cedf3e0e5
commit aafd1e10be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 93 additions and 8 deletions

View File

@ -1717,7 +1717,7 @@ bool isConstFunctionCall(const Token* ftok, const Library& library)
}
// TODO: Only check const on lvalues
std::vector<const Token*> args = getArguments(ftok);
if (memberFunction && args.empty())
if (args.empty())
return false;
return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) {
const Variable* var = tok->variable();
@ -2553,6 +2553,8 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
if (tok->variable()->isConst())
return false;
global = !tok->variable()->isLocal() && !tok->variable()->isArgument();
} else if (tok->isIncompleteVar() && !tok->isIncompleteConstant()) {
global = true;
}
if (tok->exprId() > 0) {

View File

@ -1466,6 +1466,8 @@ void SymbolDatabase::createSymbolDatabaseEscapeFunctions()
static bool isExpression(const Token* tok)
{
if (!tok)
return false;
if (Token::simpleMatch(tok, "{") && tok->scope() && tok->scope()->bodyStart != tok &&
(tok->astOperand1() || tok->astOperand2()))
return true;
@ -1484,6 +1486,14 @@ static bool isExpression(const Token* tok)
return true;
}
static std::string getIncompleteNameID(const Token* tok)
{
std::string result = tok->str() + "@";
while (Token::Match(tok->astParent(), ".|::"))
tok = tok->astParent();
return result + tok->expressionString();
}
void SymbolDatabase::createSymbolDatabaseExprIds()
{
nonneg int base = 0;
@ -1494,10 +1504,56 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
base = std::max<MathLib::bigint>(base, var->declarationId());
}
nonneg int id = base + 1;
// Find incomplete vars that are used in constant context
std::unordered_map<std::string, nonneg int> unknownConstantIds;
const Token* inConstExpr = nullptr;
for (const Token* tok = mTokenizer->list.front(); tok != mTokenizer->list.back(); tok = tok->next()) {
if (Token::Match(tok, "decltype|sizeof|typeof (") && tok->next()->link()) {
tok = tok->next()->link()->previous();
} else if (tok == inConstExpr) {
inConstExpr = nullptr;
} else if (inConstExpr) {
if (!tok->isIncompleteVar())
continue;
if (!isExpression(tok->astParent()))
continue;
const std::string& name = getIncompleteNameID(tok);
if (unknownConstantIds.count(name) > 0)
continue;
unknownConstantIds[name] = id++;
} else if (tok->link() && tok->str() == "<") {
inConstExpr = tok->link();
} else if (Token::Match(tok, "%var% [") && tok->variable() && tok->variable()->nameToken() == tok) {
inConstExpr = tok->next()->link();
}
}
for (const Scope * scope : functionScopes) {
nonneg int thisId = 0;
std::unordered_map<std::string, std::vector<Token*>> exprs;
std::unordered_map<std::string, nonneg int> unknownIds;
// Assign IDs to incomplete vars which are part of an expression
// Such variables should be assumed global
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
if (!tok->isIncompleteVar())
continue;
if (!isExpression(tok->astParent()))
continue;
const std::string& name = getIncompleteNameID(tok);
nonneg int sid = 0;
if (unknownConstantIds.count(name) > 0) {
sid = unknownConstantIds.at(name);
tok->isIncompleteConstant(true);
} else if (unknownIds.count(name) == 0) {
sid = id++;
unknownIds[name] = sid;
} else {
sid = unknownIds.at(name);
}
assert(sid > 0);
tok->exprId(sid);
}
// Assign IDs
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
if (tok->varId() > 0) {

View File

@ -602,6 +602,13 @@ public:
setFlag(fIncompleteVar, b);
}
bool isIncompleteConstant() const {
return getFlag(fIsIncompleteConstant);
}
void isIncompleteConstant(bool b) {
setFlag(fIsIncompleteConstant, b);
}
bool isConstexpr() const {
return getFlag(fConstexpr);
}
@ -1269,6 +1276,7 @@ private:
fIsTemplate = (1ULL << 33),
fIsSimplifedScope = (1ULL << 34), // scope added when simplifying e.g. if (int i = ...; ...)
fIsRemovedVoidParameter = (1ULL << 35), // A void function parameter has been removed
fIsIncompleteConstant = (1ULL << 36),
};
Token::Type mTokType;

View File

@ -2799,8 +2799,10 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
setupExprVarIds(v.tokvalue, depth + 1);
}
}
if (depth == 0 && tok->varId() == 0 && !tok->function() && tok->isName() && tok->previous()->str() != ".") {
// unknown variable
if (depth == 0 && tok->isIncompleteVar()) {
// TODO: Treat incomplete var as global, but we need to update
// the alias variables to just expr ids instead of requiring
// Variable
unknown = true;
return ChildrenToVisit::none;
}
@ -4899,8 +4901,13 @@ static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldataba
}))
continue;
if (findAstNode(tok, [](const Token* child) {
return child->isIncompleteVar();
}))
continue;
Token* start = nextAfterAstRightmostLeaf(tok);
const Token* end = scope->bodyEnd;
const Token* end = getEndOfExprScope(tok->astOperand1(), scope);
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
rhs.errorPath.emplace_back(tok,
@ -6169,7 +6176,7 @@ struct SymbolicConditionHandler : SimpleConditionHandler {
return tok->astOperand1();
}
virtual std::vector<Condition> parse(const Token* tok, const Settings*) const override
virtual std::vector<Condition> parse(const Token* tok, const Settings* settings) const override
{
if (!Token::Match(tok, "%comp%"))
return {};
@ -6179,6 +6186,8 @@ struct SymbolicConditionHandler : SimpleConditionHandler {
return {};
if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral())
return {};
if (!isConstExpression(tok, settings->library, true, true))
return {};
std::vector<Condition> result;
auto addCond = [&](const Token* lhsTok, const Token* rhsTok, bool inverted) {

View File

@ -2687,7 +2687,7 @@ private:
" }\n"
" int FileIndex;\n"
"};");
ASSERT_EQUALS("", errout.str());
ASSERT_EQUALS("[test.cpp:5]: (style) Condition 'this->FileIndex<0' is always false\n", errout.str());
// #8858 - #if
check("short Do() {\n"

View File

@ -2437,8 +2437,7 @@ private:
"std :: vector < CharacterConversion > ( ) . swap ( c2c ) ; "
"}";
ASSERT_EQUALS(expected, tok(code, false));
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
"[test.cpp:4]: (debug) valueflow.cpp:4635:(valueFlow) bailout: variable 'it' used in loop\n", errout.str());
ASSERT_EQUALS_WITHOUT_LINENUMBERS("", errout.str());
}
void simplifyTypedef117() { // #6507

View File

@ -5337,6 +5337,17 @@ private:
" a++;\n"
"}\n");
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str());
// #11006
valueFlowUninit("int g(int);\n"
"void f() {\n"
" int received[NSIG];\n"
" for (int sig = 0; sig < NSIG; sig++)\n"
" received[sig] = g(sig);\n"
" for (int sig = 0; sig < NSIG; sig++)\n"
" if (received[sig]) {}\n"
"}\n");
ASSERT_EQUALS("", errout.str());
}
void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value