Skip to content

Commit f02d8ec

Browse files
committed
More accurate spans for arg removal suggestion
1 parent 9bb6e60 commit f02d8ec

23 files changed

+202
-108
lines changed

compiler/rustc_errors/src/diagnostic.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -612,7 +612,7 @@ impl Diagnostic {
612612
pub fn multipart_suggestion_with_style(
613613
&mut self,
614614
msg: impl Into<SubdiagnosticMessage>,
615-
suggestion: Vec<(Span, String)>,
615+
mut suggestion: Vec<(Span, String)>,
616616
applicability: Applicability,
617617
style: SuggestionStyle,
618618
) -> &mut Self {
@@ -634,6 +634,7 @@ impl Diagnostic {
634634
None,
635635
"suggestion must not have overlapping parts",
636636
);
637+
suggestion.sort_by_key(|(span, _)| (span.lo(), span.hi()));
637638

638639
self.push_suggestion(CodeSuggestion {
639640
substitutions: vec![Substitution { parts }],

compiler/rustc_errors/src/emitter.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1768,7 +1768,7 @@ impl EmitterWriter {
17681768

17691769
// Render the replacements for each suggestion
17701770
let suggestions = suggestion.splice_lines(sm);
1771-
debug!("emit_suggestion_default: suggestions={:?}", suggestions);
1771+
debug!(?suggestions);
17721772

17731773
if suggestions.is_empty() {
17741774
// Suggestions coming from macros can have malformed spans. This is a heavy handed
@@ -1797,6 +1797,7 @@ impl EmitterWriter {
17971797
for (complete, parts, highlights, only_capitalization) in
17981798
suggestions.iter().take(MAX_SUGGESTIONS)
17991799
{
1800+
debug!(?complete, ?parts, ?highlights);
18001801
notice_capitalization |= only_capitalization;
18011802

18021803
let has_deletion = parts.iter().any(|p| p.is_deletion(sm));

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -755,15 +755,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
755755
}
756756

757757
errors.drain_filter(|error| {
758-
let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false };
759-
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
760-
let trace = mk_trace(provided_span, formal_and_expected_inputs[*expected_idx], provided_ty);
761-
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
762-
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
763-
return true;
764-
}
765-
false
766-
});
758+
let Error::Invalid(
759+
provided_idx,
760+
expected_idx,
761+
Compatibility::Incompatible(Some(e)),
762+
) = error else { return false };
763+
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
764+
let trace = mk_trace(
765+
provided_span,
766+
formal_and_expected_inputs[*expected_idx],
767+
provided_ty,
768+
);
769+
if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) {
770+
self.err_ctxt().report_and_explain_type_error(trace, *e).emit();
771+
return true;
772+
}
773+
false
774+
});
767775

768776
// We're done if we found errors, but we already emitted them.
769777
if errors.is_empty() {
@@ -864,7 +872,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
864872
}
865873
let mut suggestion_text = SuggestionText::None;
866874

875+
let ty_to_snippet = |ty: Ty<'tcx>, expected_idx: ExpectedIdx| {
876+
if ty.is_unit() {
877+
"()".to_string()
878+
} else if ty.is_suggestable(tcx, false) {
879+
format!("/* {} */", ty)
880+
} else if let Some(fn_def_id) = fn_def_id
881+
&& self.tcx.def_kind(fn_def_id).is_fn_like()
882+
&& let self_implicit =
883+
matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
884+
&& let Some(arg) = self.tcx.fn_arg_names(fn_def_id)
885+
.get(expected_idx.as_usize() + self_implicit)
886+
&& arg.name != kw::SelfLower
887+
{
888+
format!("/* {} */", arg.name)
889+
} else {
890+
"/* value */".to_string()
891+
}
892+
};
893+
867894
let mut errors = errors.into_iter().peekable();
895+
let mut suggestions = vec![];
868896
while let Some(error) = errors.next() {
869897
match error {
870898
Error::Invalid(provided_idx, expected_idx, compatibility) => {
@@ -906,6 +934,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
906934
};
907935
labels
908936
.push((provided_span, format!("argument{} unexpected", provided_ty_name)));
937+
let mut span = provided_span;
938+
if let Some((_, next)) = provided_arg_tys.get(
939+
ProvidedIdx::from_usize(arg_idx.index() + 1),
940+
) {
941+
// Include next comma
942+
span = span.until(*next);
943+
} else if arg_idx.index() > 0
944+
&& let Some((_, prev)) = provided_arg_tys
945+
.get(ProvidedIdx::from_usize(arg_idx.index() - 1)
946+
) {
947+
// Last argument, include previous comma
948+
span = span.with_lo(prev.hi());
949+
}
950+
suggestions.push((span, String::new()));
951+
909952
suggestion_text = match suggestion_text {
910953
SuggestionText::None => SuggestionText::Remove(false),
911954
SuggestionText::Remove(_) => SuggestionText::Remove(true),
@@ -1095,6 +1138,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
10951138
}
10961139
}
10971140

1141+
// Incorporate the argument changes in the removal suggestion.
1142+
let mut prev = -1;
1143+
for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
1144+
if let Some(provided_idx) = provided_idx {
1145+
prev = provided_idx.index() as i64;
1146+
}
1147+
let idx = ProvidedIdx::from_usize((prev + 1) as usize);
1148+
if let None = provided_idx
1149+
&& let Some((_, arg_span)) = provided_arg_tys.get(idx)
1150+
{
1151+
let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
1152+
suggestions.push((*arg_span, ty_to_snippet(expected_ty, expected_idx)));
1153+
}
1154+
}
1155+
10981156
// If we have less than 5 things to say, it would be useful to call out exactly what's wrong
10991157
if labels.len() <= 5 {
11001158
for (span, label) in labels {
@@ -1112,7 +1170,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11121170
Some(format!("provide the argument{}", if plural { "s" } else { "" }))
11131171
}
11141172
SuggestionText::Remove(plural) => {
1115-
Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
1173+
err.multipart_suggestion_verbose(
1174+
&format!("remove the extra argument{}", if plural { "s" } else { "" }),
1175+
suggestions,
1176+
Applicability::HasPlaceholders,
1177+
);
1178+
None
11161179
}
11171180
SuggestionText::Swap => Some("swap these arguments".to_string()),
11181181
SuggestionText::Reorder => Some("reorder these arguments".to_string()),
@@ -1151,20 +1214,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
11511214
} else {
11521215
// Propose a placeholder of the correct type
11531216
let (_, expected_ty) = formal_and_expected_inputs[expected_idx];
1154-
if expected_ty.is_unit() {
1155-
"()".to_string()
1156-
} else if expected_ty.is_suggestable(tcx, false) {
1157-
format!("/* {} */", expected_ty)
1158-
} else if let Some(fn_def_id) = fn_def_id
1159-
&& self.tcx.def_kind(fn_def_id).is_fn_like()
1160-
&& let self_implicit = matches!(call_expr.kind, hir::ExprKind::MethodCall(..)) as usize
1161-
&& let Some(arg) = self.tcx.fn_arg_names(fn_def_id).get(expected_idx.as_usize() + self_implicit)
1162-
&& arg.name != kw::SelfLower
1163-
{
1164-
format!("/* {} */", arg.name)
1165-
} else {
1166-
"/* value */".to_string()
1167-
}
1217+
ty_to_snippet(expected_ty, expected_idx)
11681218
};
11691219
suggestion += &suggestion_text;
11701220
}

