From: Douglas Gregor Date: Mon, 14 Nov 2011 22:10:01 +0000 (+0000) Subject: Use Sema::RequireCompleteType to check for the completeness of X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=b3029960632ca8a3248e74770eda64d6c16f7246;p=clang Use Sema::RequireCompleteType to check for the completeness of Objective-C classes. This has two purposes: to consistently provide "forward declaration here" notes when we hit an incomplete type, and to give LLDB a chance to complete the type. RequireCompleteType bits from Sean Callanan! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@144573 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 312ea82458..3d9b5b9214 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -373,6 +373,8 @@ def warn_property_attribute : Warning< def warn_property_types_are_incompatible : Warning< "property type %0 is incompatible with type %1 inherited from %2">; def err_undef_interface : Error<"cannot find interface declaration for %0">; +def err_category_forward_interface : Error< + "cannot define %select{category|class extension}0 for undefined class %1">; def err_class_extension_after_impl : Error< "cannot declare class extension for %0 after class implementation">; def note_implementation_declared : Note< @@ -3617,7 +3619,7 @@ def err_property_not_as_forward_class : Error< "property %0 refers to an incomplete Objective-C class %1 " "(with no @interface available)">; def note_forward_class : Note< - "forward class is declared here">; + "forward declaration of class here">; def err_duplicate_property : Error< "property has a previous declaration">; def ext_gnu_void_ptr : Extension< diff --git a/lib/Sema/SemaDeclObjC.cpp b/lib/Sema/SemaDeclObjC.cpp index fe9a7841cf..f4e2fb3205 100644 --- a/lib/Sema/SemaDeclObjC.cpp +++ b/lib/Sema/SemaDeclObjC.cpp @@ -463,11 +463,12 @@ ActOnStartClassInterface(SourceLocation AtInterfaceLoc, if (!SuperClassDecl) Diag(SuperLoc, diag::err_undef_superclass) << SuperName << ClassName << SourceRange(AtInterfaceLoc, ClassLoc); - else if (SuperClassDecl->isForwardDecl()) { - Diag(SuperLoc, diag::err_forward_superclass) - << SuperClassDecl->getDeclName() << ClassName - << SourceRange(AtInterfaceLoc, ClassLoc); - Diag(SuperClassDecl->getLocation(), diag::note_forward_class); + else if (RequireCompleteType(SuperLoc, + Context.getObjCInterfaceType(SuperClassDecl), + PDiag(diag::err_forward_superclass) + << SuperClassDecl->getDeclName() + << ClassName + << SourceRange(AtInterfaceLoc, ClassLoc))) { SuperClassDecl = 0; } } @@ -746,14 +747,20 @@ ActOnStartCategoryInterface(SourceLocation AtInterfaceLoc, ObjCInterfaceDecl *IDecl = getObjCInterfaceDecl(ClassName, ClassLoc, true); /// Check that class of this category is already completely declared. - if (!IDecl || IDecl->isForwardDecl()) { + + if (!IDecl + || RequireCompleteType(ClassLoc, Context.getObjCInterfaceType(IDecl), + PDiag(diag::err_category_forward_interface) + << (CategoryName == 0))) { // Create an invalid ObjCCategoryDecl to serve as context for // the enclosing method declarations. We mark the decl invalid // to make it clear that this isn't a valid AST. CDecl = ObjCCategoryDecl::Create(Context, CurContext, AtInterfaceLoc, ClassLoc, CategoryLoc, CategoryName,IDecl); CDecl->setInvalidDecl(); - Diag(ClassLoc, diag::err_undef_interface) << ClassName; + + if (!IDecl) + Diag(ClassLoc, diag::err_undef_interface) << ClassName; return ActOnObjCContainerStartDefinition(CDecl); } @@ -820,9 +827,12 @@ Decl *Sema::ActOnStartCategoryImplementation( ObjCCategoryImplDecl::Create(Context, CurContext, CatName, IDecl, ClassLoc, AtCatImplLoc); /// Check that class of this category is already completely declared. - if (!IDecl || IDecl->isForwardDecl()) { + if (!IDecl) { Diag(ClassLoc, diag::err_undef_interface) << ClassName; CDecl->setInvalidDecl(); + } else if (RequireCompleteType(ClassLoc, Context.getObjCInterfaceType(IDecl), + diag::err_undef_interface)) { + CDecl->setInvalidDecl(); } // FIXME: PushOnScopeChains? @@ -867,11 +877,9 @@ Decl *Sema::ActOnStartClassImplementation( Diag(ClassLoc, diag::err_redefinition_different_kind) << ClassName; Diag(PrevDecl->getLocation(), diag::note_previous_definition); } else if ((IDecl = dyn_cast_or_null(PrevDecl))) { - // If this is a forward declaration of an interface, warn. - if (IDecl->isForwardDecl()) { - Diag(ClassLoc, diag::warn_undef_interface) << ClassName; + if (RequireCompleteType(ClassLoc, Context.getObjCInterfaceType(IDecl), + diag::warn_undef_interface)) IDecl = 0; - } } else { // We did not find anything with the name ClassName; try to correct for // typos in the class name. diff --git a/lib/Sema/SemaExprObjC.cpp b/lib/Sema/SemaExprObjC.cpp index 28a5a43055..c4f01c093a 100644 --- a/lib/Sema/SemaExprObjC.cpp +++ b/lib/Sema/SemaExprObjC.cpp @@ -591,13 +591,13 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, } IdentifierInfo *Member = MemberName.getAsIdentifierInfo(); - - if (IFace->isForwardDecl()) { - Diag(MemberLoc, diag::err_property_not_found_forward_class) - << MemberName << QualType(OPT, 0); - Diag(IFace->getLocation(), diag::note_forward_class); + SourceRange BaseRange = Super? SourceRange(SuperLoc) + : BaseExpr->getSourceRange(); + if (RequireCompleteType(MemberLoc, OPT->getPointeeType(), + PDiag(diag::err_property_not_found_forward_class) + << MemberName << BaseRange)) return ExprError(); - } + // Search for a declared property first. if (ObjCPropertyDecl *PD = IFace->FindPropertyDeclaration(Member)) { // Check whether we can reference this property. @@ -722,14 +722,10 @@ HandleExprPropertyRefExpr(const ObjCObjectPointerType *OPT, QualType T = Ivar->getType(); if (const ObjCObjectPointerType * OBJPT = T->getAsObjCInterfacePointerType()) { - const ObjCInterfaceType *IFaceT = OBJPT->getInterfaceType(); - if (ObjCInterfaceDecl *IFace = IFaceT->getDecl()) - if (IFace->isForwardDecl()) { - Diag(MemberLoc, diag::err_property_not_as_forward_class) - << MemberName << IFace; - Diag(IFace->getLocation(), diag::note_forward_class); - return ExprError(); - } + if (RequireCompleteType(MemberLoc, OBJPT->getPointeeType(), + PDiag(diag::err_property_not_as_forward_class) + << MemberName << BaseExpr->getSourceRange())) + return ExprError(); } Diag(MemberLoc, diag::err_ivar_access_using_property_syntax_suggest) @@ -1083,13 +1079,14 @@ ExprResult Sema::BuildClassMessage(TypeSourceInfo *ReceiverTypeInfo, (void)DiagnoseUseOfDecl(Class, Loc); // Find the method we are messaging. if (!Method) { - if (Class->isForwardDecl()) { - if (getLangOptions().ObjCAutoRefCount) { - Diag(Loc, diag::err_arc_receiver_forward_class) << ReceiverType; - } else { - Diag(Loc, diag::warn_receiver_forward_class) << Class->getDeclName(); - } - + SourceRange TypeRange + = SuperLoc.isValid()? SourceRange(SuperLoc) + : ReceiverTypeInfo->getTypeLoc().getSourceRange(); + if (RequireCompleteType(Loc, Context.getObjCInterfaceType(Class), + (getLangOptions().ObjCAutoRefCount + ? PDiag(diag::err_arc_receiver_forward_class) + : PDiag(diag::warn_receiver_forward_class)) + << TypeRange)) { // A forward class used in messaging is treated as a 'Class' Method = LookupFactoryMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); @@ -1322,13 +1319,21 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // We allow sending a message to a pointer to an interface (an object). ClassDecl = OCIType->getInterfaceDecl(); - if (ClassDecl->isForwardDecl() && getLangOptions().ObjCAutoRefCount) { - Diag(Loc, diag::err_arc_receiver_forward_instance) - << OCIType->getPointeeType() - << (Receiver ? Receiver->getSourceRange() : SourceRange(SuperLoc)); - return ExprError(); + // Try to complete the type. Under ARC, this is a hard error from which + // we don't try to recover. + const ObjCInterfaceDecl *forwardClass = 0; + if (RequireCompleteType(Loc, OCIType->getPointeeType(), + getLangOptions().ObjCAutoRefCount + ? PDiag(diag::err_arc_receiver_forward_instance) + << (Receiver ? Receiver->getSourceRange() + : SourceRange(SuperLoc)) + : PDiag())) { + if (getLangOptions().ObjCAutoRefCount) + return ExprError(); + + forwardClass = OCIType->getInterfaceDecl(); } - + // FIXME: consider using LookupInstanceMethodInGlobalPool, since it will be // faster than the following method (which can do *many* linear searches). // The idea is to add class info to MethodPool. @@ -1338,7 +1343,6 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, // Search protocol qualifiers. Method = LookupMethodInQualifiedType(Sel, OCIType, true); - const ObjCInterfaceDecl *forwardClass = 0; if (!Method) { // If we have implementations in scope, check "private" methods. Method = LookupPrivateInstanceMethod(Sel, ClassDecl); @@ -1356,8 +1360,6 @@ ExprResult Sema::BuildInstanceMessage(Expr *Receiver, if (OCIType->qual_empty()) { Method = LookupInstanceMethodInGlobalPool(Sel, SourceRange(LBracLoc, RBracLoc)); - if (OCIType->getInterfaceDecl()->isForwardDecl()) - forwardClass = OCIType->getInterfaceDecl(); if (Method && !forwardClass) Diag(Loc, diag::warn_maynot_respond) << OCIType->getInterfaceDecl()->getIdentifier() << Sel; diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index a424a8a1b0..e8a61044b5 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -1060,13 +1060,13 @@ Sema::ActOnObjCForCollectionOperand(SourceLocation forLoc, Expr *collection) { ObjCInterfaceDecl *iface = objectType->getInterface(); // If we have a forward-declared type, we can't do this check. - if (iface && iface->isForwardDecl()) { - // This is ill-formed under ARC. - if (getLangOptions().ObjCAutoRefCount) { - Diag(forLoc, diag::err_arc_collection_forward) - << pointerType->getPointeeType() << collection->getSourceRange(); - } - + // Under ARC, it is an error not to have a forward-declared class. + if (iface && + RequireCompleteType(forLoc, QualType(objectType, 0), + getLangOptions().ObjCAutoRefCount + ? PDiag(diag::err_arc_collection_forward) + << collection->getSourceRange() + : PDiag(0))) { // Otherwise, if we have any useful type information, check that // the type declares the appropriate method. } else if (iface || !objectType->qual_empty()) { diff --git a/lib/Sema/SemaType.cpp b/lib/Sema/SemaType.cpp index c0d910b428..6ea3f4827a 100644 --- a/lib/Sema/SemaType.cpp +++ b/lib/Sema/SemaType.cpp @@ -4098,21 +4098,36 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, return true; const TagType *Tag = T->getAs(); + const ObjCInterfaceType *IFace = 0; + + if (Tag) { + // Avoid diagnosing invalid decls as incomplete. + if (Tag->getDecl()->isInvalidDecl()) + return true; - // Avoid diagnosing invalid decls as incomplete. - if (Tag && Tag->getDecl()->isInvalidDecl()) - return true; - - // Give the external AST source a chance to complete the type. - if (Tag && Tag->getDecl()->hasExternalLexicalStorage()) { - Context.getExternalSource()->CompleteType(Tag->getDecl()); - if (!Tag->isIncompleteType()) - return false; + // Give the external AST source a chance to complete the type. + if (Tag->getDecl()->hasExternalLexicalStorage()) { + Context.getExternalSource()->CompleteType(Tag->getDecl()); + if (!Tag->isIncompleteType()) + return false; + } } - + else if ((IFace = T->getAs())) { + // Avoid diagnosing invalid decls as incomplete. + if (IFace->getDecl()->isInvalidDecl()) + return true; + + // Give the external AST source a chance to complete the type. + if (IFace->getDecl()->hasExternalLexicalStorage()) { + Context.getExternalSource()->CompleteType(IFace->getDecl()); + if (!IFace->isIncompleteType()) + return false; + } + } + // We have an incomplete type. Produce a diagnostic. Diag(Loc, PD) << T; - + // If we have a note, produce it. if (!Note.first.isInvalid()) Diag(Note.first, Note.second); @@ -4123,7 +4138,11 @@ bool Sema::RequireCompleteType(SourceLocation Loc, QualType T, Diag(Tag->getDecl()->getLocation(), Tag->isBeingDefined() ? diag::note_type_being_defined : diag::note_forward_declaration) - << QualType(Tag, 0); + << QualType(Tag, 0); + + // If the Objective-C class was a forward declaration, produce a note. + if (IFace && !IFace->getDecl()->isInvalidDecl()) + Diag(IFace->getDecl()->getLocation(), diag::note_forward_class); return true; } diff --git a/test/Analysis/rdar-6540084.m b/test/Analysis/rdar-6540084.m index b2a113c20d..d710c475de 100644 --- a/test/Analysis/rdar-6540084.m +++ b/test/Analysis/rdar-6540084.m @@ -10,7 +10,7 @@ typedef struct _NSZone NSZone; @interface NSObject {} @end extern id NSAllocateObject(Class aClass, NSUInteger extraBytes, NSZone *zone); @class NSArray; -@class NSMutableArray, NSIndexSet, NSView, NSPredicate, NSString, NSViewAnimation, NSTimer; +@class NSMutableArray, NSIndexSet, NSView, NSPredicate, NSString, NSViewAnimation, NSTimer; // expected-note{{forward declaration of class here}} @interface FooBazController : NSObject {} @end typedef struct {} TazVersion; diff --git a/test/SemaObjC/arc.m b/test/SemaObjC/arc.m index c0393c46cc..b3405c05e2 100644 --- a/test/SemaObjC/arc.m +++ b/test/SemaObjC/arc.m @@ -378,7 +378,7 @@ void test16(void) { [v test16_6: 0]; } -@class Test17; +@class Test17; // expected-note 2{{forward declaration of class here}} @protocol Test17p - (void) test17; + (void) test17; @@ -651,7 +651,7 @@ void test36(int first, ...) { __builtin_va_end(arglist); } -@class Test37; +@class Test37; // expected-note{{forward declaration of class here}} void test37(Test37 *c) { for (id y in c) { // expected-error {{collection expression type 'Test37' is a forward declaration}} (void) y; diff --git a/test/SemaObjC/category-1.m b/test/SemaObjC/category-1.m index 4cc5dafaa9..f2d4abd5ae 100644 --- a/test/SemaObjC/category-1.m +++ b/test/SemaObjC/category-1.m @@ -31,9 +31,9 @@ @interface UnknownClass (Category) @end // expected-error {{cannot find interface declaration for 'UnknownClass'}} -@class MyClass2; +@class MyClass2; // expected-note{{forward declaration of class here}} -@interface MyClass2 (Category) @end // expected-error {{cannot find interface declaration for 'MyClass2'}} +@interface MyClass2 (Category) @end // expected-error {{cannot define category for undefined class 'MyClass2'}} @interface XCRemoteComputerManager @end diff --git a/test/SemaObjC/class-impl-1.m b/test/SemaObjC/class-impl-1.m index 90a4112c71..170ccb98fa 100644 --- a/test/SemaObjC/class-impl-1.m +++ b/test/SemaObjC/class-impl-1.m @@ -31,7 +31,7 @@ typedef int INTF3; // expected-note {{previous definition is here}} @implementation INTF4 @end // expected-warning {{cannot find interface declaration for 'INTF4'}} -@class INTF5; +@class INTF5; // expected-note{{forward declaration of class here}} @implementation INTF5 { // expected-warning {{cannot find interface declaration for 'INTF5'}} int x; diff --git a/test/SemaObjC/class-proto-1.m b/test/SemaObjC/class-proto-1.m index 8030976433..02c40aab65 100644 --- a/test/SemaObjC/class-proto-1.m +++ b/test/SemaObjC/class-proto-1.m @@ -23,7 +23,7 @@ @interface E2 @end // expected-warning {{cannot find protocol definition for 'p3'}} -@class U1, U2; // expected-note {{forward class is declared here}} +@class U1, U2; // expected-note {{forward declaration of class here}} @interface E3 : U1 @end // expected-error {{attempting to use the forward class 'U1' as superclass of 'E3'}} diff --git a/test/SemaObjC/exprs.m b/test/SemaObjC/exprs.m index 2b505e0eae..b198cba4a9 100644 --- a/test/SemaObjC/exprs.m +++ b/test/SemaObjC/exprs.m @@ -33,7 +33,7 @@ void test3(Object *o) { __sync_bool_compare_and_swap(&g, 0, o); } -@class Incomplete_ObjC_class; +@class Incomplete_ObjC_class; // expected-note{{forward declaration of class here}} struct Incomplete_struct; // expected-note {{forward declaration}} void test_encode() { diff --git a/test/SemaObjC/forward-class-1.m b/test/SemaObjC/forward-class-1.m index de94e884ae..3a9a8ac82d 100644 --- a/test/SemaObjC/forward-class-1.m +++ b/test/SemaObjC/forward-class-1.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@class FOO, BAR; // expected-note {{forward class is declared here}} +@class FOO, BAR; // expected-note {{forward declaration of class here}} @class FOO, BAR; @interface INTF : FOO // expected-error {{attempting to use the forward class 'FOO' as superclass of 'INTF'}} @@ -46,7 +46,7 @@ typedef NSObject XCElement; // rdar://9653341 -@class B; // expected-note {{forward class is declared here}} +@class B; // expected-note {{forward declaration of class here}} @interface A : B {} // expected-error {{attempting to use the forward class 'B' as superclass of 'A'}} @end diff --git a/test/SemaObjC/forward-class-receiver.m b/test/SemaObjC/forward-class-receiver.m index 55b29c15c5..9bcb03961d 100644 --- a/test/SemaObjC/forward-class-receiver.m +++ b/test/SemaObjC/forward-class-receiver.m @@ -5,7 +5,7 @@ @end Class isa; -@class NotKnown; +@class NotKnown; // expected-note{{forward declaration of class here}} void foo(NotKnown *n) { [isa new]; diff --git a/test/SemaObjC/property-9.m b/test/SemaObjC/property-9.m index 2b6564d295..4ef21cc145 100644 --- a/test/SemaObjC/property-9.m +++ b/test/SemaObjC/property-9.m @@ -97,13 +97,13 @@ typedef signed char BOOL; @end // rdar://8774513 -@class MDAInstance; // expected-note {{forward class is declared here}} +@class MDAInstance; // expected-note {{forward declaration of class here}} @interface MDATestDocument @property(retain) MDAInstance *instance; @end id f0(MDATestDocument *d) { - return d.instance.path; // expected-error {{property 'path' cannot be found in forward class object 'MDAInstance *'}} + return d.instance.path; // expected-error {{property 'path' cannot be found in forward class object 'MDAInstance'}} } diff --git a/test/SemaObjC/property-missing.m b/test/SemaObjC/property-missing.m index bf75601b79..3ebf0a8115 100644 --- a/test/SemaObjC/property-missing.m +++ b/test/SemaObjC/property-missing.m @@ -21,7 +21,7 @@ void f3(id o) } // rdar://8851803 -@class SomeOtherClass; // expected-note {{forward class is declared here}} +@class SomeOtherClass; // expected-note {{forward declaration of class here}} @interface MyClass { SomeOtherClass *someOtherObject; diff --git a/test/SemaObjC/sizeof-interface.m b/test/SemaObjC/sizeof-interface.m index 43870a17a3..dd8c1fdca0 100644 --- a/test/SemaObjC/sizeof-interface.m +++ b/test/SemaObjC/sizeof-interface.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -verify -fsyntax-only %s -@class I0; +@class I0; // expected-note 3{{forward declaration of class here}} // rdar://6811884 int g0 = sizeof(I0); // expected-error{{invalid application of 'sizeof' to an incomplete type 'I0'}} diff --git a/test/SemaObjC/special-dep-unavail-warning.m b/test/SemaObjC/special-dep-unavail-warning.m index fb0f6604e6..9164111441 100644 --- a/test/SemaObjC/special-dep-unavail-warning.m +++ b/test/SemaObjC/special-dep-unavail-warning.m @@ -27,7 +27,7 @@ @end -@class C; // expected-note 5 {{forward class is declared here}} +@class C; // expected-note 5 {{forward declaration of class here}} void test(C *c) { [c depInA]; // expected-warning {{'depInA' maybe deprecated because receiver type is unknown}} diff --git a/test/SemaObjC/undef-superclass-1.m b/test/SemaObjC/undef-superclass-1.m index 41cf1439bd..ef2f192971 100644 --- a/test/SemaObjC/undef-superclass-1.m +++ b/test/SemaObjC/undef-superclass-1.m @@ -1,6 +1,6 @@ // RUN: %clang_cc1 -fsyntax-only -verify %s -@class SUPER, Y; // expected-note 2 {{forward class is declared here}} +@class SUPER, Y; // expected-note 2 {{forward declaration of class here}} @interface INTF :SUPER // expected-error {{attempting to use the forward class 'SUPER' as superclass of 'INTF'}} @end