Skip to content

Commit 8d05192

Browse files
authored
Merge pull request #27728 from xedin/port-extraneous-args
[Diagnostics] Diagnose extraneous argument(s) via fixes
2 parents 403af12 + 1ffe97f commit 8d05192

28 files changed

+471
-162
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1122,13 +1122,20 @@ ERROR(extra_argument_named,none,
11221122
"extra argument %0 in call", (Identifier))
11231123
ERROR(extra_argument_positional,none,
11241124
"extra argument in call", ())
1125+
ERROR(extra_arguments_in_call,none,
1126+
"extra arguments at positions %0 in call", (StringRef))
11251127
ERROR(extra_argument_to_nullary_call,none,
11261128
"argument passed to call that takes no arguments", ())
11271129
ERROR(extra_trailing_closure_in_call,none,
11281130
"extra trailing closure passed in call", ())
11291131
ERROR(trailing_closure_bad_param,none,
11301132
"trailing closure passed to parameter of type %0 that does not "
11311133
"accept a closure", (Type))
1134+
NOTE(candidate_with_extraneous_args,none,
1135+
"candidate %0 requires %1 argument%s1, "
1136+
"but %2 %select{were|was}3 %select{provided|used in closure body}4",
1137+
(Type, unsigned, unsigned, bool, bool))
1138+
11321139
ERROR(no_accessible_initializers,none,
11331140
"%0 cannot be constructed because it has no accessible initializers",
11341141
(Type))

lib/Sema/CSDiag.cpp

