Skip forwarding values for unique expressions (#5103)

This commit is contained in:
Paul Fultz II 2023-06-05 13:49:19 -05:00 committed by GitHub
parent 93595aeff0
commit 26ed052e8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 89 additions and 16 deletions

View File

@ -181,6 +181,9 @@ struct Analyzer {
virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0; virtual void assume(const Token* tok, bool state, unsigned int flags = 0) = 0;
/// Return analyzer for expression at token /// Return analyzer for expression at token
virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg = emptyString) const = 0; virtual ValuePtr<Analyzer> reanalyze(Token* tok, const std::string& msg = emptyString) const = 0;
virtual bool invalid() const {
return false;
}
virtual ~Analyzer() {} virtual ~Analyzer() {}
Analyzer(const Analyzer&) = default; Analyzer(const Analyzer&) = default;
protected: protected:

View File

@ -894,6 +894,8 @@ struct ForwardTraversal {
Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings) Analyzer::Result valueFlowGenericForward(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings)
{ {
if (a->invalid())
return Analyzer::Result{Analyzer::Action::None, Analyzer::Terminate::Bail};
ForwardTraversal ft{a, settings}; ForwardTraversal ft{a, settings};
ft.updateRange(start, end); ft.updateRange(start, end);
return Analyzer::Result{ ft.actions, ft.terminate }; return Analyzer::Result{ ft.actions, ft.terminate };
@ -903,6 +905,8 @@ Analyzer::Result valueFlowGenericForward(Token* start, const ValuePtr<Analyzer>&
{ {
if (Settings::terminated()) if (Settings::terminated())
throw TerminateException(); throw TerminateException();
if (a->invalid())
return Analyzer::Result{Analyzer::Action::None, Analyzer::Terminate::Bail};
ForwardTraversal ft{a, settings}; ForwardTraversal ft{a, settings};
ft.updateRecursive(start); ft.updateRecursive(start);
return Analyzer::Result{ ft.actions, ft.terminate }; return Analyzer::Result{ ft.actions, ft.terminate };

View File

@ -390,6 +390,8 @@ struct ReverseTraversal {
void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings) void valueFlowGenericReverse(Token* start, const Token* end, const ValuePtr<Analyzer>& a, const Settings& settings)
{ {
if (a->invalid())
return;
ReverseTraversal rt{a, settings}; ReverseTraversal rt{a, settings};
rt.traverse(start, end); rt.traverse(start, end);
} }

View File

@ -1625,7 +1625,7 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
exprs[tok->str()].push_back(tok); exprs[tok->str()].push_back(tok);
tok->exprId(id++); tok->exprId(id++);
if (id == std::numeric_limits<nonneg int>::max()) { if (id == std::numeric_limits<nonneg int>::max() / 4) {
throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL); throw InternalError(nullptr, "Ran out of expression ids.", InternalError::INTERNAL);
} }
} else if (isCPP() && Token::simpleMatch(tok, "this")) { } else if (isCPP() && Token::simpleMatch(tok, "this")) {
@ -1653,6 +1653,27 @@ void SymbolDatabase::createSymbolDatabaseExprIds()
} }
} }
} }
// Mark expressions that are unique
std::unordered_map<nonneg int, Token*> exprMap;
for (Token* tok = const_cast<Token*>(scope->bodyStart); tok != scope->bodyEnd; tok = tok->next()) {
if (tok->exprId() == 0)
continue;
auto p = exprMap.emplace(tok->exprId(), tok);
// Already exists so set it to null
if (!p.second) {
p.first->second = nullptr;
}
}
for (const auto& p : exprMap) {
if (!p.second)
continue;
if (p.second->variable()) {
const Variable* var = p.second->variable();
if (var->nameToken() != p.second)
continue;
}
p.second->setUniqueExprId();
}
} }
} }

View File

@ -901,6 +901,20 @@ public:
mImpl->mExprId = id; mImpl->mExprId = id;
} }
void setUniqueExprId()
{
assert(mImpl->mExprId > 0);
mImpl->mExprId |= 1 << efIsUnique;
}
bool isUniqueExprId() const
{
if (mImpl->mExprId > 0) {
return (mImpl->mExprId & (1 << efIsUnique)) != 0;
}
return false;
}
/** /**
* For debugging purposes, prints token and all tokens * For debugging purposes, prints token and all tokens
* followed by it. * followed by it.
@ -1327,6 +1341,11 @@ private:
fIsFinalType = (1ULL << 40), // Is this a type with final specifier fIsFinalType = (1ULL << 40), // Is this a type with final specifier
}; };
enum : uint64_t {
efMaxSize = sizeof(nonneg int) * 8,
efIsUnique = efMaxSize - 2,
};
Token::Type mTokType; Token::Type mTokType;
uint64_t mFlags; uint64_t mFlags;

View File

@ -2438,10 +2438,6 @@ struct ValueFlowAnalyzer : Analyzer {
return false; return false;
} }
virtual bool invalid() const {
return false;
}
bool isCPP() const { bool isCPP() const {
return tokenlist->isCPP(); return tokenlist->isCPP();
} }
@ -3134,11 +3130,20 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
bool local; bool local;
bool unknown; bool unknown;
bool dependOnThis; bool dependOnThis;
bool uniqueExprId;
ExpressionAnalyzer() : SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false), dependOnThis(false) {} ExpressionAnalyzer()
: SingleValueFlowAnalyzer(), expr(nullptr), local(true), unknown(false), dependOnThis(false), uniqueExprId(false)
{}
ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const TokenList* t, const Settings* s) ExpressionAnalyzer(const Token* e, ValueFlow::Value val, const TokenList* t, const Settings* s)
: SingleValueFlowAnalyzer(std::move(val), t, s), expr(e), local(true), unknown(false), dependOnThis(false) { : SingleValueFlowAnalyzer(std::move(val), t, s),
expr(e),
local(true),
unknown(false),
dependOnThis(false),
uniqueExprId(false)
{
assert(e && e->exprId() != 0 && "Not a valid expression"); assert(e && e->exprId() != 0 && "Not a valid expression");
dependOnThis = exprDependsOnThis(expr); dependOnThis = exprDependsOnThis(expr);
@ -3147,6 +3152,8 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
dependOnThis |= exprDependsOnThis(value.tokvalue); dependOnThis |= exprDependsOnThis(value.tokvalue);
setupExprVarIds(value.tokvalue); setupExprVarIds(value.tokvalue);
} }
uniqueExprId =
expr->isUniqueExprId() && (Token::Match(expr, "%cop%") || !isVariableChanged(expr, 0, s, t->isCPP()));
} }
static bool nonLocal(const Variable* var, bool deref) { static bool nonLocal(const Variable* var, bool deref) {
@ -3196,7 +3203,13 @@ struct ExpressionAnalyzer : SingleValueFlowAnalyzer {
}); });
} }
virtual bool skipUniqueExprIds() const {
return true;
}
bool invalid() const override { bool invalid() const override {
if (skipUniqueExprIds() && uniqueExprId)
return true;
return unknown; return unknown;
} }
@ -3231,6 +3244,10 @@ struct SameExpressionAnalyzer : ExpressionAnalyzer {
: ExpressionAnalyzer(e, std::move(val), t, s) : ExpressionAnalyzer(e, std::move(val), t, s)
{} {}
bool skipUniqueExprIds() const override {
return false;
}
bool match(const Token* tok) const override bool match(const Token* tok) const override
{ {
return isSameExpression(isCPP(), true, expr, tok, getSettings()->library, true, true); return isSameExpression(isCPP(), true, expr, tok, getSettings()->library, true, true);
@ -3246,6 +3263,10 @@ struct OppositeExpressionAnalyzer : ExpressionAnalyzer {
: ExpressionAnalyzer(e, std::move(val), t, s), isNot(pIsNot) : ExpressionAnalyzer(e, std::move(val), t, s), isNot(pIsNot)
{} {}
bool skipUniqueExprIds() const override {
return false;
}
bool match(const Token* tok) const override { bool match(const Token* tok) const override {
return isOppositeCond(isNot, isCPP(), expr, tok, getSettings()->library, true, true); return isOppositeCond(isNot, isCPP(), expr, tok, getSettings()->library, true, true);
} }

View File

@ -1602,7 +1602,9 @@ private:
"}"; "}";
checkSimplifyTypedef(code); checkSimplifyTypedef(code);
ASSERT_EQUALS("", errout.str()); ASSERT_EQUALS_WITHOUT_LINENUMBERS(
"[test.cpp:3]: (debug) valueflow.cpp:6541:(valueFlow) bailout: valueFlowAfterCondition: bailing in conditional block\n",
errout.str());
} }
void simplifyTypedef46() { void simplifyTypedef46() {

View File

@ -3749,14 +3749,15 @@ private:
" return x + y + a.y + b.y;\n" " return x + y + a.y + b.y;\n"
"}\n"); "}\n");
const char expected[] = "1: struct A {\n" const char expected[] =
"2: int x ; int y ;\n" "1: struct A {\n"
"3: } ;\n" "2: int x ; int y ;\n"
"4: int f ( A a , A b ) {\n" "3: } ;\n"
"5: int x@5 ; x@5 =@9 a@3 .@10 x@6 +@11 b@4 .@12 x@7 ;\n" "4: int f ( A a , A b ) {\n"
"6: int y@8 ; y@8 =@13 b@4 .@12 x@7 +@11 a@3 .@10 x@6 ;\n" "5: int x@5 ; x@5 =@9 a@3 .@10 x@6 +@11 b@4 .@12 x@7 ;\n"
"7: return x@5 +@17 y@8 +@18 a@3 .@19 y@9 +@20 b@4 .@21 y@10 ;\n" "6: int y@8 ; y@8 =@1073741837 b@4 .@12 x@7 +@11 a@3 .@10 x@6 ;\n"
"8: }\n"; "7: return x@5 +@1073741841 y@8 +@1073741842 a@3 .@1073741843 y@9 +@1073741844 b@4 .@1073741845 y@10 ;\n"
"8: }\n";
ASSERT_EQUALS(expected, actual); ASSERT_EQUALS(expected, actual);
} }