forked from OSchip/llvm-project
Suppress -Wmissing-braces warning when aggregate-initializing a struct with a single field that is itself an aggregate.
In C++, such initialization of std::array<T, N> types is guaranteed to work by the standard, is completely idiomatic, and the "suggested" alternative from Clang was technically invalid. llvm-svn: 314838
This commit is contained in:
parent
389b7cedc3
commit
283e2076f6
|
@ -826,6 +826,34 @@ int InitListChecker::numStructUnionElements(QualType DeclType) {
|
||||||
return InitializableMembers - structDecl->hasFlexibleArrayMember();
|
return InitializableMembers - structDecl->hasFlexibleArrayMember();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Determine whether Entity is an entity for which it is idiomatic to elide
|
||||||
|
/// the braces in aggregate initialization.
|
||||||
|
static bool isIdiomaticBraceElisionEntity(const InitializedEntity &Entity) {
|
||||||
|
// Recursive initialization of the one and only field within an aggregate
|
||||||
|
// class is considered idiomatic. This case arises in particular for
|
||||||
|
// initialization of std::array, where the C++ standard suggests the idiom of
|
||||||
|
//
|
||||||
|
// std::array<T, N> arr = {1, 2, 3};
|
||||||
|
//
|
||||||
|
// (where std::array is an aggregate struct containing a single array field.
|
||||||
|
|
||||||
|
// FIXME: Should aggregate initialization of a struct with a single
|
||||||
|
// base class and no members also suppress the warning?
|
||||||
|
if (Entity.getKind() != InitializedEntity::EK_Member || !Entity.getParent())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto *ParentRD =
|
||||||
|
Entity.getParent()->getType()->castAs<RecordType>()->getDecl();
|
||||||
|
if (CXXRecordDecl *CXXRD = dyn_cast<CXXRecordDecl>(ParentRD))
|
||||||
|
if (CXXRD->getNumBases())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
auto FieldIt = ParentRD->field_begin();
|
||||||
|
assert(FieldIt != ParentRD->field_end() &&
|
||||||
|
"no fields but have initializer for member?");
|
||||||
|
return ++FieldIt == ParentRD->field_end();
|
||||||
|
}
|
||||||
|
|
||||||
/// Check whether the range of the initializer \p ParentIList from element
|
/// Check whether the range of the initializer \p ParentIList from element
|
||||||
/// \p Index onwards can be used to initialize an object of type \p T. Update
|
/// \p Index onwards can be used to initialize an object of type \p T. Update
|
||||||
/// \p Index to indicate how many elements of the list were consumed.
|
/// \p Index to indicate how many elements of the list were consumed.
|
||||||
|
@ -887,7 +915,8 @@ void InitListChecker::CheckImplicitInitList(const InitializedEntity &Entity,
|
||||||
|
|
||||||
// Complain about missing braces.
|
// Complain about missing braces.
|
||||||
if ((T->isArrayType() || T->isRecordType()) &&
|
if ((T->isArrayType() || T->isRecordType()) &&
|
||||||
!ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts())) {
|
!ParentIList->isIdiomaticZeroInitializer(SemaRef.getLangOpts()) &&
|
||||||
|
!isIdiomaticBraceElisionEntity(Entity)) {
|
||||||
SemaRef.Diag(StructuredSubobjectInitList->getLocStart(),
|
SemaRef.Diag(StructuredSubobjectInitList->getLocStart(),
|
||||||
diag::warn_missing_braces)
|
diag::warn_missing_braces)
|
||||||
<< StructuredSubobjectInitList->getSourceRange()
|
<< StructuredSubobjectInitList->getSourceRange()
|
||||||
|
|
|
@ -6,6 +6,7 @@ struct bar { struct foo a; struct foo b; };
|
||||||
struct A { int a; };
|
struct A { int a; };
|
||||||
struct B { struct A a; };
|
struct B { struct A a; };
|
||||||
struct C { struct B b; };
|
struct C { struct B b; };
|
||||||
|
struct D { struct C c; int n; };
|
||||||
|
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
|
@ -20,7 +21,8 @@ int main(void)
|
||||||
struct bar n = { { 0 }, { 9, 9 } }; // no-warning
|
struct bar n = { { 0 }, { 9, 9 } }; // no-warning
|
||||||
struct bar o = { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
|
struct bar o = { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
|
||||||
struct C p = { 0 }; // no-warning
|
struct C p = { 0 }; // no-warning
|
||||||
struct C q = { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{suggest braces around initialization of subobject}}
|
struct C q = { 9 }; // warning suppressed for struct with single element
|
||||||
|
struct D r = { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{missing field 'n' initializer}}
|
||||||
f = (struct foo ) { 0 }; // no-warning
|
f = (struct foo ) { 0 }; // no-warning
|
||||||
g = (struct foo ) { 9 }; // expected-warning {{missing field 'y' initializer}}
|
g = (struct foo ) { 9 }; // expected-warning {{missing field 'y' initializer}}
|
||||||
h = (struct foo ) { 9, 9 }; // no-warning
|
h = (struct foo ) { 9, 9 }; // no-warning
|
||||||
|
@ -32,7 +34,8 @@ int main(void)
|
||||||
n = (struct bar) { { 0 }, { 9, 9 } }; // no-warning
|
n = (struct bar) { { 0 }, { 9, 9 } }; // no-warning
|
||||||
o = (struct bar) { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
|
o = (struct bar) { { 9 }, { 9, 9 } }; // expected-warning {{missing field 'y' initializer}}
|
||||||
p = (struct C) { 0 }; // no-warning
|
p = (struct C) { 0 }; // no-warning
|
||||||
q = (struct C) { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{suggest braces around initialization of subobject}}
|
q = (struct C) { 9 }; // warning suppressed for struct with single element
|
||||||
|
r = (struct D) { 9 }; // expected-warning {{suggest braces around initialization of subobject}} expected-warning {{missing field 'n' initializer}}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++11 %s
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++14 %s
|
||||||
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++1z %s
|
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++17 %s
|
||||||
|
|
||||||
// Verify that using an initializer list for a non-aggregate looks for
|
// Verify that using an initializer list for a non-aggregate looks for
|
||||||
// constructors..
|
// constructors..
|
||||||
|
@ -150,3 +150,33 @@ namespace ProtectedBaseCtor {
|
||||||
// expected-error@-5 {{protected constructor}}
|
// expected-error@-5 {{protected constructor}}
|
||||||
// expected-note@-30 {{here}}
|
// expected-note@-30 {{here}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace IdiomaticStdArrayInitDoesNotWarn {
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic warning "-Wmissing-braces"
|
||||||
|
template<typename T, int N> struct StdArray {
|
||||||
|
T contents[N];
|
||||||
|
};
|
||||||
|
StdArray<int, 3> x = {1, 2, 3};
|
||||||
|
|
||||||
|
template<typename T, int N> struct ArrayAndSomethingElse {
|
||||||
|
T contents[N];
|
||||||
|
int something_else;
|
||||||
|
};
|
||||||
|
ArrayAndSomethingElse<int, 3> y = {1, 2, 3}; // expected-warning {{suggest braces}}
|
||||||
|
|
||||||
|
#if __cplusplus >= 201703L
|
||||||
|
template<typename T, int N> struct ArrayAndBaseClass : StdArray<int, 3> {
|
||||||
|
T contents[N];
|
||||||
|
};
|
||||||
|
ArrayAndBaseClass<int, 3> z = {1, 2, 3}; // expected-warning {{suggest braces}}
|
||||||
|
|
||||||
|
// It's not clear whether we should be warning in this case. If this
|
||||||
|
// pattern becomes idiomatic, it would be reasonable to suppress the
|
||||||
|
// warning here too.
|
||||||
|
template<typename T, int N> struct JustABaseClass : StdArray<T, N> {};
|
||||||
|
JustABaseClass<int, 3> w = {1, 2, 3}; // expected-warning {{suggest braces}}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue