Skip to content

Commit 07254e9

Browse files
autoantwortclaudeclaude[bot]
authored
Fix #15080 FP uninitMemberVarNoCtor for const and reference members (#8910)
https://trac.cppcheck.net/ticket/15080 ```cpp struct S { const std::array<std::uint8_t, 2> a; const std::vector<std::uint8_t>::const_iterator it; }; S f(const std::vector<std::uint8_t>& v) { return S{ {}, v.cbegin() }; } ``` gives `Member variable 'S::it' has no initializer. [uninitMemberVarNoCtor]`, and `struct S { int a = 0; const int b; int& c; };` warns for `b` and `c`. A non-static const or reference member without default member initializer can't stay uninitialized: for trivial types the implicit default constructor is deleted, so every instance must initialize it, and a const class type is default-constructed. These members are now skipped by the no-constructor check. Since f90a76f (#15072) this also shows up for class templates whose member type is a `using` alias. This PR was created by Claude Code (an AI coding agent) on behalf of @autoantwort. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
1 parent 83bf285 commit 07254e9

2 files changed

Lines changed: 95 additions & 1 deletion

File tree

‎lib/checkclass.cpp‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,19 +347,25 @@ void CheckClassImpl::constructors()
347347
// Variables with default initializers
348348
bool hasAnyDefaultInit = false;
349349
bool hasAnySelfInit = false;
350+
bool isDefaultConstructible = true;
350351
const bool cpp14OrLater = mSettings.standards.cpp >= Standards::CPP14;
351352
for (Usage& usage : usageList) {
352353
const Variable& var = *usage.var;
354+
// isReference() is also true for a function pointer that returns a reference
355+
const bool isReferenceMember = var.isReference() && var.nameToken() && Token::Match(var.nameToken()->previous(), "&|&&");
353356

354357
// check for C++11 initializer
355358
if (var.hasDefault()) {
356359
usage.init = true;
357360
hasAnyDefaultInit = true;
361+
} else if (!var.isStatic() && (isReferenceMember || (var.isConst() && !var.isClass()))) {
362+
// the default constructor is deleted, so all members are initialized by every instance
363+
isDefaultConstructible = false;
358364
} else if (cpp14OrLater && !hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) {
359365
hasAnySelfInit = true;
360366
}
361367
}
362-
if (!hasAnyDefaultInit && !hasAnySelfInit)
368+
if (!isDefaultConstructible || (!hasAnyDefaultInit && !hasAnySelfInit))
363369
continue;
364370

365371
handleUnionMembers(usageList);
@@ -373,6 +379,10 @@ void CheckClassImpl::constructors()
373379
if (var.typeScope() && var.typeScope()->numConstructors > 0)
374380
continue;
375381

382+
// a const member of class type is default constructed or makes the class not default constructible
383+
if (var.isConst())
384+
continue;
385+
376386
if (diagVars.count(&var) == 0)
377387
uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true);
378388
}

‎test/testconstructors.cpp‎

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -797,6 +797,90 @@ class TestConstructors : public TestFixture {
797797
ASSERT_EQUALS("[test.cpp:4:9]: (warning) Member variable 'T::j' has no initializer. [uninitMemberVarNoCtor]\n"
798798
"[test.cpp:8:9]: (warning) Member variable 'U::k' has no initializer. [uninitMemberVarNoCtor]\n",
799799
errout_str());
800+
801+
check("struct S {\n" // #15080
802+
" int a = 0;\n"
803+
" const int b;\n"
804+
" int k;\n"
805+
"};\n");
806+
ASSERT_EQUALS("", errout_str());
807+
808+
check("struct S {\n"
809+
" int a = 0;\n"
810+
" int& c;\n"
811+
" int k;\n"
812+
"};\n");
813+
ASSERT_EQUALS("", errout_str());
814+
815+
check("struct S {\n"
816+
" int a = 0;\n"
817+
" const int* d;\n"
818+
"};\n");
819+
ASSERT_EQUALS("[test.cpp:3:16]: (warning) Member variable 'S::d' has no initializer. [uninitMemberVarNoCtor]\n", errout_str());
820+
821+
check("struct S {\n"
822+
" int a = 0;\n"
823+
" int* const p;\n"
824+
" int k;\n"
825+
"};\n");
826+
ASSERT_EQUALS("", errout_str());
827+
828+
check("struct S {\n" // static members don't affect the default constructor
829+
" int a = 0;\n"
830+
" static const int N;\n"
831+
" static int& r;\n"
832+
" int k;\n"
833+
"};\n");
834+
ASSERT_EQUALS("[test.cpp:5:9]: (warning) Member variable 'S::k' has no initializer. [uninitMemberVarNoCtor]\n", errout_str());
835+
836+
check("struct S {\n" // function pointers returning a reference are no references
837+
" int a = 0;\n"
838+
" std::string& (*f)(int, int);\n"
839+
" int& (*g[2])();\n"
840+
" int k;\n"
841+
"};\n");
842+
ASSERT_EQUALS("[test.cpp:3:20]: (warning) Member variable 'S::f' has no initializer. [uninitMemberVarNoCtor]\n"
843+
"[test.cpp:4:12]: (warning) Member variable 'S::g' has no initializer. [uninitMemberVarNoCtor]\n"
844+
"[test.cpp:5:9]: (warning) Member variable 'S::k' has no initializer. [uninitMemberVarNoCtor]\n",
845+
errout_str());
846+
847+
check("struct S {\n"
848+
" int a = 0;\n"
849+
" int (*&f)();\n"
850+
" int k;\n"
851+
"};\n");
852+
ASSERT_EQUALS("", errout_str());
853+
854+
check("struct S {\n"
855+
" int a = 0;\n"
856+
" int (&arr)[2];\n"
857+
" int k;\n"
858+
"};\n");
859+
ASSERT_EQUALS("", errout_str());
860+
861+
check("typedef const unsigned int Mode;\n"
862+
"struct S {\n"
863+
" std::vector<int> v;\n"
864+
" Mode m;\n"
865+
" int k;\n"
866+
"};\n");
867+
ASSERT_EQUALS("", errout_str());
868+
869+
check("struct S {\n"
870+
" std::string a;\n"
871+
" const std::string s;\n"
872+
" int k;\n"
873+
"};\n");
874+
ASSERT_EQUALS("[test.cpp:4:9]: (warning) Member variable 'S::k' has no initializer. [uninitMemberVarNoCtor]\n", errout_str());
875+
876+
check("struct S {\n"
877+
" const std::array<std::uint8_t, 2> a;\n"
878+
" const std::vector<std::uint8_t>::const_iterator it;\n"
879+
"};\n"
880+
"S f(const std::vector<std::uint8_t>& v) {\n"
881+
" return S{ {}, v.cbegin() };\n"
882+
"}\n");
883+
ASSERT_EQUALS("", errout_str());
800884
}
801885

802886
// ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor."

0 commit comments

Comments
 (0)