Skip to content

Commit e2a4621

Browse files
committed
[CodeCompletion] Suggest #selector and #keyPath after # only if applicable
Also, add type annotation, and make it `TypeRelation[Identical]`. 'ExprSpecific' is too strong.
1 parent 3c957de commit e2a4621

File tree

4 files changed

+80
-56
lines changed

4 files changed

+80
-56
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 46 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,37 +2246,29 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
22462246
// #selector is only available when the Objective-C runtime is.
22472247
if (!Ctx.LangOpts.EnableObjCInterop) return;
22482248

2249-
// After #, this is a very likely result. When just in a String context,
2250-
// it's not.
2251-
auto semanticContext = needPound ? SemanticContextKind::None
2252-
: SemanticContextKind::ExpressionSpecific;
2253-
22542249
CodeCompletionResultBuilder Builder(
2255-
Sink,
2256-
CodeCompletionResult::ResultKind::Keyword,
2257-
semanticContext, expectedTypeContext);
2250+
Sink, CodeCompletionResult::ResultKind::Keyword,
2251+
SemanticContextKind::None, {});
22582252
if (needPound)
22592253
Builder.addTextChunk("#selector");
22602254
else
22612255
Builder.addTextChunk("selector");
22622256
Builder.addLeftParen();
22632257
Builder.addSimpleTypedParameter("@objc method", /*IsVarArg=*/false);
22642258
Builder.addRightParen();
2259+
Builder.addTypeAnnotation("Selector");
2260+
// This function is called only if the context type is 'Selector'.
2261+
Builder.setExpectedTypeRelation(
2262+
CodeCompletionResult::ExpectedTypeRelation::Identical);
22652263
}
22662264

22672265
void addPoundKeyPath(bool needPound) {
22682266
// #keyPath is only available when the Objective-C runtime is.
22692267
if (!Ctx.LangOpts.EnableObjCInterop) return;
22702268

2271-
// After #, this is a very likely result. When just in a String context,
2272-
// it's not.
2273-
auto semanticContext = needPound ? SemanticContextKind::None
2274-
: SemanticContextKind::ExpressionSpecific;
2275-
22762269
CodeCompletionResultBuilder Builder(
2277-
Sink,
2278-
CodeCompletionResult::ResultKind::Keyword,
2279-
semanticContext, expectedTypeContext);
2270+
Sink, CodeCompletionResult::ResultKind::Keyword,
2271+
SemanticContextKind::None, {});
22802272
if (needPound)
22812273
Builder.addTextChunk("#keyPath");
22822274
else
@@ -2285,6 +2277,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
22852277
Builder.addSimpleTypedParameter("@objc property sequence",
22862278
/*IsVarArg=*/false);
22872279
Builder.addRightParen();
2280+
Builder.addTypeAnnotation("String");
2281+
// This function is called only if the context type is 'String'.
2282+
Builder.setExpectedTypeRelation(
2283+
CodeCompletionResult::ExpectedTypeRelation::Identical);
22882284
}
22892285

22902286
SemanticContextKind getSemanticContextKind(const ValueDecl *VD) {
@@ -3679,6 +3675,38 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
36793675
}
36803676
}
36813677

3678+
void addObjCPoundKeywordCompletions(bool needPound) {
3679+
if (!Ctx.LangOpts.EnableObjCInterop)
3680+
return;
3681+
3682+
// If the expected type is ObjectiveC.Selector, add #selector. If
3683+
// it's String, add #keyPath.
3684+
bool addedSelector = false;
3685+
bool addedKeyPath = false;
3686+
3687+
for (auto T : expectedTypeContext.possibleTypes) {
3688+
T = T->lookThroughAllOptionalTypes();
3689+
if (auto structDecl = T->getStructOrBoundGenericStruct()) {
3690+
if (!addedSelector && structDecl->getName() == Ctx.Id_Selector &&
3691+
structDecl->getParentModule()->getName() == Ctx.Id_ObjectiveC) {
3692+
addPoundSelector(needPound);
3693+
if (addedKeyPath)
3694+
break;
3695+
addedSelector = true;
3696+
continue;
3697+
}
3698+
}
3699+
3700+
if (!addedKeyPath && T->getAnyNominal() == Ctx.getStringDecl()) {
3701+
addPoundKeyPath(needPound);
3702+
if (addedSelector)
3703+
break;
3704+
addedKeyPath = true;
3705+
continue;
3706+
}
3707+
}
3708+
}
3709+
36823710
struct FilteredDeclConsumer : public swift::VisibleDeclConsumer {
36833711
swift::VisibleDeclConsumer &Consumer;
36843712
DeclFilter Filter;
@@ -3742,33 +3770,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
37423770
addPoundLiteralCompletions(/*needPound=*/true);
37433771
}
37443772

