From: Douglas Gregor Date: Thu, 26 Aug 2010 15:07:07 +0000 (+0000) Subject: Implement code completion for @selector expressions X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=458433d2f0f5c96a9e0d21decdd44bebccf20b11;p=clang Implement code completion for @selector expressions git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112186 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Action.h b/include/clang/Sema/Action.h index 5fb6d1f00f..1af23ba776 100644 --- a/include/clang/Sema/Action.h +++ b/include/clang/Sema/Action.h @@ -3078,6 +3078,16 @@ public: virtual void CodeCompleteObjCForCollection(Scope *S, DeclGroupPtrTy IterationVar) { } + /// \brief Code completion for an Objective-C @selector expression, in which + /// we may have already parsed parts of the selector. + /// + /// \param S The scope in which the @selector expression occurs. + /// \param SelIdents The identifiers that describe the selector (thus far). + /// \param NumSelIdents The number of identifiers in \p SelIdents. + virtual void CodeCompleteObjCSelector(Scope *S, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { } + /// \brief Code completion for a list of protocol references in Objective-C, /// such as P1 and P2 in \c id. /// diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h index d290d39a93..55bd576f96 100644 --- a/include/clang/Sema/CodeCompleteConsumer.h +++ b/include/clang/Sema/CodeCompleteConsumer.h @@ -190,7 +190,9 @@ public: /// /// This context usually implies that no completions should be added, /// unless they come from an appropriate natural-language dictionary. - CCC_NaturalLanguage + CCC_NaturalLanguage, + /// \brief Code completion for a selector, as in an @selector expression. + CCC_SelectorName }; private: diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 9504f77f19..f23c4a1e93 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -4202,6 +4202,9 @@ public: unsigned NumSelIdents); virtual void CodeCompleteObjCForCollection(Scope *S, DeclGroupPtrTy IterationVar); + virtual void CodeCompleteObjCSelector(Scope *S, + IdentifierInfo **SelIdents, + unsigned NumSelIdents); virtual void CodeCompleteObjCProtocolReferences(IdentifierLocPair *Protocols, unsigned NumProtocols); virtual void CodeCompleteObjCProtocolDecl(Scope *S); diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index 874fd012b3..851c6669fe 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -1554,6 +1554,7 @@ void CalculateHiddenNames(const CodeCompletionContext &Context, case CodeCompletionContext::CCC_PreprocessorExpression: case CodeCompletionContext::CCC_PreprocessorDirective: case CodeCompletionContext::CCC_NaturalLanguage: + case CodeCompletionContext::CCC_SelectorName: // We're looking for nothing, or we're looking for names that cannot // be hidden. return; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index 1b09b1fe84..4b65652011 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -2194,6 +2194,15 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) { llvm::SmallVector KeyIdents; SourceLocation LParenLoc = ConsumeParen(); SourceLocation sLoc; + + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents.data(), + KeyIdents.size()); + ConsumeCodeCompletionToken(); + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return ExprError(); + } + IdentifierInfo *SelIdent = ParseObjCSelectorPiece(sLoc); if (!SelIdent && Tok.isNot(tok::colon)) // missing selector name. return ExprError(Diag(Tok, diag::err_expected_ident)); @@ -2209,6 +2218,15 @@ ExprResult Parser::ParseObjCSelectorExpression(SourceLocation AtLoc) { ConsumeToken(); // Eat the ':'. if (Tok.is(tok::r_paren)) break; + + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCSelector(getCurScope(), KeyIdents.data(), + KeyIdents.size()); + ConsumeCodeCompletionToken(); + MatchRHSPunctuation(tok::r_paren, LParenLoc); + return ExprError(); + } + // Check for another keyword selector. SourceLocation Loc; SelIdent = ParseObjCSelectorPiece(Loc); diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index d61ddcdf75..b75a7d05bb 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -25,6 +25,7 @@ #include "llvm/ADT/SmallPtrSet.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/Twine.h" #include #include #include @@ -3401,26 +3402,33 @@ enum ObjCMethodKind { MK_OneArgSelector //< One-argument selector. }; -static bool isAcceptableObjCMethod(ObjCMethodDecl *Method, - ObjCMethodKind WantKind, - IdentifierInfo **SelIdents, - unsigned NumSelIdents) { - Selector Sel = Method->getSelector(); +static bool isAcceptableObjCSelector(Selector Sel, + ObjCMethodKind WantKind, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { if (NumSelIdents > Sel.getNumArgs()) return false; - + switch (WantKind) { - case MK_Any: break; - case MK_ZeroArgSelector: return Sel.isUnarySelector(); - case MK_OneArgSelector: return Sel.getNumArgs() == 1; + case MK_Any: break; + case MK_ZeroArgSelector: return Sel.isUnarySelector(); + case MK_OneArgSelector: return Sel.getNumArgs() == 1; } - + for (unsigned I = 0; I != NumSelIdents; ++I) if (SelIdents[I] != Sel.getIdentifierInfoForSlot(I)) return false; - + return true; } + +static bool isAcceptableObjCMethod(ObjCMethodDecl *Method, + ObjCMethodKind WantKind, + IdentifierInfo **SelIdents, + unsigned NumSelIdents) { + return isAcceptableObjCSelector(Method->getSelector(), WantKind, SelIdents, + NumSelIdents); +} /// \brief Add all of the Objective-C methods in the given Objective-C /// container to the set of results. @@ -3979,6 +3987,57 @@ void Sema::CodeCompleteObjCForCollection(Scope *S, CodeCompleteExpression(S, Data); } +void Sema::CodeCompleteObjCSelector(Scope *S, IdentifierInfo **SelIdents, + unsigned NumSelIdents) { + // If we have an external source, load the entire class method + // pool from the AST file. + if (ExternalSource) { + for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors(); + I != N; ++I) { + Selector Sel = ExternalSource->GetExternalSelector(I); + if (Sel.isNull() || MethodPool.count(Sel)) + continue; + + ReadMethodPool(Sel); + } + } + + ResultBuilder Results(*this); + Results.EnterNewScope(); + for (GlobalMethodPool::iterator M = MethodPool.begin(), + MEnd = MethodPool.end(); + M != MEnd; ++M) { + + Selector Sel = M->first; + if (!isAcceptableObjCSelector(Sel, MK_Any, SelIdents, NumSelIdents)) + continue; + + CodeCompletionString *Pattern = new CodeCompletionString; + if (Sel.isUnarySelector()) { + Pattern->AddTypedTextChunk(Sel.getIdentifierInfoForSlot(0)->getName()); + Results.AddResult(Pattern); + continue; + } + + for (unsigned I = 0, N = Sel.getNumArgs(); I != N; ++I) { + std::string Piece = Sel.getIdentifierInfoForSlot(I)->getName().str(); + Piece += ':'; + if (I < NumSelIdents) + Pattern->AddInformativeChunk(Piece); + else if (I == NumSelIdents) + Pattern->AddTypedTextChunk(Piece); + else + Pattern->AddTextChunk(Piece); + } + Results.AddResult(Pattern); + } + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, + CodeCompletionContext::CCC_SelectorName, + Results.data(), Results.size()); +} + /// \brief Add all of the protocol declarations that we find in the given /// (translation unit) context. static void AddProtocolResults(DeclContext *Ctx, DeclContext *CurContext, diff --git a/test/Index/complete-at-exprstmt.m b/test/Index/complete-at-exprstmt.m index 85370989fd..834b42e4b3 100644 --- a/test/Index/complete-at-exprstmt.m +++ b/test/Index/complete-at-exprstmt.m @@ -9,6 +9,16 @@ @synchronized (@encode(MyClass)) { } } @end + +@interface A ++ (int)add:(int)x to:(int)y; ++ (int)add:(int)x to:(int)y plus:(int)z; +@end + +void f() { + @selector(add:to:); +} + // RUN: c-index-test -code-completion-at=%s:9:4 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s // CHECK-CC1: {TypedText encode}{LeftParen (}{Placeholder type-name}{RightParen )} // CHECK-CC1: {TypedText protocol}{LeftParen (}{Placeholder protocol-name}{RightParen )} @@ -35,3 +45,11 @@ // CHECK-CC3: ObjCInterfaceDecl:{TypedText MyClass} // CHECK-CC3: TypedefDecl:{TypedText SEL} // CHECK-CC3: NotImplemented:{ResultType MyClass *}{TypedText self} +// RUN: c-index-test -code-completion-at=%s:19:13 %s | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: NotImplemented:{TypedText add:}{Text to:} (30) +// CHECK-CC4: NotImplemented:{TypedText add:}{Text to:}{Text plus:} (30) +// CHECK-CC4: NotImplemented:{TypedText myMethod:} (30) +// RUN: c-index-test -code-completion-at=%s:19:17 %s | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5: NotImplemented:{Informative add:}{TypedText to:} (30) +// CHECK-CC5: NotImplemented:{Informative add:}{TypedText to:}{Text plus:} (30) + diff --git a/tools/libclang/CIndexCodeCompletion.cpp b/tools/libclang/CIndexCodeCompletion.cpp index 825031bd4b..cebfc8478b 100644 --- a/tools/libclang/CIndexCodeCompletion.cpp +++ b/tools/libclang/CIndexCodeCompletion.cpp @@ -818,4 +818,4 @@ extern "C" { unsigned NumResults) { std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); } -} \ No newline at end of file +}