Skip to content

Commit 15f5222

Browse files
author
Nathan Hawes
committed
[CodeCompletion][Sema] Allow missing args when solving if the completion location indicates the user may intend to write them later.
func foo(a: Int, b: Int) {} func foo(a: String) {} // Int and String should both be valid, despite the missing argument for the // first overload since the second arg may just have not been written yet. foo(a: <complete here> func bar(a: (Int) -> ()) {} func bar(a: (String, Int) -> ()) {} // $0 being of type String should be valid, rather than just Int, since $1 may // just have not been written yet. bar { $0.<complete here> }
1 parent abd4608 commit 15f5222

File tree

9 files changed

+306
-44
lines changed

9 files changed

+306
-44
lines changed

include/swift/Sema/CSFix.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,18 +1178,21 @@ class AllowClosureParamDestructuring final : public ConstraintFix {
11781178
ConstraintLocator *locator);
11791179
};
11801180

1181+
struct SynthesizedArg {
1182+
unsigned paramIdx;
1183+
AnyFunctionType::Param param;
1184+
};
1185+
11811186
class AddMissingArguments final
11821187
: public ConstraintFix,
11831188
private llvm::TrailingObjects<
1184-
AddMissingArguments, std::pair<unsigned, AnyFunctionType::Param>> {
1189+
AddMissingArguments, SynthesizedArg> {
11851190
friend TrailingObjects;
11861191

1187-
using SynthesizedParam = std::pair<unsigned, AnyFunctionType::Param>;
1188-
11891192
unsigned NumSynthesized;
11901193

11911194
AddMissingArguments(ConstraintSystem &cs,
1192-
ArrayRef<SynthesizedParam> synthesizedArgs,
1195+
ArrayRef<SynthesizedArg> synthesizedArgs,
11931196
ConstraintLocator *locator)
11941197
: ConstraintFix(cs, FixKind::AddMissingArguments, locator),
11951198
NumSynthesized(synthesizedArgs.size()) {
@@ -1200,8 +1203,8 @@ class AddMissingArguments final
12001203
public:
12011204
std::string getName() const override { return "synthesize missing argument(s)"; }
12021205

1203-
ArrayRef<SynthesizedParam> getSynthesizedArguments() const {
1204-
return {getTrailingObjects<SynthesizedParam>(), NumSynthesized};
1206+
ArrayRef<SynthesizedArg> getSynthesizedArguments() const {
1207+
return {getTrailingObjects<SynthesizedArg>(), NumSynthesized};
12051208
}
12061209

12071210
bool diagnose(const Solution &solution, bool asNote = false) const override;
@@ -1211,12 +1214,16 @@ class AddMissingArguments final
12111214
}
12121215

12131216
static AddMissingArguments *create(ConstraintSystem &cs,
1214-
ArrayRef<SynthesizedParam> synthesizedArgs,
1217+
ArrayRef<SynthesizedArg> synthesizedArgs,
12151218
ConstraintLocator *locator);
12161219

1220+
static bool classof(const ConstraintFix *fix) {
1221+
return fix->getKind() == FixKind::AddMissingArguments;
1222+
}
1223+
12171224
private:
1218-
MutableArrayRef<SynthesizedParam> getSynthesizedArgumentsBuf() {
1219-
return {getTrailingObjects<SynthesizedParam>(), NumSynthesized};
1225+
MutableArrayRef<SynthesizedArg> getSynthesizedArgumentsBuf() {
1226+
return {getTrailingObjects<SynthesizedArg>(), NumSynthesized};
12201227
}
12211228
};
12221229

include/swift/Sema/ConstraintSystem.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5393,7 +5393,10 @@ class MatchCallArgumentListener {
53935393
/// indices.
53945394
///
53955395
/// \param paramIdx The index of the parameter that is missing an argument.
5396-
virtual Optional<unsigned> missingArgument(unsigned paramIdx);
5396+
/// \param argInsertIdx The index in the argument list where this argument was
5397+
/// expected.
5398+
virtual Optional<unsigned> missingArgument(unsigned paramIdx,
5399+
unsigned argInsertIdx);
53975400

53985401
/// Indicate that there was no label given when one was expected by parameter.
53995402
///

lib/Sema/CSDiagnostics.cpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4097,9 +4097,9 @@ bool MissingArgumentsFailure::diagnoseAsError() {
40974097

40984098
interleave(
40994099
SynthesizedArgs,
4100-
[&](const std::pair<unsigned, AnyFunctionType::Param> &e) {
4101-
const auto paramIdx = e.first;
4102-
const auto &arg = e.second;
4100+
[&](const SynthesizedArg &e) {
4101+
const auto paramIdx = e.paramIdx;
4102+
const auto &arg = e.param;
41034103

41044104
if (arg.hasLabel()) {
41054105
arguments << "'" << arg.getLabel().str() << "'";
@@ -4126,8 +4126,8 @@ bool MissingArgumentsFailure::diagnoseAsError() {
41264126
llvm::raw_svector_ostream fixIt(scratch);
41274127
interleave(
41284128
SynthesizedArgs,
4129-
[&](const std::pair<unsigned, AnyFunctionType::Param> &arg) {
4130-
forFixIt(fixIt, arg.second);
4129+
[&](const SynthesizedArg &arg) {
4130+
forFixIt(fixIt, arg.param);
41314131
},
41324132
[&] { fixIt << ", "; });
41334133

@@ -4176,8 +4176,8 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
41764176
return false;
41774177

41784178
const auto &argument = SynthesizedArgs.front();
4179-
auto position = argument.first;
4180-
auto label = argument.second.getLabel();
4179+
auto position = argument.paramIdx;
4180+
auto label = argument.param.getLabel();
41814181

41824182
Expr *fnExpr = nullptr;
41834183
Expr *argExpr = nullptr;
@@ -4192,7 +4192,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
41924192
}
41934193

41944194
// Will the parameter accept a trailing closure?
4195-
Type paramType = resolveType(argument.second.getPlainType());
4195+
Type paramType = resolveType(argument.param.getPlainType());
41964196
bool paramAcceptsTrailingClosure = paramType
41974197
->lookThroughAllOptionalTypes()->is<AnyFunctionType>();
41984198

@@ -4208,7 +4208,7 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
42084208
else if (position != 0)
42094209
insertText << ", ";
42104210

4211-
forFixIt(insertText, argument.second);
4211+
forFixIt(insertText, argument.param);
42124212

42134213
if (position == 0 && numArgs > 0 &&
42144214
(!firstTrailingClosure || position < *firstTrailingClosure))
@@ -6036,7 +6036,7 @@ bool ArgumentMismatchFailure::diagnoseMisplacedMissingArgument() const {
60366036
auto anchor = getRawAnchor();
60376037

60386038
MissingArgumentsFailure failure(
6039-
solution, {std::make_pair(0, param)},
6039+
solution, {SynthesizedArg{0, param}},
60406040
getConstraintLocator(anchor, ConstraintLocator::ApplyArgument));
60416041

60426042
return failure.diagnoseSingleMissingArgument();

lib/Sema/CSDiagnostics.h

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1271,13 +1271,11 @@ class ImplicitInitOnNonConstMetatypeFailure final
12711271
};
12721272

12731273
class MissingArgumentsFailure final : public FailureDiagnostic {
1274-
using SynthesizedParam = std::pair<unsigned, AnyFunctionType::Param>;
1275-
1276-
SmallVector<SynthesizedParam, 4> SynthesizedArgs;
1274+
SmallVector<SynthesizedArg, 4> SynthesizedArgs;
12771275

12781276
public:
12791277
MissingArgumentsFailure(const Solution &solution,
1280-
ArrayRef<SynthesizedParam> synthesizedArgs,
1278+
ArrayRef<SynthesizedArg> synthesizedArgs,
12811279
ConstraintLocator *locator)
12821280
: FailureDiagnostic(solution, locator),
12831281
SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) {

lib/Sema/CSFix.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -748,9 +748,9 @@ bool AddMissingArguments::diagnose(const Solution &solution,
748748

749749
AddMissingArguments *
750750
AddMissingArguments::create(ConstraintSystem &cs,
751-
ArrayRef<SynthesizedParam> synthesizedArgs,
751+
ArrayRef<SynthesizedArg> synthesizedArgs,
752752
ConstraintLocator *locator) {
753-
unsigned size = totalSizeToAlloc<SynthesizedParam>(synthesizedArgs.size());
753+
unsigned size = totalSizeToAlloc<SynthesizedArg>(synthesizedArgs.size());
754754
void *mem = cs.getAllocator().Allocate(size, alignof(AddMissingArguments));
755755
return new (mem) AddMissingArguments(cs, synthesizedArgs, locator);
756756
}

lib/Sema/CSGen.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -589,6 +589,13 @@ namespace {
589589

590590
// Determine whether the given declaration is favored.
591591
auto isFavoredDecl = [&](ValueDecl *value, Type type) -> bool {
592+
// We want to consider all options for calls that might contain the code
593+
// completion location, as missing arguments after the completion
594+
// location are valid (since it might be that they just haven't been
595+
// written yet).
596+
if (CS.isForCodeCompletion())
597+
return false;
598+
592599
if (!type->is<AnyFunctionType>())
593600
return false;
594601

lib/Sema/CSSimplify.cpp

Lines changed: 76 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ MatchCallArgumentListener::~MatchCallArgumentListener() { }
4040
bool MatchCallArgumentListener::extraArgument(unsigned argIdx) { return true; }
4141

4242
Optional<unsigned>
43-
MatchCallArgumentListener::missingArgument(unsigned paramIdx) {
43+
MatchCallArgumentListener::missingArgument(unsigned paramIdx,
44+
unsigned argInsertIdx) {
4445
return None;
4546
}
4647

@@ -701,11 +702,14 @@ static bool matchCallArgumentsImpl(
701702
}
702703

703704
// If we have any unfulfilled parameters, check them now.
705+
Optional<unsigned> prevArgIdx;
704706
if (haveUnfulfilledParams) {
705707
for (auto paramIdx : indices(params)) {
706708
// If we have a binding for this parameter, we're done.
707-
if (!parameterBindings[paramIdx].empty())
709+
if (!parameterBindings[paramIdx].empty()) {
710+
prevArgIdx = parameterBindings[paramIdx].back();
708711
continue;
712+
}
709713

710714
const auto &param = params[paramIdx];
711715

@@ -717,7 +721,8 @@ static bool matchCallArgumentsImpl(
717721
if (paramInfo.hasDefaultArgument(paramIdx))
718722
continue;
719723

720-
if (auto newArgIdx = listener.missingArgument(paramIdx)) {
724+
unsigned argInsertIdx = prevArgIdx ? *prevArgIdx + 1 : 0;
725+
if (auto newArgIdx = listener.missingArgument(paramIdx, argInsertIdx)) {
721726
parameterBindings[paramIdx].push_back(*newArgIdx);
722727
continue;
723728
}
@@ -980,14 +985,47 @@ constraints::matchCallArguments(
980985
};
981986
}
982987

988+
static Optional<unsigned>
989+
getCompletionArgIndex(ASTNode anchor, SourceManager &SM) {
990+
Expr *arg = nullptr;
991+
if (auto *CE = getAsExpr<CallExpr>(anchor))
992+
arg = CE->getArg();
993+
if (auto *SE = getAsExpr<SubscriptExpr>(anchor))
994+
arg = SE->getIndex();
995+
if (auto *OLE = getAsExpr<ObjectLiteralExpr>(anchor))
996+
arg = OLE->getArg();
997+
998+
if (!arg)
999+
return None;
1000+
1001+
auto containsCompletion = [&](Expr *elem) {
1002+
if (!elem)
1003+
return false;
1004+
SourceRange range = elem->getSourceRange();
1005+
return range.isValid() && SM.rangeContainsCodeCompletionLoc(range);
1006+
};
1007+
1008+
if (auto *TE = dyn_cast<TupleExpr>(arg)) {
1009+
auto elems = TE->getElements();
1010+
auto idx = llvm::find_if(elems, containsCompletion);
1011+
if (idx != elems.end())
1012+
return std::distance(elems.begin(), idx);
1013+
} else if (auto *PE = dyn_cast<ParenExpr>(arg)) {
1014+
if (containsCompletion(PE->getSubExpr()))
1015+
return 0;
1016+
}
1017+
return None;
1018+
}
1019+
9831020
class ArgumentFailureTracker : public MatchCallArgumentListener {
9841021
ConstraintSystem &CS;
9851022
SmallVectorImpl<AnyFunctionType::Param> &Arguments;
9861023
ArrayRef<AnyFunctionType::Param> Parameters;
9871024
ConstraintLocatorBuilder Locator;
9881025

989-
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> MissingArguments;
1026+
SmallVector<SynthesizedArg, 4> MissingArguments;
9901027
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> ExtraArguments;
1028+
Optional<unsigned> CompletionArgIdx;
9911029

9921030
public:
9931031
ArgumentFailureTracker(ConstraintSystem &cs,
@@ -1006,7 +1044,8 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
10061044
}
10071045
}
10081046

1009-
Optional<unsigned> missingArgument(unsigned paramIdx) override {
1047+
Optional<unsigned> missingArgument(unsigned paramIdx,
1048+
unsigned argInsertIdx) override {
10101049
if (!CS.shouldAttemptFixes())
10111050
return None;
10121051

@@ -1023,10 +1062,22 @@ class ArgumentFailureTracker : public MatchCallArgumentListener {
10231062
TVO_CanBindToNoEscape | TVO_CanBindToHole);
10241063

10251064
auto synthesizedArg = param.withType(argType);
1026-
1027-
MissingArguments.push_back(std::make_pair(paramIdx, synthesizedArg));
10281065
Arguments.push_back(synthesizedArg);
10291066

1067+
if (CS.isForCodeCompletion()) {
1068+
// When solving for code completion, if any argument contains the
1069+
// completion location, later arguments shouldn't be considered missing
1070+
// (causing the solution to have a worse score) as the user just hasn't
1071+
// written them yet. Early exit to avoid recording them in this case.
1072+
SourceManager &SM = CS.getASTContext().SourceMgr;
1073+
if (!CompletionArgIdx)
1074+
CompletionArgIdx = getCompletionArgIndex(Locator.getAnchor(), SM);
1075+
if (CompletionArgIdx && *CompletionArgIdx < argInsertIdx)
1076+
return newArgIdx;
1077+
}
1078+
1079+
MissingArguments.push_back(SynthesizedArg{paramIdx, synthesizedArg});
1080+
10301081
return newArgIdx;
10311082
}
10321083

@@ -1190,12 +1241,11 @@ ConstraintSystem::TypeMatchResult constraints::matchCallArguments(
11901241
argsWithLabels.pop_back();
11911242
// Let's make sure that labels associated with tuple elements
11921243
// line up with what is expected by argument list.
1193-
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4>
1194-
synthesizedArgs;
1244+
SmallVector<SynthesizedArg, 4> synthesizedArgs;
11951245
for (unsigned i = 0, n = argTuple->getNumElements(); i != n; ++i) {
11961246
const auto &elt = argTuple->getElement(i);
11971247
AnyFunctionType::Param argument(elt.getType(), elt.getName());
1198-
synthesizedArgs.push_back(std::make_pair(i, argument));
1248+
synthesizedArgs.push_back(SynthesizedArg{i, argument});
11991249
argsWithLabels.push_back(argument);
12001250
}
12011251

@@ -1771,15 +1821,27 @@ static bool fixMissingArguments(ConstraintSystem &cs, ASTNode anchor,
17711821
cs.createTypeVariable(argLoc, TVO_CanBindToNoEscape)));
17721822
}
17731823

1774-
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> synthesizedArgs;
1824+
SmallVector<SynthesizedArg, 4> synthesizedArgs;
17751825
synthesizedArgs.reserve(numMissing);
17761826
for (unsigned i = args.size() - numMissing, n = args.size(); i != n; ++i) {
1777-
synthesizedArgs.push_back(std::make_pair(i, args[i]));
1827+
synthesizedArgs.push_back(SynthesizedArg{i, args[i]});
1828+
}
1829+
1830+
// Treat missing anonymous arguments as valid in closures containing the
1831+
// code completion location, since they may have just not been written yet.
1832+
if (cs.isForCodeCompletion()) {
1833+
if (auto *closure = getAsExpr<ClosureExpr>(anchor)) {
1834+
SourceManager &SM = closure->getASTContext().SourceMgr;
1835+
SourceRange range = closure->getSourceRange();
1836+
if (range.isValid() && SM.rangeContainsCodeCompletionLoc(range) &&
1837+
(closure->hasAnonymousClosureVars() ||
1838+
(args.empty() && closure->getInLoc().isInvalid())))
1839+
return false;
1840+
}
17781841
}
17791842

17801843
auto *fix = AddMissingArguments::create(cs, synthesizedArgs,
17811844
cs.getConstraintLocator(locator));
1782-
17831845
if (cs.recordFix(fix))
17841846
return true;
17851847

@@ -3966,7 +4028,7 @@ bool ConstraintSystem::repairFailures(
39664028
// to diagnose this as a missing argument which can't be ignored.
39674029
if (arg != getTypeVariables().end()) {
39684030
conversionsOrFixes.push_back(AddMissingArguments::create(
3969-
*this, {std::make_pair(0, AnyFunctionType::Param(*arg))},
4031+
*this, {SynthesizedArg{0, AnyFunctionType::Param(*arg)}},
39704032
getConstraintLocator(anchor, path)));
39714033
break;
39724034
}

0 commit comments

Comments
 (0)