Skip to content

Commit 167e039

Browse files
committed
Merge pull request swiftlang#1185 from DougGregor/selector-code-completion
[Swift 2.2] Code completion improvements for #selector
2 parents 634acb4 + fbe2c08 commit 167e039

File tree

5 files changed

+176
-6
lines changed

5 files changed

+176
-6
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: 91 additions & 3 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) {
@@ -1985,7 +1991,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19851991
Builder.addRightParen();
19861992
}
19871993

1988-
void addPoundSelector() {
1994+
void addPoundSelector(bool needPound) {
19891995
// #selector is only available when the Objective-C runtime is.
19901996
if (!Ctx.LangOpts.EnableObjCInterop) return;
19911997

@@ -1994,7 +2000,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19942000
CodeCompletionResult::ResultKind::Keyword,
19952001
SemanticContextKind::ExpressionSpecific,
19962002
ExpectedTypes);
1997-
Builder.addTextChunk("selector");
2003+
if (needPound)
2004+
Builder.addTextChunk("#selector");
2005+
else
2006+
Builder.addTextChunk("selector");
19982007
Builder.addLeftParen();
19992008
Builder.addSimpleTypedParameter("@objc method", /*isVarArg=*/false);
20002009
Builder.addRightParen();
@@ -2399,6 +2408,43 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23992408
Builder.addDeclAttrKeyword(Name, Annotation);
24002409
}
24012410

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+
24022448
// Implement swift::VisibleDeclConsumer.
24032449
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
24042450
// Hide private stdlib declarations.
@@ -2421,6 +2467,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24212467
switch (Kind) {
24222468
case LookupKind::ValueExpr:
24232469
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+
24242476
if (auto MT = ExprType->getRValueType()->getAs<AnyMetatypeType>()) {
24252477
if (HaveDot) {
24262478
Type Ty;
@@ -2478,6 +2530,12 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24782530
if (FD->isAccessor())
24792531
return;
24802532

2533+
// Do we want compound function names here?
2534+
if (PreferFunctionReferencesToCalls) {
2535+
addCompoundFunctionName(FD, Reason);
2536+
return;
2537+
}
2538+
24812539
addMethodCall(FD, Reason);
24822540
return;
24832541
}
@@ -3152,6 +3210,19 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
31523210
}
31533211

31543212
addValueLiteralCompletions();
3213+
3214+
// If the expected type is ObjectiveC.Selector, add #selector.
3215+
if (Ctx.LangOpts.EnableObjCInterop) {
3216+
for (auto T : ExpectedTypes) {
3217+
if (auto structDecl = T->getStructOrBoundGenericStruct()) {
3218+
if (structDecl->getName() == Ctx.Id_Selector &&
3219+
structDecl->getParentModule()->getName() == Ctx.Id_ObjectiveC) {
3220+
addPoundSelector(/*needPound=*/true);
3221+
break;
3222+
}
3223+
}
3224+
}
3225+
}
31553226
}
31563227

31573228
struct LookupByName : public swift::VisibleDeclConsumer {
@@ -3743,6 +3814,9 @@ void CodeCompletionCallbacksImpl::completeDotExpr(Expr *E, SourceLoc DotLoc) {
37433814
return;
37443815

37453816
Kind = CompletionKind::DotExpr;
3817+
if (InObjCSelectorExpr)
3818+
PreferFunctionReferencesToCalls = true;
3819+
37463820
ParsedExpr = E;
37473821
this->DotLoc = DotLoc;
37483822
CurDeclContext = P.CurDeclContext;
@@ -3764,6 +3838,9 @@ void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExp
37643838
return;
37653839

37663840
Kind = CompletionKind::PostfixExprBeginning;
3841+
if (InObjCSelectorExpr)
3842+
PreferFunctionReferencesToCalls = true;
3843+
37673844
CurDeclContext = P.CurDeclContext;
37683845
CStyleForLoopIterationVariable =
37693846
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
@@ -3779,6 +3856,9 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
37793856

37803857
HasSpace = hasSpace;
37813858
Kind = CompletionKind::PostfixExpr;
3859+
if (InObjCSelectorExpr)
3860+
PreferFunctionReferencesToCalls = true;
3861+
37823862
ParsedExpr = E;
37833863
CurDeclContext = P.CurDeclContext;
37843864
}
@@ -3817,6 +3897,9 @@ void CodeCompletionCallbacksImpl::completeExprSuper(SuperRefExpr *SRE) {
38173897
return;
38183898

38193899
Kind = CompletionKind::SuperExpr;
3900+
if (InObjCSelectorExpr)
3901+
PreferFunctionReferencesToCalls = true;
3902+
38203903
ParsedExpr = SRE;
38213904
CurDeclContext = P.CurDeclContext;
38223905
}
@@ -3827,6 +3910,9 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
38273910
return;
38283911

38293912
Kind = CompletionKind::SuperExprDot;
3913+
if (InObjCSelectorExpr)
3914+
PreferFunctionReferencesToCalls = true;
3915+
38303916
ParsedExpr = SRE;
38313917
CurDeclContext = P.CurDeclContext;
38323918
}
@@ -4360,6 +4446,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
43604446
if (ExprType) {
43614447
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
43624448
}
4449+
if (PreferFunctionReferencesToCalls)
4450+
Lookup.setPreferFunctionReferencesToCalls();
43634451

43644452
auto DoPostfixExprBeginning = [&] (){
43654453
if (CStyleForLoopIterationVariable)
@@ -4584,7 +4672,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
45844672

45854673
case CompletionKind::AfterPound: {
45864674
Lookup.addPoundAvailable(ParentStmtKind);
4587-
Lookup.addPoundSelector();
4675+
Lookup.addPoundSelector(/*needPound=*/false);
45884676
break;
45894677
}
45904678
}

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;
Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,65 @@
1-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=AFTER_POUND | FileCheck %s
1+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=AFTER_POUND | FileCheck -check-prefix=CHECK-AFTER_POUND %s
2+
3+
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=SELECTOR_ARG1 | FileCheck -check-prefix=CHECK-SELECTOR_ARG %s
4+
5+
// 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
6+
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
214

315
// REQUIRES: objc_interop
416

17+
import Foundation
18+
519
{
620
if ##^AFTER_POUND^#
721
}
822

9-
// CHECK: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
10-
// CHECK: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
23+
func selectorArg1(obj: NSObject) {
24+
obj.doSelector(#^SELECTOR_ARG1^#
25+
}
26+
27+
func selectorArg2(obj: NSObject) {
28+
obj.messageSomeObject(obj, selector:#^SELECTOR_ARG2^#
29+
}
30+
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+
50+
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
51+
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
52+
53+
// 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(_:)

test/Inputs/clang-importer-sdk/usr/include/Foundation.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -992,3 +992,7 @@ int variadicFunc2(int A, ...);
992992

993993
extern NSString *NSGlobalConstant;
994994
extern void NSGlobalFunction(void);
995+
996+
@interface NSObject (Selectors)
997+
-(void)messageSomeObject:(nonnull id)object selector:(SEL)selector;
998+
@end

0 commit comments

Comments
 (0)