aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2009-11-18 22:32:06 +0000
committerDouglas Gregor <dgregor@apple.com>2009-11-18 22:32:06 +0000
commit322328b8a65ad2e45829eb06d245addb64037f6f (patch)
tree8c29fa7b0aa3f9c85d69912583507e2da5b1900c
parent19d70731388c4ed84d9270ddc5b1937218dcf572 (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.h18
-rw-r--r--lib/Parse/ParseObjc.cpp16
-rw-r--r--lib/Sema/Sema.h5
-rw-r--r--lib/Sema/SemaCodeComplete.cpp95
-rw-r--r--test/Index/complete-properties.m30
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}