Skip to content

Commit 65d8d29

Browse files
committed
[CodeCompletion] Infer keypath root type from the context type
So that this works: ``` func foo(_: KeyPath<SomeObject, Int>) {} foo(\.<HERE>) ``` rdar://problem/46102807
1 parent 9faabb7 commit 65d8d29

File tree

2 files changed

+38
-15
lines changed

2 files changed

+38
-15
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5004,19 +5004,30 @@ void CodeCompletionCallbacksImpl::doneParsing() {
50045004
bool OnRoot = !KPE->getComponents().front().isValid();
50055005
Lookup.setIsSwiftKeyPathExpr(OnRoot);
50065006

5007-
auto ParsedType = BGT->getGenericArgs()[1];
5008-
auto Components = KPE->getComponents();
5009-
if (Components.back().getKind() ==
5010-
KeyPathExpr::Component::Kind::OptionalWrap) {
5007+
Type baseType = BGT->getGenericArgs()[OnRoot ? 0 : 1];
5008+
if (OnRoot && baseType->is<UnresolvedType>()) {
5009+
// Infer the root type of the keypath from the context type.
5010+
ExprContextInfo ContextInfo(CurDeclContext, ParsedExpr);
5011+
for (auto T : ContextInfo.getPossibleTypes()) {
5012+
if (auto unwrapped = T->getOptionalObjectType())
5013+
T = unwrapped;
5014+
if (!T->getAnyNominal() || !T->getAnyNominal()->getKeyPathTypeKind() ||
5015+
T->hasUnresolvedType() || !T->is<BoundGenericType>())
5016+
continue;
5017+
// Use the first KeyPath context type found.
5018+
baseType = T->castTo<BoundGenericType>()->getGenericArgs()[0];
5019+
break;
5020+
}
5021+
}
5022+
if (!OnRoot && KPE->getComponents().back().getKind() ==
5023+
KeyPathExpr::Component::Kind::OptionalWrap) {
50115024
// KeyPath expr with '?' (e.g. '\Ty.[0].prop?.another').
50125025
// Althogh expected type is optional, we should unwrap it because it's
50135026
// unwrapped.
5014-
ParsedType = ParsedType->getOptionalObjectType();
5027+
baseType = baseType->getOptionalObjectType();
50155028
}
50165029

5017-
// The second generic type argument of KeyPath<Root, Value> should be
5018-
// the value we pull code completion results from.
5019-
Lookup.getValueExprCompletions(ParsedType);
5030+
Lookup.getValueExprCompletions(baseType);
50205031
break;
50215032
}
50225033

test/IDE/complete_swift_key_path.swift

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@
2121
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=EMPTY_2 | %FileCheck %s -check-prefix=INVALID
2222

2323
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_BASEONLY | %FileCheck %s -check-prefix=PERSONTYPE-DOT
24-
// FIXME: RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_EXPLICIT | %FileCheck %s -check-prefix=INVALID
24+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_EXPLICIT | %FileCheck %s -check-prefix=PERSONTYPE-DOT
25+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_GENERIC_RESULT | %FileCheck %s -check-prefix=PERSONTYPE-DOT
26+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=CONTEXT_GENERIC_RESULT_OPTIONAL | %FileCheck %s -check-prefix=PERSONTYPE-DOT
2527

2628
class Person {
2729
var name: String
@@ -131,9 +133,19 @@ let _ = \.#^EMPTY_1^#
131133
let _ = \.friends.#^EMPTY_2^#
132134
// INVALID-NOT: Begin completions
133135

134-
let _: PartialKeyPath<Person> = \.#^CONTEXT_BASEONLY^#
135-
// Same as TYPE_DOT.
136-
137-
let _: KeyPath<Person, String> = \.#^CONTEXT_EXPLICIT^#
138-
// FIXME: Currently, it's suggest nothing. Since we know the base type is
139-
// 'Person', we should suggest at least '.name'.
136+
func recvPartialKP(_ kp: PartialKeyPath<Person>) {
137+
recvPartialKP(\.#^CONTEXT_BASEONLY^#)
138+
// Same as TYPE_DOT.
139+
}
140+
func recvExplicitKP(_ kp: KeyPath<Person, String>) {
141+
recvExplicitKP(\.#^CONTEXT_EXPLICIT^#)
142+
// Same as TYPE_DOT.
143+
}
144+
func recvExplicitKPWithGenericResult<Result>(_ kp: KeyPath<Person, Result>) {
145+
recvExplicitKPWithGenericResult(\.#^CONTEXT_GENERIC_RESULT^#)
146+
// Same as TYPE_DOT.
147+
}
148+
func recvExplicitKPWithGenericResultOpt<Result>(_ kp: KeyPath<Person, Result>?) {
149+
recvExplicitKPWithGenericResult(\.#^CONTEXT_GENERIC_RESULT_OPTIONAL^#
150+
// Same as TYPE_DOT.
151+
}

0 commit comments

Comments
 (0)