Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Only point out a single function parameter if we have a single arg incompatibility #99646

Merged
merged 4 commits into from
Aug 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion compiler/rustc_parse/src/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2268,7 +2268,7 @@ impl<'a> Parser<'a> {
attrs: attrs.into(),
ty,
pat,
span: lo.to(this.token.span),
span: lo.to(this.prev_token.span),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change means that we no longer include the comma in the param span, right? I don't know if that is a good call 🤔

Maybe we can address the span issue on the other end, by creating a new span that is param.pat.span.to(param.ty.map_or(param.pat.span, |ty| ty.span). I guess it depends on what we've used the param's span for elsewhere (even though there haven't been any visible changes in our test suite from this... I guess I'm fine either way :-/

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think nobody used it, evidenced by the lack of UI test changes.

This makes it more consistent with regular fn signatures' spans.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fine, let's go ahead with that change then. We can revisit this if we ever need the param + comma (I'm sure, for removal suggestions) :)

id: DUMMY_NODE_ID,
is_placeholder: false,
},
Expand Down
58 changes: 40 additions & 18 deletions compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -440,30 +440,29 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
call_expr: &hir::Expr<'tcx>,
) {
// Next, let's construct the error
let (error_span, full_call_span, ctor_of) = match &call_expr.kind {
let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind {
hir::ExprKind::Call(
hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. },
_,
) => {
if let Res::Def(DefKind::Ctor(of, _), _) =
self.typeck_results.borrow().qpath_res(qpath, *hir_id)
{
(call_span, *span, Some(of))
(call_span, *span, Some(of), false)
} else {
(call_span, *span, None)
(call_span, *span, None, false)
}
}
hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None),
hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false),
hir::ExprKind::MethodCall(path_segment, _, span) => {
let ident_span = path_segment.ident.span;
let ident_span = if let Some(args) = path_segment.args {
ident_span.with_hi(args.span_ext.hi())
} else {
ident_span
};
(
*span, ident_span, None, // methods are never ctors
)
// methods are never ctors
(*span, ident_span, None, true)
}
k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k),
};
Expand Down Expand Up @@ -659,7 +658,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
Applicability::MachineApplicable,
);
};
self.label_fn_like(&mut err, fn_def_id, callee_ty);
self.label_fn_like(&mut err, fn_def_id, callee_ty, Some(mismatch_idx), is_method);
err.emit();
return;
}
Expand Down Expand Up @@ -701,16 +700,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

errors.drain_filter(|error| {
let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(error)) = error else { return false };
let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false };
let (provided_ty, provided_span) = provided_arg_tys[*provided_idx];
let (expected_ty, _) = formal_and_expected_inputs[*expected_idx];
let cause = &self.misc(provided_span);
let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
if let Some(e) = error {
if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
self.report_and_explain_type_error(trace, e).emit();
return true;
}
if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
self.report_and_explain_type_error(trace, e).emit();
return true;
}
false
});
Expand Down Expand Up @@ -749,7 +746,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
format!("arguments to this {} are incorrect", call_name),
);
// Call out where the function is defined
self.label_fn_like(&mut err, fn_def_id, callee_ty);
self.label_fn_like(
&mut err,
fn_def_id,
callee_ty,
Some(expected_idx.as_usize()),
is_method,
);
err.emit();
return;
}
Expand Down Expand Up @@ -1031,7 +1034,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}

// Call out where the function is defined
self.label_fn_like(&mut err, fn_def_id, callee_ty);
self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);

