Skip to content

Commit f0078d6

Browse files
committed
[Diagnostics] Refactor argument matching when dealing with a single candidate
1 parent d89c232 commit f0078d6

File tree

1 file changed

+134
-131
lines changed

1 file changed

+134
-131
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 134 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -4671,152 +4671,155 @@ static bool diagnoseSingleCandidateFailures(CalleeCandidateInfo &CCI,
46714671
CCI.closeness != CC_ArgumentCountMismatch)
46724672
return false;
46734673

4674-
SmallVector<Identifier, 4> correctNames;
4675-
unsigned OOOArgIdx = ~0U, OOOPrevArgIdx = ~0U;
4676-
unsigned extraArgIdx = ~0U, missingParamIdx = ~0U;
4677-
46784674
// If we have a single candidate that failed to match the argument list,
46794675
// attempt to use matchCallArguments to diagnose the problem.
4680-
struct OurListener : public MatchCallArgumentListener {
4681-
SmallVectorImpl<Identifier> &correctNames;
4682-
unsigned &OOOArgIdx, &OOOPrevArgIdx;
4683-
unsigned &extraArgIdx, &missingParamIdx;
4676+
class ArgumentDiagnostic : public MatchCallArgumentListener {
4677+
TypeChecker &TC;
4678+
Expr *ArgExpr;
4679+
llvm::SmallVectorImpl<CallArgParam> &Parameters;
4680+
llvm::SmallVectorImpl<CallArgParam> &Arguments;
4681+
4682+
CalleeCandidateInfo CandidateInfo;
4683+
4684+
// Indicates if problem was been found and diagnostic was emitted.
4685+
bool Diagnosed = false;
4686+
// Indicates if functions we are trying to call is a subscript.
4687+
bool IsSubscript;
4688+
4689+
// Stores parameter bindings determined by call to matchCallArguments.
4690+
SmallVector<ParamBinding, 4> Bindings;
46844691

46854692
public:
4686-
OurListener(SmallVectorImpl<Identifier> &correctNames,
4687-
unsigned &OOOArgIdx, unsigned &OOOPrevArgIdx,
4688-
unsigned &extraArgIdx, unsigned &missingParamIdx)
4689-
: correctNames(correctNames),
4690-
OOOArgIdx(OOOArgIdx), OOOPrevArgIdx(OOOPrevArgIdx),
4691-
extraArgIdx(extraArgIdx), missingParamIdx(missingParamIdx) {}
4692-
void extraArgument(unsigned argIdx) override {
4693-
extraArgIdx = argIdx;
4694-
}
4695-
void missingArgument(unsigned paramIdx) override {
4696-
missingParamIdx = paramIdx;
4697-
}
4698-
void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override{
4699-
OOOArgIdx = argIdx;
4700-
OOOPrevArgIdx = prevArgIdx;
4693+
ArgumentDiagnostic(Expr *argExpr,
4694+
llvm::SmallVectorImpl<CallArgParam> &params,
4695+
llvm::SmallVectorImpl<CallArgParam> &args,
4696+
CalleeCandidateInfo &CCI, bool isSubscript)
4697+
: TC(CCI.CS->TC), ArgExpr(argExpr), Parameters(params), Arguments(args),
4698+
CandidateInfo(CCI), IsSubscript(isSubscript) {}
4699+
4700+
void extraArgument(unsigned extraArgIdx) override {
4701+
auto name = Arguments[extraArgIdx].Label;
4702+
Expr *arg = ArgExpr;
4703+
4704+
auto tuple = dyn_cast<TupleExpr>(ArgExpr);
4705+
if (tuple)
4706+
arg = tuple->getElement(extraArgIdx);
4707+
4708+
auto loc = arg->getLoc();
4709+
if (tuple && extraArgIdx == tuple->getNumElements() - 1 &&
4710+
tuple->hasTrailingClosure())
4711+
TC.diagnose(loc, diag::extra_trailing_closure_in_call)
4712+
.highlight(arg->getSourceRange());
4713+
else if (Parameters.empty())
4714+
TC.diagnose(loc, diag::extra_argument_to_nullary_call)
4715+
.highlight(ArgExpr->getSourceRange());
4716+
else if (name.empty())
4717+
TC.diagnose(loc, diag::extra_argument_positional)
4718+
.highlight(arg->getSourceRange());
4719+
else
4720+
TC.diagnose(loc, diag::extra_argument_named, name)
4721+
.highlight(arg->getSourceRange());
4722+
4723+
Diagnosed = true;
47014724
}
4702-
bool relabelArguments(ArrayRef<Identifier> newNames) override {
4703-
correctNames.append(newNames.begin(), newNames.end());
4704-
return true;
4725+
4726+
void missingArgument(unsigned missingParamIdx) override {
4727+
Identifier name = Parameters[missingParamIdx].Label;
4728+
auto loc = ArgExpr->getStartLoc();
4729+
if (name.empty())
4730+
TC.diagnose(loc, diag::missing_argument_positional,
4731+
missingParamIdx + 1);
4732+
else
4733+
TC.diagnose(loc, diag::missing_argument_named, name);
4734+
4735+
auto candidate = CandidateInfo[0];
4736+
if (candidate.getDecl())
4737+
TC.diagnose(candidate.getDecl(), diag::decl_declared_here,
4738+
candidate.getDecl()->getFullName());
4739+
4740+
Diagnosed = true;
47054741
}
4706-
} listener(correctNames, OOOArgIdx, OOOPrevArgIdx,
4707-
extraArgIdx, missingParamIdx);
47084742

4709-
// Use matchCallArguments to determine how close the argument list is (in
4710-
// shape) to the specified candidates parameters. This ignores the
4711-
// concrete types of the arguments, looking only at the argument labels.
4712-
SmallVector<ParamBinding, 4> paramBindings;
4713-
if (!matchCallArguments(args, params, CCI.hasTrailingClosure,
4714-
/*allowFixes:*/true, listener, paramBindings))
4715-
return false;
4743+
void outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override {
4744+
auto tuple = cast<TupleExpr>(ArgExpr);
4745+
Identifier first = tuple->getElementName(argIdx);
4746+
Identifier second = tuple->getElementName(prevArgIdx);
4747+
4748+
// Build a mapping from arguments to parameters.
4749+
SmallVector<unsigned, 4> argBindings(tuple->getNumElements());
4750+
for (unsigned paramIdx = 0; paramIdx != Bindings.size(); ++paramIdx) {
4751+
for (auto argIdx : Bindings[paramIdx])
4752+
argBindings[argIdx] = paramIdx;
4753+
}
4754+
4755+
auto firstRange = tuple->getElement(argIdx)->getSourceRange();
4756+
if (!first.empty()) {
4757+
firstRange.Start = tuple->getElementNameLoc(argIdx);
4758+
}
4759+
unsigned OOOParamIdx = argBindings[argIdx];
4760+
if (Bindings[OOOParamIdx].size() > 1) {
4761+
firstRange.End =
4762+
tuple->getElement(Bindings[OOOParamIdx].back())->getEndLoc();
4763+
}
47164764

4765+
auto secondRange = tuple->getElement(prevArgIdx)->getSourceRange();
4766+
if (!second.empty()) {
4767+
secondRange.Start = tuple->getElementNameLoc(prevArgIdx);
4768+
}
4769+
unsigned OOOPrevParamIdx = argBindings[prevArgIdx];
4770+
if (Bindings[OOOPrevParamIdx].size() > 1) {
4771+
secondRange.End =
4772+
tuple->getElement(Bindings[OOOPrevParamIdx].back())->getEndLoc();
4773+
}
47174774

4718-
// If we are missing a parameter, diagnose that.
4719-
if (missingParamIdx != ~0U) {
4720-
Identifier name = params[missingParamIdx].Label;
4721-
auto loc = argExpr->getStartLoc();
4722-
if (name.empty())
4723-
TC.diagnose(loc, diag::missing_argument_positional,
4724-
missingParamIdx+1);
4725-
else
4726-
TC.diagnose(loc, diag::missing_argument_named, name);
4727-
4728-
if (candidate.getDecl())
4729-
TC.diagnose(candidate.getDecl(), diag::decl_declared_here,
4730-
candidate.getDecl()->getFullName());
4731-
return true;
4732-
}
4775+
SourceLoc diagLoc = firstRange.Start;
4776+
4777+
if (first.empty() && second.empty()) {
4778+
TC.diagnose(diagLoc, diag::argument_out_of_order_unnamed_unnamed,
4779+
argIdx + 1, prevArgIdx + 1)
4780+
.fixItExchange(firstRange, secondRange);
4781+
} else if (first.empty() && !second.empty()) {
4782+
TC.diagnose(diagLoc, diag::argument_out_of_order_unnamed_named,
4783+
argIdx + 1, second)
4784+
.fixItExchange(firstRange, secondRange);
4785+
} else if (!first.empty() && second.empty()) {
4786+
TC.diagnose(diagLoc, diag::argument_out_of_order_named_unnamed, first,
4787+
prevArgIdx + 1)
4788+
.fixItExchange(firstRange, secondRange);
4789+
} else {
4790+
TC.diagnose(diagLoc, diag::argument_out_of_order_named_named, first,
4791+
second)
4792+
.fixItExchange(firstRange, secondRange);
4793+
}
47334794

4734-
if (extraArgIdx != ~0U) {
4735-
auto name = args[extraArgIdx].Label;
4736-
Expr *arg = argExpr;
4737-
auto tuple = dyn_cast<TupleExpr>(argExpr);
4738-
if (tuple)
4739-
arg = tuple->getElement(extraArgIdx);
4740-
auto loc = arg->getLoc();
4741-
if (tuple && extraArgIdx == tuple->getNumElements()-1 &&
4742-
tuple->hasTrailingClosure())
4743-
TC.diagnose(loc, diag::extra_trailing_closure_in_call)
4744-
.highlight(arg->getSourceRange());
4745-
else if (params.empty())
4746-
TC.diagnose(loc, diag::extra_argument_to_nullary_call)
4747-
.highlight(argExpr->getSourceRange());
4748-
else if (name.empty())
4749-
TC.diagnose(loc, diag::extra_argument_positional)
4750-
.highlight(arg->getSourceRange());
4751-
else
4752-
TC.diagnose(loc, diag::extra_argument_named, name)
4753-
.highlight(arg->getSourceRange());
4754-
return true;
4755-
}
4795+
Diagnosed = true;
4796+
}
47564797

4757-
// If this is an argument label mismatch, then diagnose that error now.
4758-
if (!correctNames.empty() &&
4759-
diagnoseArgumentLabelError(TC, argExpr, correctNames,
4760-
isa<SubscriptExpr>(fnExpr)))
4761-
return true;
4798+
bool relabelArguments(ArrayRef<Identifier> newNames) override {
4799+
assert (!newNames.empty() && "No arguments were re-labeled");
47624800

4763-
// If we have an out-of-order argument, diagnose it as such.
4764-
if (OOOArgIdx != ~0U && isa<TupleExpr>(argExpr)) {
4765-
auto tuple = cast<TupleExpr>(argExpr);
4766-
Identifier first = tuple->getElementName(OOOArgIdx);
4767-
Identifier second = tuple->getElementName(OOOPrevArgIdx);
4768-
4769-
// Build a mapping from arguments to parameters.
4770-
SmallVector<unsigned, 4> argBindings(tuple->getNumElements());
4771-
for (unsigned paramIdx = 0; paramIdx != paramBindings.size(); ++paramIdx) {
4772-
for (auto argIdx : paramBindings[paramIdx])
4773-
argBindings[argIdx] = paramIdx;
4774-
}
4775-
4776-
auto firstRange = tuple->getElement(OOOArgIdx)->getSourceRange();
4777-
if (!first.empty()) {
4778-
firstRange.Start = tuple->getElementNameLoc(OOOArgIdx);
4779-
}
4780-
unsigned OOOParamIdx = argBindings[OOOArgIdx];
4781-
if (paramBindings[OOOParamIdx].size() > 1) {
4782-
firstRange.End =
4783-
tuple->getElement(paramBindings[OOOParamIdx].back())->getEndLoc();
4784-
}
4785-
4786-
auto secondRange = tuple->getElement(OOOPrevArgIdx)->getSourceRange();
4787-
if (!second.empty()) {
4788-
secondRange.Start = tuple->getElementNameLoc(OOOPrevArgIdx);
4789-
}
4790-
unsigned OOOPrevParamIdx = argBindings[OOOPrevArgIdx];
4791-
if (paramBindings[OOOPrevParamIdx].size() > 1) {
4792-
secondRange.End =
4793-
tuple->getElement(paramBindings[OOOPrevParamIdx].back())->getEndLoc();
4794-
}
4795-
4796-
SourceLoc diagLoc = firstRange.Start;
4797-
4798-
if (first.empty() && second.empty()) {
4799-
TC.diagnose(diagLoc, diag::argument_out_of_order_unnamed_unnamed,
4800-
OOOArgIdx + 1, OOOPrevArgIdx + 1)
4801-
.fixItExchange(firstRange, secondRange);
4802-
} else if (first.empty() && !second.empty()) {
4803-
TC.diagnose(diagLoc, diag::argument_out_of_order_unnamed_named,
4804-
OOOArgIdx + 1, second)
4805-
.fixItExchange(firstRange, secondRange);
4806-
} else if (!first.empty() && second.empty()) {
4807-
TC.diagnose(diagLoc, diag::argument_out_of_order_named_unnamed,
4808-
first, OOOPrevArgIdx + 1)
4809-
.fixItExchange(firstRange, secondRange);
4810-
} else {
4811-
TC.diagnose(diagLoc, diag::argument_out_of_order_named_named,
4812-
first, second)
4813-
.fixItExchange(firstRange, secondRange);
4801+
// Let's diagnose labeling problem but only related to corrected ones.
4802+
if (diagnoseArgumentLabelError(TC, ArgExpr, newNames, IsSubscript))
4803+
Diagnosed = true;
4804+
4805+
return true;
48144806
}
48154807

4816-
return true;
4817-
}
4808+
bool diagnose() {
4809+
// Use matchCallArguments to determine how close the argument list is (in
4810+
// shape) to the specified candidates parameters. This ignores the
4811+
// concrete types of the arguments, looking only at the argument labels.
4812+
matchCallArguments(Arguments, Parameters,
4813+
CandidateInfo.hasTrailingClosure,
4814+
/*allowFixes:*/ true, *this, Bindings);
48184815

4819-
return false;
4816+
return Diagnosed;
4817+
}
4818+
};
4819+
4820+
return ArgumentDiagnostic(argExpr, params, args, CCI,
4821+
isa<SubscriptExpr>(fnExpr))
4822+
.diagnose();
48204823
}
48214824

48224825
/// If the candidate set has been narrowed down to a specific structural

0 commit comments

Comments
 (0)