-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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), | ||
}; | ||
|
@@ -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; | ||
} | ||
|
@@ -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 | ||
}); | ||
|
@@ -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; | ||
} | ||
|
@@ -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 { | ||
|
@@ -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
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 😅 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||
|
@@ -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( | ||
|
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() {} |
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, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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`. |
There was a problem hiding this comment.
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 :-/There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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) :)