From f9e6cee787ddac043fb25a67f9415a991229abd8 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Mon, 13 Oct 2025 19:43:08 +0200 Subject: [PATCH 1/3] Fix #2062 FN variable scope can be reduced (class instance) --- lib/checkother.cpp | 2 +- lib/checkunusedvar.cpp | 180 +---------------------------------------- lib/checkunusedvar.h | 6 -- lib/symboldatabase.cpp | 170 +++++++++++++++++++++++++++++++++++++- lib/symboldatabase.h | 10 +++ test/testother.cpp | 23 ++++++ 6 files changed, 207 insertions(+), 184 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 266ba2ca148..f52b7cf8ba0 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -1213,7 +1213,7 @@ void CheckOther::checkVariableScope() continue; const bool isPtrOrRef = var->isPointer() || var->isReference(); - const bool isSimpleType = var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType() || (var->typeStartToken()->isC() && var->type() && var->type()->isStructType()); + const bool isSimpleType = var->typeStartToken()->isStandardType() || var->typeStartToken()->isEnumType() || var->typeStartToken()->isC() || symbolDatabase->isRecordTypeWithoutSideEffects(var->type()); if (!isPtrOrRef && !isSimpleType && !astIsContainer(var->nameToken())) continue; diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 6262bc330f0..0cbfdf0096a 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -715,7 +715,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const else if (mTokenizer->isC() || i->typeEndToken()->isStandardType() || i->isStlType() || - isRecordTypeWithoutSideEffects(i->type()) || + mTokenizer->getSymbolDatabase()->isRecordTypeWithoutSideEffects(i->type()) || mSettings->library.detectContainer(i->typeStartToken()) || mSettings->library.getTypeCheck("unusedvar", i->typeStartToken()->str()) == Library::TypeCheck::check) type = Variables::standard; @@ -963,7 +963,7 @@ void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const // is it a user defined type? if (!type->isStandardType()) { const Variable *variable = start->variable(); - if (!variable || !isRecordTypeWithoutSideEffects(variable->type())) + if (!variable || !mTokenizer->getSymbolDatabase()->isRecordTypeWithoutSideEffects(variable->type())) allocateMemory = false; } } @@ -1254,7 +1254,7 @@ void CheckUnusedVar::checkFunctionVariableUsage() if (tok->isName()) { if (isRaiiClass(tok->valueType(), tok->isCpp(), false) || (tok->valueType() && tok->valueType()->type == ValueType::RECORD && - (!tok->valueType()->typeScope || !isRecordTypeWithoutSideEffects(tok->valueType()->typeScope->definedType)))) + (!tok->valueType()->typeScope || !symbolDatabase->isRecordTypeWithoutSideEffects(tok->valueType()->typeScope->definedType)))) continue; tok = tok->next(); } @@ -1618,7 +1618,7 @@ void CheckUnusedVar::checkStructMemberUsage() for (const Variable &var : scope.varlist) { // only warn for variables without side effects - if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !isRecordTypeWithoutSideEffects(var.type())) + if (!var.typeStartToken()->isStandardType() && !var.isPointer() && !astIsContainer(var.nameToken()) && !mTokenizer->getSymbolDatabase()->isRecordTypeWithoutSideEffects(var.type())) continue; if (isInherited && !var.isPrivate()) continue; @@ -1680,99 +1680,6 @@ void CheckUnusedVar::unusedStructMemberError(const Token* tok, const std::string reportError(tok, Severity::style, "unusedStructMember", "$symbol:" + structname + "::" + varname + '\n' + prefix + " member '$symbol' is never used.", CWE563, Certainty::normal); } -bool CheckUnusedVar::isRecordTypeWithoutSideEffects(const Type* type) -{ - // a type that has no side effects (no constructors and no members with constructors) - /** @todo false negative: check constructors for side effects */ - const std::pair::iterator,bool> found=mIsRecordTypeWithoutSideEffectsMap.insert( - std::pair(type,false)); //Initialize with side effects for possible recursions - bool & withoutSideEffects = found.first->second; - if (!found.second) - return withoutSideEffects; - - // unknown types are assumed to have side effects - if (!type || !type->classScope) - return (withoutSideEffects = false); - - // Non-empty constructors => possible side effects - for (const Function& f : type->classScope->functionList) { - if (!f.isConstructor() && !f.isDestructor()) - continue; - if (f.argDef && Token::simpleMatch(f.argDef->link(), ") =")) - continue; // ignore default/deleted constructors - const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }")); - - const Token* nextToken = f.argDef ? f.argDef->link() : nullptr; - if (Token::simpleMatch(nextToken, ") :")) { - // validating initialization list - nextToken = nextToken->next(); // goto ":" - - for (const Token *initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) { - const Token* varToken = initListToken->next(); - const Variable* variable = varToken->variable(); - if (variable && !isVariableWithoutSideEffects(*variable)) { - return withoutSideEffects = false; - } - - const Token* valueEnd = initListToken->linkAt(2); - for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) { - const Variable* initValueVar = valueToken->variable(); - if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) { - return withoutSideEffects = false; - } - if ((valueToken->tokType() == Token::Type::eName) || - (valueToken->tokType() == Token::Type::eLambda) || - (valueToken->tokType() == Token::Type::eOther)) { - return withoutSideEffects = false; - } - const Function* initValueFunc = valueToken->function(); - if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken, - std::list {})) { - return withoutSideEffects = false; - } - } - } - } - - if (!emptyBody) - return (withoutSideEffects = false); - } - - // Derived from type that has side effects? - if (std::any_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& derivedFrom) { - return !isRecordTypeWithoutSideEffects(derivedFrom.type); - })) - return (withoutSideEffects = false); - - // Is there a member variable with possible side effects - for (const Variable& var : type->classScope->varlist) { - withoutSideEffects = isVariableWithoutSideEffects(var, type); - if (!withoutSideEffects) { - return withoutSideEffects; - } - } - - - return (withoutSideEffects = true); -} - -bool CheckUnusedVar::isVariableWithoutSideEffects(const Variable& var, const Type* type) -{ - const Type* variableType = var.type(); - if (variableType && variableType != type) { - if (!isRecordTypeWithoutSideEffects(variableType)) - return false; - } else { - if (WRONG_DATA(!var.valueType(), var.typeStartToken())) - return false; - const ValueType::Type valueType = var.valueType()->type; - if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD)) - return false; - } - - return true; -} - bool CheckUnusedVar::isEmptyType(const Type* type) { // a type that has no variables and no constructor @@ -1794,85 +1701,6 @@ bool CheckUnusedVar::isEmptyType(const Type* type) return (emptyType = false); } -bool CheckUnusedVar::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, - std::list checkedFuncs) -{ - // no body to analyze - if (!func.hasBody()) { - return false; - } - - for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) { - const Variable* argVar = argsToken->variable(); - if (argVar && argVar->isGlobal()) { - return false; // TODO: analyze global variable usage - } - } - - bool sideEffectReturnFound = false; - std::set pointersToGlobals; - for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; - bodyToken = bodyToken->next()) { - // check variable inside function body - const Variable* bodyVariable = bodyToken->variable(); - if (bodyVariable) { - if (!isVariableWithoutSideEffects(*bodyVariable)) { - return false; - } - // check if global variable is changed - if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) { - const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer(); - if (isVariableChanged(bodyToken, indirect, *mSettings)) { - return false; - } - // check if pointer to global variable assigned to another variable (another_var = &global_var) - if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) { - const Token* assigned_var_token = bodyToken->tokAt(-3); - if (assigned_var_token && assigned_var_token->variable()) { - pointersToGlobals.insert(assigned_var_token->variable()); - } - } - } - } - - // check nested function - const Function* bodyFunction = bodyToken->function(); - if (bodyFunction) { - if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found - continue; - } - checkedFuncs.push_back(bodyFunction); - if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) { - return false; - } - } - - // check returned value - if (Token::simpleMatch(bodyToken, "return")) { - const Token* returnValueToken = bodyToken->next(); - // TODO: handle complex return expressions - if (!Token::simpleMatch(returnValueToken->next(), ";")) { - sideEffectReturnFound = true; - continue; - } - // simple one-token return - const Variable* returnVariable = returnValueToken->variable(); - if (returnValueToken->isLiteral() || - (returnVariable && isVariableWithoutSideEffects(*returnVariable))) { - continue; - } - sideEffectReturnFound = true; - } - - // unknown name - if (bodyToken->isNameOnly()) { - return false; - } - } - - return !sideEffectReturnFound; -} - void CheckUnusedVar::runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) { CheckUnusedVar checkUnusedVar(&tokenizer, &tokenizer.getSettings(), errorLogger); diff --git a/lib/checkunusedvar.h b/lib/checkunusedvar.h index 8e8e9e8248e..b476295733b 100644 --- a/lib/checkunusedvar.h +++ b/lib/checkunusedvar.h @@ -66,11 +66,7 @@ class CPPCHECKLIB CheckUnusedVar : public Check { /** @brief %Check that all struct members are used */ void checkStructMemberUsage(); - bool isRecordTypeWithoutSideEffects(const Type* type); - bool isVariableWithoutSideEffects(const Variable& var, const Type* type = nullptr); bool isEmptyType(const Type* type); - bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, - std::list checkedFuncs); // Error messages.. void unusedStructMemberError(const Token *tok, const std::string &structname, const std::string &varname, const std::string& prefix = "struct"); @@ -96,8 +92,6 @@ class CPPCHECKLIB CheckUnusedVar : public Check { "- unused struct member\n"; } - std::map mIsRecordTypeWithoutSideEffectsMap; - std::map mIsEmptyTypeMap; }; diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 8af4d8542a8..4833862c4e0 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2180,6 +2180,174 @@ void SymbolDatabase::validateVariables() const } } +bool SymbolDatabase::isVariableWithoutSideEffects(const Variable& var, const Type* type) const +{ + const Type* variableType = var.type(); + if (variableType && variableType != type) { + if (!isRecordTypeWithoutSideEffects(variableType)) + return false; + } + else { + if (!var.valueType()) + return false; + const ValueType::Type valueType = var.valueType()->type; + if ((valueType == ValueType::Type::UNKNOWN_TYPE) || (valueType == ValueType::Type::NONSTD)) + return false; + } + + return true; +} + +bool SymbolDatabase::isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, std::list checkedFuncs) const +{ + // no body to analyze + if (!func.hasBody()) { + return false; + } + + for (const Token* argsToken = functionUsageToken->next(); !Token::simpleMatch(argsToken, ")"); argsToken = argsToken->next()) { + const Variable* argVar = argsToken->variable(); + if (argVar && argVar->isGlobal()) { + return false; // TODO: analyze global variable usage + } + } + + bool sideEffectReturnFound = false; + std::set pointersToGlobals; + for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; + bodyToken = bodyToken->next()) { + // check variable inside function body + const Variable* bodyVariable = bodyToken->variable(); + if (bodyVariable) { + if (!isVariableWithoutSideEffects(*bodyVariable)) { + return false; + } + // check if global variable is changed + if (bodyVariable->isGlobal() || (pointersToGlobals.find(bodyVariable) != pointersToGlobals.end())) { + const int indirect = bodyVariable->isArray() ? bodyVariable->dimensions().size() : bodyVariable->isPointer(); + if (isVariableChanged(bodyToken, indirect, mSettings)) { + return false; + } + // check if pointer to global variable assigned to another variable (another_var = &global_var) + if (Token::simpleMatch(bodyToken->tokAt(-1), "&") && Token::simpleMatch(bodyToken->tokAt(-2), "=")) { + const Token* assigned_var_token = bodyToken->tokAt(-3); + if (assigned_var_token && assigned_var_token->variable()) { + pointersToGlobals.insert(assigned_var_token->variable()); + } + } + } + } + + // check nested function + const Function* bodyFunction = bodyToken->function(); + if (bodyFunction) { + if (std::find(checkedFuncs.cbegin(), checkedFuncs.cend(), bodyFunction) != checkedFuncs.cend()) { // recursion found + continue; + } + checkedFuncs.push_back(bodyFunction); + if (!isFunctionWithoutSideEffects(*bodyFunction, bodyToken, checkedFuncs)) { + return false; + } + } + + // check returned value + if (Token::simpleMatch(bodyToken, "return")) { + const Token* returnValueToken = bodyToken->next(); + // TODO: handle complex return expressions + if (!Token::simpleMatch(returnValueToken->next(), ";")) { + sideEffectReturnFound = true; + continue; + } + // simple one-token return + const Variable* returnVariable = returnValueToken->variable(); + if (returnValueToken->isLiteral() || + (returnVariable && isVariableWithoutSideEffects(*returnVariable))) { + continue; + } + sideEffectReturnFound = true; + } + + // unknown name + if (bodyToken->isNameOnly()) { + return false; + } + } + + return !sideEffectReturnFound; +} + +bool SymbolDatabase::isRecordTypeWithoutSideEffects(const Type* type) const +{ + const std::pair::iterator, bool> found = mIsRecordTypeWithoutSideEffectsMap.insert( + std::pair(type, false)); //Initialize with side effects for possible recursions + bool& withoutSideEffects = found.first->second; + if (!found.second) + return withoutSideEffects; + + // unknown types are assumed to have side effects + if (!type || !type->classScope) + return (withoutSideEffects = false); + + // Non-empty constructors => possible side effects + for (const Function& f : type->classScope->functionList) { + if (!f.isConstructor() && !f.isDestructor()) + continue; + if (f.argDef && Token::simpleMatch(f.argDef->link(), ") =")) + continue; // ignore default/deleted constructors + const bool emptyBody = (f.functionScope && Token::simpleMatch(f.functionScope->bodyStart, "{ }")); + + const Token* nextToken = f.argDef ? f.argDef->link() : nullptr; + if (Token::simpleMatch(nextToken, ") :")) { + // validating initialization list + nextToken = nextToken->next(); // goto ":" + + for (const Token* initListToken = nextToken; Token::Match(initListToken, "[:,] %var% [({]"); initListToken = initListToken->linkAt(2)->next()) { + const Token* varToken = initListToken->next(); + const Variable* variable = varToken->variable(); + if (variable && !isVariableWithoutSideEffects(*variable)) { + return withoutSideEffects = false; + } + + const Token* valueEnd = initListToken->linkAt(2); + for (const Token* valueToken = initListToken->tokAt(3); valueToken != valueEnd; valueToken = valueToken->next()) { + const Variable* initValueVar = valueToken->variable(); + if (initValueVar && !isVariableWithoutSideEffects(*initValueVar)) { + return withoutSideEffects = false; + } + if ((valueToken->tokType() == Token::Type::eName) || + (valueToken->tokType() == Token::Type::eLambda) || + (valueToken->tokType() == Token::Type::eOther)) { + return withoutSideEffects = false; + } + const Function* initValueFunc = valueToken->function(); + if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken, + std::list {})) { + return withoutSideEffects = false; + } + } + } + } + + if (!emptyBody) + return (withoutSideEffects = false); + } + + // Derived from type that has side effects? + if (std::any_of(type->derivedFrom.cbegin(), type->derivedFrom.cend(), [this](const Type::BaseInfo& derivedFrom) { + return !isRecordTypeWithoutSideEffects(derivedFrom.type); + })) + return (withoutSideEffects = false); + + // Is there a member variable with possible side effects + for (const Variable& var : type->classScope->varlist) { + withoutSideEffects = isVariableWithoutSideEffects(var, type); + if (!withoutSideEffects) { + return withoutSideEffects; + } + } + return (withoutSideEffects = true); +} + void SymbolDatabase::validate() const { if (mSettings.debugwarnings) { @@ -2357,7 +2525,7 @@ void Variable::evaluate(const Settings& settings) const Library & lib = settings.library; bool isContainer = false; - if (mNameToken) { + if (mNameToken) { setFlag(fIsArray, arrayDimensions(settings, isContainer)); setFlag(fIsMaybeUnused, mNameToken->isAttributeMaybeUnused()); } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 03be22fa49d..e6bc30a5102 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1421,6 +1421,10 @@ class CPPCHECKLIB SymbolDatabase { static void getErrorMessages(ErrorLogger &errorLogger); + // check if type has no side effects (no constructors and no members with constructors) + /** @todo false negative: check constructors for side effects */ + bool isRecordTypeWithoutSideEffects(const Type* type) const; + private: friend class Scope; friend class Function; @@ -1476,6 +1480,10 @@ class CPPCHECKLIB SymbolDatabase { */ void validateVariables() const; + bool isVariableWithoutSideEffects(const Variable& var, const Type* type = nullptr) const; + bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, + std::list checkedFuncs) const; + Tokenizer& mTokenizer; const Settings &mSettings; ErrorLogger &mErrorLogger; @@ -1487,6 +1495,8 @@ class CPPCHECKLIB SymbolDatabase { std::list mBlankTypes; ValueType::Sign mDefaultSignedness; + + mutable std::map mIsRecordTypeWithoutSideEffectsMap; }; diff --git a/test/testother.cpp b/test/testother.cpp index 4cef19c0575..c3c40540aa6 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -1102,6 +1102,7 @@ class TestOther : public TestFixture { // classes may have extra side effects check("class fred {\n" "public:\n" + " fred();\n" " void x();\n" "};\n" "void test(int a) {\n" @@ -1111,6 +1112,28 @@ class TestOther : public TestFixture { " }\n" "}"); ASSERT_EQUALS("", errout_str()); + + check("class fred {\n" // #2062 + "public:\n" + " void x();\n" + "};\n" + "void test(int a) {\n" + " fred f;\n" + " if (a == 2) {\n" + " f.x();\n" + " }\n" + "}"); + ASSERT_EQUALS("[test.cpp:6:10]: (style) The scope of the variable 'f' can be reduced. [variableScope]\n", errout_str()); + + check("struct S { int a, b; };\n" + "bool f() {\n" + " S s{};\n" + " {\n" + " bool b = s.a && a.b;\n" + " return b;\n" + " }\n" + "}\n"); + ASSERT_EQUALS("[test.cpp:3:7]: (style) The scope of the variable 's' can be reduced. [variableScope]\n", errout_str()); } void varScope10() { From ac01ae7462e26f937c820ca19430f02cde220512 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Mon, 13 Oct 2025 19:56:50 +0200 Subject: [PATCH 2/3] Format --- lib/symboldatabase.cpp | 6 +++--- lib/symboldatabase.h | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index 4833862c4e0..2b35a90c0ed 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -2215,7 +2215,7 @@ bool SymbolDatabase::isFunctionWithoutSideEffects(const Function& func, const To bool sideEffectReturnFound = false; std::set pointersToGlobals; for (const Token* bodyToken = func.functionScope->bodyStart->next(); bodyToken != func.functionScope->bodyEnd; - bodyToken = bodyToken->next()) { + bodyToken = bodyToken->next()) { // check variable inside function body const Variable* bodyVariable = bodyToken->variable(); if (bodyVariable) { @@ -2321,7 +2321,7 @@ bool SymbolDatabase::isRecordTypeWithoutSideEffects(const Type* type) const } const Function* initValueFunc = valueToken->function(); if (initValueFunc && !isFunctionWithoutSideEffects(*initValueFunc, valueToken, - std::list {})) { + std::list {})) { return withoutSideEffects = false; } } @@ -2525,7 +2525,7 @@ void Variable::evaluate(const Settings& settings) const Library & lib = settings.library; bool isContainer = false; - if (mNameToken) { + if (mNameToken) { setFlag(fIsArray, arrayDimensions(settings, isContainer)); setFlag(fIsMaybeUnused, mNameToken->isAttributeMaybeUnused()); } diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index e6bc30a5102..50f37fc1ebf 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -1482,7 +1482,7 @@ class CPPCHECKLIB SymbolDatabase { bool isVariableWithoutSideEffects(const Variable& var, const Type* type = nullptr) const; bool isFunctionWithoutSideEffects(const Function& func, const Token* functionUsageToken, - std::list checkedFuncs) const; + std::list checkedFuncs) const; Tokenizer& mTokenizer; const Settings &mSettings; From 8b45f7aad049a65d69892c6287f9e93a1e88bf3c Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Mon, 13 Oct 2025 21:27:02 +0200 Subject: [PATCH 3/3] const --- lib/checkunusedvar.cpp | 2 +- lib/checkunusedvar.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkunusedvar.cpp b/lib/checkunusedvar.cpp index 0cbfdf0096a..30e6fe45b16 100644 --- a/lib/checkunusedvar.cpp +++ b/lib/checkunusedvar.cpp @@ -689,7 +689,7 @@ static void useFunctionArgs(const Token *tok, Variables& variables) //--------------------------------------------------------------------------- // Usage of function variables //--------------------------------------------------------------------------- -void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) +void CheckUnusedVar::checkFunctionVariableUsage_iterateScopes(const Scope* const scope, Variables& variables) const { // Find declarations if the scope is executable.. if (scope->isExecutable()) { diff --git a/lib/checkunusedvar.h b/lib/checkunusedvar.h index b476295733b..d85bbf54dd2 100644 --- a/lib/checkunusedvar.h +++ b/lib/checkunusedvar.h @@ -60,7 +60,7 @@ class CPPCHECKLIB CheckUnusedVar : public Check { void runChecks(const Tokenizer &tokenizer, ErrorLogger *errorLogger) override; /** @brief %Check for unused function variables */ - void checkFunctionVariableUsage_iterateScopes(const Scope* scope, Variables& variables); + void checkFunctionVariableUsage_iterateScopes(const Scope* scope, Variables& variables) const; void checkFunctionVariableUsage(); /** @brief %Check that all struct members are used */