]> granicus.if.org Git - clang/commitdiff
Code completion for Objective-C @synthesized.
authorDouglas Gregor <dgregor@apple.com>
Wed, 18 Nov 2009 22:32:06 +0000 (22:32 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 18 Nov 2009 22:32:06 +0000 (22:32 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89259 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Parse/Action.h
lib/Parse/ParseObjc.cpp
lib/Sema/Sema.h
lib/Sema/SemaCodeComplete.cpp
test/Index/complete-properties.m [new file with mode: 0644]

index 64d97dc015bc2325ba634a1f9aa6340e5bf1cdb9..3165b0eb2f40e4b5f30c22c73d4dcb1d6a4dd88a 100644 (file)
@@ -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) {
+  }
   //@}
 };
 
index 8d6fd209c613dcf7626236693236604a2b350ce5..305ed16a193d4c31d7ddd7dba90ba5e6aa8c7853 100644 (file)
@@ -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;
index 6a2b6d971bc132bbd511d9a30b861130deae0ae3..e00c8a1e97576dfd69038daf35a7ebd811735e27 100644 (file)
@@ -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);
   //@}
   
   //===--------------------------------------------------------------------===//
index 93aa08c4da16898d6e8aa7beef2b46658044a3ef..0090e24a0f8961c59ab60696abf3c47fa06501d8 100644 (file)
@@ -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 (file)
index 0000000..5b567df
--- /dev/null
@@ -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}