3745-
3746-
// If the expected type is ObjectiveC.Selector, add #selector. If
3747-
// it's String, add #keyPath.
3748-
if (Ctx.LangOpts.EnableObjCInterop) {
3749-
bool addedSelector = false;
3750-
bool addedKeyPath = false;
3751-
for (auto T : expectedTypeContext.possibleTypes) {
3752-
T = T->lookThroughAllOptionalTypes();
3753-
if (auto structDecl = T->getStructOrBoundGenericStruct()) {
3754-
if (!addedSelector &&
3755-
structDecl->getName() == Ctx.Id_Selector &&
3756-
structDecl->getParentModule()->getName() == Ctx.Id_ObjectiveC) {
3757-
addPoundSelector(/*needPound=*/true);
3758-
if (addedKeyPath) break;
3759-
addedSelector = true;
3760-
continue;
3761-
}
3762-
}
3763-
3764-
if (!addedKeyPath && T->getAnyNominal() == Ctx.getStringDecl()) {
3765-
addPoundKeyPath(/*needPound=*/true);
3766-
if (addedSelector) break;
3767-
addedKeyPath = true;
3768-
continue;
3769-
}
3770-
}
3771-
}
3773+
addObjCPoundKeywordCompletions(/*needPound=*/true);
37723774
}
37733775

