Fix 11006: FP uninitvar with unknown constant (#4056)
This commit is contained in:
parent
7cedf3e0e5
commit
aafd1e10be
|
@ -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) {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue