From 9881f861b38e8ad67b1555f44ce481d718a40168 Mon Sep 17 00:00:00 2001 From: firewave Date: Wed, 21 Jan 2026 08:48:29 +0100 Subject: [PATCH 1/2] refs #10765/#11262 - Token: introduced cache for `isMutableExpression()` calls --- lib/astutils.cpp | 11 ++--------- lib/astutils.h | 2 ++ lib/token.cpp | 7 +++++++ lib/token.h | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index c91f41d9651..ddd7b027d34 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2482,13 +2482,6 @@ static bool isArray(const Token* tok) return false; } -static inline -// limit it to CLang as compiling with GCC might fail with -// error: inlining failed in call to always_inline 'bool isMutableExpression(const Token*)': function not considered for inlining -// error: inlining failed in call to ‘always_inline’ ‘bool isMutableExpression(const Token*)’: recursive inlining -#if defined(__clang__) -__attribute__((always_inline)) -#endif bool isMutableExpression(const Token* tok) { if (!tok) @@ -2632,7 +2625,7 @@ static bool hasOverloadedMemberAccess(const Token* tok) bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth) { - if (!isMutableExpression(tok)) + if (!tok->isMutableExpr()) return false; if (indirect == 0 && isConstVarExpression(tok)) @@ -2924,7 +2917,7 @@ static bool isExpressionChangedAt(const F& getExprTok, { if (depth < 0) return true; - if (!isMutableExpression(tok)) + if (!tok->isMutableExpr()) return false; if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) { if (globalvar && Token::Match(tok, "%name% (") && diff --git a/lib/astutils.h b/lib/astutils.h index 06f01acab11..4002e065ab5 100644 --- a/lib/astutils.h +++ b/lib/astutils.h @@ -319,6 +319,8 @@ Token* getTokenArgumentFunction(Token* tok, int& argn); std::vector getArgumentVars(const Token* tok, int argnr); +bool isMutableExpression(const Token* tok); + /** Is variable changed by function call? * In case the answer of the question is inconclusive, e.g. because the function declaration is not known * the return value is false and the output parameter inconclusive is set to true diff --git a/lib/token.cpp b/lib/token.cpp index 5633f291083..f55fcc14830 100644 --- a/lib/token.cpp +++ b/lib/token.cpp @@ -2753,3 +2753,10 @@ const SmallVector& Token::refs(bool temporary) const mImpl->mRefs.reset(new SmallVector(followAllReferences(this, false))); return *mImpl->mRefs; } + +bool Token::isMutableExpr() const +{ + if (mImpl->mMutableExpr == -1) + mImpl->mMutableExpr = isMutableExpression(this); + return !!mImpl->mMutableExpr; +} diff --git a/lib/token.h b/lib/token.h index d26c7ac7640..5cf427974c4 100644 --- a/lib/token.h +++ b/lib/token.h @@ -173,6 +173,8 @@ class CPPCHECKLIB Token { std::unique_ptr> mRefs; std::unique_ptr> mRefsTemp; + std::int8_t mMutableExpr{-1}; + void setCppcheckAttribute(CppcheckAttributesType type, MathLib::bigint value); bool getCppcheckAttribute(CppcheckAttributesType type, MathLib::bigint &value) const; @@ -1364,6 +1366,8 @@ class CPPCHECKLIB Token { // provides and caches result of a followAllReferences() call const SmallVector& refs(bool temporary = true) const; + bool isMutableExpr() const; + /** * Sets the original name. */ From 8b8ca9cce8c019159c0081925703a82a73d88f80 Mon Sep 17 00:00:00 2001 From: firewave Date: Wed, 21 Jan 2026 21:25:47 +0100 Subject: [PATCH 2/2] astutils.cpp: added missing nullptr checks --- lib/astutils.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/astutils.cpp b/lib/astutils.cpp index ddd7b027d34..600b888e44e 100644 --- a/lib/astutils.cpp +++ b/lib/astutils.cpp @@ -2625,6 +2625,8 @@ static bool hasOverloadedMemberAccess(const Token* tok) bool isVariableChanged(const Token *tok, int indirect, const Settings &settings, int depth) { + if (!tok) + return false; if (!tok->isMutableExpr()) return false; @@ -2917,6 +2919,8 @@ static bool isExpressionChangedAt(const F& getExprTok, { if (depth < 0) return true; + if (!tok) + return false; if (!tok->isMutableExpr()) return false; if (tok->exprId() != exprid || (!tok->varId() && !tok->isName())) {