Skip to content

Commit cc2c868

Browse files
committed
[Diagnostics] Produce a tailored diagnostic for multiple missing arguments
1 parent a3e26ab commit cc2c868

File tree

3 files changed

+122
-32
lines changed

3 files changed

+122
-32
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1116,6 +1116,8 @@ ERROR(missing_argument_named,none,
11161116
"missing argument for parameter %0 in call", (Identifier))
11171117
ERROR(missing_argument_positional,none,
11181118
"missing argument for parameter #%0 in call", (unsigned))
1119+
ERROR(missing_arguments_in_call,none,
1120+
"missing arguments for parameters %0 in call", (StringRef))
11191121
ERROR(extra_argument_named,none,
11201122
"extra argument %0 in call", (Identifier))
11211123
ERROR(extra_argument_positional,none,

lib/Sema/CSDiagnostics.cpp

Lines changed: 107 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3623,17 +3623,75 @@ bool MissingArgumentsFailure::diagnoseAsError() {
36233623
if (diagnoseInvalidTupleDestructuring())
36243624
return true;
36253625

3626-
if (diagnoseSingleMissingArgument())
3627-
return true;
3626+
if (SynthesizedArgs.size() == 1)
3627+
return diagnoseSingleMissingArgument();
36283628

3629-
return false;
3629+
// At this point we know that this is a situation when
3630+
// there are multiple arguments missing, so let's produce
3631+
// a diagnostic which lists all of them and a fix-it
3632+
// to add arguments at appropriate positions.
3633+
3634+
SmallString<32> diagnostic;
3635+
llvm::raw_svector_ostream arguments(diagnostic);
3636+
3637+
interleave(
3638+
SynthesizedArgs,
3639+
[&](const AnyFunctionType::Param &arg) {
3640+
if (arg.hasLabel()) {
3641+
arguments << "'" << arg.getLabel().str() << "'";
3642+
} else {
3643+
auto *typeVar = arg.getPlainType()->castTo<TypeVariableType>();
3644+
auto *locator = typeVar->getImpl().getLocator();
3645+
auto paramIdx = locator->findLast<LocatorPathElt::ApplyArgToParam>()
3646+
->getParamIdx();
3647+
3648+
arguments << "#" << (paramIdx + 1);
3649+
}
3650+
},
3651+
[&] { arguments << ", "; });
3652+
3653+
auto diag = emitDiagnostic(anchor->getLoc(), diag::missing_arguments_in_call,
3654+
arguments.str());
3655+
3656+
Expr *fnExpr = nullptr;
3657+
Expr *argExpr = nullptr;
3658+
unsigned numArguments = 0;
3659+
bool hasTrailingClosure = false;
3660+
3661+
std::tie(fnExpr, argExpr, numArguments, hasTrailingClosure) =
3662+
getCallInfo(getRawAnchor());
3663+
3664+
// TODO(diagnostics): We should be able to suggest this fix-it
3665+
// unconditionally.
3666+
if (argExpr && numArguments == 0) {
3667+
SmallString<32> scratch;
3668+
llvm::raw_svector_ostream fixIt(scratch);
3669+
interleave(
3670+
SynthesizedArgs,
3671+
[&](const AnyFunctionType::Param &arg) { forFixIt(fixIt, arg); },
3672+
[&] { fixIt << ", "; });
3673+
3674+
auto *tuple = cast<TupleExpr>(argExpr);
3675+
diag.fixItInsertAfter(tuple->getLParenLoc(), fixIt.str());
3676+
}
3677+
3678+
diag.flush();
3679+
3680+
if (auto selectedOverload = getChoiceFor(locator)) {
3681+
if (auto *decl = selectedOverload->choice.getDeclOrNull()) {
3682+
emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName());
3683+
}
3684+
}
3685+
3686+
return true;
36303687
}
36313688

36323689
bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
36333690
auto &ctx = getASTContext();
36343691

36353692
auto *anchor = getRawAnchor();
3636-
if (!(isa<CallExpr>(anchor) || isa<SubscriptExpr>(anchor)))
3693+
if (!(isa<CallExpr>(anchor) || isa<SubscriptExpr>(anchor) ||
3694+
isa<UnresolvedMemberExpr>(anchor)))
36373695
return false;
36383696

