/// TagDecl - Represents the declaration of a struct/union/class/enum.
-class TagDecl : public TypeDecl {
+class TagDecl : public TypeDecl, public DeclContext {
public:
enum TagKind {
TK_struct,
protected:
TagDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L,
IdentifierInfo *Id, ScopedDecl *PrevDecl)
- : TypeDecl(DK, DC, L, Id, PrevDecl) {
+ : TypeDecl(DK, DC, L, Id, PrevDecl), DeclContext(DK) {
assert((DK != Enum || TK == TK_enum) &&"EnumDecl not matched with TK_enum");
TagDeclKind = TK;
IsDefinition = false;
/// EnumDecl - Represents an enum. As an extension, we allow forward-declared
/// enums.
-class EnumDecl : public TagDecl, public DeclContext {
+class EnumDecl : public TagDecl {
/// IntegerType - This represent the integer type that the enum corresponds
/// to for code generation purposes. Note that the enumerator constants may
/// have a different type than this does.
EnumDecl(DeclContext *DC, SourceLocation L,
IdentifierInfo *Id, ScopedDecl *PrevDecl)
- : TagDecl(Enum, TK_enum, DC, L, Id, PrevDecl), DeclContext(Enum) {
+ : TagDecl(Enum, TK_enum, DC, L, Id, PrevDecl) {
IntegerType = QualType();
}
public:
/// union Y { int A, B; }; // Has body with members A and B (FieldDecls).
/// This decl will be marked invalid if *any* members are invalid.
///
-class RecordDecl : public TagDecl, public DeclContext {
+class RecordDecl : public TagDecl {
/// HasFlexibleArrayMember - This is true if this struct ends with a flexible
/// array member (e.g. int X[]) or if this union contains a struct that does.
/// If so, this cannot be contained in arrays or other structs as a member.
Namespace, // [DeclContext]
// TypeDecl
Typedef,
- // TagDecl
- Enum, // [DeclContext]
- Record, // [DeclContext]
+ // TagDecl // [DeclContext]
+ Enum,
+ Record,
CXXRecord,
TemplateTypeParm,
// ValueDecl
case OriginalParmVar:
case EnumConstant:
case NonTypeTemplateParm:
- case Field:
- case ObjCAtDefsField:
- case ObjCIvar:
case ObjCInterface:
case ObjCCompatibleAlias:
case OverloadedFunction:
case CXXConversion:
case CXXClassVar:
return IDNS_Ordinary;
+
+ case Field:
+ case ObjCAtDefsField:
+ case ObjCIvar:
+ return IDNS_Member;
+
case Record:
case CXXRecord:
case TemplateTypeParm:
SourceLocation LBrac, SourceLocation RBrac,
AttributeList *AttrList) {}
- /// ActOnEnumStartDefinition - Invoked when we have entered the
- /// scope of the enumeration body and will be parsing its
- /// enumerators.
- virtual void ActOnEnumStartDefinition(Scope *S, DeclTy *EnumDecl) { }
+ /// ActOnTagStartDefinition - Invoked when we have entered the
+ /// scope of a tag's definition (e.g., for an enumeration, class,
+ /// struct, or union).
+ virtual void ActOnTagStartDefinition(Scope *S, DeclTy *TagDecl) { }
+
+ /// ActOnTagFinishDefinition - Invoked once we have finished parsing
+ /// the definition of a tag (enumeration, class, struct, or union).
+ virtual void ActOnTagFinishDefinition(Scope *S, DeclTy *TagDecl) { }
virtual DeclTy *ActOnEnumConstant(Scope *S, DeclTy *EnumDecl,
DeclTy *LastEnumConstant,
unsigned NumBases) {
}
- /// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
- /// definition, when on C++.
- virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
- SourceLocation LBrace) {
- }
-
/// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
/// declarator is parsed. 'AS' is the access specifier, 'BitfieldWidth'
/// specifies the bitfield width if there is one and 'Init' specifies the
SourceLocation RBrac) {
}
- /// ActOnFinishCXXClassDef - This is called when a class/struct/union has
- /// completed parsing, when on C++.
- virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl) {
- }
-
//===---------------------------C++ Templates----------------------------===//
/// ActOnTypeParameter - Called when a C++ template type parameter
RecordDecl::RecordDecl(Kind DK, TagKind TK, DeclContext *DC, SourceLocation L,
IdentifierInfo *Id)
- : TagDecl(DK, TK, DC, L, Id, 0), DeclContext(DK) {
-
+ : TagDecl(DK, TK, DC, L, Id, 0) {
HasFlexibleArrayMember = false;
AnonymousStructOrUnion = false;
assert(classof(static_cast<Decl*>(this)) && "Invalid Kind!");
const DeclContext *DeclContext::getLookupContext() const {
const DeclContext *Ctx = this;
+ // Skip through transparent contexts.
while (Ctx->isTransparentContext())
Ctx = Ctx->getParent();
return Ctx;
unsigned TagType, DeclTy *TagDecl) {
SourceLocation LBraceLoc = ConsumeBrace();
+ ParseScope StructScope(this, Scope::DeclScope);
+ Actions.ActOnTagStartDefinition(CurScope, TagDecl);
+
// Empty structs are an extension in C (C99 6.7.2.1p7), but are allowed in
// C++.
if (Tok.is(tok::r_brace) && !getLang().CPlusPlus)
Actions.ActOnFields(CurScope,
RecordLoc,TagDecl,&FieldDecls[0],FieldDecls.size(),
LBraceLoc, RBraceLoc,
- AttrList);
+ AttrList);
+ StructScope.Exit();
+ Actions.ActOnTagFinishDefinition(CurScope, TagDecl);
}
void Parser::ParseEnumBody(SourceLocation StartLoc, DeclTy *EnumDecl) {
// Enter the scope of the enum body and start the definition.
ParseScope EnumScope(this, Scope::DeclScope);
- Actions.ActOnEnumStartDefinition(CurScope, EnumDecl);
+ Actions.ActOnTagStartDefinition(CurScope, EnumDecl);
SourceLocation LBraceLoc = ConsumeBrace();
// If attributes exist after the identifier list, parse them.
if (Tok.is(tok::kw___attribute))
AttrList = ParseAttributes(); // FIXME: where do they do?
+
+ EnumScope.Exit();
+ Actions.ActOnTagFinishDefinition(CurScope, EnumDecl);
}
/// isTypeSpecifierQualifier - Return true if the current token could be the
// Enter a scope for the class.
ParseScope ClassScope(this, Scope::CXXClassScope|Scope::DeclScope);
- Actions.ActOnStartCXXClassDef(CurScope, TagDecl, LBraceLoc);
+ Actions.ActOnTagStartDefinition(CurScope, TagDecl);
// C++ 11p3: Members of a class defined with the keyword class are private
// by default. Members of a class defined with the keywords struct or union
// Leave the class scope.
ClassScope.Exit();
- Actions.ActOnFinishCXXClassDef(TagDecl);
+ Actions.ActOnTagFinishDefinition(CurScope, TagDecl);
}
/// ParseConstructorInitializer - Parse a C++ constructor initializer,
llvm::SmallVector<DeclTy*, 32> AllIvarDecls;
llvm::SmallVector<FieldDeclarator, 8> FieldDeclarators;
+ ParseScope ClassScope(this, Scope::DeclScope);
+
SourceLocation LBraceLoc = ConsumeBrace(); // the "{"
tok::ObjCKeywordKind visibility = tok::objc_protected;
else
return TUCtx();
+ if (!Ctx) // FIXME: HACK! We shouldn't end up with a NULL context here.
+ return TUCtx();
+
Ctx = Ctx->getLookupContext();
if (isa<TranslationUnitDecl>(Ctx))
DeclTy **Fields, unsigned NumFields,
SourceLocation LBrac, SourceLocation RBrac,
AttributeList *AttrList);
- virtual void ActOnEnumStartDefinition(Scope *S, DeclTy *EnumDecl);
+
+ /// ActOnTagStartDefinition - Invoked when we have entered the
+ /// scope of a tag's definition (e.g., for an enumeration, class,
+ /// struct, or union).
+ virtual void ActOnTagStartDefinition(Scope *S, DeclTy *TagDecl);
+
+ /// ActOnTagFinishDefinition - Invoked once we have finished parsing
+ /// the definition of a tag (enumeration, class, struct, or union).
+ virtual void ActOnTagFinishDefinition(Scope *S, DeclTy *TagDecl);
+
virtual DeclTy *ActOnEnumConstant(Scope *S, DeclTy *EnumDecl,
DeclTy *LastEnumConstant,
SourceLocation IdLoc, IdentifierInfo *Id,
virtual bool isCurrentClassName(const IdentifierInfo &II, Scope *S,
const CXXScopeSpec *SS);
- virtual void ActOnStartCXXClassDef(Scope *S, DeclTy *TagDecl,
- SourceLocation LBrace);
-
virtual DeclTy *ActOnCXXMemberDeclarator(Scope *S, AccessSpecifier AS,
Declarator &D, ExprTy *BitfieldWidth,
ExprTy *Init, DeclTy *LastInGroup);
SourceLocation LBrac,
SourceLocation RBrac);
- virtual void ActOnFinishCXXClassDef(DeclTy *TagDecl);
-
virtual void ActOnStartDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
virtual void ActOnDelayedCXXMethodParameter(Scope *S, DeclTy *Param);
virtual void ActOnFinishDelayedCXXMethodDeclaration(Scope *S, DeclTy *Method);
DeclContext::lookup_const_iterator I, E;\r
for (llvm::tie(I, E) = LookupCtx->lookup(Name); I != E; ++I) {\r
IdIsUndeclared = false;\r
- if (((*I)->getIdentifierNamespace() & Decl::IDNS_Tag) || \r
+ if (((*I)->isInIdentifierNamespace(Decl::IDNS_Tag)) || \r
isa<TypedefDecl>(*I))\r
return *I;\r
}\r
bool NamespaceNameOnly) {
if (!Name) return 0;
unsigned NS = NSI;
- if (getLangOptions().CPlusPlus && (NS & Decl::IDNS_Ordinary))
- NS |= Decl::IDNS_Tag;
-
- if (LookupCtx == 0 &&
- (!getLangOptions().CPlusPlus || (NS == Decl::IDNS_Label))) {
- // Unqualified name lookup in C/Objective-C and name lookup for
- // labels in C++ is purely lexical, so search in the
- // declarations attached to the name.
+
+ // In C++, ordinary and member lookup will always find all
+ // kinds of names.
+ if (getLangOptions().CPlusPlus &&
+ (NS & (Decl::IDNS_Ordinary | Decl::IDNS_Member)))
+ NS |= Decl::IDNS_Tag | Decl::IDNS_Member | Decl::IDNS_Ordinary;
+
+ if (LookupCtx == 0 && !getLangOptions().CPlusPlus) {
+ // Unqualified name lookup in C/Objective-C is purely lexical, so
+ // search in the declarations attached to the name.
assert(!LookupCtx && "Can't perform qualified name lookup here");
assert(!NamespaceNameOnly && "Can't perform namespace name lookup here");
+
+ // For the purposes of unqualified name lookup, structs and unions
+ // don't have scopes at all. For example:
+ //
+ // struct X {
+ // struct T { int i; } x;
+ // };
+ //
+ // void f() {
+ // struct T t; // okay: T is defined lexically within X, but
+ // // semantically at global scope
+ // };
+ //
+ // FIXME: Is there a better way to deal with this?
+ DeclContext *SearchCtx = CurContext;
+ while (isa<RecordDecl>(SearchCtx) || isa<EnumDecl>(SearchCtx))
+ SearchCtx = SearchCtx->getParent();
IdentifierResolver::iterator I
- = IdResolver.begin(Name, CurContext, LookInParent);
+ = IdResolver.begin(Name, SearchCtx, LookInParent);
// Scan up the scope chain looking for a decl that matches this
// identifier that is in the appropriate namespace. This search
if ((*I)->isInIdentifierNamespace(NS))
return *I;
} else if (LookupCtx) {
+ // If we're performing qualified name lookup (e.g., lookup into a
+ // struct), find fields as part of ordinary name lookup.
+ if (NS & Decl::IDNS_Ordinary)
+ NS |= Decl::IDNS_Member;
+
// Perform qualified name lookup into the LookupCtx.
// FIXME: Will need to look into base classes and such.
DeclContext::lookup_const_iterator I, E;
// declaration or definition.
// Use ScopedDecl instead of TagDecl, because a NamespaceDecl may come up.
PrevDecl = dyn_cast_or_null<ScopedDecl>(LookupDecl(Name, Decl::IDNS_Tag,S));
+
+ if (!getLangOptions().CPlusPlus && TK != TK_Reference) {
+ // FIXME: This makes sure that we ignore the contexts associated
+ // with C structs, unions, and enums when looking for a matching
+ // tag declaration or definition. See the similar lookup tweak
+ // in Sema::LookupDecl; is there a better way to deal with this?
+ while (isa<RecordDecl>(DC) || isa<EnumDecl>(DC))
+ DC = DC->getParent();
+ }
}
if (PrevDecl && PrevDecl->isTemplateParameter()) {
return New;
}
+void Sema::ActOnTagStartDefinition(Scope *S, DeclTy *TagD) {
+ TagDecl *Tag = cast<TagDecl>((Decl *)TagD);
+
+ // Enter the tag context.
+ PushDeclContext(S, Tag);
+
+ if (CXXRecordDecl *Record = dyn_cast<CXXRecordDecl>(Tag)) {
+ FieldCollector->StartClass();
+
+ if (Record->getIdentifier()) {
+ // C++ [class]p2:
+ // [...] The class-name is also inserted into the scope of the
+ // class itself; this is known as the injected-class-name. For
+ // purposes of access checking, the injected-class-name is treated
+ // as if it were a public member name.
+ RecordDecl *InjectedClassName
+ = CXXRecordDecl::Create(Context, Record->getTagKind(),
+ CurContext, Record->getLocation(),
+ Record->getIdentifier(), Record);
+ InjectedClassName->setImplicit();
+ PushOnScopeChains(InjectedClassName, S);
+ }
+ }
+}
+
+void Sema::ActOnTagFinishDefinition(Scope *S, DeclTy *TagD) {
+ TagDecl *Tag = cast<TagDecl>((Decl *)TagD);
+
+ if (isa<CXXRecordDecl>(Tag))
+ FieldCollector->FinishClass();
+
+ // Exit this scope of this tag's definition.
+ PopDeclContext();
+
+ // Notify the consumer that we've defined a tag.
+ Consumer.HandleTagDeclDefinition(Tag);
+}
/// TryToFixInvalidVariablyModifiedType - Helper method to turn variable array
/// types into constant array types in certain situations which would otherwise
DeclSpec::SCS_mutable,
/*PrevDecl=*/0);
+ if (II) {
+ Decl *PrevDecl
+ = LookupDecl(II, Decl::IDNS_Member, S, 0, false, false, false);
+ if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)
+ && !isa<TagDecl>(PrevDecl)) {
+ Diag(Loc, diag::err_duplicate_member) << II;
+ Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+ NewFD->setInvalidDecl();
+ Record->setInvalidDecl();
+ }
+ }
+
if (getLangOptions().CPlusPlus) {
CheckExtraCXXDefaultArguments(D);
if (!T->isPODType())
if (D.getInvalidType() || InvalidDecl)
NewFD->setInvalidDecl();
- if (II && getLangOptions().CPlusPlus)
+ if (II) {
PushOnScopeChains(NewFD, S);
- else
+ } else
Record->addDecl(Context, NewFD);
return NewFD;
SourceLocation DeclStart,
Declarator &D, ExprTy *BitfieldWidth,
tok::ObjCKeywordKind Visibility) {
+
IdentifierInfo *II = D.getIdentifier();
Expr *BitWidth = (Expr*)BitfieldWidth;
SourceLocation Loc = DeclStart;
ObjCIvarDecl *NewID = ObjCIvarDecl::Create(Context, Loc, II, T, ac,
(Expr *)BitfieldWidth);
+ if (II) {
+ Decl *PrevDecl
+ = LookupDecl(II, Decl::IDNS_Member, S, 0, false, false, false);
+ if (PrevDecl && isDeclInScope(PrevDecl, CurContext, S)
+ && !isa<TagDecl>(PrevDecl)) {
+ Diag(Loc, diag::err_duplicate_member) << II;
+ Diag(PrevDecl->getLocation(), diag::note_previous_declaration);
+ NewID->setInvalidDecl();
+ }
+ }
+
// Process attributes attached to the ivar.
ProcessDeclAttributes(NewID, D);
if (D.getInvalidType() || InvalidDecl)
NewID->setInvalidDecl();
+ if (II) {
+ // FIXME: When interfaces are DeclContexts, we'll need to add
+ // these to the interface.
+ S->AddDecl(NewID);
+ IdResolver.AddDecl(NewID);
+ }
+
return NewID;
}
assert(EnclosingDecl && "missing record or interface decl");
RecordDecl *Record = dyn_cast<RecordDecl>(EnclosingDecl);
- if (Record)
- if (RecordDecl* DefRecord = Record->getDefinition(Context)) {
+ if (Record) {
+ QualType RecordType = Context.getTypeDeclType(Record);
+ if (RecordType->getAsRecordType()->getDecl()->isDefinition()) {
+ RecordDecl *Def = RecordType->getAsRecordType()->getDecl();
// Diagnose code like:
// struct S { struct S {} X; };
// We discover this when we complete the outer S. Reject and ignore the
// outer S.
- Diag(DefRecord->getLocation(), diag::err_nested_redefinition)
- << DefRecord->getDeclName();
+ Diag(Def->getLocation(), diag::err_nested_redefinition)
+ << Def->getDeclName();
Diag(RecLoc, diag::note_previous_definition);
Record->setInvalidDecl();
return;
}
+ }
// Verify that all the fields are okay.
unsigned NumNamedMembers = 0;
llvm::SmallVector<FieldDecl*, 32> RecFields;
- // FIXME: Eventually, we'd like to eliminate this in favor of
- // checking for redeclarations on-the-fly.
- llvm::DenseMap<const IdentifierInfo*, FieldDecl *> FieldIDs;
-
for (unsigned i = 0; i != NumFields; ++i) {
FieldDecl *FD = cast_or_null<FieldDecl>(static_cast<Decl*>(Fields[i]));
assert(FD && "missing field decl");
// Get the type for the field.
Type *FDTy = FD->getType().getTypePtr();
- if (FD->isAnonymousStructOrUnion()) {
- // We have found a field that represents an anonymous struct
- // or union. Introduce all of the inner fields (recursively)
- // into the list of fields we know about, so that we can produce
- // an appropriate error message in cases like:
- //
- // struct X {
- // union {
- // int x;
- // float f;
- // };
- // double x;
- // };
- llvm::SmallVector<FieldDecl *, 4> AnonStructUnionFields;
- AnonStructUnionFields.push_back(FD);
- while (!AnonStructUnionFields.empty()) {
- FieldDecl *AnonField = AnonStructUnionFields.back();
- AnonStructUnionFields.pop_back();
-
- RecordDecl *AnonRecord
- = AnonField->getType()->getAsRecordType()->getDecl();
- for (RecordDecl::field_iterator F = AnonRecord->field_begin(),
- FEnd = AnonRecord->field_end();
- F != FEnd; ++F) {
- if ((*F)->isAnonymousStructOrUnion())
- AnonStructUnionFields.push_back(*F);
- else if (const IdentifierInfo *II = (*F)->getIdentifier())
- FieldIDs[II] = *F;
- }
- }
- } else {
+ if (!FD->isAnonymousStructOrUnion()) {
// Remember all fields written by the user.
RecFields.push_back(FD);
}
continue;
}
// Keep track of the number of named members.
- if (IdentifierInfo *II = FD->getIdentifier()) {
- // Detect duplicate member names.
- if (FieldIDs[II]) {
- Diag(FD->getLocation(), diag::err_duplicate_member) << II;
- // Find the previous decl.
- Diag(FieldIDs[II]->getLocation(), diag::note_previous_definition);
- FD->setInvalidDecl();
- EnclosingDecl->setInvalidDecl();
- continue;
- }
+ if (FD->getIdentifier())
++NumNamedMembers;
- FieldIDs[II] = FD;
- }
}
// Okay, we successfully defined 'Record'.
if (Record) {
Record->completeDefinition(Context);
- // If this is a C++ record, HandleTagDeclDefinition will be invoked in
- // Sema::ActOnFinishCXXClassDef.
- if (!isa<CXXRecordDecl>(Record))
- Consumer.HandleTagDeclDefinition(Record);
} else {
ObjCIvarDecl **ClsFields = reinterpret_cast<ObjCIvarDecl**>(&RecFields[0]);
if (ObjCInterfaceDecl *ID = dyn_cast<ObjCInterfaceDecl>(EnclosingDecl)) {
ObjCIvarDecl* prevIvar = ID->getSuperClass()->FindIvarDeclaration(II);
if (prevIvar) {
Diag(Ivar->getLocation(), diag::err_duplicate_member) << II;
- Diag(prevIvar->getLocation(), diag::note_previous_definition);
+ Diag(prevIvar->getLocation(), diag::note_previous_declaration);
}
}
}
ProcessDeclAttributeList(Record, Attr);
}
-void Sema::ActOnEnumStartDefinition(Scope *S, DeclTy *EnumD) {
- EnumDecl *Enum = cast<EnumDecl>((Decl *)EnumD);
-
- // Enter the enumeration context.
- PushDeclContext(S, Enum);
-}
-
Sema::DeclTy *Sema::ActOnEnumConstant(Scope *S, DeclTy *theEnumDecl,
DeclTy *lastEnumConst,
SourceLocation IdLoc, IdentifierInfo *Id,
<< Enum->getDeclName();
Diag(Enum->getLocation(), diag::note_previous_definition);
Enum->setInvalidDecl();
- PopDeclContext();
return;
}
}
Enum->completeDefinition(Context, BestType);
- Consumer.HandleTagDeclDefinition(Enum);
-
- // Leave the context of the enumeration.
- PopDeclContext();
}
Sema::DeclTy *Sema::ActOnFileScopeAsmDecl(SourceLocation Loc,
// C++ class member Handling
//===----------------------------------------------------------------------===//
-/// ActOnStartCXXClassDef - This is called at the start of a class/struct/union
-/// definition, when on C++.
-void Sema::ActOnStartCXXClassDef(Scope *S, DeclTy *D, SourceLocation LBrace) {
- CXXRecordDecl *Dcl = cast<CXXRecordDecl>(static_cast<Decl *>(D));
- PushDeclContext(S, Dcl);
- FieldCollector->StartClass();
-
- if (Dcl->getIdentifier()) {
- // C++ [class]p2:
- // [...] The class-name is also inserted into the scope of the
- // class itself; this is known as the injected-class-name. For
- // purposes of access checking, the injected-class-name is treated
- // as if it were a public member name.
- RecordDecl *InjectedClassName
- = CXXRecordDecl::Create(Context, Dcl->getTagKind(),
- CurContext, Dcl->getLocation(),
- Dcl->getIdentifier(), Dcl);
- InjectedClassName->setImplicit();
- PushOnScopeChains(InjectedClassName, S);
- }
-}
-
/// ActOnCXXMemberDeclarator - This is invoked when a C++ class member
/// declarator is parsed. 'AS' is the access specifier, 'BW' specifies the
/// bitfield width if there is one and 'InitExpr' specifies the initializer if
}
}
-void Sema::ActOnFinishCXXClassDef(DeclTy *D) {
- CXXRecordDecl *Rec = cast<CXXRecordDecl>(static_cast<Decl *>(D));
- FieldCollector->FinishClass();
- PopDeclContext();
-
- // Everything, including inline method definitions, have been parsed.
- // Let the consumer know of the new TagDecl definition.
- Consumer.HandleTagDeclDefinition(Rec);
-}
-
/// ActOnStartDelayedCXXMethodDeclaration - We have completed
/// parsing a top-level (non-nested) C++ class, and we are now
/// parsing those parts of the given Method declaration that could
s->i = 1;
}
+typedef int x;
+struct S {
+ int x;
+ x z;
+};
+
+void g(void) {
+ struct S s[1];
+ s->x = 1;
+ s->z = 2;
+}
--- /dev/null
+// RUN: clang -fsyntax-only -verify %s
+struct X { // expected-note{{previous definition is here}}
+ struct X { } x; // expected-error{{nested redefinition of 'X'}}
+};
+
+struct Y { };
+void f(void) {
+ struct Y { }; // okay: this is a different Y
+}
+
+struct T;
+struct Z {
+ struct T { int x; } t;
+ struct U { int x; } u;
+};
+
+void f2(void) {
+ struct T t;
+ // FIXME: this is well-formed, but Clang breaks on it struct U u;
+}
+
+
union {
int x; // expected-error{{member of anonymous union redeclares 'x'}}
float y;
- double z; // expected-note{{previous definition is here}}
+ double z; // expected-note{{previous declaration is here}}
double zz; // expected-note{{previous definition is here}}
};
typedef int A;
- virtual int vi; // expected-error {{error: 'virtual' can only appear on non-static member functions}}
+ virtual int viv; // expected-error {{error: 'virtual' can only appear on non-static member functions}}
virtual static int vsif(); // expected-error {{error: 'virtual' can only appear on non-static member functions}}
virtual int vif();
struct C4;
C4; // expected-error {{declaration does not declare anything}}
}
+
+struct C4 {
+ void f(); // expected-note{{previous declaration is here}}
+ int f; // expected-error{{duplicate member 'f'}}
+};
@interface B1 {
@public
- double fill_B; // expected-note {{previous definition is here}}
+ double fill_B; // expected-note {{previous declaration is here}}
}
@end
@interface B : B1 {
@public
- int one; // expected-note {{previous definition is here}}
+ int one; // expected-note {{previous declaration is here}}
int one; // expected-error {{duplicate member 'one'}}
}
@end
--- /dev/null
+// RUN: clang -fsyntax-only -verify %s
+
+@interface I1 {
+@private
+ int x;
+ struct {
+ unsigned int x : 3;
+ unsigned int y : 3;
+ } flags;
+ int y;
+}
+@end
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 definition is here}}
+ int kaka; // expected-note {{previous declaration is here}}
int kaka; // expected-error {{duplicate member 'kaka'}}
char ch[]; // expected-error {{field 'ch' has incomplete type}}
}