diff --git a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp --- a/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp +++ b/clang-tools-extra/clang-tidy/cppcoreguidelines/ProTypeMemberInitCheck.cpp @@ -61,6 +61,17 @@ } } +void removeFieldInitialized(const FieldDecl *M, + SmallPtrSetImpl &FieldDecls) { + const RecordDecl *R = M->getParent(); + if (R && R->isUnion()) { + // Erase all members in a union if any member of it is initialized. + for (const auto *F : R->fields()) + FieldDecls.erase(F); + } else + FieldDecls.erase(M); +} + void removeFieldsInitializedInBody( const Stmt &Stmt, ASTContext &Context, SmallPtrSetImpl &FieldDecls) { @@ -70,7 +81,7 @@ hasLHS(memberExpr(member(fieldDecl().bind("fieldDecl")))))), Stmt, Context); for (const auto &Match : Matches) - FieldDecls.erase(Match.getNodeAs("fieldDecl")); + removeFieldInitialized(Match.getNodeAs("fieldDecl"), FieldDecls); } StringRef getName(const FieldDecl *Field) { return Field->getName(); } @@ -418,13 +429,20 @@ // Gather all fields (direct and indirect) that need to be initialized. SmallPtrSet FieldsToInit; - forEachField(ClassDecl, ClassDecl.fields(), [&](const FieldDecl *F) { + bool AnyMemberHasInitPerUnion = false; + forEachFieldWithFilter(ClassDecl, ClassDecl.fields(), + AnyMemberHasInitPerUnion, [&](const FieldDecl *F) { if (IgnoreArrays && F->getType()->isArrayType()) return; + if (F->hasInClassInitializer() && F->getParent()->isUnion()) { + AnyMemberHasInitPerUnion = true; + removeFieldInitialized(F, FieldsToInit); + } if (!F->hasInClassInitializer() && utils::type_traits::isTriviallyDefaultConstructible(F->getType(), Context) && - !isEmpty(Context, F->getType()) && !F->isUnnamedBitfield()) + !isEmpty(Context, F->getType()) && !F->isUnnamedBitfield() && + !AnyMemberHasInitPerUnion) FieldsToInit.insert(F); }); if (FieldsToInit.empty()) @@ -437,7 +455,7 @@ if (Init->isAnyMemberInitializer() && Init->isWritten()) { if (IsUnion) return; // We can only initialize one member of a union. - FieldsToInit.erase(Init->getAnyMember()); + removeFieldInitialized(Init->getAnyMember(), FieldsToInit); } } removeFieldsInitializedInBody(*Ctor->getBody(), Context, FieldsToInit); @@ -478,7 +496,7 @@ // Collect all fields but only suggest a fix for the first member of unions, // as initializing more than one union member is an error. SmallPtrSet FieldsToFix; - bool AnyMemberHasInitPerUnion = false; + AnyMemberHasInitPerUnion = false; forEachFieldWithFilter(ClassDecl, ClassDecl.fields(), AnyMemberHasInitPerUnion, [&](const FieldDecl *F) { if (!FieldsToInit.count(F)) diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -120,6 +120,11 @@ ` check. Partial support for C++14 signal handler rules was added. Bug report generation was improved. + +- Fixed a false positive in :doc:`cppcoreguidelines-pro-type-member-init + ` when warnings + would be emitted for uninitialized members of an anonymous union despite + there being an initializer for one of the other members. - Improved `modernize-use-emplace `_ check. diff --git a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp --- a/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/cppcoreguidelines/pro-type-member-init.cpp @@ -552,3 +552,30 @@ int A; // CHECK-FIXES-NOT: int A{}; }; + +struct S1 { + S1() {} + union { + int a = 0; + int b; + }; +}; + +struct S2 { + S2() : a{} {} + union { + int a; + int b; + }; +}; + +struct S3 { + S3() {} + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: constructor does not initialize these fields: A [cppcoreguidelines-pro-type-member-init] + int A; + // CHECK-FIXES: int A{}; + union { + int B; + int C = 0; + }; +};