From 68d66bc28baa41c9e72d54405e1e34d1f8526056 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 13 Oct 2026 09:17:31 +0200 Subject: [PATCH 1/7] test --- test/testconstructors.cpp | 80 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 4de49f54311..4c05798b520 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -232,6 +232,14 @@ class TestConstructors : public TestFixture { TEST_CASE(uninitTemplate1); // ticket #7372 TEST_CASE(unknownTemplateType); + + TEST_CASE(inheritedCtor1); + TEST_CASE(inheritedCtor2); + TEST_CASE(inheritedCtor3); + TEST_CASE(inheritedCtor4); + TEST_CASE(inheritedCtor5); + TEST_CASE(inheritedCtor6); + TEST_CASE(inheritedCtor7); } @@ -4648,6 +4656,78 @@ class TestConstructors : public TestFixture { ASSERT_EQUALS("", errout_str()); } + void inheritedCtor1() { + check("struct Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:17]: (warning) Member variable 'Derived::x' is not initialized in the constructor inherited from 'Base'. [uninitMemberVar]\n", errout_str()); + } + + void inheritedCtor2() { + check("struct Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x = 0;\n" + " int y{};\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void inheritedCtor3() { + check("struct Base { Base(int); };\n" + "struct S { S(); int i; };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " S s;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void inheritedCtor4() { + check("struct Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x = 0;\n" + " int y;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:17]: (warning) Member variable 'Derived::y' is not initialized in the constructor inherited from 'Base'. [uninitMemberVar]\n", errout_str()); + } + + void inheritedCtor5() { + check("struct Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " Derived() : x(0) {}\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:17]: (warning) Member variable 'Derived::x' is not initialized in the constructor inherited from 'Base'. [uninitMemberVar]\n", errout_str()); + } + + void inheritedCtor6() { + check("struct A { A(int); };\n" + "struct B { B(double); };\n" + "struct D : A, B {\n" + " using A::A;\n" + " using B::B;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4:14]: (warning) Member variable 'D::x' is not initialized in the constructor inherited from 'A'. [uninitMemberVar]\n" + "[test.cpp:5:14]: (warning) Member variable 'D::x' is not initialized in the constructor inherited from 'B'. [uninitMemberVar]\n", + errout_str()); + } + + void inheritedCtor7() { + check("struct Base { void f(); };\n" + "struct Derived : Base {\n" + " using Base::f;\n" + " int x = 0;\n" + " int y;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:5:9]: (warning) Member variable 'Derived::y' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); + } + }; REGISTER_TEST(TestConstructors) From 1ff8dd1ec5d05055601a936fa954eaded7fb750c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Mon, 12 Oct 2026 15:13:09 +0200 Subject: [PATCH 2/7] fix --- lib/checkclass.cpp | 75 ++++++++++++++++++++++++++++++++++++++++++++-- lib/checkclass.h | 2 +- 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 0f02016bd01..cb34536f963 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -195,6 +195,47 @@ void CheckClassImpl::handleUnionMembers(std::vector& usageList) } } +static std::vector getInheritedConstructors(const Scope *scope) +{ + std::vector result; + + if (!scope || !scope->bodyStart || !scope->definedType) + return result; + + const Token *baseName = nullptr; + for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { + if (Token::Match(tok, "using %name%")) { + const bool isBase = std::any_of( + scope->definedType->derivedFrom.cbegin(), + scope->definedType->derivedFrom.cend(), + [&tok](const Type::BaseInfo &info) { + return info.name == tok->strAt(1); + } + ); + if (isBase) + baseName = tok->next(); + continue; + } + + if (tok->str() == ";") { + baseName = nullptr; + continue; + } + + if (tok->str() == "{") { + baseName = nullptr; + tok = tok->link(); + continue; + } + + if (baseName && Token::Match(tok, "%name% :: %name% ;") && + tok->str() == baseName->str() && tok->tokAt(2)->str() == baseName->str()) + result.push_back(tok->tokAt(2)); + } + + return result; +} + //--------------------------------------------------------------------------- // ClassCheck: Check that all class constructors are ok. //--------------------------------------------------------------------------- @@ -223,9 +264,11 @@ void CheckClassImpl::constructors() }); }); + const std::vector inheritedCtors = getInheritedConstructors(scope); + // There are no constructors. std::set diagVars; - if (scope->numConstructors == 0 && printStyle && !usedInUnion) { + if (scope->numConstructors == 0 && inheritedCtors.empty() && printStyle && !usedInUnion) { // If there is a private variable, there should be a constructor.. int needInit = 0, haveInit = 0; std::vector uninitVars; @@ -339,6 +382,30 @@ void CheckClassImpl::constructors() } } + if (!inheritedCtors.empty()) { + clearAllVar(usageList); + + for (Usage &usage : usageList) { + if (usage.var->hasDefault()) + usage.init = true; + } + + handleUnionMembers(usageList); + + // Inherited constructors don't initialize members of the derived class + for (const Token *baseCtorTok : inheritedCtors) { + for (const Usage &usage : usageList) { + if (isInitialized(usage, FunctionType::eConstructor)) + continue; + const Variable& var = *usage.var; + if (var.typeScope() && var.typeScope()->numConstructors > 0) + continue; + uninitVarError(baseCtorTok, false, FunctionType::eConstructor, var.scope()->className, var.name(), scope != var.scope(), false, false, true); + diagVars.emplace(&var); + } + } + } + if (scope->numConstructors == 0) { // Mark all variables not used @@ -1172,12 +1239,14 @@ void CheckClassImpl::noExplicitConstructorError(const Token *tok, const std::str reportError(tok, Severity::style, "noExplicitConstructor", "$symbol:" + classname + '\n' + message + '\n' + verbose, CWE398, Certainty::normal); } -void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor) +void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor, bool inheritedCtor) { std::string message("Member variable '$symbol' "); if (noCtor) message += "has no initializer."; - else { + else if (inheritedCtor) { + message += "is not initialized in the constructor inherited from '" + tok->str() + "'."; + } else { message += "is not initialized in the "; if (functionType == FunctionType::eCopyConstructor) message += "copy "; diff --git a/lib/checkclass.h b/lib/checkclass.h index 0aa8fc29da3..74f61ee4705 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -182,7 +182,7 @@ class CPPCHECKLIB CheckClassImpl : public CheckImpl { void noCopyConstructorError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noOperatorEqError(const Scope *scope, bool isdefault, const Token *alloc, bool inconclusive); void noDestructorError(const Scope *scope, bool isdefault, const Token *alloc); - void uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor = false); + void uninitVarError(const Token *tok, bool isprivate, FunctionType functionType, const std::string &classname, const std::string &varname, bool derived, bool inconclusive, bool noCtor = false, bool inheritedCtor = false); void uninitVarError(const Token *tok, const std::string &classname, const std::string &varname); void missingMemberCopyError(const Token *tok, FunctionType functionType, const std::string& classname, const std::string& varname); void operatorEqVarError(const Token *tok, const std::string &classname, const std::string &varname, bool inconclusive); From fa2cbdc94068c413a51607cd285cb7e57367a9e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 13 Oct 2026 10:30:37 +0200 Subject: [PATCH 3/7] format --- lib/checkclass.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index cb34536f963..04b8ecf1141 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -209,9 +209,9 @@ static std::vector getInheritedConstructors(const Scope *scope) scope->definedType->derivedFrom.cbegin(), scope->definedType->derivedFrom.cend(), [&tok](const Type::BaseInfo &info) { - return info.name == tok->strAt(1); - } - ); + return info.name == tok->strAt(1); + } + ); if (isBase) baseName = tok->next(); continue; From 58e440dc5a15f5607d00afa740c9911b5b63ca0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Tue, 13 Oct 2026 15:22:36 +0200 Subject: [PATCH 4/7] selfcheck fixes --- lib/checkclass.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 04b8ecf1141..03314acd985 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -229,7 +229,7 @@ static std::vector getInheritedConstructors(const Scope *scope) } if (baseName && Token::Match(tok, "%name% :: %name% ;") && - tok->str() == baseName->str() && tok->tokAt(2)->str() == baseName->str()) + tok->str() == baseName->str() && tok->strAt(2) == baseName->str()) result.push_back(tok->tokAt(2)); } @@ -1245,7 +1245,8 @@ void CheckClassImpl::uninitVarError(const Token *tok, bool isprivate, FunctionTy if (noCtor) message += "has no initializer."; else if (inheritedCtor) { - message += "is not initialized in the constructor inherited from '" + tok->str() + "'."; + message += tok ? "is not initialized in the constructor inherited from '" + tok->str() + "'." : + "is not initialized in the inherited constructor."; } else { message += "is not initialized in the "; if (functionType == FunctionType::eCopyConstructor) From 762ef95f2f98d92c274e404103f04b6f0185cee8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 14 Oct 2026 08:52:32 +0200 Subject: [PATCH 5/7] more tests --- test/testconstructors.cpp | 60 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 4c05798b520..199da7ecb25 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -240,6 +240,9 @@ class TestConstructors : public TestFixture { TEST_CASE(inheritedCtor5); TEST_CASE(inheritedCtor6); TEST_CASE(inheritedCtor7); + TEST_CASE(inheritedCtor8); + TEST_CASE(inheritedCtor9); + TEST_CASE(inheritedCtor10); } @@ -4728,6 +4731,63 @@ class TestConstructors : public TestFixture { ASSERT_EQUALS("[test.cpp:5:9]: (warning) Member variable 'Derived::y' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); } + void inheritedCtor8() { + check("struct Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " Derived(int i) : Base(i), x(0) {}\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Base { Base(int); Base(double); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " Derived(int i) : Base(i), x(0) {}\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:3:17]: (warning) Member variable 'Derived::x' is not initialized in the constructor inherited from 'Base'. [uninitMemberVar]\n", errout_str()); + } + + void inheritedCtor9() { + check("struct Base { Base(); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Base {};\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("struct Base { Base(const Base&); Base(Base&&); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + + void inheritedCtor10() { + check("struct Base { Base(int) = delete; };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + + check("class Base { Base(int); };\n" + "struct Derived : Base {\n" + " using Base::Base;\n" + " int x;\n" + "};\n"); + ASSERT_EQUALS("", errout_str()); + } + }; REGISTER_TEST(TestConstructors) From c19716f4ef84ff6d2f06f80dbc727d507fe135a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 14 Oct 2026 08:52:01 +0200 Subject: [PATCH 6/7] handle cases where base constructor is not inherited --- lib/checkclass.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 03314acd985..bc5ed7bd341 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -202,18 +202,31 @@ static std::vector getInheritedConstructors(const Scope *scope) if (!scope || !scope->bodyStart || !scope->definedType) return result; + const auto isInherited = [](const Function &baseCtor, const Scope *scope) { + if (baseCtor.type != FunctionType::eConstructor || baseCtor.argCount() == 0) + return false; + if (baseCtor.isDelete() || baseCtor.access == AccessControl::Private) + return false; + return std::none_of(scope->functionList.cbegin(), scope->functionList.cend(), [&](const Function &func) { + return func.isConstructor() && func.argCount() == baseCtor.argCount() && + func.argsMatch(baseCtor.nestedIn, baseCtor.argDef, func.argDef, "", 0); + }); + }; + + const std::vector &bases = scope->definedType->derivedFrom; const Token *baseName = nullptr; + const std::list *baseFuncs = nullptr; for (const Token *tok = scope->bodyStart->next(); tok != scope->bodyEnd; tok = tok->next()) { if (Token::Match(tok, "using %name%")) { - const bool isBase = std::any_of( - scope->definedType->derivedFrom.cbegin(), - scope->definedType->derivedFrom.cend(), - [&tok](const Type::BaseInfo &info) { - return info.name == tok->strAt(1); - } - ); - if (isBase) + const auto base = std::find_if( + bases.cbegin(), + bases.cend(), + [&tok](const Type::BaseInfo &info) { return info.name == tok->strAt(1); } + ); + if (base != bases.cend() && base->type && base->type->classScope) { baseName = tok->next(); + baseFuncs = &base->type->classScope->functionList; + } continue; } @@ -229,7 +242,10 @@ static std::vector getInheritedConstructors(const Scope *scope) } if (baseName && Token::Match(tok, "%name% :: %name% ;") && - tok->str() == baseName->str() && tok->strAt(2) == baseName->str()) + tok->str() == baseName->str() && tok->strAt(2) == baseName->str() && + std::any_of(baseFuncs->cbegin(), baseFuncs->cend(), [&](const Function &func) { + return isInherited(func, scope); + })) result.push_back(tok->tokAt(2)); } From a93405bdbc127ed512f60363f0fb2cf473cfad74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ludvig=20Gunne=20Lindstr=C3=B6m?= Date: Wed, 14 Oct 2026 09:06:50 +0200 Subject: [PATCH 7/7] format --- lib/checkclass.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index bc5ed7bd341..7810783d50e 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -221,8 +221,10 @@ static std::vector getInheritedConstructors(const Scope *scope) const auto base = std::find_if( bases.cbegin(), bases.cend(), - [&tok](const Type::BaseInfo &info) { return info.name == tok->strAt(1); } - ); + [&tok](const Type::BaseInfo &info) { + return info.name == tok->strAt(1); + } + ); if (base != bases.cend() && base->type && base->type->classScope) { baseName = tok->next(); baseFuncs = &base->type->classScope->functionList;