aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/Parse/Action.h12
-rw-r--r--lib/Parse/ParseObjc.cpp16
-rw-r--r--lib/Sema/Sema.h2
-rw-r--r--lib/Sema/SemaCodeComplete.cpp88
-rw-r--r--test/Index/complete-at-directives.m24
5 files changed, 141 insertions, 1 deletions
diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h
index 200bd8a6b2..2db254b8f5 100644
--- a/include/clang/Parse/Action.h
+++ b/include/clang/Parse/Action.h
@@ -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
diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp
index 8525c49d72..f1832e5707 100644
--- a/lib/Parse/ParseObjc.cpp
+++ b/lib/Parse/ParseObjc.cpp
@@ -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);
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index edb4528f7e..806f1e94d3 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -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,
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 5f9bba0f8c..5e231a54d1 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -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
index 0000000000..68d1ef42f8
--- /dev/null
+++ b/test/Index/complete-at-directives.m
@@ -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}