]> granicus.if.org Git - clang/commitdiff
Implement code completion for Objective-C category names in @interface
authorDouglas Gregor <dgregor@apple.com>
Wed, 18 Nov 2009 19:08:43 +0000 (19:08 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 18 Nov 2009 19:08:43 +0000 (19:08 +0000)
and @implementation declarations.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@89223 91177308-0d34-0410-b5e6-96231b3b80d8

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

index 13193ffab55edcffb2b055a08c0bf5e260deb51c..897776cdb9b8042355b291c432338a069c883fda 100644 (file)
@@ -808,7 +808,7 @@ public:
 /// - myMethod;
 /// @end
 ///
-/// Cateogries also allow you to split the implementation of a class across
+/// Categories also allow you to split the implementation of a class across
 /// several files (a feature more naturally supported in C++).
 ///
 /// Categories were originally inspired by dynamic languages such as Common
index 62f7221a881b5d5af5e9f4a24e66c8482400cc8c..64d97dc015bc2325ba634a1f9aa6340e5bf1cdb9 100644 (file)
@@ -2389,6 +2389,25 @@ public:
   /// \brief Code completion for an Objective-C implementation, after the
   /// @implementation but before any identifier.
   virtual void CodeCompleteObjCImplementationDecl(Scope *S) { }
+  
+  /// \brief Code completion for the category name in an Objective-C interface
+  /// declaration.
+  ///
+  /// This code completion action is invoked after the '(' that indicates
+  /// a category name within an Objective-C interface declaration.
+  virtual void CodeCompleteObjCInterfaceCategory(Scope *S, 
+                                                 IdentifierInfo *ClassName) {
+  }
+
+  /// \brief Code completion for the category name in an Objective-C category
+  /// implementation.
+  ///
+  /// This code completion action is invoked after the '(' that indicates
+  /// the category name within an Objective-C category implementation.
+  virtual void CodeCompleteObjCImplementationCategory(Scope *S, 
+                                                   IdentifierInfo *ClassName) {
+  }
+  
   //@}
 };
 
