Skip to content

Commit bd0d3ac

Browse files
authored
Merge pull request #35136 from nathawes/dont-allow-missing-args-if-trailing-and-not-function-type
2 parents 13f8f73 + 44e09bc commit bd0d3ac

File tree

4 files changed

+135
-40
lines changed

4 files changed

+135
-40
lines changed

include/swift/Sema/ConstraintLocator.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -511,12 +511,14 @@ class LocatorPathElt::ApplyArgToParam final : public StoredIntegerElement<3> {
511511
}
512512
};
513513

514-
class LocatorPathElt::SynthesizedArgument final : public StoredIntegerElement<1> {
514+
class LocatorPathElt::SynthesizedArgument final : public StoredIntegerElement<2> {
515515
public:
516-
SynthesizedArgument(unsigned index)
517-
: StoredIntegerElement(ConstraintLocator::SynthesizedArgument, index) {}
516+
SynthesizedArgument(unsigned index, bool afterCodeCompletionLoc = false)
517+
: StoredIntegerElement(ConstraintLocator::SynthesizedArgument, index,
518+
afterCodeCompletionLoc) {}
518519

519-
unsigned getIndex() const { return getValue(); }
520+
unsigned getIndex() const { return getValue<0>(); }
521+
bool isAfterCodeCompletionLoc() const { return getValue<1>(); }
520522

521523
static bool classof(const LocatorPathElt *elt) {
522524
return elt->getKind() == ConstraintLocator::SynthesizedArgument;

lib/Sema/CSBindings.cpp

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,20 +1697,34 @@ bool TypeVariableBinding::attempt(ConstraintSystem &cs) const {
16971697

16981698
cs.addConstraint(ConstraintKind::Bind, TypeVar, type, srcLocator);
16991699

1700+
auto reportHole = [&]() {
1701+
if (cs.isForCodeCompletion()) {
1702+
// Don't penalize solutions with unresolved generics.
1703+
if (TypeVar->getImpl().getGenericParameter())
1704+
return false;
1705+
// Don't penalize solutions with holes due to missing arguments after the
1706+
// code completion position.
1707+
auto argLoc = srcLocator->findLast<LocatorPathElt::SynthesizedArgument>();
1708+
if (argLoc && argLoc->isAfterCodeCompletionLoc())
1709+
return false;
1710+
}
1711+
// Reflect in the score that this type variable couldn't be
1712+
// resolved and had to be bound to a placeholder "hole" type.
1713+
cs.increaseScore(SK_Hole);
1714+
1715+
if (auto fix = fixForHole(cs)) {
1716+
if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second))
1717+
return true;
1718+
}
1719+
return false;
1720+
};
1721+
17001722
// If this was from a defaultable binding note that.
17011723
if (Binding.isDefaultableBinding()) {
17021724
cs.DefaultedConstraints.push_back(srcLocator);
17031725

1704-
if (type->isPlaceholder()) {
1705-
// Reflect in the score that this type variable couldn't be
1706-
// resolved and had to be bound to a placeholder "hole" type.
1707-
cs.increaseScore(SK_Hole);
1708-
1709-
if (auto fix = fixForHole(cs)) {
1710-
if (cs.recordFix(/*fix=*/fix->first, /*impact=*/fix->second))
1711-
return true;
1712-
}
1713-
}
1726+
if (type->isPlaceholder() && reportHole())
1727+
return true;
17141728
}
17151729

17161730
return !cs.failedConstraint && !cs.simplify();

lib/Sema/CSSimplify.cpp

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -986,8 +986,35 @@ constraints::matchCallArguments(
986986
};
987987
}
988988

989-
static Optional<unsigned>
990-
getCompletionArgIndex(ASTNode anchor, ConstraintSystem &CS) {
989+
struct CompletionArgInfo {
990+
unsigned completionIdx;
991+
Optional<unsigned> firstTrailingIdx;
992+
993+
bool isAllowableMissingArg(unsigned argInsertIdx,
994+
AnyFunctionType::Param param) {
995+
// If the argument is before or at the index of the argument containing the
996+
// completion, the user would likely have already written it if they
997+
// intended this overload.
998+
if (completionIdx >= argInsertIdx)
999+
return false;
1000+
1001+
// If the argument is after the first trailing closure, the user can only
1002+
// continue on to write more trailing arguments, so only allow this overload
1003+
// if the missing argument is of function type.
1004+
if (firstTrailingIdx && argInsertIdx > *firstTrailingIdx) {
1005+
if (param.isInOut())
1006+
return false;
1007+
1008+
Type expectedTy = param.getPlainType()->lookThroughAllOptionalTypes();
1009+
return expectedTy->is<FunctionType>() || expectedTy->isAny() ||
1010+
expectedTy->isTypeVariableOrMember();
1011+
}
1012+
return true;
1013+
}
1014+
};
1015+
1016+
static Optional<CompletionArgInfo>
1017+
getCompletionArgInfo(ASTNode anchor, ConstraintSystem &CS) {
9911018
Expr *arg = nullptr;
9921019
if (auto *CE = getAsExpr<CallExpr>(anchor))
9931020
arg = CE->getArg();
@@ -999,16 +1026,15 @@ getCompletionArgIndex(ASTNode anchor, ConstraintSystem &CS) {
9991026
if (!arg)
10001027
return None;
10011028

1002-
if (auto *TE = dyn_cast<TupleExpr>(arg)) {
1003-
auto elems = TE->getElements();
1004-
auto idx = llvm::find_if(elems, [&](Expr *elem) {
1005-
return CS.containsCodeCompletionLoc(elem);
1006-
});
1007-
if (idx != elems.end())
1008-
return std::distance(elems.begin(), idx);
1009-
} else if (auto *PE = dyn_cast<ParenExpr>(arg)) {
1029+
auto trailingIdx = arg->getUnlabeledTrailingClosureIndexOfPackedArgument();
1030+
if (auto *PE = dyn_cast<ParenExpr>(arg)) {
10101031
if (CS.containsCodeCompletionLoc(PE->getSubExpr()))
1011-
return 0;
1032+
return CompletionArgInfo{ 0, trailingIdx };
1033+
} else if (auto *TE = dyn_cast<TupleExpr>(arg)) {
1034+
for (unsigned i: indices(TE->getElements())) {
1035+
if (CS.containsCodeCompletionLoc(TE->getElement(i)))
1036+
return CompletionArgInfo{ i, trailingIdx };
1037+
}
10121038
}
10131039
return None;
10141040
}
@@ -1021,7 +1047,7 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
10211047

10221048
SmallVector<SynthesizedArg, 4> MissingArguments;
10231049
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> ExtraArguments;
1024-
Optional<unsigned> CompletionArgIdx;
1050+
Optional<CompletionArgInfo> CompletionArgInfo;
10251051

10261052
public:
10271053
ArgumentFailureTracker(ConstraintSystem &cs,
@@ -1048,10 +1074,19 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
10481074
const auto &param = Parameters[paramIdx];
10491075

10501076
unsigned newArgIdx = Arguments.size();
1077+
1078+
bool isAfterCodeCompletionLoc = false;
1079+
if (CS.isForCodeCompletion()) {
1080+
if (!CompletionArgInfo)
1081+
CompletionArgInfo = getCompletionArgInfo(Locator.getAnchor(), CS);
1082+
isAfterCodeCompletionLoc = CompletionArgInfo &&
1083+
CompletionArgInfo->isAllowableMissingArg(argInsertIdx, param);
1084+
}
1085+
10511086
auto *argLoc = CS.getConstraintLocator(
10521087
Locator, {LocatorPathElt::ApplyArgToParam(newArgIdx, paramIdx,
10531088
param.getParameterFlags()),
1054-
LocatorPathElt::SynthesizedArgument(newArgIdx)});
1089+
LocatorPathElt::SynthesizedArgument(newArgIdx, isAfterCodeCompletionLoc)});
10551090

10561091
auto *argType =
10571092
CS.createTypeVariable(argLoc, TVO_CanBindToInOut | TVO_CanBindToLValue |
@@ -1060,16 +1095,12 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
10601095
auto synthesizedArg = param.withType(argType);
10611096
Arguments.push_back(synthesizedArg);
10621097

1063-
if (CS.isForCodeCompletion()) {
1064-
// When solving for code completion, if any argument contains the
1065-
// completion location, later arguments shouldn't be considered missing
1066-
// (causing the solution to have a worse score) as the user just hasn't
1067-
// written them yet. Early exit to avoid recording them in this case.
1068-
if (!CompletionArgIdx)
1069-
CompletionArgIdx = getCompletionArgIndex(Locator.getAnchor(), CS);
1070-
if (CompletionArgIdx && *CompletionArgIdx < argInsertIdx)
1071-
return newArgIdx;
1072-
}
1098+
// When solving for code completion, if any argument contains the
1099+
// completion location, later arguments shouldn't be considered missing
1100+
// (causing the solution to have a worse score) as the user just hasn't
1101+
// written them yet. Early exit to avoid recording them in this case.
1102+
if (isAfterCodeCompletionLoc)
1103+
return newArgIdx;
10731104

10741105
MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg});
10751106

test/IDE/complete_ambiguous.swift

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@
4848
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=REGULAR_MULTICLOSURE_APPLIED | %FileCheck %s --check-prefix=POINT_MEMBER
4949
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=BEST_SOLUTION_FILTER | %FileCheck %s --check-prefix=BEST_SOLUTION_FILTER
5050
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=BEST_SOLUTION_FILTER2 | %FileCheck %s --check-prefix=BEST_SOLUTION_FILTER
51+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MISSINGARG_INLINE | %FileCheck %s --check-prefix=MISSINGARG_INLINE
52+
// RUN: %swift-ide-test -code-completion -source-filename %s -code-completion-token=MISSINGARG_TRAILING | %FileCheck %s --check-prefix=MISSINGARG_TRAILING
5153

5254

5355
struct A {
@@ -313,9 +315,55 @@ func testMissingArgs() {
313315

314316
test3(after: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFORE^#);
315317
test4(both: Test.#^OVERLOADEDFUNC_MISSINGARG_BEFOREANDAFTER^#)
316-
}
317-
318318

319+
enum Bop { case bop }
320+
enum Bix { case bix }
321+
enum Blu { case blu }
322+
enum Baz { case baz }
323+
enum Boy { case boy }
324+
325+
func trailing(x: Int, _ y: () -> Foo, z: () -> ()) {}
326+
func trailing(x: Int, _ y: () -> Bar, z: (() -> ())?) {}
327+
func trailing(x: Int, _ y: () -> Bop, z: Any) {}
328+
func trailing<T>(x: Int, _ y: () -> Bix, z: T) {}
329+
func trailing<T>(x: Int, _ y: () -> Boy, z: T?) {}
330+
func trailing<T>(x: Int, _ y: () -> Blu, z: [T]?) {}
331+
func trailing(x: Int, _ y: () -> Blu, z: inout Any) {}
332+
func trailing(x: Int, _ y: () -> Baz, z: Int) {}
333+
334+
trailing(x: 2, { .#^MISSINGARG_INLINE^# })
335+
trailing(x: 2) { .#^MISSINGARG_TRAILING^# }
336+
337+
// MISSINGARG_INLINE: Begin completions, 14 items
338+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#Foo#]; name=foo
339+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(self: Foo)
340+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Bar#]; name=bar
341+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bar)
342+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bop[#Bop#]; name=bop
343+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bop)
344+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bix[#Bix#]; name=bix
345+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bix)
346+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: boy[#Boy#]; name=boy
347+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(self: Boy)
348+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: blu[#Blu#]; name=blu
349+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Blu#})[#(into: inout Hasher) -> Void#]; name=hash(self: Blu)
350+
// MISSINGARG_INLINE-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: baz[#Baz#]; name=baz
351+
// MISSINGARG_INLINE-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Baz#})[#(into: inout Hasher) -> Void#]; name=hash(self: Baz)
352+
// MISSINGARG_INLINE: End completions
353+
354+
// MISSINGARG_TRAILING: Begin completions, 10 items
355+
// MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: foo[#Foo#]; name=foo
356+
// MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Foo#})[#(into: inout Hasher) -> Void#]; name=hash(self: Foo)
357+
// MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bar[#Bar#]; name=bar
358+
// MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bar#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bar)
359+
// MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bop[#Bop#]; name=bop
360+
// MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bop#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bop)
361+
// MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: bix[#Bix#]; name=bix
362+
// MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Bix#})[#(into: inout Hasher) -> Void#]; name=hash(self: Bix)
363+
// MISSINGARG_TRAILING-DAG: Decl[EnumElement]/ExprSpecific/TypeRelation[Identical]: boy[#Boy#]; name=boy
364+
// MISSINGARG_TRAILING-DAG: Decl[InstanceMethod]/CurrNominal: hash({#(self): Boy#})[#(into: inout Hasher) -> Void#]; name=hash(self: Boy)
365+
// MISSINGARG_TRAILING: End completions
366+
}
319367

320368
protocol C {
321369
associatedtype Element
@@ -450,7 +498,7 @@ struct Struct123: Equatable {
450498
func testNoBestSolutionFilter() {
451499
let a = Struct123();
452500
let b = [Struct123]().first(where: { $0 == a && 1 + 90 * 5 / 8 == 45 * -10 })?.structMem != .#^BEST_SOLUTION_FILTER^#
453-
let c = min(10.3, 10 / 10.4) < 6 + 5 / (10 - 3) ? true : Optional(a)?.structMem != .#^BEST_SOLUTION_FILTER2^#
501+
let c = min(10.3, 10 / 10.4) < 6 / 7 ? true : Optional(a)?.structMem != .#^BEST_SOLUTION_FILTER2^#
454502
}
455503

456504
// BEST_SOLUTION_FILTER: Begin completions

0 commit comments

Comments
 (0)