diff options
author | Douglas Gregor <dgregor@apple.com> | 2009-11-18 22:32:06 +0000 |
---|---|---|
committer | Douglas Gregor <dgregor@apple.com> | 2009-11-18 22:32:06 +0000 |
commit | 322328b8a65ad2e45829eb06d245addb64037f6f (patch) | |
tree | 8c29fa7b0aa3f9c85d69912583507e2da5b1900c | |
parent | 19d70731388c4ed84d9270ddc5b1937218dcf572 (diff) |
Code completion for Objective-C @synthesized.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89259 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r-- | include/clang/Parse/Action.h | 18 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 16 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 5 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 95 | ||||
-rw-r--r-- | test/Index/complete-properties.m | 30 |
5 files changed, 150 insertions, 14 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 64d97dc015..3165b0eb2f 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -2408,6 +2408,24 @@ public: IdentifierInfo *ClassName) { } + /// \brief Code completion for the property names when synthesizing an + /// Objective-C property. + /// + /// This code completion action is invoked after the @synthesized and after + /// each "," in an @synthesized definition. + virtual void CodeCompleteObjCPropertySynthesize(Scope *S, + DeclPtrTy ObjCImpDecl) { + } + + /// \brief Code completion for the instance variable name that should + /// follow an '=' when synthesizing an Objective-C property. + /// + /// This code completion action is invoked after each '=' that occurs within + /// an @synthesized definition. + virtual void CodeCompleteObjCPropertySynthesizeIvar(Scope *S, + IdentifierInfo *PropertyName, + DeclPtrTy ObjCImpDecl) { + } //@} }; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 8d6fd209c6..305ed16a19 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -1231,12 +1231,13 @@ Parser::DeclPtrTy Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { assert(Tok.isObjCAtKeyword(tok::objc_synthesize) && "ParseObjCPropertyDynamic(): Expected '@synthesize'"); SourceLocation loc = ConsumeToken(); // consume synthesize - if (Tok.isNot(tok::identifier)) { - Diag(Tok, diag::err_expected_ident); - return DeclPtrTy(); - } while (true) { + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCPropertySynthesize(CurScope, ObjCImpDecl); + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_synthesized_property_name); SkipUntil(tok::semi); @@ -1249,6 +1250,13 @@ Parser::DeclPtrTy Parser::ParseObjCPropertySynthesize(SourceLocation atLoc) { if (Tok.is(tok::equal)) { // property '=' ivar-name ConsumeToken(); // consume '=' + + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCPropertySynthesizeIvar(CurScope, propertyId, + ObjCImpDecl); + ConsumeToken(); + } + if (Tok.isNot(tok::identifier)) { Diag(Tok, diag::err_expected_ident); break; diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index 6a2b6d971b..e00c8a1e97 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3654,6 +3654,11 @@ public: IdentifierInfo *ClassName); virtual void CodeCompleteObjCImplementationCategory(Scope *S, IdentifierInfo *ClassName); + virtual void CodeCompleteObjCPropertySynthesize(Scope *S, + DeclPtrTy ObjCImpDecl); + virtual void CodeCompleteObjCPropertySynthesizeIvar(Scope *S, + IdentifierInfo *PropertyName, + DeclPtrTy ObjCImpDecl); //@} //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index 93aa08c4da..0090e24a0f 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -1132,6 +1132,7 @@ void Sema::CodeCompleteOrdinaryName(Scope *S) { } static void AddObjCProperties(ObjCContainerDecl *Container, + bool AllowCategories, DeclContext *CurContext, ResultBuilder &Results) { typedef CodeCompleteConsumer::Result Result; @@ -1148,29 +1149,32 @@ static void AddObjCProperties(ObjCContainerDecl *Container, for (ObjCProtocolDecl::protocol_iterator P = Protocol->protocol_begin(), PEnd = Protocol->protocol_end(); P != PEnd; ++P) - AddObjCProperties(*P, CurContext, Results); + AddObjCProperties(*P, AllowCategories, CurContext, Results); } else if (ObjCInterfaceDecl *IFace = dyn_cast<ObjCInterfaceDecl>(Container)){ - // Look through categories. - for (ObjCCategoryDecl *Category = IFace->getCategoryList(); - Category; Category = Category->getNextClassCategory()) - AddObjCProperties(Category, CurContext, Results); + if (AllowCategories) { + // Look through categories. + for (ObjCCategoryDecl *Category = IFace->getCategoryList(); + Category; Category = Category->getNextClassCategory()) + AddObjCProperties(Category, AllowCategories, CurContext, Results); + } // Look through protocols. for (ObjCInterfaceDecl::protocol_iterator I = IFace->protocol_begin(), E = IFace->protocol_end(); I != E; ++I) - AddObjCProperties(*I, CurContext, Results); + AddObjCProperties(*I, AllowCategories, CurContext, Results); // Look in the superclass. if (IFace->getSuperClass()) - AddObjCProperties(IFace->getSuperClass(), CurContext, Results); + AddObjCProperties(IFace->getSuperClass(), AllowCategories, CurContext, + Results); } else if (const ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(Container)) { // Look through protocols. for (ObjCInterfaceDecl::protocol_iterator P = Category->protocol_begin(), PEnd = Category->protocol_end(); P != PEnd; ++P) - AddObjCProperties(*P, CurContext, Results); + AddObjCProperties(*P, AllowCategories, CurContext, Results); } } @@ -1234,13 +1238,13 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *BaseE, const ObjCObjectPointerType *ObjCPtr = BaseType->getAsObjCInterfacePointerType(); assert(ObjCPtr && "Non-NULL pointer guaranteed above!"); - AddObjCProperties(ObjCPtr->getInterfaceDecl(), CurContext, Results); + AddObjCProperties(ObjCPtr->getInterfaceDecl(), true, CurContext, Results); // Add properties from the protocols in a qualified interface. for (ObjCObjectPointerType::qual_iterator I = ObjCPtr->qual_begin(), E = ObjCPtr->qual_end(); I != E; ++I) - AddObjCProperties(*I, CurContext, Results); + AddObjCProperties(*I, true, CurContext, Results); // FIXME: We could (should?) also look for "implicit" properties, identified // only by the presence of nullary and unary selectors. @@ -2021,3 +2025,74 @@ void Sema::CodeCompleteObjCImplementationCategory(Scope *S, HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } + +void Sema::CodeCompleteObjCPropertySynthesize(Scope *S, DeclPtrTy ObjCImpDecl) { + typedef CodeCompleteConsumer::Result Result; + ResultBuilder Results(*this); + + // Figure out where this @synthesize lives. + ObjCContainerDecl *Container + = dyn_cast_or_null<ObjCContainerDecl>(ObjCImpDecl.getAs<Decl>()); + if (!Container || + (!isa<ObjCImplementationDecl>(Container) && + !isa<ObjCCategoryImplDecl>(Container))) + return; + + // Ignore any properties that have already been implemented. + for (DeclContext::decl_iterator D = Container->decls_begin(), + DEnd = Container->decls_end(); + D != DEnd; ++D) + if (ObjCPropertyImplDecl *PropertyImpl = dyn_cast<ObjCPropertyImplDecl>(*D)) + Results.Ignore(PropertyImpl->getPropertyDecl()); + + // Add any properties that we find. + Results.EnterNewScope(); + if (ObjCImplementationDecl *ClassImpl + = dyn_cast<ObjCImplementationDecl>(Container)) + AddObjCProperties(ClassImpl->getClassInterface(), false, CurContext, + Results); + else + AddObjCProperties(cast<ObjCCategoryImplDecl>(Container)->getCategoryDecl(), + false, CurContext, Results); + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); +} + +void Sema::CodeCompleteObjCPropertySynthesizeIvar(Scope *S, + IdentifierInfo *PropertyName, + DeclPtrTy ObjCImpDecl) { + typedef CodeCompleteConsumer::Result Result; + ResultBuilder Results(*this); + + // Figure out where this @synthesize lives. + ObjCContainerDecl *Container + = dyn_cast_or_null<ObjCContainerDecl>(ObjCImpDecl.getAs<Decl>()); + if (!Container || + (!isa<ObjCImplementationDecl>(Container) && + !isa<ObjCCategoryImplDecl>(Container))) + return; + + // Figure out which interface we're looking into. + ObjCInterfaceDecl *Class = 0; + if (ObjCImplementationDecl *ClassImpl + = dyn_cast<ObjCImplementationDecl>(Container)) + Class = ClassImpl->getClassInterface(); + else + Class = cast<ObjCCategoryImplDecl>(Container)->getCategoryDecl() + ->getClassInterface(); + + // Add all of the instance variables in this class and its superclasses. + Results.EnterNewScope(); + for(; Class; Class = Class->getSuperClass()) { + // FIXME: We could screen the type of each ivar for compatibility with + // the property, but is that being too paternal? + for (ObjCInterfaceDecl::ivar_iterator IVar = Class->ivar_begin(), + IVarEnd = Class->ivar_end(); + IVar != IVarEnd; ++IVar) + Results.MaybeAddResult(Result(*IVar, 0), CurContext); + } + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); +} diff --git a/test/Index/complete-properties.m b/test/Index/complete-properties.m new file mode 100644 index 0000000000..5b567dfc00 --- /dev/null +++ b/test/Index/complete-properties.m @@ -0,0 +1,30 @@ +/* Note: the RUN lines are near the end of the file, since line/column + matter for this test. */ + +@interface I1 +{ + id StoredProp3; + int RandomIVar; +} +@property int Prop1; +@property float Prop2; +@end + +@interface I2 : I1 +@property id Prop3; +@end + +@implementation I2 +@synthesize Prop2, Prop1, Prop3 = StoredProp3; +@end + +// RUN: c-index-test -code-completion-at=%s:18:13 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: ObjCPropertyDecl:{TypedText Prop1} +// CHECK-CC1: ObjCPropertyDecl:{TypedText Prop2} +// CHECK-CC1: ObjCPropertyDecl:{TypedText Prop3} +// RUN: c-index-test -code-completion-at=%s:18:20 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: ObjCPropertyDecl:{TypedText Prop1} +// CHECK-CC2-NEXT: ObjCPropertyDecl:{TypedText Prop3} +// RUN: c-index-test -code-completion-at=%s:18:35 %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: ObjCIvarDecl:{TypedText RandomIVar} +// CHECK-CC3: ObjCIvarDecl:{TypedText StoredProp3} |