Skip to content

Commit 42655e1

Browse files
authored
Merge pull request #58736 from ahoppen/pr/fix-dynamic-keypath-completion
[CodeCompletion] Look through @dynamicMemberLookup key path functions
2 parents bc43ced + 11266eb commit 42655e1

File tree

2 files changed

+102
-44
lines changed

2 files changed

+102
-44
lines changed

lib/IDE/ArgumentCompletion.cpp

Lines changed: 99 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,98 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
7878
return ShowGlobalCompletions;
7979
}
8080

81+
/// Information that \c getSelectedOverloadInfo gathered about a
82+
/// \c SelectedOverload.
83+
struct SelectedOverloadInfo {
84+
/// The function that is being called.
85+
ValueDecl *FuncD = nullptr;
86+
/// The type of the called function itself (not its result type)
87+
Type FuncTy;
88+
/// The type on which the function is being called. \c null if the function is
89+
/// a free function.
90+
Type CallBaseTy;
91+
};
92+
93+
/// Extract additional information about the overload that is being called by
94+
/// \p CalleeLocator.
95+
SelectedOverloadInfo getSelectedOverloadInfo(const Solution &S,
96+
ConstraintLocator *CalleeLocator) {
97+
auto &CS = S.getConstraintSystem();
98+
99+
SelectedOverloadInfo Result;
100+
101+
auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator);
102+
if (!SelectedOverload) {
103+
return Result;
104+
}
105+
106+
switch (SelectedOverload->choice.getKind()) {
107+
case OverloadChoiceKind::KeyPathApplication:
108+
case OverloadChoiceKind::Decl:
109+
case OverloadChoiceKind::DeclViaDynamic:
110+
case OverloadChoiceKind::DeclViaBridge:
111+
case OverloadChoiceKind::DeclViaUnwrappedOptional: {
112+
Result.CallBaseTy = SelectedOverload->choice.getBaseType();
113+
if (Result.CallBaseTy) {
114+
Result.CallBaseTy = S.simplifyType(Result.CallBaseTy)->getRValueType();
115+
}
116+
117+
Result.FuncD = SelectedOverload->choice.getDeclOrNull();
118+
Result.FuncTy =
119+
S.simplifyTypeForCodeCompletion(SelectedOverload->openedType);
120+
121+
// For completion as the arg in a call to the implicit [keypath: _]
122+
// subscript the solver can't know what kind of keypath is expected without
123+
// an actual argument (e.g. a KeyPath vs WritableKeyPath) so it ends up as a
124+
// hole. Just assume KeyPath so we show the expected keypath's root type to
125+
// users rather than '_'.
126+
if (SelectedOverload->choice.getKind() ==
127+
OverloadChoiceKind::KeyPathApplication) {
128+
auto Params = Result.FuncTy->getAs<AnyFunctionType>()->getParams();
129+
if (Params.size() == 1 &&
130+
Params[0].getPlainType()->is<UnresolvedType>()) {
131+
auto *KPDecl = CS.getASTContext().getKeyPathDecl();
132+
Type KPTy =
133+
KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType());
134+
Type KPValueTy = KPTy->castTo<BoundGenericType>()->getGenericArgs()[1];
135+
KPTy = BoundGenericType::get(KPDecl, Type(),
136+
{Result.CallBaseTy, KPValueTy});
137+
Result.FuncTy =
138+
FunctionType::get({Params[0].withType(KPTy)}, KPValueTy);
139+
}
140+
}
141+
break;
142+
}
143+
case OverloadChoiceKind::KeyPathDynamicMemberLookup: {
144+
auto *fnType = SelectedOverload->openedType->castTo<FunctionType>();
145+
assert(fnType->getParams().size() == 1 &&
146+
"subscript always has one argument");
147+
// Parameter type is KeyPath<T, U> where `T` is a root type
148+
// and U is a leaf type (aka member type).
149+
auto keyPathTy =
150+
fnType->getParams()[0].getPlainType()->castTo<BoundGenericType>();
151+
152+
auto *keyPathDecl = keyPathTy->getAnyNominal();
153+
assert(isKnownKeyPathType(keyPathTy) &&
154+
"parameter is supposed to be a keypath");
155+
156+
auto KeyPathDynamicLocator = CS.getConstraintLocator(
157+
CalleeLocator, LocatorPathElt::KeyPathDynamicMember(keyPathDecl));
158+
Result = getSelectedOverloadInfo(S, KeyPathDynamicLocator);
159+
break;
160+
}
161+
case OverloadChoiceKind::DynamicMemberLookup:
162+
case OverloadChoiceKind::TupleIndex:
163+
// If it's DynamicMemberLookup, we don't know which function is being
164+
// called, so we can't extract any information from it.
165+
// TupleIndex isn't a function call and is not relevant for argument
166+
// completion because it doesn't take arguments.
167+
break;
168+
}
169+
170+
return Result;
171+
}
172+
81173
void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
82174
Type ExpectedTy = getTypeForCompletion(S, CompletionExpr);
83175

@@ -104,41 +196,8 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
104196