37743776
void getUnresolvedMemberCompletions(Type T) {
@@ -5475,8 +5477,7 @@ void CodeCompletionCallbacksImpl::doneParsing() {
54755477

54765478
Lookup.addPoundAvailable(ParentStmtKind);
54775479
Lookup.addPoundLiteralCompletions(/*needPound=*/false);
5478-
Lookup.addPoundSelector(/*needPound=*/false);
5479-
Lookup.addPoundKeyPath(/*needPound=*/false);
5480+
Lookup.addObjCPoundKeywordCompletions(/*needPound=*/false);
54805481
break;
54815482
}
54825483

test/IDE/complete_pound_expr.swift

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
1-
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POUND_EXPR_1 | %FileCheck %s -check-prefix=POUND_EXPR_STRINGCONTEXT
1+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POUND_EXPR_1 | %FileCheck %s -check-prefix=POUND_EXPR_INTCONTEXT
2+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POUND_EXPR_2 | %FileCheck %s -check-prefix=POUND_EXPR_STRINGCONTEXT
3+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=POUND_EXPR_3 | %FileCheck %s -check-prefix=POUND_EXPR_SELECTORCONTEXT
24
// REQUIRES: objc_interop
35

4-
func use(_ str: String) -> Bool {}
6+
import ObjectiveC
7+
8+
func useInt(_ str: Int) -> Bool {}
9+
func useString(_ str: String) -> Bool {}
10+
func useSelector(_ sel: Selector) -> Bool {}
511

612
func test1() {
7-
_ = use(##^POUND_EXPR_1^#)
13+
let _ = useInt(##^POUND_EXPR_1^#)
14+
let _ = useString(##^POUND_EXPR_2^#)
15+
let _ = useSelector(##^POUND_EXPR_3^#)
816
}
917

10-
// POUND_EXPR_STRINGCONTEXT: Begin completions, 7 items
11-
// POUND_EXPR_STRINGCONTEXT-NOT: available
18+
// POUND_EXPR_INTCONTEXT: Begin completions, 5 items
19+
// POUND_EXPR_INTCONTEXT-DAG: Keyword[#function]/None: function[#String#]; name=function
20+
// POUND_EXPR_INTCONTEXT-DAG: Keyword[#file]/None: file[#String#]; name=file
21+
// POUND_EXPR_INTCONTEXT-DAG: Keyword[#line]/None/TypeRelation[Identical]: line[#Int#]; name=line
22+
// POUND_EXPR_INTCONTEXT-DAG: Keyword[#column]/None/TypeRelation[Identical]: column[#Int#]; name=column
23+
// POUND_EXPR_INTCONTEXT-DAG: Keyword[#dsohandle]/None: dsohandle[#UnsafeRawPointer#]; name=dsohandle
24+
// POUND_EXPR_INTCONTEXT: End completions
25+
26+
// POUND_EXPR_STRINGCONTEXT: Begin completions, 6 items
1227
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword[#function]/None/TypeRelation[Identical]: function[#String#];
1328
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword[#file]/None/TypeRelation[Identical]: file[#String#];
1429
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword[#line]/None: line[#Int#];
1530
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword[#column]/None: column[#Int#];
1631
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword[#dsohandle]/None: dsohandle[#UnsafeRawPointer#];
17-
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword/ExprSpecific: selector({#@objc method#});
18-
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword/ExprSpecific: keyPath({#@objc property sequence#});
32+
// POUND_EXPR_STRINGCONTEXT-DAG: Keyword/None/TypeRelation[Identical]: keyPath({#@objc property sequence#})[#String#];
1933
// POUND_EXPR_STRINGCONTEXT: End completions
34+
35+
// POUND_EXPR_SELECTORCONTEXT: Begin completions, 6 items
36+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword[#function]/None/TypeRelation[Identical]: function[#Selector#];
37+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword[#file]/None/TypeRelation[Identical]: file[#Selector#];
38+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword[#line]/None: line[#Int#];
39+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword[#column]/None: column[#Int#];
40+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword[#dsohandle]/None: dsohandle[#UnsafeRawPointer#];
41+
// POUND_EXPR_SELECTORCONTEXT-DAG: Keyword/None/TypeRelation[Identical]: selector({#@objc method#})[#Selector#];
42+
// POUND_EXPR_SELECTORCONTEXT: End completions

test/IDE/complete_pound_keypath.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ func completeInKeyPath2() {
3434
_ = #keyPath(ObjCClass.#^IN_KEYPATH_2^#
3535
}
3636

37-
// CHECK-AFTER_POUND: Keyword/ExprSpecific: keyPath({#@objc property sequence#}); name=keyPath(@objc property sequence)
37+
// CHECK-AFTER_POUND-NOT: keyPath
3838

39-
// CHECK-KEYPATH_ARG: Keyword/None: #keyPath({#@objc property sequence#}); name=#keyPath(@objc property sequence)
39+
// CHECK-KEYPATH_ARG: Keyword/None/TypeRelation[Identical]: #keyPath({#@objc property sequence#})[#String#]; name=#keyPath(@objc property sequence)
4040

4141
// CHECK-IN_KEYPATH: Decl[InstanceVar]/CurrNominal: prop1[#String#]; name=prop1
4242
// CHECK-IN_KEYPATH: Decl[InstanceVar]/CurrNominal: prop2[#ObjCClass?#]; name=prop2

test/IDE/complete_pound_selector.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,10 @@ class Subclass : NSObject {
7171
}
7272

7373

74+
// CHECK-AFTER_POUND-NOT: selector
7475
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
75-
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
7676

77-
// CHECK-CONTEXT_SELECTOR: Keyword/None: #selector({#@objc method#}); name=#selector(@objc method)
77+
// CHECK-CONTEXT_SELECTOR: Keyword/None/TypeRelation[Identical]: #selector({#@objc method#})[#Selector#]; name=#selector(@objc method)
7878

7979
// CHECK-SELECTOR_BASIC: Keyword/None: getter: {#@objc property#}; name=getter: @objc property
8080
// CHECK-SELECTOR_BASIC: Keyword/None: setter: {#@objc property#}; name=setter: @objc property

0 commit comments

Comments
 (0)