diff --git a/lib/checkclass.cpp b/lib/checkclass.cpp index 3db1ca19793..0c00590ebeb 100644 --- a/lib/checkclass.cpp +++ b/lib/checkclass.cpp @@ -111,21 +111,28 @@ CheckClassImpl::CheckClassImpl(const Tokenizer *tokenizer, const Settings &setti mSymbolDatabase(tokenizer?tokenizer->getSymbolDatabase():nullptr) {} -bool CheckClassImpl::isInitialized(const Usage& usage, FunctionType funcType) const +CheckClassImpl::Initialization CheckClassImpl::checkInitialization(const Usage& usage, FunctionType funcType, bool noConstructor) const { const Variable& var = *usage.var; if (usage.assign || usage.init || var.isStatic()) - return true; + return Initialization::Initialized; if (!var.nameToken() || var.nameToken()->isAnonymous()) - return true; + return Initialization::None; if (var.valueType() && var.valueType()->pointer == 0 && var.type() && var.type()->needInitialization == Type::NeedInitialization::False && var.type()->derivedFrom.empty()) - return true; + return Initialization::Initialized; - if (var.isConst() && funcType == FunctionType::eOperatorEqual) // We can't set const members in assignment operator - return true; + if (var.isConst() && (funcType == FunctionType::eOperatorEqual || // We can't set const members in assignment operator + (funcType == FunctionType::eConstructor && noConstructor))) // a const member of class type is default constructed or makes the class not default constructible + return Initialization::None; + + if (funcType == FunctionType::eConstructor && noConstructor && var.nameToken()->isBitfield() && mSettings.standards.cpp < Standards::CPP20) + return Initialization::None; + + if (var.isArray() && ((var.dimensionKnown(0) && var.dimensions()[0].num == 0) || Token::simpleMatch(var.nameToken()->next(), "[ ]"))) // HACK for unreliable dimensions + return Initialization::None; // Check if this is a class constructor if (!var.isPointer() && !var.isPointerArray() && var.isClass() && funcType == FunctionType::eConstructor) { @@ -141,33 +148,33 @@ bool CheckClassImpl::isInitialized(const Usage& usage, FunctionType funcType) co if (!ctt->isStandardType() && (!ctt->type() || ctt->type()->needInitialization != Type::NeedInitialization::True) && !mSettings.library.podtype(ctt->str())) // TODO: handle complex type expression - return true; + return Initialization::Initialized; } else - return true; + return Initialization::Initialized; } else - return true; + return Initialization::Initialized; } // Known type that doesn't need initialization or // known type that has member variables of an unknown type else if (var.type()->needInitialization != Type::NeedInitialization::True) - return true; + return Initialization::Initialized; } // Check if type can't be copied if (!var.isPointer() && !var.isPointerArray() && var.typeScope()) { if (funcType == FunctionType::eMoveConstructor) { if (canNotMove(var.typeScope())) - return true; + return Initialization::None; } else { if (canNotCopy(var.typeScope())) - return true; + return Initialization::None; } } - return false; + return Initialization::Missing; } void CheckClassImpl::handleUnionMembers(std::vector& usageList) @@ -348,7 +355,7 @@ void CheckClassImpl::constructors() // Check if any variables are uninitialized for (const Usage &usage : usageList) { - if (isInitialized(usage, func.type)) + if (checkInitialization(usage, func.type) != Initialization::Missing) continue; // Is there missing member copy in copy/move constructor or assignment operator? @@ -413,7 +420,7 @@ void CheckClassImpl::constructors() // 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)) + if (checkInitialization(usage, FunctionType::eConstructor) != Initialization::Missing) continue; const Variable& var = *usage.var; if (var.typeScope() && var.typeScope()->numConstructors > 0) @@ -446,7 +453,7 @@ void CheckClassImpl::constructors() } else if (!var.isStatic() && (isReferenceMember || (var.isConst() && !var.isClass()))) { // the default constructor is deleted, so all members are initialized by every instance isDefaultConstructible = false; - } else if (cpp14OrLater && !hasAnySelfInit && isInitialized(usage, FunctionType::eConstructor)) { + } else if (cpp14OrLater && !hasAnySelfInit && checkInitialization(usage, FunctionType::eConstructor) == Initialization::Initialized) { hasAnySelfInit = true; } } @@ -457,17 +464,13 @@ void CheckClassImpl::constructors() // Check if any variables are uninitialized for (const Usage& usage : usageList) { - if (isInitialized(usage, FunctionType::eConstructor)) + if (checkInitialization(usage, FunctionType::eConstructor, /*noConstructor*/ true) != Initialization::Missing) continue; const Variable& var = *usage.var; if (var.typeScope() && var.typeScope()->numConstructors > 0) continue; - // a const member of class type is default constructed or makes the class not default constructible - if (var.isConst()) - continue; - if (diagVars.count(&var) == 0) uninitVarError(var.nameToken(), false, FunctionType::eConstructor, var.scope()->className, var.name(), false, false, true); } diff --git a/lib/checkclass.h b/lib/checkclass.h index 74f61ee4705..376953ab6fd 100644 --- a/lib/checkclass.h +++ b/lib/checkclass.h @@ -251,7 +251,12 @@ class CPPCHECKLIB CheckClassImpl : public CheckImpl { static void handleUnionMembers(std::vector& usageList); - bool isInitialized(const Usage& usage, FunctionType funcType) const; + enum class Initialization : std::uint8_t { + Missing, // a member that is missing initialization + Initialized, // a member that has a constructor or an explicit initializer + None // a member that cannot be initialized (does not trigger a warning for other uninitialized members) + }; + Initialization checkInitialization(const Usage& usage, FunctionType funcType, bool noConstructor = false) const; static bool isBaseClassMutableMemberFunc(const Token *tok, const Scope *scope); diff --git a/lib/token.h b/lib/token.h index 3df24a86cb6..8e96c405e97 100644 --- a/lib/token.h +++ b/lib/token.h @@ -775,7 +775,6 @@ class CPPCHECKLIB Token { return this; } - // cppcheck-suppress unusedFunction bool isBitfield() const { return mImpl->mBits >= 0; } diff --git a/test/testconstructors.cpp b/test/testconstructors.cpp index 8668772b199..47a5512fc79 100644 --- a/test/testconstructors.cpp +++ b/test/testconstructors.cpp @@ -893,6 +893,53 @@ class TestConstructors : public TestFixture { " return S{ {}, v.cbegin() };\n" "}\n"); ASSERT_EQUALS("", errout_str()); + + const char code2[] = "struct S {\n" // #15177 + " int i = 0;\n" + " unsigned u : 1;\n" + " int a[];\n" + " int b[0];\n" + "};\n"; + const Settings s17 = settingsBuilder(settings).cpp(Standards::CPP17).build(); + check(code2, s17); + ASSERT_EQUALS("", errout_str()); + check(code2); + ASSERT_EQUALS("[test.cpp:3:14]: (warning) Member variable 'S::u' has no initializer. [uninitMemberVarNoCtor]\n", + errout_str()); + + const char code3[] = "struct S {\n" + " S() = default;\n" + " unsigned u : 1;\n" + "};\n"; + check(code3, s17); + ASSERT_EQUALS("[test.cpp:2:5]: (warning) Member variable 'S::u' is not initialized in the constructor. [uninitMemberVar]\n", + errout_str()); + check(code3); + ASSERT_EQUALS("[test.cpp:2:5]: (warning) Member variable 'S::u' is not initialized in the constructor. [uninitMemberVar]\n", + errout_str()); + + check("struct S {\n" + " int i = 0;\n" + " int a[];\n" + "};\n" + "struct T {\n" + " T() : i(0) {} \n" + " int i;\n" + " int a[];\n" + "};"); + ASSERT_EQUALS("", errout_str()); + + check("struct S {\n" + " static std::string s;\n" + " int x;\n" + "};\n" + "struct T {\n" + " static const int i = 0;\n" + " int x;\n" + "};"); + ASSERT_EQUALS("[test.cpp:3:9]: (warning) Member variable 'S::x' has no initializer. [uninitMemberVarNoCtor]\n" + "[test.cpp:7:9]: (warning) Member variable 'T::x' has no initializer. [uninitMemberVarNoCtor]\n", + errout_str()); } // ticket #4290 "False Positive: style (noConstructor): The class 'foo' does not have a constructor."