105197
auto *CallLocator = CS.getConstraintLocator(ParentCall);
106198
auto *CalleeLocator = S.getCalleeLocator(CallLocator);
107-
ValueDecl *FuncD = nullptr;
108-
Type FuncTy;
109-
Type CallBaseTy;
110-
// If we are calling a closure in-place there is no overload choice, but we
111-
// still have all the other required information (like the argument's
112-
// expected type) to provide useful code completion results.
113-
if (auto SelectedOverload = S.getOverloadChoiceIfAvailable(CalleeLocator)) {
114-
115-
CallBaseTy = SelectedOverload->choice.getBaseType();
116-
if (CallBaseTy) {
117-
CallBaseTy = S.simplifyType(CallBaseTy)->getRValueType();
118-
}
119-
120-
FuncD = SelectedOverload->choice.getDeclOrNull();
121-
FuncTy = S.simplifyTypeForCodeCompletion(SelectedOverload->openedType);
122199

123-
// For completion as the arg in a call to the implicit [keypath: _]
124-
// subscript the solver can't know what kind of keypath is expected without
125-
// an actual argument (e.g. a KeyPath vs WritableKeyPath) so it ends up as a
126-
// hole. Just assume KeyPath so we show the expected keypath's root type to
127-
// users rather than '_'.
128-
if (SelectedOverload->choice.getKind() ==
129-
OverloadChoiceKind::KeyPathApplication) {
130-
auto Params = FuncTy->getAs<AnyFunctionType>()->getParams();
131-
if (Params.size() == 1 &&
132-
Params[0].getPlainType()->is<UnresolvedType>()) {
133-
auto *KPDecl = CS.getASTContext().getKeyPathDecl();
134-
Type KPTy =
135-
KPDecl->mapTypeIntoContext(KPDecl->getDeclaredInterfaceType());
136-
Type KPValueTy = KPTy->castTo<BoundGenericType>()->getGenericArgs()[1];
137-
KPTy = BoundGenericType::get(KPDecl, Type(), {CallBaseTy, KPValueTy});
138-
FuncTy = FunctionType::get({Params[0].withType(KPTy)}, KPValueTy);
139-
}
140-
}
141-
}
200+
auto Info = getSelectedOverloadInfo(S, CalleeLocator);
142201

143202
// Find the parameter the completion was bound to (if any), as well as which
144203
// parameters are already bound (so we don't suggest them even when the args
@@ -174,9 +233,6 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
174233
ClaimedParams.insert(i);
175234
}
176235
}
177-
} else {
178-
// FIXME: We currently don't look through @dynamicMemberLookup applications
179-
// for subscripts (rdar://90363138)
180236
}
181237

182238
bool HasLabel = false;
@@ -190,8 +246,9 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
190246

191247
// If this is a duplicate of any other result, ignore this solution.
192248
if (llvm::any_of(Results, [&](const Result &R) {
193-
return R.FuncD == FuncD && nullableTypesEqual(R.FuncTy, FuncTy) &&
194-
nullableTypesEqual(R.BaseType, CallBaseTy) &&
249+
return R.FuncD == Info.FuncD &&
250+
nullableTypesEqual(R.FuncTy, Info.FuncTy) &&
251+
nullableTypesEqual(R.BaseType, Info.CallBaseTy) &&
195252
R.ParamIdx == ParamIdx &&
196253
R.IsNoninitialVariadic == IsNoninitialVariadic;
197254
})) {
@@ -201,9 +258,9 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
201258
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
202259
getSolutionSpecificVarTypes(S, SolutionSpecificVarTypes);
203260

204-
Results.push_back({ExpectedTy, isa<SubscriptExpr>(ParentCall), FuncD, FuncTy,
205-
ArgIdx, ParamIdx, std::move(ClaimedParams),
206-
IsNoninitialVariadic, CallBaseTy, HasLabel, IsAsync,
261+
Results.push_back({ExpectedTy, isa<SubscriptExpr>(ParentCall), Info.FuncD,
262+
Info.FuncTy, ArgIdx, ParamIdx, std::move(ClaimedParams),
263+
IsNoninitialVariadic, Info.CallBaseTy, HasLabel, IsAsync,
207264
SolutionSpecificVarTypes});
208265
}
209266

test/IDE/complete_call_arg.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1354,7 +1354,7 @@ func testDynamicMemberSubscriptLookup() {
13541354
}
13551355

13561356
@dynamicMemberLookup public struct Binding<Value> {
1357-
subscript<Subject>(dynamicMember keyPath: WritableKeyPath<Value, Subject>) -> Binding<Subject> {
1357+
subscript<Subject>(dynamicMember keyPath: KeyPath<Value, Subject>) -> Binding<Subject> {
13581358
fatalError()
13591359
}
13601360
}
@@ -1363,14 +1363,15 @@ func testDynamicMemberSubscriptLookup() {
13631363
var bar: Binding<MyStruct>
13641364

13651365
func test(index: Int) {
1366-
_ = bar[#^DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP?xfail=rdar90363138^#index]
1366+
_ = bar[#^DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP^#index]
13671367
}
13681368
}
13691369
}
13701370

13711371
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP: Begin completions
13721372
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Decl[LocalVar]/Local/TypeRelation[Convertible]: index[#Int#]; name=index
13731373
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Pattern/CurrNominal/Flair[ArgLabels]: ['[']{#keyPath: KeyPath<Binding<MyStruct>, Value>#}[']'][#Value#]; name=keyPath:
1374+
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP-DAG: Decl[Subscript]/CurrNominal/Flair[ArgLabels]: ['[']{#(index): Int#}[']'][#Int#]; name=:
13741375
// DYNAMIC_MEMBER_SUBSCRIPT_LOOKUP: End completions
13751376

13761377
func testVarInitializedByCallingClosure() {

0 commit comments

Comments
 (0)