Lines changed: 0 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2155,56 +2155,6 @@ class ArgumentMatcher : public MatchCallArgumentListener {
21552155
ParamInfo(paramInfo), Arguments(args), CandidateInfo(CCI),
21562156
IsSubscript(isSubscript) {}
21572157

2158-
void extraArgument(unsigned extraArgIdx) override {
2159-
auto name = Arguments[extraArgIdx].getLabel();
2160-
Expr *arg = ArgExpr;
2161-
2162-
auto tuple = dyn_cast<TupleExpr>(ArgExpr);
2163-
if (tuple)
2164-
arg = tuple->getElement(extraArgIdx);
2165-
2166-
auto loc = arg->getLoc();
2167-
if (tuple && extraArgIdx == tuple->getNumElements() - 1 &&
2168-
tuple->hasTrailingClosure())
2169-
TC.diagnose(loc, diag::extra_trailing_closure_in_call)
2170-
.highlight(arg->getSourceRange());
2171-
else if (Parameters.empty()) {
2172-
auto Paren = dyn_cast<ParenExpr>(ArgExpr);
2173-
Expr *SubExpr = nullptr;
2174-
if (Paren) {
2175-
SubExpr = Paren->getSubExpr();
2176-
}
2177-
2178-
if (SubExpr && CandidateInfo.CS.getType(SubExpr) &&
2179-
CandidateInfo.CS.getType(SubExpr)->isVoid()) {
2180-
TC.diagnose(loc, diag::extra_argument_to_nullary_call)
2181-
.fixItRemove(SubExpr->getSourceRange());
2182-
} else {
2183-
TC.diagnose(loc, diag::extra_argument_to_nullary_call)
2184-
.highlight(ArgExpr->getSourceRange());
2185-
}
2186-
} else if (name.empty())
2187-
TC.diagnose(loc, diag::extra_argument_positional)
2188-
.highlight(arg->getSourceRange());
2189-
else
2190-
TC.diagnose(loc, diag::extra_argument_named, name)
2191-
.highlight(arg->getSourceRange());
2192-
2193-
Diagnosed = true;
2194-
}
2195-
2196-
bool missingLabel(unsigned paramIdx) override {
2197-
return false;
2198-
}
2199-
2200-
bool extraneousLabel(unsigned paramIdx) override {
2201-
return false;
2202-
}
2203-
2204-
bool incorrectLabel(unsigned paramIdx) override {
2205-
return false;
2206-
}
2207-
22082158
bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override {
22092159
auto &cs = CandidateInfo.CS;
22102160
OutOfOrderArgumentFailure failure(nullptr, cs, argIdx, prevArgIdx, Bindings,

lib/Sema/CSDiagnostics.cpp

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4219,7 +4219,137 @@ bool OutOfOrderArgumentFailure::diagnoseAsError() {
42194219
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_named,
42204220
first, second));
42214221
}
4222+
return true;
4223+
}
4224+
4225+
bool ExtraneousArgumentsFailure::diagnoseAsError() {
4226+
// Simplified anchor would point directly to the
4227+
// argument in case of contextual mismatch.
4228+
auto *anchor = getAnchor();
4229+
if (auto *closure = dyn_cast<ClosureExpr>(anchor)) {
4230+
auto fnType = ContextualType;
4231+
auto params = closure->getParameters();
42224232

4233+
auto diag = emitDiagnostic(
4234+
params->getStartLoc(), diag::closure_argument_list_tuple, fnType,
4235+
fnType->getNumParams(), params->size(), (params->size() == 1));
4236+
4237+
bool onlyAnonymousParams =
4238+
std::all_of(params->begin(), params->end(),
4239+
[](ParamDecl *param) { return !param->hasName(); });
4240+
4241+
// If closure expects no parameters but N was given,
4242+
// and all of them are anonymous let's suggest removing them.
4243+
if (fnType->getNumParams() == 0 && onlyAnonymousParams) {
4244+
auto inLoc = closure->getInLoc();
4245+
auto &sourceMgr = getASTContext().SourceMgr;
4246+
4247+
if (inLoc.isValid())
4248+
diag.fixItRemoveChars(params->getStartLoc(),
4249+
Lexer::getLocForEndOfToken(sourceMgr, inLoc));
4250+
}
4251+
return true;
4252+
}
4253+
4254+
if (isContextualMismatch()) {
4255+
auto *locator = getLocator();
4256+
emitDiagnostic(anchor->getLoc(),
4257+
locator->isLastElement<LocatorPathElt::ContextualType>()
4258+
? diag::cannot_convert_initializer_value
4259+
: diag::cannot_convert_argument_value,
4260+
getType(anchor), ContextualType);
4261+
return true;
4262+
}
4263+
4264+
if (ExtraArgs.size() == 1) {
4265+
return diagnoseSingleExtraArgument();
4266+
}
4267+
4268+
if (ContextualType->getNumParams() == 0) {
4269+
if (auto argExpr = getArgumentListExprFor(getLocator())) {
4270+
emitDiagnostic(anchor->getLoc(), diag::extra_argument_to_nullary_call)
4271+
.highlight(argExpr->getSourceRange())
4272+
.fixItRemove(argExpr->getSourceRange());
4273+
return true;
4274+
}
4275+
}
4276+
4277+
if (ExtraArgs.size() < 2)
4278+
return false;
4279+
4280+
llvm::SmallString<64> positions;
4281+
llvm::raw_svector_ostream OS(positions);
4282+
4283+
interleave(
4284+
ExtraArgs,
4285+
[&](const std::pair<unsigned, AnyFunctionType::Param> &arg) {
4286+
OS << "#" << (arg.first + 1);
4287+
},
4288+
[&] { OS << ", "; });
4289+
4290+
emitDiagnostic(anchor->getLoc(), diag::extra_arguments_in_call, OS.str());
4291+
4292+
if (auto overload = getChoiceFor(getLocator())) {
4293+
if (auto *decl = overload->choice.getDeclOrNull()) {
4294+
emitDiagnostic(decl, diag::decl_declared_here, decl->getFullName());
4295+
}
4296+
}
4297+
4298+
return true;
4299+
}
4300+
4301+
bool ExtraneousArgumentsFailure::diagnoseAsNote() {
4302+
auto overload = getChoiceFor(getLocator());
4303+
if (!(overload && overload->choice.isDecl()))
4304+
return false;
4305+
4306+
auto *decl = overload->choice.getDecl();
4307+
auto *anchor = getAnchor();
4308+
auto numArgs = getTotalNumArguments();
4309+
emitDiagnostic(decl, diag::candidate_with_extraneous_args, ContextualType,
4310+
ContextualType->getNumParams(), numArgs, (numArgs == 1),
4311+
isa<ClosureExpr>(anchor));
4312+
return true;
4313+
}
4314+
4315+
bool ExtraneousArgumentsFailure::diagnoseSingleExtraArgument() const {
4316+
auto *arguments = getArgumentListExprFor(getLocator());
4317+
if (!arguments)
4318+
return false;
4319+
4320+
const auto &e = ExtraArgs.front();
4321+
auto index = e.first;
4322+
auto argument = e.second;
4323+
4324+
auto tuple = dyn_cast<TupleExpr>(arguments);
4325+
auto argExpr = tuple ? tuple->getElement(index)
4326+
: cast<ParenExpr>(arguments)->getSubExpr();
4327+
4328+
auto loc = argExpr->getLoc();
4329+
if (tuple && index == tuple->getNumElements() - 1 &&
4330+
tuple->hasTrailingClosure()) {
4331+
emitDiagnostic(loc, diag::extra_trailing_closure_in_call)
4332+
.highlight(argExpr->getSourceRange());
4333+
} else if (ContextualType->getNumParams() == 0) {
4334+
auto *PE = dyn_cast<ParenExpr>(arguments);
4335+
Expr *subExpr = nullptr;
4336+
if (PE)
4337+
subExpr = PE->getSubExpr();
4338+
4339+
if (subExpr && argument.getPlainType()->isVoid()) {
4340+
emitDiagnostic(loc, diag::extra_argument_to_nullary_call)
4341+
.fixItRemove(subExpr->getSourceRange());
4342+
} else {
4343+
emitDiagnostic(loc, diag::extra_argument_to_nullary_call)
4344+
.highlight(argExpr->getSourceRange());
4345+
}
4346+
} else if (argument.hasLabel()) {
4347+
emitDiagnostic(loc, diag::extra_argument_named, argument.getLabel())
4348+
.highlight(argExpr->getSourceRange());
4349+
} else {
4350+
emitDiagnostic(loc, diag::extra_argument_positional)
4351+
.highlight(argExpr->getSourceRange());
4352+
}
42234353
return true;
42244354
}
42254355