index a60c89a0f122ccf11c2db1bae43d86c7ba4ad205..e1f045bd8e2abda91a58f32b1e086b6ffffc691a 100644 (file)
@@ -143,6 +143,11 @@ Parser::DeclPtrTy Parser::ParseObjCAtInterfaceDeclaration(
     SourceLocation categoryLoc, rparenLoc;
     IdentifierInfo *categoryId = 0;
 
+    if (Tok.is(tok::code_completion)) {
+      Actions.CodeCompleteObjCInterfaceCategory(CurScope, nameId);
+      ConsumeToken();
+    }
+    
     // For ObjC2, the category name is optional (not an error).
     if (Tok.is(tok::identifier)) {
       categoryId = Tok.getIdentifierInfo();
@@ -1111,6 +1116,11 @@ Parser::DeclPtrTy Parser::ParseObjCAtImplementationDeclaration(
     SourceLocation categoryLoc, rparenLoc;
     IdentifierInfo *categoryId = 0;
 
+    if (Tok.is(tok::code_completion)) {
+      Actions.CodeCompleteObjCImplementationCategory(CurScope, nameId);
+      ConsumeToken();
+    }
+    
     if (Tok.is(tok::identifier)) {
       categoryId = Tok.getIdentifierInfo();
       categoryLoc = ConsumeToken();
index abef317d62a06c213623c3bfe609b7729691624d..a13ffc54da341256094aef208c9fc15a3bf40b29 100644 (file)
@@ -3649,6 +3649,10 @@ public:
   virtual void CodeCompleteObjCSuperclass(Scope *S, 
                                           IdentifierInfo *ClassName);
   virtual void CodeCompleteObjCImplementationDecl(Scope *S);
+  virtual void CodeCompleteObjCInterfaceCategory(Scope *S, 
+                                                 IdentifierInfo *ClassName);
+  virtual void CodeCompleteObjCImplementationCategory(Scope *S, 
+                                                    IdentifierInfo *ClassName);
   //@}
   
   //===--------------------------------------------------------------------===//
index eaa4f1514afcfc7a3ffccc87dc4eed193ccca160..93aa08c4da16898d6e8aa7beef2b46658044a3ef 100644 (file)
@@ -1933,7 +1933,7 @@ void Sema::CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName) {
   // Make sure that we ignore the class we're currently defining.
   NamedDecl *CurClass
     = LookupSingleName(TUScope, ClassName, LookupOrdinaryName);
-  if (isa<ObjCInterfaceDecl>(CurClass))
+  if (CurClass && isa<ObjCInterfaceDecl>(CurClass))
     Results.Ignore(CurClass);
 
   // Add all classes.
@@ -1955,3 +1955,69 @@ void Sema::CodeCompleteObjCImplementationDecl(Scope *S) {
   Results.ExitScope();
   HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
 }
+
+void Sema::CodeCompleteObjCInterfaceCategory(Scope *S, 
+                                             IdentifierInfo *ClassName) {
+  typedef CodeCompleteConsumer::Result Result;
+  
+  ResultBuilder Results(*this);
+  
+  // Ignore any categories we find that have already been implemented by this
+  // interface.
+  llvm::SmallPtrSet<IdentifierInfo *, 16> CategoryNames;
+  NamedDecl *CurClass
+    = LookupSingleName(TUScope, ClassName, LookupOrdinaryName);
+  if (ObjCInterfaceDecl *Class = dyn_cast_or_null<ObjCInterfaceDecl>(CurClass))
+    for (ObjCCategoryDecl *Category = Class->getCategoryList(); Category;
+         Category = Category->getNextClassCategory())
+      CategoryNames.insert(Category->getIdentifier());
+  
+  // Add all of the categories we know about.
+  Results.EnterNewScope();
+  TranslationUnitDecl *TU = Context.getTranslationUnitDecl();
+  for (DeclContext::decl_iterator D = TU->decls_begin(), 
+                               DEnd = TU->decls_end();
+       D != DEnd; ++D) 
+    if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(*D))
+      if (CategoryNames.insert(Category->getIdentifier()))
+          Results.MaybeAddResult(Result(Category, 0), CurContext);
+  Results.ExitScope();
+  
+  HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());  
+}
+
+void Sema::CodeCompleteObjCImplementationCategory(Scope *S, 
+                                                  IdentifierInfo *ClassName) {
+  typedef CodeCompleteConsumer::Result Result;
+  
+  // Find the corresponding interface. If we couldn't find the interface, the
+  // program itself is ill-formed. However, we'll try to be helpful still by
+  // providing the list of all of the categories we know about.
+  NamedDecl *CurClass
+    = LookupSingleName(TUScope, ClassName, LookupOrdinaryName);
+  ObjCInterfaceDecl *Class = dyn_cast_or_null<ObjCInterfaceDecl>(CurClass);
+  if (!Class)
+    return CodeCompleteObjCInterfaceCategory(S, ClassName);
+    
+  ResultBuilder Results(*this);
+  
+  // Add all of the categories that have have corresponding interface 
+  // declarations in this class and any of its superclasses, except for
+  // already-implemented categories in the class itself.
+  llvm::SmallPtrSet<IdentifierInfo *, 16> CategoryNames;
+  Results.EnterNewScope();
+  bool IgnoreImplemented = true;
+  while (Class) {
+    for (ObjCCategoryDecl *Category = Class->getCategoryList(); Category;
+         Category = Category->getNextClassCategory())
+      if ((!IgnoreImplemented || !Category->getImplementation()) &&
+          CategoryNames.insert(Category->getIdentifier()))
+        Results.MaybeAddResult(Result(Category, 0), CurContext);
+    
+    Class = Class->getSuperClass();
+    IgnoreImplemented = false;
+  }
+  Results.ExitScope();
+  
+  HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());  
+}
diff --git a/test/Index/complete-categories.m b/test/Index/complete-categories.m
new file mode 100644 (file)
index 0000000..92b14db
--- /dev/null
@@ -0,0 +1,39 @@
+/* Note: the RUN lines are near the end of the file, since line/column
+   matter for this test. */
+
+@interface I1 @end
+@interface I2 @end
+@interface I3 : I2 @end
+
+@interface I1(Cat1) @end
+@interface I1(Cat2) @end
+@interface I1(Cat3) @end
+
+@interface I2 (Cat2) @end
+@interface I2 (Cat3) @end
+@interface I2 (Cat2) @end
+@interface I3 (Cat1) @end
+@interface I3 (Cat2) @end
+
+@implementation I1(Cat2) @end
+@implementation I1(Cat3) @end
+@implementation I3(Cat2) @end
+
+// RUN: c-index-test -code-completion-at=%s:12:16 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat1}
+// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat2}
+// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat3}
+// RUN: c-index-test -code-completion-at=%s:13:16 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: ObjCCategoryDecl:{TypedText Cat1}
+// CHECK-CC2-NEXT: ObjCCategoryDecl:{TypedText Cat3}
+// RUN: c-index-test -code-completion-at=%s:18:20 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat1}
+// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat2}
+// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat3}
+// RUN: c-index-test -code-completion-at=%s:19:20 %s | FileCheck -check-prefix=CHECK-CC4 %s
+// CHECK-CC4: ObjCCategoryDecl:{TypedText Cat1}
+// CHECK-CC4-NEXT: ObjCCategoryDecl:{TypedText Cat3}
+// RUN: c-index-test -code-completion-at=%s:20:20 %s | FileCheck -check-prefix=CHECK-CC5 %s
+// CHECK-CC5: ObjCCategoryDecl:{TypedText Cat1}
+// CHECK-CC5-NEXT: ObjCCategoryDecl:{TypedText Cat2}
+// CHECK-CC5-NEXT: ObjCCategoryDecl:{TypedText Cat3}