]> granicus.if.org Git - clang/commitdiff
Code completion for Objective-C @ directives
authorDouglas Gregor <dgregor@apple.com>
Mon, 7 Dec 2009 09:27:33 +0000 (09:27 +0000)
committerDouglas Gregor <dgregor@apple.com>
Mon, 7 Dec 2009 09:27:33 +0000 (09:27 +0000)
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@90756 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-at-directives.m [new file with mode: 0644]

index 200bd8a6b21ef94fdde229204498825a500a2951..2db254b8f502faf6fade1fa4b3611fe169e000b8 100644 (file)
@@ -2408,6 +2408,18 @@ public:
   /// \param S the scope in which the operator keyword occurs.
   virtual void CodeCompleteOperatorName(Scope *S) { }
 
+  /// \brief Code completion after the '@' at the top level.
+  ///
+  /// \param S the scope in which the '@' occurs.
+  ///
+  /// \param ObjCImpDecl the Objective-C implementation or category 
+  /// implementation.
+  ///
+  /// \param InInterface whether we are in an Objective-C interface or
+  /// protocol.
+  virtual void CodeCompleteObjCAtDirective(Scope *S, DeclPtrTy ObjCImpDecl,
+                                           bool InInterface) { }
+
   /// \brief Code completion for an ObjC property decl.
   ///
   /// This code completion action is invoked when the code-completion token is