lib/Sema/CSDiagnostics.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1329,6 +1329,36 @@ class MissingArgumentsFailure final : public FailureDiagnostic {
13291329
ConstraintLocator *locator);
13301330
};
13311331

1332+
class ExtraneousArgumentsFailure final : public FailureDiagnostic {
1333+
FunctionType *ContextualType;
1334+
SmallVector<std::pair<unsigned, AnyFunctionType::Param>, 4> ExtraArgs;
1335+
1336+
public:
1337+
ExtraneousArgumentsFailure(
1338+
Expr *root, ConstraintSystem &cs, FunctionType *contextualType,
1339+
ArrayRef<std::pair<unsigned, AnyFunctionType::Param>> extraArgs,
1340+
ConstraintLocator *locator)
1341+
: FailureDiagnostic(root, cs, locator),
1342+
ContextualType(resolveType(contextualType)->castTo<FunctionType>()),
1343+
ExtraArgs(extraArgs.begin(), extraArgs.end()) {}
1344+
1345+
bool diagnoseAsError() override;
1346+
bool diagnoseAsNote() override;
1347+
1348+
private:
1349+
bool diagnoseSingleExtraArgument() const;
1350+
1351+
unsigned getTotalNumArguments() const {
1352+
return ContextualType->getNumParams() + ExtraArgs.size();
1353+
}
1354+
1355+
bool isContextualMismatch() const {
1356+
auto *locator = getLocator();
1357+
return locator->isLastElement<LocatorPathElt::ContextualType>() ||
1358+
locator->isLastElement<LocatorPathElt::ApplyArgToParam>();
1359+
}
1360+
};
1361+
13321362
class OutOfOrderArgumentFailure final : public FailureDiagnostic {
13331363
using ParamBinding = SmallVector<unsigned, 1>;
13341364

lib/Sema/CSFix.cpp

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,42 @@ AddMissingArguments::create(ConstraintSystem &cs,
518518
return new (mem) AddMissingArguments(cs, synthesizedArgs, locator);
519519
}
520520

521+
bool RemoveExtraneousArguments::diagnose(Expr *root, bool asNote) const {
522+
ExtraneousArgumentsFailure failure(root, getConstraintSystem(),
523+
ContextualType, getExtraArguments(),
524+
getLocator());
525+
return failure.diagnose(asNote);
526+
}
527+
528+
bool RemoveExtraneousArguments::isMinMaxNameShadowing(
529+
ConstraintSystem &cs, ConstraintLocatorBuilder locator) {
530+
auto *anchor = dyn_cast_or_null<CallExpr>(locator.getAnchor());
531+
if (!anchor)
532+
return false;
533+
534+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor->getFn())) {
535+
if (auto *baseExpr = dyn_cast<DeclRefExpr>(UDE->getBase())) {
536+
auto *decl = baseExpr->getDecl();
537+
if (baseExpr->isImplicit() && decl &&
538+
decl->getFullName() == cs.getASTContext().Id_self) {
539+
auto memberName = UDE->getName();
540+
return memberName.isSimpleName("min") || memberName.isSimpleName("max");
541+
}
542+
}
543+
}
544+
545+
return false;
546+
}
547+
548+
RemoveExtraneousArguments *RemoveExtraneousArguments::create(
549+
ConstraintSystem &cs, FunctionType *contextualType,
550+
llvm::ArrayRef<IndexedParam> extraArgs, ConstraintLocator *locator) {
551+
unsigned size = totalSizeToAlloc<IndexedParam>(extraArgs.size());
552+
void *mem = cs.getAllocator().Allocate(size, alignof(RemoveExtraneousArguments));
553+
return new (mem)
554+
RemoveExtraneousArguments(cs, contextualType, extraArgs, locator);
555+
}
556+
521557
bool MoveOutOfOrderArgument::diagnose(Expr *root, bool asNote) const {
522558
OutOfOrderArgumentFailure failure(root, getConstraintSystem(), ArgIdx,
523559
PrevArgIdx, Bindings, getLocator());
@@ -761,8 +797,15 @@ bool AllowTupleSplatForSingleParameter::attempt(
761797
//
762798
// We'd want to suggest argument list to be `x: (0, 1)` instead
763799
// of `(x: 0, 1)` which would be incorrect.
764-
if (index == 0 && param.getLabel() == label)
765-
label = Identifier();
800+
if (param.hasLabel() && label == param.getLabel()) {
801+
if (index == 0) {
802+
label = Identifier();
803+
} else {
804+
// If label match anything other than first argument,
805+
// this can't be a tuple splat.
806+
return true;
807+
}
808+
}
766809

767810
// Tuple can't have `inout` elements.
768811
if (flags.isInOut())

lib/Sema/CSFix.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,10 @@ enum class FixKind : uint8_t {
150150
/// by adding new arguments to the list represented as type variables.
151151
AddMissingArguments,
152152

153+
/// If there are more arguments than parameters, let's fix that up
154+
/// by removing extraneous arguments.
155+
RemoveExtraneousArguments,
156+
153157
/// Allow single tuple closure parameter destructuring into N arguments.
154158
AllowClosureParameterDestructuring,
155159

@@ -1019,6 +1023,55 @@ class AddMissingArguments final
10191023
}
10201024
};
10211025

1026+
class RemoveExtraneousArguments final
1027+
: public ConstraintFix,
1028+
private llvm::TrailingObjects<
1029+
RemoveExtraneousArguments,
1030+
std::pair<unsigned, AnyFunctionType::Param>> {
1031+
friend TrailingObjects;
1032+
1033+
using IndexedParam = std::pair<unsigned, AnyFunctionType::Param>;
1034+
1035+
FunctionType *ContextualType;
1036+
unsigned NumExtraneous;
1037+
1038+
RemoveExtraneousArguments(ConstraintSystem &cs, FunctionType *contextualType,
1039+
llvm::ArrayRef<IndexedParam> extraArgs,
1040+
ConstraintLocator *locator)
1041+
: ConstraintFix(cs, FixKind::RemoveExtraneousArguments, locator),
1042+
ContextualType(contextualType), NumExtraneous(extraArgs.size()) {
1043+
std::uninitialized_copy(extraArgs.begin(), extraArgs.end(),
1044+
getExtraArgumentsBuf().begin());
1045+
}
1046+
1047+
public:
1048+
std::string getName() const override { return "remove extraneous argument(s)"; }
1049+
1050+
ArrayRef<IndexedParam> getExtraArguments() const {
1051+
return {getTrailingObjects<IndexedParam>(), NumExtraneous};
1052+
}
1053+
1054+
bool diagnose(Expr *root, bool asNote = false) const override;
1055+
1056+
/// FIXME(diagnostics): Once `resolveDeclRefExpr` is gone this
1057+
/// logic would be obsolete.
1058+
///
1059+
/// Determine whether presence of extraneous arguments indicates
1060+
/// potential name shadowing problem with local `min`/`max` shadowing
1061+
/// global definitions with different number of arguments.
1062+
static bool isMinMaxNameShadowing(ConstraintSystem &cs,
1063+
ConstraintLocatorBuilder locator);
1064+
1065+
static RemoveExtraneousArguments *
1066+
create(ConstraintSystem &cs, FunctionType *contextualType,
1067+
llvm::ArrayRef<IndexedParam> extraArgs, ConstraintLocator *locator);
1068+
1069+
private:
1070+
MutableArrayRef<IndexedParam> getExtraArgumentsBuf() {
1071+
return {getTrailingObjects<IndexedParam>(), NumExtraneous};
1072+
}
1073+
};
1074+
10221075
class MoveOutOfOrderArgument final : public ConstraintFix {
10231076
using ParamBinding = SmallVector<unsigned, 1>;
10241077

lib/Sema/CSGen.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1281,8 +1281,9 @@ namespace {
12811281
SmallVector<AnyFunctionType::Param, 8> params;
12821282
AnyFunctionType::decomposeInput(constrParamType, params);
12831283

1284+
auto funcType = constr->getMethodInterfaceType()->castTo<FunctionType>();
12841285
::matchCallArguments(
1285-
CS, args, params, ConstraintKind::ArgumentConversion,
1286+
CS, funcType, args, params, ConstraintKind::ArgumentConversion,
12861287
CS.getConstraintLocator(expr, ConstraintLocator::ApplyArgument));
12871288

12881289
Type result = tv;

0 commit comments

Comments
 (0)