// And add a suggestion block for all of the parameters
let suggestion_text = match suggestion_text {
Expand Down Expand Up @@ -1781,6 +1784,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err: &mut Diagnostic,
callable_def_id: Option<DefId>,
callee_ty: Option<Ty<'tcx>>,
// A specific argument should be labeled, instead of all of them
expected_idx: Option<usize>,
Comment on lines +1787 to +1788
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this only works if there's a single error in the function arguments, right? would it be possible to extend it to multiple arguments? https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7a212f35d6f07a993c223f3e4662db39

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The further we move from certainty, the more we might mislead if we point at things too confidently. I feel like doing this only for single args for now is a good compromise while we come up with better heuristics. The suggestion being wrong is counteracted by pointing at the whole fn signature, but if we no longer do that then we're implicitly claiming infallibility 😅

Copy link
Member

@jyn514 jyn514 Aug 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For context, I was hoping this change would make the awful error in https://mobile.twitter.com/joshuayn514/status/1557913836337786880 better.

Maybe we can somehow limit this to cases where the arguments are "close", for some definition of close? like, say, a function pointer where only the arguments differ? :P

(I don't actually have great general suggestions here but I would love that error to be smarter than it is now ...)

is_method: bool,
) {
let Some(mut def_id) = callable_def_id else {
return;
Expand Down Expand Up @@ -1881,14 +1887,30 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
.get_if_local(def_id)
.and_then(|node| node.body_id())
.into_iter()
.flat_map(|id| self.tcx.hir().body(id).params);
.flat_map(|id| self.tcx.hir().body(id).params)
.skip(if is_method { 1 } else { 0 });

for param in params {
for (_, param) in params
.into_iter()
.enumerate()
.filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx))
{
spans.push_span_label(param.span, "");
}

let def_kind = self.tcx.def_kind(def_id);
err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id)));
} else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id)
&& let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind
{
let param = expected_idx
.and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
let (kind, span) = if let Some(param) = param {
("closure parameter", param.span)
} else {
("closure", self.tcx.def_span(def_id))
};
err.span_note(span, &format!("{} defined here", kind));
} else {
let def_kind = self.tcx.def_kind(def_id);
err.span_note(
Expand Down
20 changes: 10 additions & 10 deletions src/test/ui/argument-suggestions/invalid_arguments.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:6:4
|
LL | fn two_arg_same(_a: i32, _b: i32) {}
| ^^^^^^^^^^^^ ------- -------
| ^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:17:16
Expand All @@ -38,7 +38,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:6:4
|
LL | fn two_arg_same(_a: i32, _b: i32) {}
| ^^^^^^^^^^^^ ------- -------
| ^^^^^^^^^^^^ -------

error[E0308]: arguments to this function are incorrect
--> $DIR/invalid_arguments.rs:18:3
Expand Down Expand Up @@ -66,7 +66,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:7:4
|
LL | fn two_arg_diff(_a: i32, _b: f32) {}
| ^^^^^^^^^^^^ ------- -------
| ^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:20:16
Expand All @@ -80,7 +80,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:7:4
|
LL | fn two_arg_diff(_a: i32, _b: f32) {}
| ^^^^^^^^^^^^ ------- -------
| ^^^^^^^^^^^^ -------

error[E0308]: arguments to this function are incorrect
--> $DIR/invalid_arguments.rs:21:3
Expand Down Expand Up @@ -108,7 +108,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:8:4
|
LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
| ^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:25:21
Expand All @@ -122,7 +122,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:8:4
|
LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
| ^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:26:26
Expand All @@ -136,7 +136,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:8:4
|
LL | fn three_arg_diff(_a: i32, _b: f32, _c: &str) {}
| ^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^ --------

error[E0308]: arguments to this function are incorrect
--> $DIR/invalid_arguments.rs:28:3
Expand Down Expand Up @@ -207,7 +207,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:9:4
|
LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
| ^^^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:35:23
Expand All @@ -221,7 +221,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:9:4
|
LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
| ^^^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^^^ -------

error[E0308]: mismatched types
--> $DIR/invalid_arguments.rs:36:26
Expand All @@ -235,7 +235,7 @@ note: function defined here
--> $DIR/invalid_arguments.rs:9:4
|
LL | fn three_arg_repeat(_a: i32, _b: i32, _c: &str) {}
| ^^^^^^^^^^^^^^^^ ------- ------- --------
| ^^^^^^^^^^^^^^^^ --------

error[E0308]: arguments to this function are incorrect
--> $DIR/invalid_arguments.rs:38:3
Expand Down
41 changes: 41 additions & 0 deletions src/test/ui/argument-suggestions/too-long.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
struct Qux;

impl Qux {
fn foo(
&self,
a: i32,
b: i32,
c: i32,
d: i32,
e: i32,
f: i32,
g: i32,
h: i32,
i: i32,
j: i32,
k: i32,
l: i32,
) {
}
}

fn what(
qux: &Qux,
a: i32,
b: i32,
c: i32,
d: i32,
e: i32,
f: &i32,
g: i32,
h: i32,
i: i32,
j: i32,
k: i32,
l: i32,
) {
qux.foo(a, b, c, d, e, f, g, h, i, j, k, l);
//~^ ERROR mismatched types
}

fn main() {}
24 changes: 24 additions & 0 deletions src/test/ui/argument-suggestions/too-long.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0308]: mismatched types
--> $DIR/too-long.rs:37:28
|
LL | qux.foo(a, b, c, d, e, f, g, h, i, j, k, l);
| --- ^ expected `i32`, found `&i32`
| |
| arguments to this function are incorrect
|
note: associated function defined here
--> $DIR/too-long.rs:4:8
|
LL | fn foo(
| ^^^
...
LL | f: i32,
Copy link
Member Author

@compiler-errors compiler-errors Jul 23, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could say "parameter defined here" if we have just one (or maybe just if the signature is multiline)

| ------
help: consider dereferencing the borrow
|
LL | qux.foo(a, b, c, d, e, *f, g, h, i, j, k, l);
| +

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ note: function defined here
--> $DIR/associated-type-projection-from-supertrait.rs:25:4
|
LL | fn dent<C:Car>(c: C, color: C::Color) { c.chip_paint(color) }
| ^^^^ ---- ---------------
| ^^^^ ---------------

error[E0308]: mismatched types
--> $DIR/associated-type-projection-from-supertrait.rs:28:23
Expand All @@ -24,7 +24,7 @@ note: function defined here
--> $DIR/associated-type-projection-from-supertrait.rs:25:4
|
LL | fn dent<C:Car>(c: C, color: C::Color) { c.chip_paint(color) }
| ^^^^ ---- ---------------
| ^^^^ ---------------

error[E0308]: mismatched types
--> $DIR/associated-type-projection-from-supertrait.rs:32:28
Expand All @@ -38,7 +38,7 @@ note: associated function defined here
--> $DIR/associated-type-projection-from-supertrait.rs:12:8
|
LL | fn chip_paint(&self, c: Self::Color) { }
| ^^^^^^^^^^ ----- --------------
| ^^^^^^^^^^ --------------

error[E0308]: mismatched types
--> $DIR/associated-type-projection-from-supertrait.rs:33:28
Expand All @@ -52,7 +52,7 @@ note: associated function defined here
--> $DIR/associated-type-projection-from-supertrait.rs:12:8
|
LL | fn chip_paint(&self, c: Self::Color) { }
| ^^^^^^^^^^ ----- --------------
| ^^^^^^^^^^ --------------

error: aborting due to 4 previous errors

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ note: function defined here
--> $DIR/associated-types-path-2.rs:13:8
|
LL | pub fn f1<T: Foo>(a: T, x: T::A) {}
| ^^ ---- -------
| ^^ -------
help: change the type of the numeric literal from `i32` to `u32`
|
LL | f1(2i32, 4u32);
Expand Down
4 changes: 2 additions & 2 deletions src/test/ui/async-await/generator-desc.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ note: function defined here
--> $DIR/generator-desc.rs:8:4
|
LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {}
| ^^^ ----- -----
| ^^^ -----

error[E0308]: mismatched types
--> $DIR/generator-desc.rs:14:26
Expand All @@ -67,7 +67,7 @@ note: function defined here
--> $DIR/generator-desc.rs:8:4
|
LL | fn fun<F: Future<Output = ()>>(f1: F, f2: F) {}
| ^^^ ----- -----
| ^^^ -----

error: aborting due to 3 previous errors

Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/coercion/coerce-reborrow-multi-arg-fail.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ note: function defined here
--> $DIR/coerce-reborrow-multi-arg-fail.rs:1:4
|
LL | fn test<T>(_a: T, _b: T) {}
| ^^^^ ----- -----
| ^^^^ -----

error: aborting due to previous error

Expand Down
Loading