diff --git a/lib/astutils.cpp b/lib/astutils.cpp index 6462afa4e..6e18f3aad 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -1717,7 +1717,7 @@ bool isConstFunctionCall(const Token* ftok, const Library& library) } // TODO: Only check const on lvalues std::vector 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) { diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index ef0b6923b..36da09873 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -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(base, var->declarationId()); } nonneg int id = base + 1; + // Find incomplete vars that are used in constant context + std::unordered_map 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> exprs; + std::unordered_map unknownIds; + // Assign IDs to incomplete vars which are part of an expression + // Such variables should be assumed global + for (Token* tok = const_cast(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(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) { if (tok->varId() > 0) { diff --git a/lib/token.h b/lib/token.h index 05809f6e9..8377a43d4 100644 --- a/lib/token.h +++ b/lib/token.h @@ -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; diff --git a/lib/valueflow.cpp b/lib/valueflow.cpp index 5a72f3737..cae3f8c3f 100644 --- a/lib/valueflow.cpp +++ b/lib/valueflow.cpp @@ -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 parse(const Token* tok, const Settings*) const override + virtual std::vector 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 result; auto addCond = [&](const Token* lhsTok, const Token* rhsTok, bool inverted) { diff --git a/test/testcondition.cpp b/test/testcondition.cpp index 4f648ed03..4b94009b5 100644 --- a/test/testcondition.cpp +++ b/test/testcondition.cpp @@ -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" diff --git a/test/testsimplifytypedef.cpp b/test/testsimplifytypedef.cpp index a4d478f64..c80ada3fc 100644 --- a/test/testsimplifytypedef.cpp +++ b/test/testsimplifytypedef.cpp @@ -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 diff --git a/test/testuninitvar.cpp b/test/testuninitvar.cpp index 5c5cb4305..ec2f23bf3 100644 --- a/test/testuninitvar.cpp +++ b/test/testuninitvar.cpp @@ -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