Skip to content

Commit f5b317d

Browse files
committed
[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.
1 parent 1c3f362 commit f5b317d

File tree

4 files changed

+134
-0
lines changed

4 files changed

+134
-0
lines changed

include/swift/Parse/CodeCompletionCallbacks.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ class CodeCompletionCallbacks {
3737
/// case.
3838
bool InEnumElementRawValue = false;
3939

40+
/// True if code completion is done inside a #selector expression.
41+
bool InObjCSelectorExpr = false;
42+
4043
std::vector<Expr *> leadingSequenceExprs;
4144

4245
public:
@@ -95,6 +98,24 @@ class CodeCompletionCallbacks {
9598
}
9699
};
97100

101+
/// RAII type that temporarily sets the "in Objective-C #selector expression"
102+
/// flag on the code completion callbacks object.
103+
class InObjCSelectorExprRAII {
104+
CodeCompletionCallbacks *Callbacks;
105+
106+
public:
107+
InObjCSelectorExprRAII(CodeCompletionCallbacks *Callbacks)
108+
: Callbacks(Callbacks) {
109+
if (Callbacks)
110+
Callbacks->InObjCSelectorExpr = true;
111+
}
112+
113+
InObjCSelectorExprRAII() {
114+
if (Callbacks)
115+
Callbacks->InObjCSelectorExpr = false;
116+
}
117+
};
118+
98119
/// \brief Complete the whole expression. This is a fallback that should
99120
/// produce results when more specific completion methods failed.
100121
virtual void completeExpr() = 0;

lib/IDE/CodeCompletion.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
10861086
bool HasSpace = false;
10871087
bool HasRParen = false;
10881088
bool ShouldCompleteCallPatternAfterParen = true;
1089+
bool PreferFunctionReferencesToCalls = false;
10891090
Optional<DeclKind> AttTargetDK;
10901091

10911092
SmallVector<StringRef, 3> ParsedKeywords;
@@ -1419,6 +1420,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
14191420
bool HaveRParen = false;
14201421
bool IsSuperRefExpr = false;
14211422
bool IsDynamicLookup = false;
1423+
bool PreferFunctionReferencesToCalls = false;
14221424
bool HaveLeadingSpace = false;
14231425

14241426
/// \brief True if we are code completing inside a static method.
@@ -1566,6 +1568,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
15661568
IsDynamicLookup = true;
15671569
}
15681570

1571+
void setPreferFunctionReferencesToCalls() {
1572+
PreferFunctionReferencesToCalls = true;
1573+
}
1574+
15691575
void setHaveLeadingSpace(bool value) { HaveLeadingSpace = value; }
15701576

15711577
void addExpressionSpecificDecl(const Decl *D) {
@@ -2402,6 +2408,43 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24022408
Builder.addDeclAttrKeyword(Name, Annotation);
24032409
}
24042410

