Skip to content

Commit 2e14bec

Browse files
committed
[Diagnostics] Add out-of-order argument diagnostic
1 parent 1d2c363 commit 2e14bec

File tree

4 files changed

+121
-79
lines changed

4 files changed

+121
-79
lines changed

lib/Sema/CSDiag.cpp

Lines changed: 4 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3557,72 +3557,10 @@ class ArgumentMatcher : public MatchCallArgumentListener {
35573557
}
35583558

35593559
bool outOfOrderArgument(unsigned argIdx, unsigned prevArgIdx) override {
3560-
auto tuple = cast<TupleExpr>(ArgExpr);
3561-
Identifier first = tuple->getElementName(argIdx);
3562-
Identifier second = tuple->getElementName(prevArgIdx);
3563-
3564-
// Build a mapping from arguments to parameters.
3565-
SmallVector<unsigned, 4> argBindings(tuple->getNumElements());
3566-
for (unsigned paramIdx = 0; paramIdx != Bindings.size(); ++paramIdx) {
3567-
for (auto argIdx : Bindings[paramIdx])
3568-
argBindings[argIdx] = paramIdx;
3569-
}
3570-
3571-
auto argRange = [&](unsigned argIdx, Identifier label) -> SourceRange {
3572-
auto range = tuple->getElement(argIdx)->getSourceRange();
3573-
if (!label.empty())
3574-
range.Start = tuple->getElementNameLoc(argIdx);
3575-
3576-
unsigned paramIdx = argBindings[argIdx];
3577-
if (Bindings[paramIdx].size() > 1)
3578-
range.End = tuple->getElement(Bindings[paramIdx].back())->getEndLoc();
3579-
3580-
return range;
3581-
};
3582-
3583-
auto firstRange = argRange(argIdx, first);
3584-
auto secondRange = argRange(prevArgIdx, second);
3585-
3586-
SourceLoc diagLoc = firstRange.Start;
3587-
3588-
auto addFixIts = [&](InFlightDiagnostic diag) {
3589-
diag.highlight(firstRange).highlight(secondRange);
3590-
3591-
// Move the misplaced argument by removing it from one location and
3592-
// inserting it in another location. To maintain argument comma
3593-
// separation, since the argument is always moving to an earlier index
3594-
// the preceding comma and whitespace is removed and a new trailing
3595-
// comma and space is inserted with the moved argument.
3596-
auto &SM = TC.Context.SourceMgr;
3597-
auto text = SM.extractText(
3598-
Lexer::getCharSourceRangeFromSourceRange(SM, firstRange));
3599-
3600-
auto removalRange =
3601-
SourceRange(Lexer::getLocForEndOfToken(
3602-
SM, tuple->getElement(argIdx - 1)->getEndLoc()),
3603-
firstRange.End);
3604-
diag.fixItRemove(removalRange);
3605-
diag.fixItInsert(secondRange.Start, text.str() + ", ");
3606-
};
3607-
3608-
// There are 4 diagnostic messages variations depending on
3609-
// labeled/unlabeled arguments.
3610-
if (first.empty() && second.empty()) {
3611-
addFixIts(TC.diagnose(diagLoc,
3612-
diag::argument_out_of_order_unnamed_unnamed,
3613-
argIdx + 1, prevArgIdx + 1));
3614-
} else if (first.empty() && !second.empty()) {
3615-
addFixIts(TC.diagnose(diagLoc, diag::argument_out_of_order_unnamed_named,
3616-
argIdx + 1, second));
3617-
} else if (!first.empty() && second.empty()) {
3618-
addFixIts(TC.diagnose(diagLoc, diag::argument_out_of_order_named_unnamed,
3619-
first, prevArgIdx + 1));
3620-
} else {
3621-
addFixIts(TC.diagnose(diagLoc, diag::argument_out_of_order_named_named,
3622-
first, second));
3623-
}
3624-
3625-
Diagnosed = true;
3560+
auto &cs = CandidateInfo.CS;
3561+
OutOfOrderArgumentFailure failure(nullptr, cs, argIdx, prevArgIdx, Bindings,
3562+
cs.getConstraintLocator(ArgExpr));
3563+
Diagnosed = failure.diagnoseAsError();
36263564
return true;
36273565
}
36283566

