diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -154,6 +154,9 @@ - Fix a hang on valid C code passing a function type as an argument to ``typeof`` to form a function declaration. (`#64713 _`) +- Clang now reports missing-field-initializers warning for missing designated + initializers in C++. + (`#56628 `_) Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -2252,6 +2252,8 @@ !IList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()); bool HasDesignatedInit = false; + llvm::SmallPtrSet InitializedFields; + while (Index < IList->getNumInits()) { Expr *Init = IList->getInit(Index); SourceLocation InitLoc = Init->getBeginLoc(); @@ -2267,20 +2269,23 @@ // Handle this designated initializer. Field will be updated to // the next field that we'll be initializing. - if (CheckDesignatedInitializer(Entity, IList, DIE, 0, - DeclType, &Field, nullptr, Index, - StructuredList, StructuredIndex, - true, TopLevelObject)) + bool DesignatedInitFailed = CheckDesignatedInitializer( + Entity, IList, DIE, 0, DeclType, &Field, nullptr, Index, + StructuredList, StructuredIndex, true, TopLevelObject); + if (DesignatedInitFailed) hadError = true; - else if (!VerifyOnly) { - // Find the field named by the designated initializer. - RecordDecl::field_iterator F = RD->field_begin(); - while (std::next(F) != Field) - ++F; - QualType ET = SemaRef.Context.getBaseElementType(F->getType()); - if (checkDestructorReference(ET, InitLoc, SemaRef)) { - hadError = true; - return; + + // Find the field named by the designated initializer. + DesignatedInitExpr::Designator *D = DIE->getDesignator(0); + if (!VerifyOnly && D->isFieldDesignator()) { + FieldDecl *F = D->getFieldDecl(); + InitializedFields.insert(F); + if (!DesignatedInitFailed) { + QualType ET = SemaRef.Context.getBaseElementType(F->getType()); + if (checkDestructorReference(ET, InitLoc, SemaRef)) { + hadError = true; + return; + } } } @@ -2288,7 +2293,8 @@ // Disable check for missing fields when designators are used. // This matches gcc behaviour. - CheckForMissingFields = false; + if (!SemaRef.getLangOpts().CPlusPlus) + CheckForMissingFields = false; continue; } @@ -2367,6 +2373,7 @@ CheckSubElementType(MemberEntity, IList, Field->getType(), Index, StructuredList, StructuredIndex); InitializedSomething = true; + InitializedFields.insert(*Field); if (RD->isUnion() && StructuredList) { // Initialize the first field within the union. @@ -2378,15 +2385,20 @@ // Emit warnings for missing struct field initializers. if (!VerifyOnly && InitializedSomething && CheckForMissingFields && - Field != FieldEnd && !Field->getType()->isIncompleteArrayType() && !RD->isUnion()) { // It is possible we have one or more unnamed bitfields remaining. // Find first (if any) named field and emit warning. - for (RecordDecl::field_iterator it = Field, end = RD->field_end(); + for (RecordDecl::field_iterator it = HasDesignatedInit ? RD->field_begin() + : Field, + end = RD->field_end(); it != end; ++it) { + if (HasDesignatedInit && InitializedFields.count(*it)) + continue; + if (!it->isUnnamedBitfield() && !it->hasInClassInitializer()) { SemaRef.Diag(IList->getSourceRange().getEnd(), - diag::warn_missing_field_initializers) << *it; + diag::warn_missing_field_initializers) + << *it; break; } } diff --git a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp --- a/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp +++ b/clang/test/SemaCXX/cxx2a-initializer-aggregates.cpp @@ -4,6 +4,7 @@ // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,reorder -Wno-c99-designator -Werror=reorder-init-list -Wno-initializer-overrides // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,override -Wno-c99-designator -Wno-reorder-init-list -Werror=initializer-overrides // RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected -Wno-c99-designator -Wno-reorder-init-list -Wno-initializer-overrides +// RUN: %clang_cc1 -std=c++20 %s -verify=cxx20,expected,wmissing -Wmissing-field-initializers -Wno-c99-designator -Wno-reorder-init-list -Wno-initializer-overrides namespace class_with_ctor { @@ -49,15 +50,17 @@ A a4 = { .x = 1, // override-note {{previous}} .x = 1 // override-error {{overrides prior initialization}} -}; +}; // wmissing-warning {{missing field 'y' initializer}} A a5 = { .y = 1, // override-note {{previous}} .y = 1 // override-error {{overrides prior initialization}} -}; +}; // wmissing-warning {{missing field 'x' initializer}} B b2 = {.a = 1}; // pedantic-error {{brace elision for designated initializer is a C99 extension}} + // wmissing-warning@-1 {{missing field 'y' initializer}} B b3 = {.a = 1, 2}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}} B b4 = {.a = 1, 2, 3}; // pedantic-error {{mixture of designated and non-designated}} pedantic-note {{first non-designated}} pedantic-error {{brace elision}} expected-error {{excess elements}} B b5 = {.a = nullptr}; // expected-error {{cannot initialize}} + // wmissing-warning@-1 {{missing field 'y' initializer}} struct C { int :0, x, :0, y, :0; }; C c = { .x = 1, // override-note {{previous}} @@ -67,6 +70,13 @@ .x = 1, // reorder-error {{declaration order}} override-error {{overrides prior initialization}} override-note {{previous}} .x = 1, // override-error {{overrides prior initialization}} }; + +struct Foo { int a, b; }; + +struct Foo foo0 = { 1 }; // wmissing-warning {{missing field 'b' initializer}} +struct Foo foo1 = { .a = 1 }; // wmissing-warning {{missing field 'b' initializer}} +struct Foo foo2 = { .b = 1 }; // wmissing-warning {{missing field 'a' initializer}} + } namespace base_class {