aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-08-26 15:07:07 +0000
committerDouglas Gregor <dgregor@apple.com>2010-08-26 15:07:07 +0000
commit458433d2f0f5c96a9e0d21decdd44bebccf20b11 (patch)
tree794359d35a7e2ddc77c732b524ab73c367639c6a
parent9a2d44c5fbc46ce9ee6dd829a7bac8ca750ad090 (diff)
Implement code completion for @selector expressions
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@112186 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/clang/Sema/Action.h10
-rw-r--r--include/clang/Sema/CodeCompleteConsumer.h4
-rw-r--r--include/clang/Sema/Sema.h3
-rw-r--r--lib/Frontend/ASTUnit.cpp1
-rw-r--r--lib/Parse/ParseObjc.cpp18
-rw-r--r--lib/Sema/SemaCodeComplete.cpp81
-rw-r--r--test/Index/complete-at-exprstmt.m18
-rw-r--r--tools/libclang/CIndexCodeCompletion.cpp2
8 files changed, 124 insertions, 13 deletions
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<P1,P2>.
///
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<IdentifierInfo *, 12> 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 <list>
#include <map>
#include <vector>
@@ -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
+}