lib/Sema/CSDiagnostics.cpp

Lines changed: 90 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ Expr *FailureDiagnostic::findParentExpr(Expr *subExpr) const {
8686
return E ? E->getParentMap()[subExpr] : nullptr;
8787
}
8888

89+
Expr *FailureDiagnostic::getArgumentExprFor(Expr *anchor) const {
90+
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
91+
if (auto *call = dyn_cast_or_null<CallExpr>(findParentExpr(UDE)))
92+
return call->getArg();
93+
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
94+
return UME->getArgument();
95+
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
96+
return call->getArg();
97+
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
98+
return SE->getIndex();
99+
}
100+
return nullptr;
101+
}
102+
89103
Type RequirementFailure::getOwnerType() const {
90104
return getType(getRawAnchor())
91105
->getInOutObjectType()
@@ -369,18 +383,7 @@ bool LabelingFailure::diagnoseAsError() {
369383
auto &cs = getConstraintSystem();
370384
auto *anchor = getRawAnchor();
371385

372-
Expr *argExpr = nullptr;
373-
if (auto *UDE = dyn_cast<UnresolvedDotExpr>(anchor)) {
374-
if (auto *call = dyn_cast_or_null<CallExpr>(findParentExpr(UDE)))
375-
argExpr = call->getArg();
376-
} else if (auto *UME = dyn_cast<UnresolvedMemberExpr>(anchor)) {
377-
argExpr = UME->getArgument();
378-
} else if (auto *call = dyn_cast<CallExpr>(anchor)) {
379-
argExpr = call->getArg();
380-
} else if (auto *SE = dyn_cast<SubscriptExpr>(anchor)) {
381-
argExpr = SE->getIndex();
382-
}
383-
386+
auto *argExpr = getArgumentExprFor(anchor);
384387
if (!argExpr)
385388
return false;
386389

@@ -2133,3 +2136,78 @@ bool ClosureParamDestructuringFailure::diagnoseAsError() {
21332136
.fixItInsert(bodyLoc, OS.str());
21342137
return true;
21352138
}
2139+
2140+
bool OutOfOrderArgumentFailure::diagnoseAsError() {
2141+
auto *anchor = getRawAnchor();
2142+
auto *argExpr = isa<TupleExpr>(anchor) ? anchor : getArgumentExprFor(anchor);
2143+
if (!argExpr)
2144+
return false;
2145+
2146+
auto *tuple = cast<TupleExpr>(argExpr);
2147+
2148+
Identifier first = tuple->getElementName(ArgIdx);
2149+
Identifier second = tuple->getElementName(PrevArgIdx);
2150+
2151+
// Build a mapping from arguments to parameters.
2152+
SmallVector<unsigned, 4> argBindings(tuple->getNumElements());
2153+
for (unsigned paramIdx = 0; paramIdx != Bindings.size(); ++paramIdx) {
2154+
for (auto argIdx : Bindings[paramIdx])
2155+
argBindings[argIdx] = paramIdx;
2156+
}
2157+
2158+
auto argRange = [&](unsigned argIdx, Identifier label) -> SourceRange {
2159+
auto range = tuple->getElement(argIdx)->getSourceRange();
2160+
if (!label.empty())
2161+
range.Start = tuple->getElementNameLoc(argIdx);
2162+
2163+
unsigned paramIdx = argBindings[argIdx];
2164+
if (Bindings[paramIdx].size() > 1)
2165+
range.End = tuple->getElement(Bindings[paramIdx].back())->getEndLoc();
2166+
2167+
return range;
2168+
};
2169+
2170+
auto firstRange = argRange(ArgIdx, first);
2171+
auto secondRange = argRange(PrevArgIdx, second);
2172+
2173+
SourceLoc diagLoc = firstRange.Start;
2174+
2175+
auto addFixIts = [&](InFlightDiagnostic diag) {
2176+
diag.highlight(firstRange).highlight(secondRange);
2177+
2178+
// Move the misplaced argument by removing it from one location and
2179+
// inserting it in another location. To maintain argument comma
2180+
// separation, since the argument is always moving to an earlier index
2181+
// the preceding comma and whitespace is removed and a new trailing
2182+
// comma and space is inserted with the moved argument.
2183+
auto &SM = getASTContext().SourceMgr;
2184+
auto text = SM.extractText(
2185+
Lexer::getCharSourceRangeFromSourceRange(SM, firstRange));
2186+
2187+
auto removalRange =
2188+
SourceRange(Lexer::getLocForEndOfToken(
2189+
SM, tuple->getElement(ArgIdx - 1)->getEndLoc()),
2190+
firstRange.End);
2191+
diag.fixItRemove(removalRange);
2192+
diag.fixItInsert(secondRange.Start, text.str() + ", ");
2193+
};
2194+
2195+
// There are 4 diagnostic messages variations depending on
2196+
// labeled/unlabeled arguments.
2197+
if (first.empty() && second.empty()) {
2198+
addFixIts(emitDiagnostic(diagLoc,
2199+
diag::argument_out_of_order_unnamed_unnamed,
2200+
ArgIdx + 1, PrevArgIdx + 1));
2201+
} else if (first.empty() && !second.empty()) {
2202+
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_unnamed_named,
2203+
ArgIdx + 1, second));
2204+
} else if (!first.empty() && second.empty()) {
2205+
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_unnamed,
2206+
first, PrevArgIdx + 1));
2207+
} else {
2208+
addFixIts(emitDiagnostic(diagLoc, diag::argument_out_of_order_named_named,
2209+
first, second));
2210+
}
2211+
2212+
return true;
2213+
}

