Skip to content

Commit 8140605

Browse files
Fix #15177 FP uninitMemberVarNoCtor for bitfields and flexible array members (#8997)
Co-authored-by: chrchr-github <noreply@github.com>
1 parent 869dc32 commit 8140605

4 files changed

Lines changed: 77 additions & 23 deletions

File tree

‎lib/checkclass.cpp‎

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -111,21 +111,28 @@ CheckClassImpl::CheckClassImpl(const Tokenizer *tokenizer, const Settings &setti
111111
mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr)
112112
{}
113113

114-
bool CheckClassImpl::isInitialized(const Usage& usage, FunctionType funcType) const
114+
CheckClassImpl::Initialization CheckClassImpl::checkInitialization(const Usage& usage, FunctionType funcType, bool noConstructor) const
115115
{
116116
const Variable& var = *usage.var;
117117

118118
if (usage.assign || usage.init || var.isStatic())
119-
return true;
119+
return Initialization::Initialized;
120120

121121
if (!var.nameToken() || var.nameToken()->isAnonymous())
122-
return true;
122+
return Initialization::None;
123123

124124
if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty())
125-
return true;
125+
return Initialization::Initialized;
126126

127-
if (var.isConst() && funcType == FunctionType::eOperatorEqual) // We can't set const members in assignment operator
128-
return true;
127+
if (var.isConst() && (funcType == FunctionType::eOperatorEqual || // We can't set const members in assignment operator
128+
(funcType == FunctionType::eConstructor && noConstructor))) // a const member of class type is default constructed or makes the class not default constructible
129+
return Initialization::None;
130+
131+
if (funcType == FunctionType::eConstructor && noConstructor && var.nameToken()->isBitfield() && mSettings.standards.cpp < Standards::CPP20)
132+
return Initialization::None;
133+
134+
if (var.isArray() && ((var.dimensionKnown(0) && var.dimensions()[0].num == 0) || Token::simpleMatch(var.nameToken()->next(), "[ ]"))) // HACK for unreliable dimensions
135+
return Initialization::None;
129136

130137
// Check if this is a class constructor
131138
if (!var.isPointer() && !var.isPointerArray() && var.isClass() && funcType == FunctionType::eConstructor) {
@@ -141,33 +148,33 @@ bool CheckClassImpl::isInitialized(const Usage& usage, FunctionType funcType) co
141148
if (!ctt->isStandardType() &&
142149
(!ctt->type() || ctt->type()->needInitialization != Type::NeedInitialization::True) &&
143150
!mSettings.library.podtype(ctt->str())) // TODO: handle complex type expression
144-
return true;
151+
return Initialization::Initialized;
145152
}
146153
else
147-
return true;
154+
return Initialization::Initialized;
148155
}
149156
else
150-
return true;
157+
return Initialization::Initialized;
151158
}
152159

153160
// Known type that doesn't need initialization or
154161
// known type that has member variables of an unknown type
155162
else if (var.type()->needInitialization != Type::NeedInitialization::True)
156-
return true;
163+
return Initialization::Initialized;
157164
}
158165

159166
// Check if type can't be copied
160167
if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) {
161168
if (funcType == FunctionType::eMoveConstructor) {
162169
if (canNotMove(var.typeScope()))
163-
return true;
170+
return Initialization::None;
164171
}
165172
else {
166173
if (canNotCopy(var.typeScope()))
167-
return true;
174+
return Initialization::None;
168175
}
169176
}
170-
return false;
177+
return Initialization::Missing;
171178
}
172179

173180
void CheckClassImpl::handleUnionMembers(std::vector<Usage>& usageList)
@@ -348,7 +355,7 @@ void CheckClassImpl::constructors()
348355

349356
// Check if any variables are uninitialized
350357
for (const Usage &usage : usageList) {
351-
if (isInitialized(usage, func.type))
358+
if (checkInitialization(usage, func.type) != Initialization::Missing)
352359
continue;
353360

354361
// Is there missing member copy in copy/move constructor or assignment operator?
@@ -413,7 +420,7 @@ void CheckClassImpl::constructors()
413420
// Inherited constructors don't initialize members of the derived class
414421
for (const Token *baseCtorTok : inheritedCtors) {
415422
for (const Usage &usage : usageList) {
416-
if (isInitialized(usage, FunctionType::eConstructor))
423+
if (checkInitialization(usage, FunctionType::eConstructor) != Initialization::Missing)
417424
continue;
418425
const Variable& var = *usage.var;
419426
if (var.typeScope() && var.typeScope()->numConstructors > 0)
@@ -443,7 +450,7 @@ void CheckClassImpl::constructors()
443450
} else if (!var.isStatic() && (var.isReference() || (var.isConst() && !var.isClass()))) {
444451
// the default constructor is deleted, so all members are initialized by every instance
445452
isDefaultConstructible = false;
446-
} else if (cpp14OrLater && !hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) {
453+
} else if (cpp14OrLater && !hasAnySelfInit && checkInitialization(usage, FunctionType::eConstructor) == Initialization::Initialized) {
447454
hasAnySelfInit = true;
448455
}
449456
}
@@ -454,17 +461,13 @@ void CheckClassImpl::constructors()
454461

455462
// Check if any variables are uninitialized
456463
for (const Usage& usage : usageList) {
457-
if (isInitialized(usage, FunctionType::eConstructor))
464+
if (checkInitialization(usage, FunctionType::eConstructor, /*noConstructor*/ true) != Initialization::Missing)
458465
continue;
459466

460467
const Variable& var = *usage.var;
461468
if (var.typeScope() && var.typeScope()->numConstructors > 0)
462469
continue;
463470

464-
// a const member of class type is default constructed or makes the class not default constructible
465-
if (var.isConst())
466-
continue;
467-
468471
if (diagVars.count(&var) == 0)
469472
uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true);
470473
}

‎lib/checkclass.h‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,12 @@ class CPPCHECKLIB CheckClassImpl : public CheckImpl {
251251

252252
static void handleUnionMembers(std::vector<Usage>& usageList);
253253

254-
bool isInitialized(const Usage& usage, FunctionType funcType) const;
254+
enum class Initialization : std::uint8_t {
255+
Missing, // a member that is missing initialization
256+
Initialized, // a member that has a constructor or an explicit initializer
257+
None // a member that cannot be initialized (does not trigger a warning for other uninitialized members)
258+
};
259+
Initialization checkInitialization(const Usage& usage, FunctionType funcType, bool noConstructor = false) const;
255260

256261
static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope);
257262

‎lib/token.h‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -775,7 +775,6 @@ class CPPCHECKLIB Token {
775775
return this;
776776
}
777777

778-
// cppcheck-suppress unusedFunction
779778
bool isBitfield() const {
780779
return mImpl->mBits >= 0;
781780
}

‎test/testconstructors.cpp‎

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,53 @@ class TestConstructors : public TestFixture {
893893
" return S{ {}, v.cbegin() };\n"
894894
"}\n");
895895
ASSERT_EQUALS("", errout_str());
896+
897+
const char code2[] = "struct S {\n" // #15177
898+
" int i = 0;\n"
899+
" unsigned u : 1;\n"
900+
" int a[];\n"
901+
" int b[0];\n"
902+
"};\n";
903+
const Settings s17 = settingsBuilder(settings).cpp(Standards::CPP17).build();
904+
check(code2, s17);
905+
ASSERT_EQUALS("", errout_str());
906+
check(code2);
907+
ASSERT_EQUALS("[test.cpp:3:14]: (warning) Member variable 'S::u' has no initializer. [uninitMemberVarNoCtor]\n",
908+
errout_str());
909+
910+
const char code3[] = "struct S {\n"
911+
" S() = default;\n"
912+
" unsigned u : 1;\n"
913+
"};\n";
914+
check(code3, s17);
915+
ASSERT_EQUALS("[test.cpp:2:5]: (warning) Member variable 'S::u' is not initialized in the constructor. [uninitMemberVar]\n",
916+
errout_str());
917+
check(code3);
918+
ASSERT_EQUALS("[test.cpp:2:5]: (warning) Member variable 'S::u' is not initialized in the constructor. [uninitMemberVar]\n",
919+
errout_str());
920+
921+
check("struct S {\n"
922+
" int i = 0;\n"
923+
" int a[];\n"
924+
"};\n"
925+
"struct T {\n"
926+
" T() : i(0) {} \n"
927+
" int i;\n"
928+
" int a[];\n"
929+
"};");
930+
ASSERT_EQUALS("", errout_str());
931+
932+
check("struct S {\n"
933+
" static std::string s;\n"
934+
" int x;\n"
935+
"};\n"
936+
"struct T {\n"
937+
" static const int i = 0;\n"
938+
" int x;\n"
939+
"};");
940+
ASSERT_EQUALS("[test.cpp:3:9]: (warning) Member variable 'S::x' has no initializer. [uninitMemberVarNoCtor]\n"
941+
"[test.cpp:7:9]: (warning) Member variable 'T::x' has no initializer. [uninitMemberVarNoCtor]\n",
942+
errout_str());
896943
}
897944

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

0 commit comments

Comments
 (0)