Rollup merge of #116250 - estebank:closure-arg-inference-span, r=petrochenkov
On type error of closure call argument, point at earlier calls that affected inference Mitigate part of https://github.com/rust-lang/rust/issues/71209. When we encounter a type error on a specific argument of a closure call argument, where the closure's definition doesn't have a type specified, look for other calls of the closure to try and find the specific call that cased that argument to be inferred of the expected type. ``` error[E0308]: mismatched types --> $DIR/unboxed-closures-type-mismatch.rs:30:18 | LL | identity(1u16); | -------- ^^^^ expected `u8`, found `u16` | | | arguments to this function are incorrect | note: expected because the closure was earlier called with an argument of type `u8` --> $DIR/unboxed-closures-type-mismatch.rs:29:18 | LL | identity(1u8); | -------- ^^^ expected because this argument is of type `u8` | | | in this closure call note: closure parameter defined here --> $DIR/unboxed-closures-type-mismatch.rs:28:25 | LL | let identity = |x| x; | ^ help: change the type of the numeric literal from `u16` to `u8` | LL | identity(1u8); | ~~ ```
This commit is contained in:
commit
0e5e04b89a
@ -16,6 +16,7 @@
|
|||||||
use rustc_hir as hir;
|
use rustc_hir as hir;
|
||||||
use rustc_hir::def::{CtorOf, DefKind, Res};
|
use rustc_hir::def::{CtorOf, DefKind, Res};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
|
use rustc_hir::intravisit::Visitor;
|
||||||
use rustc_hir::{ExprKind, Node, QPath};
|
use rustc_hir::{ExprKind, Node, QPath};
|
||||||
use rustc_hir_analysis::astconv::AstConv;
|
use rustc_hir_analysis::astconv::AstConv;
|
||||||
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
|
use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt;
|
||||||
@ -28,7 +29,7 @@
|
|||||||
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
use rustc_infer::infer::{DefineOpaqueTypes, InferOk};
|
||||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||||
use rustc_middle::ty::visit::TypeVisitableExt;
|
use rustc_middle::ty::visit::TypeVisitableExt;
|
||||||
use rustc_middle::ty::{self, IsSuggestable, Ty};
|
use rustc_middle::ty::{self, IsSuggestable, Ty, TyCtxt};
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
use rustc_span::symbol::{kw, Ident};
|
use rustc_span::symbol::{kw, Ident};
|
||||||
use rustc_span::{self, sym, BytePos, Span};
|
use rustc_span::{self, sym, BytePos, Span};
|
||||||
@ -722,6 +723,8 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
|
|||||||
&mut err,
|
&mut err,
|
||||||
fn_def_id,
|
fn_def_id,
|
||||||
callee_ty,
|
callee_ty,
|
||||||
|
call_expr,
|
||||||
|
None,
|
||||||
Some(mismatch_idx),
|
Some(mismatch_idx),
|
||||||
is_method,
|
is_method,
|
||||||
);
|
);
|
||||||
@ -826,6 +829,8 @@ fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool {
|
|||||||
&mut err,
|
&mut err,
|
||||||
fn_def_id,
|
fn_def_id,
|
||||||
callee_ty,
|
callee_ty,
|
||||||
|
call_expr,
|
||||||
|
Some(expected_ty),
|
||||||
Some(expected_idx.as_usize()),
|
Some(expected_idx.as_usize()),
|
||||||
is_method,
|
is_method,
|
||||||
);
|
);
|
||||||
@ -1208,7 +1213,7 @@ enum SuggestionText {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Call out where the function is defined
|
// Call out where the function is defined
|
||||||
self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method);
|
self.label_fn_like(&mut err, fn_def_id, callee_ty, call_expr, None, None, is_method);
|
||||||
|
|
||||||
// And add a suggestion block for all of the parameters
|
// And add a suggestion block for all of the parameters
|
||||||
let suggestion_text = match suggestion_text {
|
let suggestion_text = match suggestion_text {
|
||||||
@ -1899,6 +1904,8 @@ fn label_fn_like(
|
|||||||
err: &mut Diagnostic,
|
err: &mut Diagnostic,
|
||||||
callable_def_id: Option<DefId>,
|
callable_def_id: Option<DefId>,
|
||||||
callee_ty: Option<Ty<'tcx>>,
|
callee_ty: Option<Ty<'tcx>>,
|
||||||
|
call_expr: &'tcx hir::Expr<'tcx>,
|
||||||
|
expected_ty: Option<Ty<'tcx>>,
|
||||||
// A specific argument should be labeled, instead of all of them
|
// A specific argument should be labeled, instead of all of them
|
||||||
expected_idx: Option<usize>,
|
expected_idx: Option<usize>,
|
||||||
is_method: bool,
|
is_method: bool,
|
||||||
@ -2015,6 +2022,46 @@ fn label_fn_like(
|
|||||||
let param = expected_idx
|
let param = expected_idx
|
||||||
.and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
|
.and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx));
|
||||||
let (kind, span) = if let Some(param) = param {
|
let (kind, span) = if let Some(param) = param {
|
||||||
|
// Try to find earlier invocations of this closure to find if the type mismatch
|
||||||
|
// is because of inference. If we find one, point at them.
|
||||||
|
let mut call_finder = FindClosureArg { tcx: self.tcx, calls: vec![] };
|
||||||
|
let node = self.tcx
|
||||||
|
.opt_local_def_id_to_hir_id(self.tcx.hir().get_parent_item(call_expr.hir_id))
|
||||||
|
.and_then(|hir_id| self.tcx.hir().find(hir_id));
|
||||||
|
match node {
|
||||||
|
Some(hir::Node::Item(item)) => call_finder.visit_item(item),
|
||||||
|
Some(hir::Node::TraitItem(item)) => call_finder.visit_trait_item(item),
|
||||||
|
Some(hir::Node::ImplItem(item)) => call_finder.visit_impl_item(item),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
let typeck = self.typeck_results.borrow();
|
||||||
|
for (rcvr, args) in call_finder.calls {
|
||||||
|
if let Some(rcvr_ty) = typeck.node_type_opt(rcvr.hir_id)
|
||||||
|
&& let ty::Closure(call_def_id, _) = rcvr_ty.kind()
|
||||||
|
&& def_id == *call_def_id
|
||||||
|
&& let Some(idx) = expected_idx
|
||||||
|
&& let Some(arg) = args.get(idx)
|
||||||
|
&& let Some(arg_ty) = typeck.node_type_opt(arg.hir_id)
|
||||||
|
&& let Some(expected_ty) = expected_ty
|
||||||
|
&& self.can_eq(self.param_env, arg_ty, expected_ty)
|
||||||
|
{
|
||||||
|
let mut sp: MultiSpan = vec![arg.span].into();
|
||||||
|
sp.push_span_label(
|
||||||
|
arg.span,
|
||||||
|
format!("expected because this argument is of type `{arg_ty}`"),
|
||||||
|
);
|
||||||
|
sp.push_span_label(rcvr.span, "in this closure call");
|
||||||
|
err.span_note(
|
||||||
|
sp,
|
||||||
|
format!(
|
||||||
|
"expected because the closure was earlier called with an \
|
||||||
|
argument of type `{arg_ty}`",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
("closure parameter", param.span)
|
("closure parameter", param.span)
|
||||||
} else {
|
} else {
|
||||||
("closure", self.tcx.def_span(def_id))
|
("closure", self.tcx.def_span(def_id))
|
||||||
@ -2028,3 +2075,23 @@ fn label_fn_like(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct FindClosureArg<'tcx> {
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
calls: Vec<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> Visitor<'tcx> for FindClosureArg<'tcx> {
|
||||||
|
type NestedFilter = rustc_middle::hir::nested_filter::All;
|
||||||
|
|
||||||
|
fn nested_visit_map(&mut self) -> Self::Map {
|
||||||
|
self.tcx.hir()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
||||||
|
if let hir::ExprKind::Call(rcvr, args) = ex.kind {
|
||||||
|
self.calls.push((rcvr, args));
|
||||||
|
}
|
||||||
|
hir::intravisit::walk_expr(self, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,7 +1,35 @@
|
|||||||
use std::ops::FnMut;
|
use std::ops::FnMut;
|
||||||
|
|
||||||
pub fn main() {
|
fn main() {
|
||||||
let mut f = |x: isize, y: isize| -> isize { x + y };
|
let mut f = |x: isize, y: isize| -> isize { x + y };
|
||||||
let z = f(1_usize, 2); //~ ERROR mismatched types
|
let z = f(1_usize, 2); //~ ERROR mismatched types
|
||||||
println!("{}", z);
|
println!("{}", z);
|
||||||
|
let mut g = |x, y| { x + y };
|
||||||
|
let y = g(1_i32, 2);
|
||||||
|
let z = g(1_usize, 2); //~ ERROR mismatched types
|
||||||
|
println!("{}", z);
|
||||||
|
}
|
||||||
|
|
||||||
|
trait T {
|
||||||
|
fn bar(&self) {
|
||||||
|
let identity = |x| x;
|
||||||
|
identity(1u8);
|
||||||
|
identity(1u16); //~ ERROR mismatched types
|
||||||
|
let identity = |x| x;
|
||||||
|
identity(&1u8);
|
||||||
|
identity(&1u16); //~ ERROR mismatched types
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl T for S {
|
||||||
|
fn bar(&self) {
|
||||||
|
let identity = |x| x;
|
||||||
|
identity(1u8);
|
||||||
|
identity(1u16); //~ ERROR mismatched types
|
||||||
|
let identity = |x| x;
|
||||||
|
identity(&1u8);
|
||||||
|
identity(&1u16); //~ ERROR mismatched types
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,127 @@ help: change the type of the numeric literal from `usize` to `isize`
|
|||||||
LL | let z = f(1_isize, 2);
|
LL | let z = f(1_isize, 2);
|
||||||
| ~~~~~
|
| ~~~~~
|
||||||
|
|
||||||
error: aborting due to previous error
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:9:15
|
||||||
|
|
|
||||||
|
LL | let z = g(1_usize, 2);
|
||||||
|
| - ^^^^^^^ expected `i32`, found `usize`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
note: expected because the closure was earlier called with an argument of type `i32`
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:8:15
|
||||||
|
|
|
||||||
|
LL | let y = g(1_i32, 2);
|
||||||
|
| - ^^^^^ expected because this argument is of type `i32`
|
||||||
|
| |
|
||||||
|
| in this closure call
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:7:18
|
||||||
|
|
|
||||||
|
LL | let mut g = |x, y| { x + y };
|
||||||
|
| ^
|
||||||
|
help: change the type of the numeric literal from `usize` to `i32`
|
||||||
|
|
|
||||||
|
LL | let z = g(1_i32, 2);
|
||||||
|
| ~~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:17:18
|
||||||
|
|
|
||||||
|
LL | identity(1u16);
|
||||||
|
| -------- ^^^^ expected `u8`, found `u16`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
note: expected because the closure was earlier called with an argument of type `u8`
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:16:18
|
||||||
|
|
|
||||||
|
LL | identity(1u8);
|
||||||
|
| -------- ^^^ expected because this argument is of type `u8`
|
||||||
|
| |
|
||||||
|
| in this closure call
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:15:25
|
||||||
|
|
|
||||||
|
LL | let identity = |x| x;
|
||||||
|
| ^
|
||||||
|
help: change the type of the numeric literal from `u16` to `u8`
|
||||||
|
|
|
||||||
|
LL | identity(1u8);
|
||||||
|
| ~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:20:18
|
||||||
|
|
|
||||||
|
LL | identity(&1u16);
|
||||||
|
| -------- ^^^^^ expected `&u8`, found `&u16`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected reference `&u8`
|
||||||
|
found reference `&u16`
|
||||||
|
note: expected because the closure was earlier called with an argument of type `&u8`
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:19:18
|
||||||
|
|
|
||||||
|
LL | identity(&1u8);
|
||||||
|
| -------- ^^^^ expected because this argument is of type `&u8`
|
||||||
|
| |
|
||||||
|
| in this closure call
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:18:25
|
||||||
|
|
|
||||||
|
LL | let identity = |x| x;
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:30:18
|
||||||
|
|
|
||||||
|
LL | identity(1u16);
|
||||||
|
| -------- ^^^^ expected `u8`, found `u16`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
note: expected because the closure was earlier called with an argument of type `u8`
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:29:18
|
||||||
|
|
|
||||||
|
LL | identity(1u8);
|
||||||
|
| -------- ^^^ expected because this argument is of type `u8`
|
||||||
|
| |
|
||||||
|
| in this closure call
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:28:25
|
||||||
|
|
|
||||||
|
LL | let identity = |x| x;
|
||||||
|
| ^
|
||||||
|
help: change the type of the numeric literal from `u16` to `u8`
|
||||||
|
|
|
||||||
|
LL | identity(1u8);
|
||||||
|
| ~~
|
||||||
|
|
||||||
|
error[E0308]: mismatched types
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:33:18
|
||||||
|
|
|
||||||
|
LL | identity(&1u16);
|
||||||
|
| -------- ^^^^^ expected `&u8`, found `&u16`
|
||||||
|
| |
|
||||||
|
| arguments to this function are incorrect
|
||||||
|
|
|
||||||
|
= note: expected reference `&u8`
|
||||||
|
found reference `&u16`
|
||||||
|
note: expected because the closure was earlier called with an argument of type `&u8`
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:32:18
|
||||||
|
|
|
||||||
|
LL | identity(&1u8);
|
||||||
|
| -------- ^^^^ expected because this argument is of type `&u8`
|
||||||
|
| |
|
||||||
|
| in this closure call
|
||||||
|
note: closure parameter defined here
|
||||||
|
--> $DIR/unboxed-closures-type-mismatch.rs:31:25
|
||||||
|
|
|
||||||
|
LL | let identity = |x| x;
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to 6 previous errors
|
||||||
|
|
||||||
For more information about this error, try `rustc --explain E0308`.
|
For more information about this error, try `rustc --explain E0308`.
|
||||||
|
Loading…
Reference in New Issue
Block a user