[Code completion] Code complete compound function names within #selector.

When we're code completing a postfix or dot expression inside the
subexpression of an #selector expression, prefer compound function
names. This helps us write, e.g.,

  #selector(UIView.

and get completions such as "insertSubview(_:aboveSubview:)". Fixes
rdar://problem/24470075.
This commit is contained in:
Doug Gregor
2016-02-02 16:03:02 -08:00
parent 9736d54a77
commit f5cb1151c1
4 changed files with 134 additions and 0 deletions

View File

@@ -37,6 +37,9 @@ protected:
/// case.
bool InEnumElementRawValue = false;
/// True if code completion is done inside a #selector expression.
bool InObjCSelectorExpr = false;
std::vector<Expr *> leadingSequenceExprs;
public:
@@ -95,6 +98,24 @@ public:
}
};
/// RAII type that temporarily sets the "in Objective-C #selector expression"
/// flag on the code completion callbacks object.
class InObjCSelectorExprRAII {
CodeCompletionCallbacks *Callbacks;
public:
InObjCSelectorExprRAII(CodeCompletionCallbacks *Callbacks)
: Callbacks(Callbacks) {
if (Callbacks)
Callbacks->InObjCSelectorExpr = true;
}
InObjCSelectorExprRAII() {
if (Callbacks)
Callbacks->InObjCSelectorExpr = false;
}
};
/// \brief Complete the whole expression. This is a fallback that should
/// produce results when more specific completion methods failed.
virtual void completeExpr() = 0;

View File