lib/Sema/CSDiagnostics.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,10 @@ class FailureDiagnostic {
151151
/// in the root expression or `nullptr` otherwise.
152152
Expr *findParentExpr(Expr *subExpr) const;
153153

154+
/// \returns An argument expression if given anchor is a call, member
155+
/// reference or subscript, nullptr otherwise.
156+
Expr *getArgumentExprFor(Expr *anchor) const;
157+
154158
private:
155159
/// Compute anchor expression associated with current diagnostic.
156160
std::pair<Expr *, bool> computeAnchor() const;
@@ -899,6 +903,26 @@ class MissingArgumentsFailure final : public FailureDiagnostic {
899903
bool diagnoseTrailingClosure(ClosureExpr *closure);
900904
};
901905

906+
class OutOfOrderArgumentFailure final : public FailureDiagnostic {
907+
using ParamBinding = SmallVector<unsigned, 1>;
908+
909+
unsigned ArgIdx;
910+
unsigned PrevArgIdx;
911+
912+
SmallVector<ParamBinding, 4> Bindings;
913+
914+
public:
915+
OutOfOrderArgumentFailure(Expr *root, ConstraintSystem &cs,
916+
unsigned argIdx,
917+
unsigned prevArgIdx,
918+
ArrayRef<ParamBinding> bindings,
919+
ConstraintLocator *locator)
920+
: FailureDiagnostic(root, cs, locator), ArgIdx(argIdx),
921+
PrevArgIdx(prevArgIdx), Bindings(bindings.begin(), bindings.end()) {}
922+
923+
bool diagnoseAsError() override;
924+
};
925+
902926
/// Diagnose an attempt to destructure a single tuple closure parameter
903927
/// into a multiple (possibly anonymous) arguments e.g.
904928
///

lib/Sema/CSFix.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -373,7 +373,9 @@ AddMissingArguments::create(ConstraintSystem &cs, FunctionType *funcType,
373373
}
374374

375375
bool MoveOutOfOrderArgument::diagnose(Expr *root, bool asNote) const {
376-
return false;
376+
OutOfOrderArgumentFailure failure(root, getConstraintSystem(), ArgIdx,
377+
PrevArgIdx, Bindings, getLocator());
378+
return failure.diagnose(asNote);
377379
}
378380

379381
MoveOutOfOrderArgument *MoveOutOfOrderArgument::create(

0 commit comments

Comments
 (0)