index 8525c49d72666e2e8c8fdee8c03d1c7160532430..f1832e5707f2f7d460bcae952aa4f63f2300adf9 100644 (file)
@@ -30,6 +30,11 @@ using namespace clang;
 Parser::DeclPtrTy Parser::ParseObjCAtDirectives() {
   SourceLocation AtLoc = ConsumeToken(); // the "@"
 
+  if (Tok.is(tok::code_completion)) {
+    Actions.CodeCompleteObjCAtDirective(CurScope, ObjCImpDecl, false);
+    ConsumeToken();
+  }
+
   switch (Tok.getObjCKeywordID()) {
   case tok::objc_class:
     return ParseObjCAtClassDeclaration(AtLoc);
@@ -345,6 +350,12 @@ void Parser::ParseObjCInterfaceDeclList(DeclPtrTy interfaceDecl,
 
     // Otherwise, we have an @ directive, eat the @.
     SourceLocation AtLoc = ConsumeToken(); // the "@"
+    if (Tok.is(tok::code_completion)) {
+      Actions.CodeCompleteObjCAtDirective(CurScope, ObjCImpDecl, true);
+      ConsumeToken();
+      break;
+    }
+
     tok::ObjCKeywordKind DirectiveKind = Tok.getObjCKeywordID();
 
     if (DirectiveKind == tok::objc_end) { // @end -> terminate list
@@ -401,7 +412,10 @@ void Parser::ParseObjCInterfaceDeclList(DeclPtrTy interfaceDecl,
 
   // We break out of the big loop in two cases: when we see @end or when we see
   // EOF.  In the former case, eat the @end.  In the later case, emit an error.
-  if (Tok.isObjCAtKeyword(tok::objc_end))
+  if (Tok.is(tok::code_completion)) {
+    Actions.CodeCompleteObjCAtDirective(CurScope, ObjCImpDecl, true);
+    ConsumeToken();
+  } else if (Tok.isObjCAtKeyword(tok::objc_end))
     ConsumeToken(); // the "end" identifier
   else
     Diag(Tok, diag::err_objc_missing_end);
index edb4528f7e6cfffcc48df558e59631f0056751af..806f1e94d3a2c5f73d2cf8d76051fa1c79555996 100644 (file)
@@ -3778,6 +3778,8 @@ public:
   virtual void CodeCompleteNamespaceAliasDecl(Scope *S);
   virtual void CodeCompleteOperatorName(Scope *S);
   
+  virtual void CodeCompleteObjCAtDirective(Scope *S, DeclPtrTy ObjCImpDecl,
+                                           bool InInterface);
   virtual void CodeCompleteObjCPropertyFlags(Scope *S, ObjCDeclSpec &ODS);
   virtual void CodeCompleteObjCPropertyGetter(Scope *S, DeclPtrTy ClassDecl,
                                               DeclPtrTy *Methods,
index 5f9bba0f8c8fe42fd0887516e118cd253b54f1aa..5e231a54d1a219d681912add98a364474b060675 100644 (file)
@@ -1847,6 +1847,94 @@ void Sema::CodeCompleteOperatorName(Scope *S) {
   HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
 }
 
+void Sema::CodeCompleteObjCAtDirective(Scope *S, DeclPtrTy ObjCImpDecl,
+                                       bool InInterface) {
+  typedef CodeCompleteConsumer::Result Result;
+  ResultBuilder Results(*this);
+  Results.EnterNewScope();
+  if (ObjCImpDecl) {
+    // Since we have an implementation, we can end it.
+    Results.MaybeAddResult(Result("end", 0));
+
+    CodeCompletionString *Pattern = 0;
+    Decl *ImpDecl = ObjCImpDecl.getAs<Decl>();
+    if (isa<ObjCImplementationDecl>(ImpDecl) || 
+        isa<ObjCCategoryImplDecl>(ImpDecl)) {
+      // @dynamic
+      Pattern = new CodeCompletionString;
+      Pattern->AddTypedTextChunk("dynamic");
+      Pattern->AddTextChunk(" ");
+      Pattern->AddPlaceholderChunk("property");
+      Results.MaybeAddResult(Result(Pattern, 0));
+
+      // @synthesize
+      Pattern = new CodeCompletionString;
+      Pattern->AddTypedTextChunk("synthesize");
+      Pattern->AddTextChunk(" ");
+      Pattern->AddPlaceholderChunk("property");
+      Results.MaybeAddResult(Result(Pattern, 0));
+    }
+  } else if (InInterface) {
+    // Since we have an interface or protocol, we can end it.
+    Results.MaybeAddResult(Result("end", 0));
+
+    if (LangOpts.ObjC2) {
+      // @property
+      Results.MaybeAddResult(Result("property", 0));
+    }
+
+    // @required
+    Results.MaybeAddResult(Result("required", 0));
+
+    // @optional
+    Results.MaybeAddResult(Result("optional", 0));
+  } else {
+    CodeCompletionString *Pattern = 0;
+
+    // @class name ;
+    Pattern = new CodeCompletionString;
+    Pattern->AddTypedTextChunk("class");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("identifier");
+    Pattern->AddTextChunk(";"); // add ';' chunk
+    Results.MaybeAddResult(Result(Pattern, 0));
+
+    // @interface name 
+    // FIXME: Could introduce the whole pattern, including superclasses and 
+    // such.
+    Pattern = new CodeCompletionString;
+    Pattern->AddTypedTextChunk("interface");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("class");
+    Results.MaybeAddResult(Result(Pattern, 0));
+
+    // @protocol name
+    Pattern = new CodeCompletionString;
+    Pattern->AddTypedTextChunk("protocol");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("protocol");
+    Results.MaybeAddResult(Result(Pattern, 0));
+
+    // @implementation name
+    Pattern = new CodeCompletionString;
+    Pattern->AddTypedTextChunk("implementation");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("class");
+    Results.MaybeAddResult(Result(Pattern, 0));
+
+    // @compatibility_alias name
+    Pattern = new CodeCompletionString;
+    Pattern->AddTypedTextChunk("compatibility_alias");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("alias");
+    Pattern->AddTextChunk(" ");
+    Pattern->AddPlaceholderChunk("class");
+    Results.MaybeAddResult(Result(Pattern, 0));
+  }
+  Results.ExitScope();
+  HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
+}
+
 /// \brief Determine whether the addition of the given flag to an Objective-C
 /// property's attributes will cause a conflict.
 static bool ObjCPropertyFlagConflicts(unsigned Attributes, unsigned NewFlag) {
diff --git a/test/Index/complete-at-directives.m b/test/Index/complete-at-directives.m
new file mode 100644 (file)
index 0000000..68d1ef4
--- /dev/null
@@ -0,0 +1,24 @@
+/* Run lines are at the end, since line/column matter in this test. */
+@interface MyClass { }
+@end
+
+@implementation MyClass
+@end
+
+// RUN: c-index-test -code-completion-at=%s:2:2 %s | FileCheck -check-prefix=CHECK-CC1 %s
+// CHECK-CC1: {TypedText class}{Text  }{Placeholder identifier}{Text ;}
+// CHECK-CC1: {TypedText compatibility_alias}{Text  }{Placeholder alias}{Text  }{Placeholder class}
+// CHECK-CC1: {TypedText implementation}{Text  }{Placeholder class}
+// CHECK-CC1: {TypedText interface}{Text  }{Placeholder class}
+// CHECK-CC1: {TypedText protocol}{Text  }{Placeholder protocol}
+
+// RUN: c-index-test -code-completion-at=%s:3:2 %s | FileCheck -check-prefix=CHECK-CC2 %s
+// CHECK-CC2: {TypedText end}
+// CHECK-CC2: {TypedText optional}
+// CHECK-CC2: {TypedText property}
+// CHECK-CC2: {TypedText required}
+
+// RUN: c-index-test -code-completion-at=%s:6:2 %s | FileCheck -check-prefix=CHECK-CC3 %s
+// CHECK-CC3: {TypedText dynamic}{Text  }{Placeholder property}
+// CHECK-CC3: {TypedText end}
+// CHECK-CC3: {TypedText synthesize}{Text  }{Placeholder property}