From 46185d675aa09a7875b5a855ec86f7335a8ce237 Mon Sep 17 00:00:00 2001 From: Johan Crone Date: Thu, 19 Feb 2026 15:40:10 +0100 Subject: [PATCH 1/4] Fix #13944: FN constParameterPointer in method in derived class Add inconclusive and default=false (instead of true) in checkConstPointer. --- lib/checkother.cpp | 9 +++++---- lib/checkother.h | 2 +- lib/symboldatabase.cpp | 9 +++++++-- lib/symboldatabase.h | 2 +- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index 4285154d841..a8b63902984 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2008,8 +2008,9 @@ void CheckOther::checkConstPointer() nonConstPointers.emplace(var); } for (const Variable *p: pointers) { + bool inconclusive = false; if (p->isArgument()) { - if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(true) || p->scope()->function->hasVirtualSpecifier()) + if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(false, (bool*)nullptr, &inconclusive) || p->scope()->function->hasVirtualSpecifier()) continue; if (p->isMaybeUnused()) continue; @@ -2026,12 +2027,12 @@ void CheckOther::checkConstPointer() continue; if (p->typeStartToken() && p->typeStartToken()->isSimplifiedTypedef() && !(Token::simpleMatch(p->typeEndToken(), "*") && !p->typeEndToken()->isSimplifiedTypedef())) continue; - constVariableError(p, p->isArgument() ? p->scope()->function : nullptr); + constVariableError(p, p->isArgument() ? p->scope()->function : nullptr, &inconclusive); } } } -void CheckOther::constVariableError(const Variable *var, const Function *function) +void CheckOther::constVariableError(const Variable *var, const Function *function, bool* inconclusive) { if (!var) { reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const"); @@ -2062,7 +2063,7 @@ void CheckOther::constVariableError(const Variable *var, const Function *functio id += "Pointer"; } - reportError(std::move(errorPath), Severity::style, id.c_str(), message, CWE398, Certainty::normal); + reportError(std::move(errorPath), Severity::style, id.c_str(), message, CWE398, (inconclusive && *inconclusive) ? Certainty::inconclusive : Certainty::normal); } //--------------------------------------------------------------------------- diff --git a/lib/checkother.h b/lib/checkother.h index 28ee25cfa76..e8b6c7d1d75 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -215,7 +215,7 @@ class CPPCHECKLIB CheckOther : public Check { void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false); - void constVariableError(const Variable *var, const Function *function); + void constVariableError(const Variable *var, const Function *function, bool * inconclusive = nullptr); void constStatementError(const Token *tok, const std::string &type, bool inconclusive); void signedCharArrayIndexError(const Token *tok); void unknownSignCharArrayIndexError(const Token *tok); diff --git a/lib/symboldatabase.cpp b/lib/symboldatabase.cpp index d731c2a8884..f5dd2488064 100644 --- a/lib/symboldatabase.cpp +++ b/lib/symboldatabase.cpp @@ -4813,8 +4813,10 @@ void Function::addArguments(const Scope *scope) } } -bool Function::isImplicitlyVirtual(bool defaultVal, bool* pFoundAllBaseClasses) const +bool Function::isImplicitlyVirtual(bool defaultVal, bool* pFoundAllBaseClasses, bool* inconclusive) const { + if (inconclusive) + *inconclusive = false; // assume not inconclusive. if (hasVirtualSpecifier() || hasOverrideSpecifier() || hasFinalSpecifier()) return true; bool foundAllBaseClasses = true; @@ -4824,7 +4826,10 @@ bool Function::isImplicitlyVirtual(bool defaultVal, bool* pFoundAllBaseClasses) *pFoundAllBaseClasses = foundAllBaseClasses; if (foundAllBaseClasses) //If we've seen all the base classes and none of the above were true then it must not be virtual return false; - return defaultVal; //If we can't see all the bases classes then we can't say conclusively + //If we can't see all the bases classes then we can't say conclusively, set inconclusive (if possible) and return default value + if (inconclusive) + *inconclusive = true; + return defaultVal; } std::vector Function::getOverloadedFunctions() const diff --git a/lib/symboldatabase.h b/lib/symboldatabase.h index 0eaa0aab9ee..c67273c8009 100644 --- a/lib/symboldatabase.h +++ b/lib/symboldatabase.h @@ -783,7 +783,7 @@ class CPPCHECKLIB Function { void addArguments(const Scope *scope); /** @brief check if this function is virtual in the base classes */ - bool isImplicitlyVirtual(bool defaultVal = false, bool* pFoundAllBaseClasses = nullptr) const; + bool isImplicitlyVirtual(bool defaultVal = false, bool* pFoundAllBaseClasses = nullptr, bool* inconclusive = nullptr) const; std::vector getOverloadedFunctions() const; From 9aa2c855379927525359b3d13f41f9dc1db3331b Mon Sep 17 00:00:00 2001 From: Johan Crone Date: Thu, 19 Feb 2026 15:52:02 +0100 Subject: [PATCH 2/4] Fix #13944 tests --- test/testother.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/testother.cpp b/test/testother.cpp index 4727c15ef27..637b50f53ff 100644 --- a/test/testother.cpp +++ b/test/testother.cpp @@ -4749,6 +4749,31 @@ class TestOther : public TestFixture { " return [](int* p) { return *p; }(&i);\n" "}\n"); ASSERT_EQUALS("[test.cpp:3:20]: (style) Parameter 'p' can be declared as pointer to const [constParameterPointer]\n", errout_str()); + + check("class A {\n" + "public:\n" + " void func01(QPoint* pt1) {\n" + " if (nullptr == pt1) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:25]: (style) Parameter 'pt1' can be declared as pointer to const [constParameterPointer]\n", + errout_str()); + + check("class A : public QObject {\n" + "public:\n" + " void func01(QPoint* pt2) {\n" + " if (nullptr == pt2) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:25]: (style) Either there is missing override/final keyword, or the Parameter 'pt2' can be pointer to const [constParameterPointer]\n", + errout_str()); + check("class A : public QObject {\n" + "public:\n" + " void func01(QPoint* pt3) override {\n" + " if (nullptr == pt3) {}\n" + " }\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); } void constArray() { From 80376755578e1117037c6e1c4ca4cb95b965c988 Mon Sep 17 00:00:00 2001 From: Johan Crone Date: Thu, 19 Feb 2026 16:44:36 +0100 Subject: [PATCH 3/4] fix --- lib/checkother.cpp | 4 ++-- lib/checkother.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index a8b63902984..bc9e1efbd89 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2010,7 +2010,7 @@ void CheckOther::checkConstPointer() for (const Variable *p: pointers) { bool inconclusive = false; if (p->isArgument()) { - if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(false, (bool*)nullptr, &inconclusive) || p->scope()->function->hasVirtualSpecifier()) + if (!p->scope() || !p->scope()->function || p->scope()->function->isImplicitlyVirtual(false, nullptr, &inconclusive) || p->scope()->function->hasVirtualSpecifier()) continue; if (p->isMaybeUnused()) continue; @@ -2032,7 +2032,7 @@ void CheckOther::checkConstPointer() } } -void CheckOther::constVariableError(const Variable *var, const Function *function, bool* inconclusive) +void CheckOther::constVariableError(const Variable *var, const Function *function, const bool *inconclusive) { if (!var) { reportError(nullptr, Severity::style, "constParameter", "Parameter 'x' can be declared with const"); diff --git a/lib/checkother.h b/lib/checkother.h index e8b6c7d1d75..bab08518857 100644 --- a/lib/checkother.h +++ b/lib/checkother.h @@ -215,7 +215,7 @@ class CPPCHECKLIB CheckOther : public Check { void suspiciousFloatingPointCastError(const Token *tok); void invalidPointerCastError(const Token* tok, const std::string& from, const std::string& to, bool inconclusive, bool toIsInt); void passedByValueError(const Variable* var, bool inconclusive, bool isRangeBasedFor = false); - void constVariableError(const Variable *var, const Function *function, bool * inconclusive = nullptr); + void constVariableError(const Variable *var, const Function *function, const bool *inconclusive = nullptr); void constStatementError(const Token *tok, const std::string &type, bool inconclusive); void signedCharArrayIndexError(const Token *tok); void unknownSignCharArrayIndexError(const Token *tok); From 9433a254685b93354d9dbf5e67c4916d7f2b8e95 Mon Sep 17 00:00:00 2001 From: Johan Crone Date: Fri, 20 Feb 2026 13:53:19 +0100 Subject: [PATCH 4/4] Updated according to findings Changed so that error message is in format "either ... or can be const" format. Added testcase with override, i.e. no warning. --- lib/checkother.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/checkother.cpp b/lib/checkother.cpp index bc9e1efbd89..1a09d622782 100644 --- a/lib/checkother.cpp +++ b/lib/checkother.cpp @@ -2051,7 +2051,9 @@ void CheckOther::constVariableError(const Variable *var, const Function *functio ErrorPath errorPath; std::string id = "const" + vartype; - std::string message = "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray; + std::string message = !(inconclusive && *inconclusive) ? + "$symbol:" + varname + "\n" + vartype + " '$symbol' can be declared as " + ptrRefArray : + "$symbol:" + varname + "\nEither there is missing override/final keyword, or the " + vartype + " '$symbol' can be " + ptrRefArray; errorPath.emplace_back(var->nameToken(), message); if (var->isArgument() && function && function->functionPointerUsage) { errorPath.emplace_front(function->functionPointerUsage, "You might need to cast the function pointer here"); @@ -2063,7 +2065,7 @@ void CheckOther::constVariableError(const Variable *var, const Function *functio id += "Pointer"; } - reportError(std::move(errorPath), Severity::style, id.c_str(), message, CWE398, (inconclusive && *inconclusive) ? Certainty::inconclusive : Certainty::normal); + reportError(std::move(errorPath), Severity::style, id.c_str(), message, CWE398, Certainty::normal); } //---------------------------------------------------------------------------