Centralize error reporting of improper uses of incomplete types in the

new DiagnoseIncompleteType. It provides additional information about
struct/class/union/enum types when possible, either by pointing to the
forward declaration of that type or by pointing to the definition (if
we're in the process of defining that type). 
Fixes <rdar://problem/6500531>.

llvm-svn: 62521
This commit is contained in:
Douglas Gregor 2009-01-19 19:26:10 +00:00
parent ababe7d47d
commit dd430f7ec9
20 changed files with 177 additions and 73 deletions

View File

@ -36,7 +36,10 @@ DIAG(note_previous_use, NOTE,
"previous use is here")
DIAG(note_duplicate_case_prev, NOTE,
"previous case defined here")
DIAG(note_forward_declaration, NOTE,
"forward declaration of %0")
DIAG(note_type_being_defined, NOTE,
"definition of %0 is not complete until the closing '}'")
/// note_matching - this is used as a continuation of a previous diagnostic,
/// e.g. to specify the '(' when we expected a ')'.
DIAG(note_matching, NOTE,
@ -1117,7 +1120,7 @@ DIAG(err_func_returning_array_function, ERROR,
DIAG(err_field_declared_as_function, ERROR,
"field %0 declared as a function")
DIAG(err_field_incomplete, ERROR,
"field %0 has incomplete type")
"field has incomplete type %0")
DIAG(err_variable_sized_type_in_struct, EXTENSION,
"variable sized type %0 must be at end of struct or class")
DIAG(err_flexible_array_empty_struct, ERROR,
@ -1235,6 +1238,8 @@ DIAG(err_typecheck_illegal_increment_decrement, ERROR,
"cannot modify value of type %0")
DIAG(err_typecheck_arithmetic_incomplete_type, ERROR,
"arithmetic on pointer to incomplete type %0")
DIAG(err_typecheck_pointer_arith_function_type, ERROR,
"arithmetic on pointer to function type %0")
DIAG(err_typecheck_decl_incomplete_type, ERROR,
"variable has incomplete type %0")
// FIXME: Use %select
@ -1341,8 +1346,12 @@ DIAG(err_decrement_bool, ERROR,
"cannot decrement expression of type bool")
DIAG(warn_increment_bool, WARNING,
"incrementing expression of type bool is deprecated")
DIAG(err_catch_incomplete_ptr, ERROR,
"cannot catch pointer to incomplete type %0")
DIAG(err_catch_incomplete_ref, ERROR,
"cannot catch reference to incomplete type %0")
DIAG(err_catch_incomplete, ERROR,
"cannot catch%select{| pointer to| reference to}1 incomplete type %0")
"cannot catch incomplete type %0")
DIAG(err_qualified_catch_declarator, ERROR,
"exception declarator cannot be qualified")
DIAG(err_early_catch_all, ERROR,

View File

@ -270,6 +270,11 @@ public:
virtual TypeResult ActOnTypeName(Scope *S, Declarator &D);
bool DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag,
SourceRange Range1 = SourceRange(),
SourceRange Range2 = SourceRange(),
QualType PrintType = QualType());
//===--------------------------------------------------------------------===//
// Symbol table / Decl tracking callbacks: SemaDecl.cpp.
//

View File

@ -705,10 +705,9 @@ bool Sema::CheckParmsForFunctionDef(FunctionDecl *FD) {
// C99 6.7.5.3p4: the parameters in a parameter type list in a
// function declarator that is part of a function definition of
// that function shall not have incomplete type.
if (Param->getType()->isIncompleteType() &&
!Param->isInvalidDecl()) {
Diag(Param->getLocation(), diag::err_typecheck_decl_incomplete_type)
<< Param->getType();
if (!Param->isInvalidDecl() &&
DiagnoseIncompleteType(Param->getLocation(), Param->getType(),
diag::err_typecheck_decl_incomplete_type)) {
Param->setInvalidDecl();
HasInvalidParm = true;
}
@ -2503,10 +2502,10 @@ Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) {
// no linkage (C99 6.2.2p6), the type for the object shall be complete...
if (IDecl->isBlockVarDecl() &&
IDecl->getStorageClass() != VarDecl::Extern) {
if (T->isIncompleteType() && !IDecl->isInvalidDecl()) {
Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)<<T;
if (!IDecl->isInvalidDecl() &&
DiagnoseIncompleteType(IDecl->getLocation(), T,
diag::err_typecheck_decl_incomplete_type))
IDecl->setInvalidDecl();
}
}
// File scope. C99 6.9.2p2: A declaration of an identifier for and
// object that has file scope without an initializer, and without a
@ -2517,13 +2516,13 @@ Sema::DeclTy *Sema::FinalizeDeclaratorGroup(Scope *S, DeclTy *group) {
if (T->isIncompleteArrayType()) {
// C99 6.9.2 (p2, p5): Implicit initialization causes an incomplete
// array to be completed. Don't issue a diagnostic.
} else if (T->isIncompleteType() && !IDecl->isInvalidDecl()) {
} else if (!IDecl->isInvalidDecl() &&
DiagnoseIncompleteType(IDecl->getLocation(), T,
diag::err_typecheck_decl_incomplete_type))
// C99 6.9.2p3: If the declaration of an identifier for an object is
// a tentative definition and has internal linkage (C99 6.2.2p3), the
// declared type shall not be an incomplete type.
Diag(IDecl->getLocation(), diag::err_typecheck_decl_incomplete_type)<<T;
IDecl->setInvalidDecl();
}
}
if (IDecl->isFileVarDecl())
CheckForFileScopedRedefinitions(S, IDecl);
@ -3382,7 +3381,8 @@ void Sema::ActOnFields(Scope* S,
// C99 6.7.2.1p2 - A field may not be an incomplete type except...
if (FDTy->isIncompleteType()) {
if (!Record) { // Incomplete ivar type is always an error.
Diag(FD->getLocation(), diag::err_field_incomplete) <<FD->getDeclName();
DiagnoseIncompleteType(FD->getLocation(), FD->getType(),
diag::err_field_incomplete);
FD->setInvalidDecl();
EnclosingDecl->setInvalidDecl();
continue;
@ -3390,7 +3390,8 @@ void Sema::ActOnFields(Scope* S,
if (i != NumFields-1 || // ... that the last member ...
!Record->isStruct() || // ... of a structure ...
!FDTy->isArrayType()) { //... may have incomplete array type.
Diag(FD->getLocation(), diag::err_field_incomplete) <<FD->getDeclName();
DiagnoseIncompleteType(FD->getLocation(), FD->getType(),
diag::err_field_incomplete);
FD->setInvalidDecl();
EnclosingDecl->setInvalidDecl();
continue;

View File

@ -339,8 +339,9 @@ Sema::ActOnBaseSpecifier(DeclTy *classdecl, SourceRange SpecifierRange,
// C++ [class.derived]p2:
// The class-name in a base-specifier shall not be an incompletely
// defined class.
if (BaseType->isIncompleteType())
return Diag(BaseLoc, diag::err_incomplete_base_class) << SpecifierRange;
if (DiagnoseIncompleteType(BaseLoc, BaseType, diag::err_incomplete_base_class,
SpecifierRange))
return true;
// If the base class is polymorphic, the new one is, too.
RecordDecl *BaseDecl = BaseType->getAsRecordType()->getDecl();
@ -2179,17 +2180,19 @@ Sema::DeclTy *Sema::ActOnExceptionDeclarator(Scope *S, Declarator &D)
// incomplete type, other than [cv] void*.
QualType BaseType = ExDeclType;
int Mode = 0; // 0 for direct type, 1 for pointer, 2 for reference
unsigned DK = diag::err_catch_incomplete;
if (const PointerType *Ptr = BaseType->getAsPointerType()) {
BaseType = Ptr->getPointeeType();
Mode = 1;
DK = diag::err_catch_incomplete_ptr;
} else if(const ReferenceType *Ref = BaseType->getAsReferenceType()) {
BaseType = Ref->getPointeeType();
Mode = 2;
DK = diag::err_catch_incomplete_ref;
}
if ((Mode == 0 || !BaseType->isVoidType()) && BaseType->isIncompleteType()) {
if ((Mode == 0 || !BaseType->isVoidType()) &&
DiagnoseIncompleteType(Begin, BaseType, DK))
Invalid = true;
Diag(Begin, diag::err_catch_incomplete) << BaseType << Mode;
}
// FIXME: Need to test for ability to copy-construct and destroy the
// exception variable.

View File

@ -1021,10 +1021,11 @@ bool Sema::CheckSizeOfAlignOfOperand(QualType exprType,
else if (exprType->isVoidType())
Diag(OpLoc, diag::ext_sizeof_void_type)
<< (isSizeof ? "sizeof" : "__alignof") << ExprRange;
else if (exprType->isIncompleteType())
return Diag(OpLoc, isSizeof ? diag::err_sizeof_incomplete_type :
diag::err_alignof_incomplete_type)
<< exprType << ExprRange;
else
return DiagnoseIncompleteType(OpLoc, exprType,
isSizeof ? diag::err_sizeof_incomplete_type :
diag::err_alignof_incomplete_type,
ExprRange);
return false;
}
@ -1466,9 +1467,11 @@ Sema::ActOnMemberReferenceExpr(Scope *S, ExprArg Base, SourceLocation OpLoc,
// of the ObjC 'id' struct.
if (const RecordType *RTy = BaseType->getAsRecordType()) {
RecordDecl *RDecl = RTy->getDecl();
if (RTy->isIncompleteType())
return ExprError(Diag(OpLoc, diag::err_typecheck_incomplete_tag)
<< RDecl->getDeclName() << BaseExpr->getSourceRange());
if (DiagnoseIncompleteType(OpLoc, BaseType,
diag::err_typecheck_incomplete_tag,
BaseExpr->getSourceRange()))
return ExprError();
// The record definition is complete, now make sure the member is valid.
// FIXME: Qualified name lookup for C++ is a bit more complicated
// than this.
@ -1906,11 +1909,10 @@ ActOnCompoundLiteral(SourceLocation LParenLoc, TypeTy *Ty,
if (literalType->isVariableArrayType())
return Diag(LParenLoc, diag::err_variable_object_no_init)
<< SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd());
} else if (literalType->isIncompleteType()) {
return Diag(LParenLoc, diag::err_typecheck_decl_incomplete_type)
<< literalType
<< SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd());
}
} else if (DiagnoseIncompleteType(LParenLoc, literalType,
diag::err_typecheck_decl_incomplete_type,
SourceRange(LParenLoc, literalExpr->getSourceRange().getEnd())))
return true;
if (CheckInitializerTypes(literalExpr, literalType, LParenLoc,
DeclarationName(), /*FIXME:DirectInit=*/false))
@ -2643,10 +2645,16 @@ inline QualType Sema::CheckAdditionOperands( // C99 6.5.6
if (PTy->getPointeeType()->isVoidType()) {
Diag(Loc, diag::ext_gnu_void_ptr)
<< lex->getSourceRange() << rex->getSourceRange();
} else {
Diag(Loc, diag::err_typecheck_arithmetic_incomplete_type)
} else if (PTy->getPointeeType()->isFunctionType()) {
Diag(Loc, diag::err_typecheck_pointer_arith_function_type)
<< lex->getType() << lex->getSourceRange();
return QualType();
} else {
DiagnoseIncompleteType(Loc, PTy->getPointeeType(),
diag::err_typecheck_arithmetic_incomplete_type,
lex->getSourceRange(), SourceRange(),
lex->getType());
return QualType();
}
}
return PExp->getType();
@ -3038,9 +3046,9 @@ static bool CheckForModifiableLvalue(Expr *E, SourceLocation Loc, Sema &S) {
break;
case Expr::MLV_IncompleteType:
case Expr::MLV_IncompleteVoidType:
Diag = diag::err_typecheck_incomplete_type_not_modifiable_lvalue;
NeedType = true;
break;
return S.DiagnoseIncompleteType(Loc, E->getType(),
diag::err_typecheck_incomplete_type_not_modifiable_lvalue,
E->getSourceRange());
case Expr::MLV_DuplicateVectorComponents:
Diag = diag::err_typecheck_duplicate_vector_components_not_mlvalue;
break;
@ -3155,10 +3163,16 @@ QualType Sema::CheckIncrementDecrementOperand(Expr *Op, SourceLocation OpLoc,
} else if (PT->getPointeeType()->isVoidType()) {
// Pointer to void is extension.
Diag(OpLoc, diag::ext_gnu_void_ptr) << Op->getSourceRange();
} else {
Diag(OpLoc, diag::err_typecheck_arithmetic_incomplete_type)
} else if (PT->getPointeeType()->isFunctionType()) {
Diag(OpLoc, diag::err_typecheck_pointer_arith_function_type)
<< ResType << Op->getSourceRange();
return QualType();
} else {
DiagnoseIncompleteType(OpLoc, PT->getPointeeType(),
diag::err_typecheck_arithmetic_incomplete_type,
Op->getSourceRange(), SourceRange(),
ResType);
return QualType();
}
} else if (ResType->isComplexType()) {
// C99 does not support ++/-- on complex types, we allow as an extension.

View File

@ -172,9 +172,10 @@ Sema::ActOnCXXTypeConstructExpr(SourceRange TypeRange, TypeTy *TypeRep,
//
if (Ty->isArrayType())
return Diag(TyBeginLoc, diag::err_value_init_for_array_type) << FullRange;
if (!Ty->isDependentType() && Ty->isIncompleteType() && !Ty->isVoidType())
return Diag(TyBeginLoc, diag::err_invalid_incomplete_type_use)
<< Ty << FullRange;
if (!Ty->isDependentType() && !Ty->isVoidType() &&
DiagnoseIncompleteType(TyBeginLoc, Ty,
diag::err_invalid_incomplete_type_use, FullRange))
return true;
return new CXXZeroInitValueExpr(Ty, TyBeginLoc, RParenLoc);
}
@ -578,9 +579,10 @@ Sema::ActOnCXXDelete(SourceLocation StartLoc, bool UseGlobal,
}
QualType Pointee = Type->getAsPointerType()->getPointeeType();
if (Pointee->isIncompleteType() && !Pointee->isVoidType())
Diag(StartLoc, diag::warn_delete_incomplete)
<< Pointee << Ex->getSourceRange();
if (!Pointee->isVoidType() &&
DiagnoseIncompleteType(StartLoc, Pointee, diag::warn_delete_incomplete,
Ex->getSourceRange()))
return true;
else if (!Pointee->isObjectType()) {
Diag(StartLoc, diag::err_delete_operand)
<< Type << Ex->getSourceRange();

View File

@ -685,11 +685,10 @@ CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
if (DestPointee->isVoidType()) {
assert(DestPointer && "Reference to void is not possible");
} else if (DestRecord) {
if (!DestRecord->getDecl()->isDefinition()) {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_incomplete)
<< DestPointee.getUnqualifiedType() << DestRange;
if (Self.DiagnoseIncompleteType(OpRange.getBegin(), DestPointee,
diag::err_bad_dynamic_cast_incomplete,
DestRange))
return;
}
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
<< DestPointee.getUnqualifiedType() << DestRange;
@ -720,11 +719,10 @@ CheckDynamicCast(Sema &Self, Expr *&SrcExpr, QualType DestType,
const RecordType *SrcRecord = SrcPointee->getAsRecordType();
if (SrcRecord) {
if (!SrcRecord->getDecl()->isDefinition()) {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_incomplete)
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();
if (Self.DiagnoseIncompleteType(OpRange.getBegin(), SrcPointee,
diag::err_bad_dynamic_cast_incomplete,
SrcExpr->getSourceRange()))
return;
}
} else {
Self.Diag(OpRange.getBegin(), diag::err_bad_dynamic_cast_not_class)
<< SrcPointee.getUnqualifiedType() << SrcExpr->getSourceRange();

View File

@ -352,9 +352,8 @@ QualType Sema::GetTypeForDeclarator(Declarator &D, Scope *S, unsigned Skip) {
// C99 6.7.5.2p1: If the element type is an incomplete or function type,
// reject it (e.g. void ary[7], struct foo ary[7], void ary[7]())
if (T->isIncompleteType()) {
Diag(D.getIdentifierLoc(), diag::err_illegal_decl_array_incomplete_type)
<< T;
if (DiagnoseIncompleteType(D.getIdentifierLoc(), T,
diag::err_illegal_decl_array_incomplete_type)) {
T = Context.IntTy;
D.setInvalidType(true);
} else if (T->isFunctionType()) {
@ -690,4 +689,67 @@ void Sema::ProcessTypeAttributeList(QualType &Result, const AttributeList *AL) {
}
}
/// @brief If the type T is incomplete and cannot be completed,
/// produce a suitable diagnostic.
///
/// This routine checks whether the type @p T is complete in any
/// context where a complete type is required. If @p T is a complete
/// type, returns false. If @p T is incomplete, issues the diagnostic
/// @p diag (giving it the type @p T) and returns true.
///
/// @param Loc The location in the source that the incomplete type
/// diagnostic should refer to.
///
/// @param T The type that this routine is examining for completeness.
///
/// @param diag The diagnostic value (e.g.,
/// @c diag::err_typecheck_decl_incomplete_type) that will be used
/// for the error message if @p T is incomplete.
///
/// @param Range1 An optional range in the source code that will be a
/// part of the "incomplete type" error message.
///
/// @param Range2 An optional range in the source code that will be a
/// part of the "incomplete type" error message.
///
/// @param PrintType If non-NULL, the type that should be printed
/// instead of @p T. This parameter should be used when the type that
/// we're checking for incompleteness isn't the type that should be
/// displayed to the user, e.g., when T is a type and PrintType is a
/// pointer to T.
///
/// @returns @c true if @p T is incomplete and a diagnostic was emitted,
/// @c false otherwise.
///
/// @todo When Clang gets proper support for C++ templates, this
/// routine will also be able perform template instantiation when @p T
/// is a class template specialization.
bool Sema::DiagnoseIncompleteType(SourceLocation Loc, QualType T, unsigned diag,
SourceRange Range1, SourceRange Range2,
QualType PrintType) {
// If we have a complete type, we're done.
if (!T->isIncompleteType())
return false;
if (PrintType.isNull())
PrintType = T;
// We have an incomplete type. Produce a diagnostic.
Diag(Loc, diag) << PrintType << Range1 << Range2;
// If the type was a forward declaration of a class/struct/union
// type, produce
const TagType *Tag = 0;
if (const RecordType *Record = T->getAsRecordType())
Tag = Record;
else if (const EnumType *Enum = T->getAsEnumType())
Tag = Enum;
if (Tag && !Tag->getDecl()->isInvalidDecl())
Diag(Tag->getDecl()->getLocation(),
Tag->isBeingDefined() ? diag::note_type_being_defined
: diag::note_forward_declaration)
<< QualType(Tag, 0);
return true;
}

View File

@ -65,6 +65,6 @@ int test6248081() {
[10] // expected-error {{expected expression}}
}
struct forward;
struct forward; // expected-note{{forward declaration of 'struct forward'}}
void x(struct forward* x) {switch(x->a) {}} // expected-error {{incomplete definition of type}}

View File

@ -1,6 +1,6 @@
// RUN: clang -fsyntax-only -verify -pedantic %s
struct s;
struct s; // expected-note {{forward declaration of 'struct s'}}
struct s* t (struct s z[]) { // expected-error {{array has incomplete element type}}
return z;
}

View File

@ -24,7 +24,7 @@ int main(int argc, char **argv) {
fooFunc(&(struct foo){ 1, 2 });
}
struct Incomplete;
struct Incomplete; // expected-note{{forward declaration of 'struct Incomplete'}}
struct Incomplete* I1 = &(struct Incomplete){1, 2, 3}; // -expected-error {{variable has incomplete type}}
void IncompleteFunc(unsigned x) {
struct Incomplete* I2 = (struct foo[x]){1, 2, 3}; // -expected-error {{variable-sized object may not be initialized}}

View File

@ -21,7 +21,9 @@ int test() {
return sizeof(enum e) ;
}
enum gccForwardEnumExtension ve; // expected-error {{variable has incomplete type 'enum gccForwardEnumExtension'}} expected-warning{{ISO C forbids forward references to 'enum' types}}
enum gccForwardEnumExtension ve; // expected-error {{variable has incomplete type 'enum gccForwardEnumExtension'}} \
// expected-warning{{ISO C forbids forward references to 'enum' types}} \
// expected-note{{forward declaration of 'enum gccForwardEnumExtension'}}
int test2(int i)
{
@ -52,7 +54,7 @@ enum someenum {}; // expected-warning {{use of empty enum extension}}
// <rdar://problem/6093889>
enum e0 { // expected-note {{previous definition is here}}
E0 = sizeof(enum e0 { E1 }) // expected-error {{nested redefinition}}
E0 = sizeof(enum e0 { E1 }), // expected-error {{nested redefinition}}
};
// PR3173
@ -66,3 +68,8 @@ void foo() {
// <rdar://problem/6503878>
typedef enum { X = 0 }; // expected-warning{{typedef requires a name}}
enum NotYetComplete { // expected-note{{definition of 'enum NotYetComplete' is not complete until the closing '}'}}
NYC1 = sizeof(enum NotYetComplete) // expected-error{{invalid application of 'sizeof' to an incomplete type 'enum NotYetComplete'}}
};

View File

@ -1,5 +1,7 @@
// RUN: clang -fsyntax-only -verify %s
struct foo; // expected-note {{forward declaration of 'struct foo'}}
void b; // expected-error {{variable has incomplete type 'void'}}
struct foo f; // expected-error {{variable has incomplete type 'struct foo'}}

View File

@ -71,7 +71,8 @@ int sym_fw1a_scr[] = {
// PR3001
struct s1 s2 = {
.a = sizeof(struct s3), // expected-error {{invalid application of 'sizeof'}}
.a = sizeof(struct s3), // expected-error {{invalid application of 'sizeof'}} \
// expected-note{{forward declaration of 'struct s3'}}
.b = bogus // expected-error {{use of undeclared identifier 'bogus'}}
}

View File

@ -1,6 +1,6 @@
// RUN: clang %s -fsyntax-only -verify -pedantic
typedef struct S S;
typedef struct S S; // expected-note{{forward declaration of 'struct S'}}
void a(S* b, void* c) {
b++; // expected-error {{arithmetic on pointer to incomplete type}}
b += 1; // expected-error {{arithmetic on pointer to incomplete type}}
@ -9,6 +9,6 @@ void a(S* b, void* c) {
b = 1+b; // expected-error {{arithmetic on pointer to incomplete type}}
/* The next couple tests are only pedantic warnings in gcc */
void (*d)(S*,void*) = a;
d += 1; // expected-error {{pointer to incomplete type}}
d++; // expected-error {{pointer to incomplete type}}
d += 1; // expected-error {{arithmetic on pointer to function type}}}
d++; // expected-error {{arithmetic on pointer to function type}}}
}

View File

@ -8,7 +8,7 @@ struct D : private A {};
struct E : A {};
struct F : B, E {};
struct Incomplete;
struct Incomplete; // expected-note{{forward declaration of 'struct Incomplete'}}
struct Poly
{

View File

@ -8,7 +8,7 @@ struct S // expected-note {{candidate}}
S(double, int); // expected-note {{candidate}} expected-note {{candidate}}
S(float, int); // expected-note {{candidate}} expected-note {{candidate}}
};
struct T;
struct T; // expected-note{{forward declaration of 'struct T'}}
struct U
{
// A special new, to verify that the global version isn't used.

View File

@ -96,7 +96,7 @@ void test_a() {
a::a::a::i = 4;
}
struct Undef {
struct Undef { // expected-note{{definition of 'struct Undef' is not complete until the closing '}'}}
typedef int type;
Undef::type member;

View File

@ -1,6 +1,6 @@
// RUN: clang -fsyntax-only -verify %s
struct A;
struct A; // expected-note{{forward declaration of 'struct A'}}
void f()
{

View File

@ -1,19 +1,19 @@
// RUN: clang -fsyntax-only -verify %s
struct S;
struct S; // expected-note{{forward declaration of 'struct S'}}
typedef int FOO();
@interface INTF
{
struct F {} JJ;
int arr[]; // expected-error {{field 'arr' has incomplete type}}
struct S IC; // expected-error {{field 'IC' has incomplete type}}
int arr[]; // expected-error {{field has incomplete type}}
struct S IC; // expected-error {{field has incomplete type}}
struct T { // expected-note {{previous definition is here}}
struct T {} X; // expected-error {{nested redefinition of 'T'}}
}YYY;
FOO BADFUNC; // expected-error {{field 'BADFUNC' declared as a function}}
int kaka; // expected-note {{previous declaration is here}}
int kaka; // expected-error {{duplicate member 'kaka'}}
char ch[]; // expected-error {{field 'ch' has incomplete type}}
char ch[]; // expected-error {{field has incomplete type}}
}
@end