From a38b81481e9e9292fe71887294da5f5372bbb18c Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 13 May 2026 23:22:42 +0200 Subject: [PATCH 1/4] Fix #14697 FN uninitMemberVarNoCtor when mixing (un)initialized member variables --- lib/checkclass.cpp | 5 ++++- test/testconstructors.cpp | 13 +++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 9fc9bdc7863..26b71b2fb6f 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -351,6 +351,7 @@ void CheckClass::constructors() // Variables with default initializers bool hasAnyDefaultInit = false; + bool hasAnySelfInit = false; for (Usage& usage : usageList) { const Variable& var = *usage.var; @@ -358,9 +359,11 @@ void CheckClass::constructors() if (var.hasDefault()) { usage.init = true; hasAnyDefaultInit = true; + } else if (!hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) { + hasAnySelfInit = true; } } - if (!hasAnyDefaultInit) + if (!hasAnyDefaultInit && !hasAnySelfInit) continue; handleUnionMembers(usageList); diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 61916e1d0bb..b91505378d1 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -769,6 +769,19 @@ class TestConstructors : public TestFixture { " int a, b;\n" "};\n"); ASSERT_EQUALS("", errout_str()); + + check("struct S { int i = 0; };\n" // #14697 + "struct T {\n" + " S s;\n" + " int j;\n" + "};\n" + "struct U {\n" + " std::string a;\n" + " int k;\n" + "};\n"); + ASSERT_EQUALS("[test.cpp:4:9]: (warning) Member variable 'T::j' has no initializer. [uninitMemberVarNoCtor]\n" + "[test.cpp:8:9]: (warning) Member variable 'U::k' has no initializer. [uninitMemberVarNoCtor]\n", + errout_str()); } // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor." From 2e694130fe01adff569a1de5a8ac535743332e30 Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Wed, 13 May 2026 23:29:55 +0200 Subject: [PATCH 2/4] Fix --- lib/checkclass.cpp | 2 +- test/testconstructors.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 26b71b2fb6f..da1e0c5b3a9 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -375,7 +375,7 @@ void CheckClass::constructors() const Variable& var = *usage.var; if (diagVars.count(&var) == 0) - uninitVarError(scope->bodyStart, false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); + uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); } } } diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index b91505378d1..69f0ae9a037 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -763,7 +763,7 @@ class TestConstructors : public TestFixture { check("struct S {\n" // #14546 " int a = 0, b;\n" "};\n"); - ASSERT_EQUALS("[test.cpp:1:10]: (warning) Member variable 'S::b' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); + ASSERT_EQUALS("[test.cpp:2:16]: (warning) Member variable 'S::b' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); check("struct S {\n" " int a, b;\n" From 44461e1cc9a32a9854cefff7ecd85568e30228fc Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Thu, 14 May 2026 14:22:26 +0200 Subject: [PATCH 3/4] Fix --- lib/checkclass.cpp | 6 +++++- test/testconstructors.cpp | 27 +++++++++++++++++++++------ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index da1e0c5b3a9..ad2c665f7c6 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -352,6 +352,7 @@ void CheckClass::constructors() // Variables with default initializers bool hasAnyDefaultInit = false; bool hasAnySelfInit = false; + const bool cpp14OrLater = mSettings->standards.cpp >= Standards::CPP14; for (Usage& usage : usageList) { const Variable& var = *usage.var; @@ -359,7 +360,7 @@ void CheckClass::constructors() if (var.hasDefault()) { usage.init = true; hasAnyDefaultInit = true; - } else if (!hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) { + } else if (cpp14OrLater && !hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) { hasAnySelfInit = true; } } @@ -374,6 +375,9 @@ void CheckClass::constructors() continue; const Variable& var = *usage.var; + if (var.typeScope() && var.typeScope()->numConstructors > 0) + continue; + if (diagVars.count(&var) == 0) uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); } diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 69f0ae9a037..0eb91ba8cca 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -770,15 +770,30 @@ class TestConstructors : public TestFixture { "};\n"); ASSERT_EQUALS("", errout_str()); - check("struct S { int i = 0; };\n" // #14697 + check("struct S {\n" + " explicit S(int);\n" + " S(const S&);\n" + " int i;\n" + "};\n" "struct T {\n" " S s;\n" - " int j;\n" - "};\n" - "struct U {\n" - " std::string a;\n" - " int k;\n" + " int j{};\n" "};\n"); + ASSERT_EQUALS("", errout_str()); + + const char code[] = "struct S { int i = 0; };\n" // #14697 + "struct T {\n" + " S s;\n" + " int j;\n" + "};\n" + "struct U {\n" + " std::string a;\n" + " int k;\n" + "};\n"; + const Settings s = settingsBuilder(settings).cpp(Standards::CPP11).build(); + check(code, s); + ASSERT_EQUALS("", errout_str()); + check(code); ASSERT_EQUALS("[test.cpp:4:9]: (warning) Member variable 'T::j' has no initializer. [uninitMemberVarNoCtor]\n" "[test.cpp:8:9]: (warning) Member variable 'U::k' has no initializer. [uninitMemberVarNoCtor]\n", errout_str()); From 54b1bb70a0a4ede87f8e4fd9c530510c9fc25cde Mon Sep 17 00:00:00 2001 From: chrchr-github Date: Thu, 14 May 2026 14:31:05 +0200 Subject: [PATCH 4/4] Format --- lib/checkclass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index ad2c665f7c6..dc679863026 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -377,7 +377,7 @@ void CheckClass::constructors() const Variable& var = *usage.var; if (var.typeScope() && var.typeScope()->numConstructors > 0) continue; - + if (diagVars.count(&var) == 0) uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); }