36393697
if (SynthesizedArgs.size() != 1)
@@ -3652,37 +3710,21 @@ bool MissingArgumentsFailure::diagnoseSingleMissingArgument() const {
36523710
if (position != 0)
36533711
insertText << ", ";
36543712

3655-
if (!label.empty())
3656-
insertText << label.str() << ": ";
3713+
forFixIt(insertText, argument);
36573714

3658-
// Explode inout type.
3659-
if (argument.isInOut())
3660-
insertText << "&";
3715+
Expr *fnExpr = nullptr;
3716+
Expr *argExpr = nullptr;
3717+
unsigned insertableEndIdx = 0;
3718+
bool hasTrailingClosure = false;
36613719

3662-
auto resolvedType = resolveType(argType);
3663-
// @autoclosure; the type should be the result type.
3664-
if (argument.isAutoClosure())
3665-
resolvedType = resolvedType->castTo<FunctionType>()->getResult();
3720+
std::tie(fnExpr, argExpr, insertableEndIdx, hasTrailingClosure) =
3721+
getCallInfo(anchor);
36663722

3667-
insertText << "<#" << resolvedType << "#>";
3723+
if (!argExpr)
3724+
return false;
36683725

3669-
unsigned insertableEndIdx = 0;
3670-
Expr *fnExpr = nullptr;
3671-
Expr *argExpr = nullptr;
3672-
if (auto *callExpr = dyn_cast<CallExpr>(anchor)) {
3673-
fnExpr = callExpr->getFn();
3674-
argExpr = callExpr->getArg();
3675-
insertableEndIdx = callExpr->getNumArguments();
3676-
if (callExpr->hasTrailingClosure())
3677-
insertableEndIdx -= 1;
3678-
} else {
3679-
auto *SE = cast<SubscriptExpr>(anchor);
3680-
fnExpr = SE;
3681-
argExpr = SE->getIndex();
3682-
insertableEndIdx = SE->getNumArguments();
3683-
if (SE->hasTrailingClosure())
3684-
insertableEndIdx -= 1;
3685-
}
3726+
if (hasTrailingClosure)
3727+
insertableEndIdx -= 1;
36863728

36873729
if (position == 0 && insertableEndIdx != 0)
36883730
insertText << ", ";
@@ -3969,6 +4011,40 @@ bool MissingArgumentsFailure::isMisplacedMissingArgument(
39694011
return TC.isConvertibleTo(argType, paramType, cs.DC);
39704012
}
39714013

4014+
std::tuple<Expr *, Expr *, unsigned, bool>
4015+
MissingArgumentsFailure::getCallInfo(Expr *anchor) const {
4016+
if (auto *call = dyn_cast<CallExpr>(anchor)) {
4017+
return std::make_tuple(call->getFn(), call->getArg(),
4018+
call->getNumArguments(), call->hasTrailingClosure());
4019+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
4020+
return std::make_tuple(UME, UME->getArgument(), UME->getNumArguments(),
4021+
UME->hasTrailingClosure());
4022+
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
4023+
return std::make_tuple(SE, SE->getIndex(), SE->getNumArguments(),
4024+
SE->hasTrailingClosure());
4025+
}
4026+
4027+
return std::make_tuple(nullptr, nullptr, 0, false);
4028+
}
4029+
4030+
void MissingArgumentsFailure::forFixIt(
4031+
llvm::raw_svector_ostream &out,
4032+
const AnyFunctionType::Param &argument) const {
4033+
if (argument.hasLabel())
4034+
out << argument.getLabel().str() << ": ";
4035+
4036+
// Explode inout type.
4037+
if (argument.isInOut())
4038+
out << "&";
4039+
4040+
auto resolvedType = resolveType(argument.getPlainType());
4041+
// @autoclosure; the type should be the result type.
4042+
if (argument.isAutoClosure())
4043+
resolvedType = resolvedType->castTo<FunctionType>()->getResult();
4044+
4045+
out << "<#" << resolvedType << "#>";
4046+
}
4047+
39724048
bool ClosureParamDestructuringFailure::diagnoseAsError() {
39734049
auto *closure = cast<ClosureExpr>(getAnchor());
39744050
auto params = closure->getParameters();

lib/Sema/CSDiagnostics.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1193,7 +1193,9 @@ class MissingArgumentsFailure final : public FailureDiagnostic {
11931193
ArrayRef<Param> synthesizedArgs,
11941194
ConstraintLocator *locator)
11951195
: FailureDiagnostic(root, cs, locator),
1196-
SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) {}
1196+
SynthesizedArgs(synthesizedArgs.begin(), synthesizedArgs.end()) {
1197+
assert(!SynthesizedArgs.empty() && "No missing arguments?!");
1198+
}
11971199

11981200
bool diagnoseAsError() override;
11991201

@@ -1213,6 +1215,16 @@ class MissingArgumentsFailure final : public FailureDiagnostic {
12131215
/// `@Foo(answer: 42) var question = "ultimate question"`
12141216
bool isPropertyWrapperInitialization() const;
12151217

1218+
/// Gather informatioin associated with expression that represents
1219+
/// a call - function, arguments, # of arguments and whether it has
1220+
/// a trailing closure.
1221+
std::tuple<Expr *, Expr *, unsigned, bool> getCallInfo(Expr *anchor) const;
1222+
1223+
/// Transform given argument into format suitable for a fix-it
1224+
/// text e.g. `[<label>:]? <#<type#>`
1225+
void forFixIt(llvm::raw_svector_ostream &out,
1226+
const AnyFunctionType::Param &argument) const;
1227+
12161228
public:
12171229
/// Due to the fact that `matchCallArgument` can't and
12181230
/// doesn't take types into consideration while matching

0 commit comments

Comments
 (0)