tests/ui/alloc-error/alloc-error-handler-bad-signature-3.stderr

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@ LL | fn oom() -> ! {
1717
= note: this error originates in the attribute macro `alloc_error_handler` (in Nightly builds, run with -Z macro-backtrace for more info)
1818
help: remove the extra argument
1919
|
20-
LL | fn oom() -> !() {
21-
| ++
20+
LL - fn oom() -> ! {
21+
LL - loop {}
22+
LL - }
23+
|
2224

2325
error: aborting due to previous error
2426

tests/ui/argument-suggestions/basic.stderr

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ LL | fn extra() {}
2525
| ^^^^^
2626
help: remove the extra argument
2727
|
28-
LL | extra();
29-
| ~~
28+
LL - extra("");
29+
LL + extra();
30+
|
3031

3132
error[E0061]: this function takes 1 argument but 0 arguments were supplied
3233
--> $DIR/basic.rs:22:5

tests/ui/argument-suggestions/exotic-calls.stderr

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ LL | fn foo<T: Fn()>(t: T) {
1111
| ^^^^
1212
help: remove the extra argument
1313
|
14-
LL | t();
15-
| ~~
14+
LL - t(1i32);
15+
LL + t();
16+
|
1617

1718
error[E0057]: this function takes 0 arguments but 1 argument was supplied
1819
--> $DIR/exotic-calls.rs:7:5
@@ -27,8 +28,9 @@ LL | fn bar(t: impl Fn()) {
2728
| ^^^^^^^^^
2829
help: remove the extra argument
2930
|
30-
LL | t();
31-
| ~~
31+
LL - t(1i32);
32+
LL + t();
33+
|
3234

3335
error[E0057]: this function takes 0 arguments but 1 argument was supplied
3436
--> $DIR/exotic-calls.rs:16:5
@@ -43,8 +45,9 @@ LL | fn baz() -> impl Fn() {
4345
| ^^^^^^^^^
4446
help: remove the extra argument
4547
|
46-
LL | baz()()
47-
| ~~
48+
LL - baz()(1i32)
49+
LL + baz()()
50+
|
4851

4952
error[E0057]: this function takes 0 arguments but 1 argument was supplied
5053
--> $DIR/exotic-calls.rs:22:5
@@ -59,8 +62,9 @@ LL | let x = || {};
5962
| ^^
6063
help: remove the extra argument
6164
|
62-
LL | x();
63-
| ~~
65+
LL - x(1i32);
66+
LL + x();
67+
|
6468

6569
error: aborting due to 4 previous errors
6670

0 commit comments

Comments
 (0)