2411+
/// Add the compound function name for the given function.
2412+
void addCompoundFunctionName(AbstractFunctionDecl *AFD,
2413+
DeclVisibilityKind Reason) {
2414+
CommandWordsPairs Pairs;
2415+
CodeCompletionResultBuilder Builder(
2416+
Sink, CodeCompletionResult::ResultKind::Declaration,
2417+
getSemanticContext(AFD, Reason), ExpectedTypes);
2418+
setClangDeclKeywords(AFD, Pairs, Builder);
2419+
Builder.setAssociatedDecl(AFD);
2420+
2421+
// Base name
2422+
addLeadingDot(Builder);
2423+
Builder.addTextChunk(AFD->getFullName().getBaseName().str());
2424+
2425+
// Add the argument labels.
2426+
auto ArgLabels = AFD->getFullName().getArgumentNames();
2427+
if (ArgLabels.size() > 0) {
2428+
if (!HaveLParen)
2429+
Builder.addLeftParen();
2430+
else
2431+
Builder.addAnnotatedLeftParen();
2432+
2433+
for (auto ArgLabel : ArgLabels) {
2434+
if (ArgLabel.empty())
2435+
Builder.addTextChunk("_");
2436+
else
2437+
Builder.addTextChunk(ArgLabel.str());
2438+
Builder.addTextChunk(":");
2439+
}
2440+
2441+
if (!HaveRParen)
2442+
Builder.addRightParen();
2443+
else
2444+
Builder.addAnnotatedRightParen();
2445+
}
2446+
}
2447+
24052448
// Implement swift::VisibleDeclConsumer.
24062449
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
24072450
// Hide private stdlib declarations.
@@ -2424,6 +2467,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24242467
switch (Kind) {
24252468
case LookupKind::ValueExpr:
24262469
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
2470+
// Do we want compound function names here?
2471+
if (PreferFunctionReferencesToCalls) {
2472+
addCompoundFunctionName(CD, Reason);
2473+
return;
2474+
}
2475+
24272476
if (auto MT = ExprType->getRValueType()->getAs<AnyMetatypeType>()) {
24282477
if (HaveDot) {
24292478
Type Ty;
@@ -2481,6 +2530,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24812530
if (FD->isAccessor())
24822531
return;
24832532

2533+
// Do we want compound function names here?
2534+
if (PreferFunctionReferencesToCalls) {
2535+
addCompoundFunctionName(FD, Reason);
2536+
return;
2537+
}
2538+
24842539
addMethodCall(FD, Reason);
24852540
return;
24862541
}
@@ -3759,6 +3814,9 @@ void CodeCompletionCallbacksImpl::completeDotExpr(Expr *E, SourceLoc DotLoc) {
37593814
return;
37603815

37613816
Kind = CompletionKind::DotExpr;
3817+
if (InObjCSelectorExpr)
3818+
PreferFunctionReferencesToCalls = true;
3819+
37623820
ParsedExpr = E;
37633821
this->DotLoc = DotLoc;
37643822
CurDeclContext = P.CurDeclContext;
@@ -3780,6 +3838,9 @@ void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExp
37803838
return;
37813839

37823840
Kind = CompletionKind::PostfixExprBeginning;
3841+
if (InObjCSelectorExpr)
3842+
PreferFunctionReferencesToCalls = true;
3843+
37833844
CurDeclContext = P.CurDeclContext;
37843845
CStyleForLoopIterationVariable =
37853846
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
@@ -3795,6 +3856,9 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
37953856

37963857
HasSpace = hasSpace;
37973858
Kind = CompletionKind::PostfixExpr;
3859+
if (InObjCSelectorExpr)
3860+
PreferFunctionReferencesToCalls = true;
3861+
37983862
ParsedExpr = E;
37993863
CurDeclContext = P.CurDeclContext;
38003864
}
@@ -3833,6 +3897,9 @@ void CodeCompletionCallbacksImpl::completeExprSuper(SuperRefExpr *SRE) {
38333897
return;
38343898

38353899
Kind = CompletionKind::SuperExpr;
3900+
if (InObjCSelectorExpr)
3901+
PreferFunctionReferencesToCalls = true;
3902+
38363903
ParsedExpr = SRE;
38373904
CurDeclContext = P.CurDeclContext;
38383905
}
@@ -3843,6 +3910,9 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
38433910
return;
38443911

38453912
Kind = CompletionKind::SuperExprDot;
3913+
if (InObjCSelectorExpr)
3914+
PreferFunctionReferencesToCalls = true;
3915+
38463916
ParsedExpr = SRE;
38473917
CurDeclContext = P.CurDeclContext;
38483918
}
@@ -4376,6 +4446,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
43764446
if (ExprType) {
43774447
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
43784448
}
4449+
if (PreferFunctionReferencesToCalls)
4450+
Lookup.setPreferFunctionReferencesToCalls();
43794451

43804452
auto DoPostfixExprBeginning = [&] (){
43814453
if (CStyleForLoopIterationVariable)

lib/Parse/ParseExpr.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,8 @@ ParserResult<Expr> Parser::parseExprSelector() {
503503
SourceLoc lParenLoc = consumeToken(tok::l_paren);
504504

505505
// Parse the subexpression.
506+
CodeCompletionCallbacks::InObjCSelectorExprRAII
507+
InObjCSelectorExpr(CodeCompletion);
506508
ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
507509
if (subExpr.hasCodeCompletion())
508510
return subExpr;

test/IDE/complete_pound_selector.swift

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@
44

55
// 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
66

7+
// 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
8+
9+
// 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
10+
11+
// 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
12+
13+
// 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
14+
715
// REQUIRES: objc_interop
816

917
import Foundation
@@ -20,7 +28,38 @@ func selectorArg2(obj: NSObject) {
2028
obj.messageSomeObject(obj, selector:#^SELECTOR_ARG2^#
2129
}
2230

31+
func inSelector1() {
32+
_ = #selector(NSObject.#^IN_SELECTOR1^#)
33+
}
34+
35+
func inSelector2() {
36+
_ = #selector(NSObject#^IN_SELECTOR2^#)
37+
}
38+
39+
class Subclass : NSObject {
40+
func inSelector3() {
41+
_ = #selector(super.#^IN_SELECTOR3^#)
42+
}
43+
44+
func inSelector4() {
45+
_ = #selector(super#^IN_SELECTOR4^#)
46+
}
47+
}
48+
49+
2350
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
2451
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
2552

2653
// CHECK-SELECTOR_ARG: Keyword/ExprSpecific: #selector({#@objc method#}); name=#selector(@objc method)
54+
55+
// CHECK-IN_SELECTOR: Decl[Constructor]/CurrNominal: {{.?}}init; name=init
56+
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
57+
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
58+
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
59+
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}description; name=description
60+
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
61+
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
62+
63+
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
64+
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
65+
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)

0 commit comments

Comments
 (0)