@@ -1086,6 +1086,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
bool HasSpace = false;
bool HasRParen = false;
bool ShouldCompleteCallPatternAfterParen = true;
bool PreferFunctionReferencesToCalls = false;
Optional<DeclKind> AttTargetDK;
SmallVector<StringRef, 3> ParsedKeywords;
@@ -1420,6 +1421,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
bool HaveRParen = false;
bool IsSuperRefExpr = false;
bool IsDynamicLookup = false;
bool PreferFunctionReferencesToCalls = false;
bool HaveLeadingSpace = false;
/// \brief True if we are code completing inside a static method.
@@ -1567,6 +1569,10 @@ public:
IsDynamicLookup = true;
}
void setPreferFunctionReferencesToCalls() {
PreferFunctionReferencesToCalls = true;
}
void setHaveLeadingSpace(bool value) { HaveLeadingSpace = value; }
void addExpressionSpecificDecl(const Decl *D) {
@@ -2404,6 +2410,43 @@ public:
Builder.addDeclAttrKeyword(Name, Annotation);
}
/// Add the compound function name for the given function.
void addCompoundFunctionName(AbstractFunctionDecl *AFD,
DeclVisibilityKind Reason) {
CommandWordsPairs Pairs;
CodeCompletionResultBuilder Builder(
Sink, CodeCompletionResult::ResultKind::Declaration,
getSemanticContext(AFD, Reason), ExpectedTypes);
setClangDeclKeywords(AFD, Pairs, Builder);
Builder.setAssociatedDecl(AFD);
// Base name
addLeadingDot(Builder);
Builder.addTextChunk(AFD->getFullName().getBaseName().str());
// Add the argument labels.
auto ArgLabels = AFD->getFullName().getArgumentNames();
if (ArgLabels.size() > 0) {
if (!HaveLParen)
Builder.addLeftParen();
else
Builder.addAnnotatedLeftParen();
for (auto ArgLabel : ArgLabels) {
if (ArgLabel.empty())
Builder.addTextChunk("_");
else
Builder.addTextChunk(ArgLabel.str());
Builder.addTextChunk(":");
}
if (!HaveRParen)
Builder.addRightParen();
else
Builder.addAnnotatedRightParen();
}
}
// Implement swift::VisibleDeclConsumer.
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
// Hide private stdlib declarations.
@@ -2426,6 +2469,12 @@ public:
switch (Kind) {
case LookupKind::ValueExpr:
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
// Do we want compound function names here?
if (PreferFunctionReferencesToCalls) {
addCompoundFunctionName(CD, Reason);
return;
}
if (auto MT = ExprType->getRValueType()->getAs<AnyMetatypeType>()) {
if (HaveDot) {
Type Ty;
@@ -2483,6 +2532,12 @@ public:
if (FD->isAccessor())
return;
// Do we want compound function names here?
if (PreferFunctionReferencesToCalls) {
addCompoundFunctionName(FD, Reason);
return;
}
addMethodCall(FD, Reason);
return;
}
@@ -3765,6 +3820,9 @@ void CodeCompletionCallbacksImpl::completeDotExpr(Expr *E, SourceLoc DotLoc) {
return;
Kind = CompletionKind::DotExpr;
if (InObjCSelectorExpr)
PreferFunctionReferencesToCalls = true;
ParsedExpr = E;
this->DotLoc = DotLoc;
CurDeclContext = P.CurDeclContext;
@@ -3786,6 +3844,9 @@ void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExp
return;
Kind = CompletionKind::PostfixExprBeginning;
if (InObjCSelectorExpr)
PreferFunctionReferencesToCalls = true;
CurDeclContext = P.CurDeclContext;
CStyleForLoopIterationVariable =
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
@@ -3801,6 +3862,9 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
HasSpace = hasSpace;
Kind = CompletionKind::PostfixExpr;
if (InObjCSelectorExpr)
PreferFunctionReferencesToCalls = true;
ParsedExpr = E;
CurDeclContext = P.CurDeclContext;
}
@@ -3839,6 +3903,9 @@ void CodeCompletionCallbacksImpl::completeExprSuper(SuperRefExpr *SRE) {
return;
Kind = CompletionKind::SuperExpr;
if (InObjCSelectorExpr)
PreferFunctionReferencesToCalls = true;
ParsedExpr = SRE;
CurDeclContext = P.CurDeclContext;
}
@@ -3849,6 +3916,9 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
return;
Kind = CompletionKind::SuperExprDot;
if (InObjCSelectorExpr)
PreferFunctionReferencesToCalls = true;
ParsedExpr = SRE;
CurDeclContext = P.CurDeclContext;
}
@@ -4389,6 +4459,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
if (ExprType) {
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
}
if (PreferFunctionReferencesToCalls)
Lookup.setPreferFunctionReferencesToCalls();
auto DoPostfixExprBeginning = [&] (){
if (CStyleForLoopIterationVariable)

View File

@@ -522,6 +522,8 @@ ParserResult<Expr> Parser::parseExprSelector() {
SourceLoc lParenLoc = consumeToken(tok::l_paren);
// Parse the subexpression.
CodeCompletionCallbacks::InObjCSelectorExprRAII
InObjCSelectorExpr(CodeCompletion);
ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
if (subExpr.hasCodeCompletion())
return subExpr;

View File

@@ -4,6 +4,14 @@
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=SELECTOR_ARG2 | FileCheck -check-prefix=CHECK-SELECTOR_ARG %s
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR1 | FileCheck -check-prefix=CHECK-IN_SELECTOR %s
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR2 | FileCheck -check-prefix=CHECK-IN_SELECTOR %s
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR3 | FileCheck -check-prefix=CHECK-IN_SUPER_SELECTOR %s
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR4 | FileCheck -check-prefix=CHECK-IN_SUPER_SELECTOR %s
// REQUIRES: objc_interop
import Foundation
@@ -20,7 +28,38 @@ func selectorArg2(obj: NSObject) {
obj.messageSomeObject(obj, selector:#^SELECTOR_ARG2^#
}
func inSelector1() {
_ = #selector(NSObject.#^IN_SELECTOR1^#)
}
func inSelector2() {
_ = #selector(NSObject#^IN_SELECTOR2^#)
}
class Subclass : NSObject {
func inSelector3() {
_ = #selector(super.#^IN_SELECTOR3^#)
}
func inSelector4() {
_ = #selector(super#^IN_SELECTOR4^#)
}
}
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
// CHECK-SELECTOR_ARG: Keyword/ExprSpecific: #selector({#@objc method#}); name=#selector(@objc method)
// CHECK-IN_SELECTOR: Decl[Constructor]/CurrNominal: {{.?}}init; name=init
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}description; name=description
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)