Skip to content

Commit 4d888e5

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 (cherry picked from commit 65d8d29)
1 parent dca057d commit 4d888e5

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
@@ -5000,19 +5000,30 @@ void CodeCompletionCallbacksImpl::doneParsing() {
50005000
bool OnRoot = !KPE->getComponents().front().isValid();
50015001
Lookup.setIsSwiftKeyPathExpr(OnRoot);
50025002

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

5013-
// The second generic type argument of KeyPath<Root, Value> should be
5014-
// the value we pull code completion results from.
5015-
Lookup.getValueExprCompletions(ParsedType);
5026+
Lookup.getValueExprCompletions(baseType);
50165027
break;
50175028
}
50185029

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)