diff options
-rw-r--r-- | include/clang/Parse/Action.h | 12 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 16 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 2 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 88 | ||||
-rw-r--r-- | test/Index/complete-at-directives.m | 24 |
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} |