Skip to content

Commit 6d8b1f4

Browse files
committed
[CodeCompletion] Fix a crash when completing an argument to a function taking a parameter pack
We previously asserted that for a call the function type had the same number of parameters as the declaration. But that’s not true for parameter packs anymore because the parameter pack will be exploded in the function type to account for passing multiple arguments to the pack. To fix this, use `ConcreteDeclRef` instead of a `ValueDecl`, which has a substitution map and is able to account for the exploded parameter packs when accessed using `getParameterAt`. rdar://100066716
1 parent ed3555a commit 6d8b1f4

File tree

6 files changed

+65
-39
lines changed

6 files changed

+65
-39
lines changed

include/swift/IDE/ArgumentCompletion.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
2929
Type ExpectedCallType;
3030
/// True if this is a subscript rather than a function call.
3131
bool IsSubscript;
32-
/// The FuncDecl or SubscriptDecl associated with the call.
33-
ValueDecl *FuncD;
32+
/// The reference to the FuncDecl or SubscriptDecl associated with the call.
33+
ConcreteDeclRef FuncDeclRef;
3434
/// The type of the function being called.
3535
AnyFunctionType *FuncTy;
3636
/// The index of the argument containing the completion location
@@ -55,6 +55,9 @@ class ArgumentTypeCheckCompletionCallback : public TypeCheckCompletionCallback {
5555
/// this result. This in particular includes parameters of closures that
5656
/// were type-checked with the code completion expression.
5757
llvm::SmallDenseMap<const VarDecl *, Type> SolutionSpecificVarTypes;
58+
59+
/// Return the \c FuncDecl or \c SubscriptDecl associated with this call.
60+
ValueDecl *getFuncD() const { return FuncDeclRef.getDecl(); }
5861
};
5962

6063
CodeCompletionExpr *CompletionExpr;

include/swift/IDE/SelectedOverloadInfo.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ using namespace swift::constraints;
2525
/// \c SelectedOverload.
2626
struct SelectedOverloadInfo {
2727
/// The function that is being called or the value that is being accessed.
28-
ValueDecl *Value = nullptr;
28+
ConcreteDeclRef ValueRef;
2929
/// For a function, type of the called function itself (not its result type),
3030
/// for an arbitrary value the type of that value.
3131
Type ValueTy;
3232
/// The type on which the overload is being accessed. \c null if it does not
3333
/// have a base type, e.g. for a free function.
3434
Type BaseTy;
35+
36+
ValueDecl *getValue() const { return ValueRef.getDecl(); }
3537
};
3638

3739
/// Extract additional information about the overload that is being called by

lib/IDE/ArgumentCompletion.cpp

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,6 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
3838

3939
ArrayRef<AnyFunctionType::Param> ParamsToPass = Res.FuncTy->getParams();
4040

