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
|
// TODO: Only check const on lvalues
|
||||||
std::vector<const Token*> args = getArguments(ftok);
|
std::vector<const Token*> args = getArguments(ftok);
|
||||||
if (memberFunction && args.empty())
|
if (args.empty())
|
||||||
return false;
|
return false;
|
||||||
return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) {
|
return constMember && std::all_of(args.begin(), args.end(), [](const Token* tok) {
|
||||||
const Variable* var = tok->variable();
|
const Variable* var = tok->variable();
|
||||||
|
@ -2553,6 +2553,8 @@ bool isExpressionChanged(const Token* expr, const Token* start, const Token* end
|
||||||
if (tok->variable()->isConst())
|
if (tok->variable()->isConst())
|
||||||
return false;
|
return false;
|
||||||
global = !tok->variable()->isLocal() && !tok->variable()->isArgument();
|
global = !tok->variable()->isLocal() && !tok->variable()->isArgument();
|
||||||
|
} else if (tok->isIncompleteVar() && !tok->isIncompleteConstant()) {
|
||||||
|
global = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tok->exprId() > 0) {
|
if (tok->exprId() > 0) {
|
||||||
|
|
|
@ -1466,6 +1466,8 @@ void SymbolDatabase::createSymbolDatabaseEscapeFunctions()
|
||||||
|
|
||||||
static bool isExpression(const Token* tok)
|
static bool isExpression(const Token* tok)
|
||||||
{
|
{
|
||||||
|
if (!tok)
|
||||||
|
return false;
|
||||||
if (Token::simpleMatch(tok, "{") && tok->scope() && tok->scope()->bodyStart != tok &&
|
if (Token::simpleMatch(tok, "{") && tok->scope() && tok->scope()->bodyStart != tok &&
|
||||||
(tok->astOperand1() || tok->astOperand2()))
|
(tok->astOperand1() || tok->astOperand2()))
|
||||||
return true;
|
return true;
|
||||||
|
@ -1484,6 +1486,14 @@ static bool isExpression(const Token* tok)
|
||||||
return true;
|
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()
|
void SymbolDatabase::createSymbolDatabaseExprIds()
|
||||||
{
|
{
|
||||||
nonneg int base = 0;
|
nonneg int base = 0;
|
||||||
|
@ -1494,10 +1504,56 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
|
||||||
base = std::max<MathLib::bigint>(base, var->declarationId());
|
base = std::max<MathLib::bigint>(base, var->declarationId());
|
||||||
}
|
}
|
||||||
nonneg int id = base + 1;
|
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) {
|
for (const Scope * scope : functionScopes) {
|
||||||
nonneg int thisId = 0;
|
nonneg int thisId = 0;
|
||||||
std::unordered_map<std::string, std::vector<Token*>> exprs;
|
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
|
// Assign IDs
|
||||||
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
|
||||||
if (tok->varId() > 0) {
|
if (tok->varId() > 0) {
|
||||||
|
|
|
@ -602,6 +602,13 @@ public:
|
||||||
setFlag(fIncompleteVar, b);
|
setFlag(fIncompleteVar, b);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isIncompleteConstant() const {
|
||||||
|
return getFlag(fIsIncompleteConstant);
|
||||||
|
}
|
||||||
|
void isIncompleteConstant(bool b) {
|
||||||
|
setFlag(fIsIncompleteConstant, b);
|
||||||
|
}
|
||||||
|
|
||||||
bool isConstexpr() const {
|
bool isConstexpr() const {
|
||||||
return getFlag(fConstexpr);
|
return getFlag(fConstexpr);
|
||||||
}
|
}
|
||||||
|
@ -1269,6 +1276,7 @@ private:
|
||||||
fIsTemplate = (1ULL << 33),
|
fIsTemplate = (1ULL << 33),
|
||||||
fIsSimplifedScope = (1ULL << 34), // scope added when simplifying e.g. if (int i = ...; ...)
|
fIsSimplifedScope = (1ULL << 34), // scope added when simplifying e.g. if (int i = ...; ...)
|
||||||
fIsRemovedVoidParameter = (1ULL << 35), // A void function parameter has been removed
|
fIsRemovedVoidParameter = (1ULL << 35), // A void function parameter has been removed
|
||||||
|
fIsIncompleteConstant = (1ULL << 36),
|
||||||
};
|
};
|
||||||
|
|
||||||
Token::Type mTokType;
|
Token::Type mTokType;
|
||||||
|
|
|
@ -2799,8 +2799,10 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
|
||||||
setupExprVarIds(v.tokvalue, depth + 1);
|
setupExprVarIds(v.tokvalue, depth + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (depth == 0 && tok->varId() == 0 && !tok->function() && tok->isName() && tok->previous()->str() != ".") {
|
if (depth == 0 && tok->isIncompleteVar()) {
|
||||||
// unknown variable
|
// TODO: Treat incomplete var as global, but we need to update
|
||||||
|
// the alias variables to just expr ids instead of requiring
|
||||||
|
// Variable
|
||||||
unknown = true;
|
unknown = true;
|
||||||
return ChildrenToVisit::none;
|
return ChildrenToVisit::none;
|
||||||
}
|
}
|
||||||
|
@ -4899,8 +4901,13 @@ static void valueFlowSymbolic(TokenList* tokenlist, SymbolDatabase* symboldataba
|
||||||
}))
|
}))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (findAstNode(tok, [](const Token* child) {
|
||||||
|
return child->isIncompleteVar();
|
||||||
|
}))
|
||||||
|
continue;
|
||||||
|
|
||||||
Token* start = nextAfterAstRightmostLeaf(tok);
|
Token* start = nextAfterAstRightmostLeaf(tok);
|
||||||
const Token* end = scope->bodyEnd;
|
const Token* end = getEndOfExprScope(tok->astOperand1(), scope);
|
||||||
|
|
||||||
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
|
ValueFlow::Value rhs = makeSymbolic(tok->astOperand2());
|
||||||
rhs.errorPath.emplace_back(tok,
|
rhs.errorPath.emplace_back(tok,
|
||||||
|
@ -6169,7 +6176,7 @@ struct SymbolicConditionHandler : SimpleConditionHandler {
|
||||||
return tok->astOperand1();
|
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%"))
|
if (!Token::Match(tok, "%comp%"))
|
||||||
return {};
|
return {};
|
||||||
|
@ -6179,6 +6186,8 @@ struct SymbolicConditionHandler : SimpleConditionHandler {
|
||||||
return {};
|
return {};
|
||||||
if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral())
|
if (!tok->astOperand2() || tok->astOperand2()->hasKnownIntValue() || tok->astOperand2()->isLiteral())
|
||||||
return {};
|
return {};
|
||||||
|
if (!isConstExpression(tok, settings->library, true, true))
|
||||||
|
return {};
|
||||||
|
|
||||||
std::vector<Condition> result;
|
std::vector<Condition> result;
|
||||||
auto addCond = [&](const Token* lhsTok, const Token* rhsTok, bool inverted) {
|
auto addCond = [&](const Token* lhsTok, const Token* rhsTok, bool inverted) {
|
||||||
|
|
|
@ -2687,7 +2687,7 @@ private:
|
||||||
" }\n"
|
" }\n"
|
||||||
" int FileIndex;\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
|
// #8858 - #if
|
||||||
check("short Do() {\n"
|
check("short Do() {\n"
|
||||||
|
|
|
@ -2437,8 +2437,7 @@ private:
|
||||||
"std :: vector < CharacterConversion > ( ) . swap ( c2c ) ; "
|
"std :: vector < CharacterConversion > ( ) . swap ( c2c ) ; "
|
||||||
"}";
|
"}";
|
||||||
ASSERT_EQUALS(expected, tok(code, false));
|
ASSERT_EQUALS(expected, tok(code, false));
|
||||||
ASSERT_EQUALS_WITHOUT_LINENUMBERS(
|
ASSERT_EQUALS_WITHOUT_LINENUMBERS("", errout.str());
|
||||||
"[test.cpp:4]: (debug) valueflow.cpp:4635:(valueFlow) bailout: variable 'it' used in loop\n", errout.str());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void simplifyTypedef117() { // #6507
|
void simplifyTypedef117() { // #6507
|
||||||
|
|
|
@ -5337,6 +5337,17 @@ private:
|
||||||
" a++;\n"
|
" a++;\n"
|
||||||
"}\n");
|
"}\n");
|
||||||
ASSERT_EQUALS("[test.cpp:3]: (error) Uninitialized variable: a\n", errout.str());
|
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
|
void valueFlowUninitBreak() { // Do not show duplicate warnings about the same uninitialized value
|
||||||
|
|
Loading…
Reference in New Issue