41-
ParameterList *PL = nullptr;
42-
if (Res.FuncD) {
43-
PL = swift::getParameterList(Res.FuncD);
44-
}
45-
assert(!PL || PL->size() == ParamsToPass.size());
46-
4741
bool ShowGlobalCompletions = false;
4842
for (auto Idx : range(*Res.ParamIdx, ParamsToPass.size())) {
4943
bool IsCompletion = (Idx == Res.ParamIdx);
@@ -53,22 +47,35 @@ bool ArgumentTypeCheckCompletionCallback::addPossibleParams(
5347
break;
5448
}
5549

56-
const AnyFunctionType::Param *P = &ParamsToPass[Idx];
57-
bool Required =
58-
!(PL && PL->get(Idx)->isDefaultArgument()) && !P->isVariadic();
50+
// We work with the parameter from the function type and the declaration
51+
// because they contain different information that we need.
52+
//
53+
// Since not all function types are backed by declarations (e.g. closure
54+
// paramters), `DeclParam` might be `nullptr`.
55+
const AnyFunctionType::Param *TypeParam = &ParamsToPass[Idx];
56+
const ParamDecl *DeclParam = getParameterAt(Res.FuncDeclRef, Idx);
57+
58+
bool Required = true;
59+
if (DeclParam && DeclParam->isDefaultArgument()) {
60+
Required = false;
61+
} else if (DeclParam && DeclParam->getType()->is<PackExpansionType>()) {
62+
Required = false;
63+
} else if (TypeParam->isVariadic()) {
64+
Required = false;
65+
}
5966

60-
if (P->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
67+
if (TypeParam->hasLabel() && !(IsCompletion && Res.IsNoninitialVariadic)) {
6168
// Suggest parameter label if parameter has label, we are completing in it
6269
// and it is not a variadic parameter that already has arguments
63-
PossibleParamInfo PP(P, Required);
70+
PossibleParamInfo PP(TypeParam, Required);
6471
if (!llvm::is_contained(Params, PP)) {
6572
Params.push_back(std::move(PP));
6673
}
6774
} else {
6875
// We have a parameter that doesn't require a label. Suggest global
6976
// results for that type.
7077
ShowGlobalCompletions = true;
71-
Types.push_back(P->getPlainType());
78+
Types.push_back(TypeParam->getPlainType());
7279
}
7380
if (Required) {
7481
// The user should only be suggested the first required param. Stop.
@@ -157,7 +164,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
157164
auto *CalleeLocator = S.getCalleeLocator(CallLocator);
158165

159166
auto Info = getSelectedOverloadInfo(S, CalleeLocator);
160-
if (Info.Value && Info.Value->shouldHideFromEditor()) {
167+
if (Info.getValue() && Info.getValue()->shouldHideFromEditor()) {
161168
return;
162169
}
163170
// Disallow invalid initializer references
@@ -215,7 +222,7 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
215222

216223
// If this is a duplicate of any other result, ignore this solution.
217224
if (llvm::any_of(Results, [&](const Result &R) {
218-
return R.FuncD == Info.Value &&
225+
return R.FuncDeclRef == Info.ValueRef &&
219226
nullableTypesEqual(R.FuncTy, Info.ValueTy) &&
220227
nullableTypesEqual(R.BaseType, Info.BaseTy) &&
221228
R.ParamIdx == ParamIdx &&
@@ -232,9 +239,10 @@ void ArgumentTypeCheckCompletionCallback::sawSolutionImpl(const Solution &S) {
232239
FuncTy = Info.ValueTy->lookThroughAllOptionalTypes()->getAs<AnyFunctionType>();
233240
}
234241
Results.push_back({ExpectedTy, ExpectedCallType,
235-
isa<SubscriptExpr>(ParentCall), Info.Value, FuncTy, ArgIdx,
236-
ParamIdx, std::move(ClaimedParams), IsNoninitialVariadic,
237-
Info.BaseTy, HasLabel, IsAsync, SolutionSpecificVarTypes});
242+
isa<SubscriptExpr>(ParentCall), Info.ValueRef, FuncTy,
243+
ArgIdx, ParamIdx, std::move(ClaimedParams),
244+
IsNoninitialVariadic, Info.BaseTy, HasLabel, IsAsync,
245+
SolutionSpecificVarTypes});
238246
}
239247

240248
void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
@@ -243,24 +251,25 @@ void ArgumentTypeCheckCompletionCallback::computeShadowedDecls(
243251
auto &ResultA = Results[i];
244252
for (size_t j = i + 1; j < Results.size(); ++j) {
245253
auto &ResultB = Results[j];
246-
if (!ResultA.FuncD || !ResultB.FuncD || !ResultA.FuncTy || !ResultB.FuncTy) {
254+
if (!ResultA.getFuncD() || !ResultB.getFuncD() || !ResultA.FuncTy ||
255+
!ResultB.FuncTy) {
247256
continue;
248257
}
249-
if (ResultA.FuncD->getName() != ResultB.FuncD->getName()) {
258+
if (ResultA.getFuncD()->getName() != ResultB.getFuncD()->getName()) {
250259
continue;
251260
}
252261
if (!ResultA.FuncTy->isEqual(ResultB.FuncTy)) {
253262
continue;
254263
}
255264
ProtocolDecl *inProtocolExtensionA =
256-
ResultA.FuncD->getDeclContext()->getExtendedProtocolDecl();
265+
ResultA.getFuncD()->getDeclContext()->getExtendedProtocolDecl();
257266
ProtocolDecl *inProtocolExtensionB =
258-
ResultB.FuncD->getDeclContext()->getExtendedProtocolDecl();
267+
ResultB.getFuncD()->getDeclContext()->getExtendedProtocolDecl();
259268

260269
if (inProtocolExtensionA && !inProtocolExtensionB) {
261-
ShadowedDecls.insert(ResultA.FuncD);
270+
ShadowedDecls.insert(ResultA.getFuncD());
262271
} else if (!inProtocolExtensionA && inProtocolExtensionB) {
263-
ShadowedDecls.insert(ResultB.FuncD);
272+
ShadowedDecls.insert(ResultB.getFuncD());
264273
}
265274
}
266275
}
@@ -301,35 +310,37 @@ void ArgumentTypeCheckCompletionCallback::deliverResults(
301310
}
302311
if ((BaseNominal = BaseTy->getAnyNominal())) {
303312
SemanticContext = SemanticContextKind::CurrentNominal;
304-
if (Result.FuncD &&
305-
Result.FuncD->getDeclContext()->getSelfNominalTypeDecl() !=
313+
if (Result.getFuncD() &&
314+
Result.getFuncD()->getDeclContext()->getSelfNominalTypeDecl() !=
306315
BaseNominal) {
307316
SemanticContext = SemanticContextKind::Super;
308317
}
309318
} else if (BaseTy->is<TupleType>() || BaseTy->is<SubstitutableType>()) {
310319
SemanticContext = SemanticContextKind::CurrentNominal;
311320
}
312321
}
313-
if (SemanticContext == SemanticContextKind::None && Result.FuncD) {
314-
if (Result.FuncD->getDeclContext()->isTypeContext()) {
322+
if (SemanticContext == SemanticContextKind::None && Result.getFuncD()) {
323+
if (Result.getFuncD()->getDeclContext()->isTypeContext()) {
315324
SemanticContext = SemanticContextKind::CurrentNominal;
316-
} else if (Result.FuncD->getDeclContext()->isLocalContext()) {
325+
} else if (Result.getFuncD()->getDeclContext()->isLocalContext()) {
317326
SemanticContext = SemanticContextKind::Local;
318-
} else if (Result.FuncD->getModuleContext() == DC->getParentModule()) {
327+
} else if (Result.getFuncD()->getModuleContext() ==
328+
DC->getParentModule()) {
319329
SemanticContext = SemanticContextKind::CurrentModule;
320330
}
321331
}
322332
if (Result.FuncTy) {
323333
if (auto FuncTy = Result.FuncTy) {
324-
if (ShadowedDecls.count(Result.FuncD) == 0) {
334+
if (ShadowedDecls.count(Result.getFuncD()) == 0) {
325335
// Don't show call pattern completions if the function is
326336
// overridden.
327337
if (Result.IsSubscript) {
328338
assert(SemanticContext != SemanticContextKind::None);
329-
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.FuncD);
339+
auto *SD = dyn_cast_or_null<SubscriptDecl>(Result.getFuncD());
330340
Lookup.addSubscriptCallPattern(FuncTy, SD, SemanticContext);
331341
} else {
332-
auto *FD = dyn_cast_or_null<AbstractFunctionDecl>(Result.FuncD);
342+
auto *FD =
343+
dyn_cast_or_null<AbstractFunctionDecl>(Result.getFuncD());
333344
Lookup.addFunctionCallPattern(FuncTy, FD, SemanticContext);
334345
}
335346
}

lib/IDE/CursorInfo.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
311311
auto Locator = CS.getConstraintLocator(ResolveExpr);
312312
auto CalleeLocator = S.getCalleeLocator(Locator);
313313
auto OverloadInfo = getSelectedOverloadInfo(S, CalleeLocator);
314-
if (!OverloadInfo.Value) {
314+
if (!OverloadInfo.ValueRef) {
315315
// We could not resolve the referenced declaration. Skip the solution.
316316
return;
317317
}
@@ -322,11 +322,12 @@ class CursorInfoTypeCheckSolutionCallback : public TypeCheckCompletionCallback {
322322
if (auto BaseExpr =
323323
simplifyLocatorToAnchor(BaseLocator).dyn_cast<Expr *>()) {
324324
IsDynamicRef =
325-
ide::isDynamicRef(BaseExpr, OverloadInfo.Value,
325+
ide::isDynamicRef(BaseExpr, OverloadInfo.getValue(),
326326
[&S](Expr *E) { return S.getResolvedType(E); });
327327
}
328328

329-
Results.push_back({OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.Value});
329+
Results.push_back(
330+
{OverloadInfo.BaseTy, IsDynamicRef, OverloadInfo.getValue()});
330331
}
331332

332333
public:

lib/IDE/SelectedOverloadInfo.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,9 @@ swift::ide::getSelectedOverloadInfo(const Solution &S,
4040
Result.BaseTy = nullptr;
4141
}
4242

43-
Result.Value = SelectedOverload->choice.getDeclOrNull();
43+
if (auto ReferencedDecl = SelectedOverload->choice.getDeclOrNull()) {
44+
Result.ValueRef = S.resolveConcreteDeclRef(ReferencedDecl, CalleeLocator);
45+
}
4446
Result.ValueTy =
4547
S.simplifyTypeForCodeCompletion(SelectedOverload->adjustedOpenedType);
4648

test/IDE/complete_call_arg.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1303,3 +1303,10 @@ func testMacroArg() {
13031303
// MACRO_CALL_ARG-DAG: Literal[Boolean]/None: true[#Bool#]; name=true
13041304
// MACRO_CALL_ARG: End completions
13051305
}
1306+
1307+
func testParameterPack(intArray: [Int]) {
1308+
func myZip<each S>(_ sequence: repeat each S, otherParam: Int) where repeat each S: Sequence {}
1309+
myZip([1], #^PARAMETER_PACK_ARG^#)
1310+
// PARAMETER_PACK_ARG: Pattern/Local/Flair[ArgLabels]: {#otherParam: Int#}[#Int#]; name=otherParam:
1311+
// PARAMETER_PACK_ARG: Decl[LocalVar]/Local/TypeRelation[Convertible]: intArray[#[Int]#]; name=intArray
1312+
}

0 commit comments

Comments
 (0)