Yeet point_at_expr_source_of_inferred_type for now
This commit is contained in:
parent
2410b0dfdf
commit
be90b53d2b
10 changed files with 6 additions and 276 deletions
|
|
@ -1,11 +1,9 @@
|
|||
use crate::FnCtxt;
|
||||
use rustc_ast::util::parser::PREC_POSTFIX;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::MultiSpan;
|
||||
use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::CtorKind;
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::lang_items::LangItem;
|
||||
use rustc_hir::{is_range_literal, Node};
|
||||
use rustc_infer::infer::InferOk;
|
||||
|
|
@ -13,14 +11,11 @@ use rustc_middle::lint::in_external_macro;
|
|||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::ty::adjustment::AllowTwoPhase;
|
||||
use rustc_middle::ty::error::{ExpectedFound, TypeError};
|
||||
use rustc_middle::ty::fold::{BottomUpFolder, TypeFolder};
|
||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
||||
use rustc_middle::ty::relate::TypeRelation;
|
||||
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeVisitable};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut};
|
||||
use rustc_span::symbol::{sym, Symbol};
|
||||
use rustc_span::{BytePos, Span};
|
||||
use rustc_trait_selection::infer::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::error_reporting::method_chain::CollectAllMismatches;
|
||||
use rustc_trait_selection::traits::ObligationCause;
|
||||
|
||||
use super::method::probe;
|
||||
|
|
@ -45,7 +40,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.annotate_alternative_method_deref(err, expr, error);
|
||||
|
||||
// Use `||` to give these suggestions a precedence
|
||||
let suggested = self.suggest_missing_parentheses(err, expr)
|
||||
let _ = self.suggest_missing_parentheses(err, expr)
|
||||
|| self.suggest_remove_last_method_call(err, expr, expected)
|
||||
|| self.suggest_associated_const(err, expr, expected)
|
||||
|| self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr)
|
||||
|
|
@ -60,9 +55,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|
||||
|| self.suggest_into(err, expr, expr_ty, expected)
|
||||
|| self.suggest_floating_point_literal(err, expr, expected);
|
||||
if !suggested {
|
||||
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn emit_coerce_suggestions(
|
||||
|
|
@ -216,216 +208,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(expected, Some(err))
|
||||
}
|
||||
|
||||
pub fn point_at_expr_source_of_inferred_type(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
expr: &hir::Expr<'_>,
|
||||
found: Ty<'tcx>,
|
||||
expected: Ty<'tcx>,
|
||||
) -> bool {
|
||||
let map = self.tcx.hir();
|
||||
|
||||
let hir::ExprKind::Path(hir::QPath::Resolved(None, p)) = expr.kind else { return false; };
|
||||
let [hir::PathSegment { ident, args: None, .. }] = p.segments else { return false; };
|
||||
let hir::def::Res::Local(hir_id) = p.res else { return false; };
|
||||
let Some(hir::Node::Pat(pat)) = map.find(hir_id) else { return false; };
|
||||
let Some(hir::Node::Local(hir::Local {
|
||||
ty: None,
|
||||
init: Some(init),
|
||||
..
|
||||
})) = map.find_parent(pat.hir_id) else { return false; };
|
||||
let Some(ty) = self.node_ty_opt(init.hir_id) else { return false; };
|
||||
if ty.is_closure() || init.span.overlaps(expr.span) || pat.span.from_expansion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Locate all the usages of the relevant binding.
|
||||
struct FindExprs<'hir> {
|
||||
hir_id: hir::HirId,
|
||||
uses: Vec<&'hir hir::Expr<'hir>>,
|
||||
}
|
||||
impl<'v> Visitor<'v> for FindExprs<'v> {
|
||||
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
|
||||
if let hir::ExprKind::Path(hir::QPath::Resolved(None, path)) = ex.kind
|
||||
&& let hir::def::Res::Local(hir_id) = path.res
|
||||
&& hir_id == self.hir_id
|
||||
{
|
||||
self.uses.push(ex);
|
||||
}
|
||||
hir::intravisit::walk_expr(self, ex);
|
||||
}
|
||||
}
|
||||
|
||||
let mut expr_finder = FindExprs { hir_id, uses: vec![] };
|
||||
let id = map.get_parent_item(hir_id);
|
||||
let hir_id: hir::HirId = id.into();
|
||||
|
||||
let Some(node) = map.find(hir_id) else { return false; };
|
||||
let Some(body_id) = node.body_id() else { return false; };
|
||||
let body = map.body(body_id);
|
||||
expr_finder.visit_expr(body.value);
|
||||
// Hack to make equality checks on types with inference variables and regions useful.
|
||||
let mut eraser = BottomUpFolder {
|
||||
tcx: self.tcx,
|
||||
lt_op: |_| self.tcx.lifetimes.re_erased,
|
||||
ct_op: |c| c,
|
||||
ty_op: |t| match *t.kind() {
|
||||
ty::Infer(ty::TyVar(vid)) => self.tcx.mk_ty_infer(ty::TyVar(self.root_var(vid))),
|
||||
ty::Infer(ty::IntVar(_)) => {
|
||||
self.tcx.mk_ty_infer(ty::IntVar(ty::IntVid { index: 0 }))
|
||||
}
|
||||
ty::Infer(ty::FloatVar(_)) => {
|
||||
self.tcx.mk_ty_infer(ty::FloatVar(ty::FloatVid { index: 0 }))
|
||||
}
|
||||
_ => t,
|
||||
},
|
||||
};
|
||||
let mut prev = eraser.fold_ty(ty);
|
||||
let mut prev_span = None;
|
||||
|
||||
for binding in expr_finder.uses {
|
||||
// In every expression where the binding is referenced, we will look at that
|
||||
// expression's type and see if it is where the incorrect found type was fully
|
||||
// "materialized" and point at it. We will also try to provide a suggestion there.
|
||||
if let Some(hir::Node::Expr(expr)
|
||||
| hir::Node::Stmt(hir::Stmt {
|
||||
kind: hir::StmtKind::Expr(expr) | hir::StmtKind::Semi(expr),
|
||||
..
|
||||
})) = &map.find_parent(binding.hir_id)
|
||||
&& let hir::ExprKind::MethodCall(segment, rcvr, args, _span) = expr.kind
|
||||
&& rcvr.hir_id == binding.hir_id
|
||||
&& let Some(def_id) = self.typeck_results.borrow().type_dependent_def_id(expr.hir_id)
|
||||
{
|
||||
// We special case methods, because they can influence inference through the
|
||||
// call's arguments and we can provide a more explicit span.
|
||||
let sig = self.tcx.fn_sig(def_id);
|
||||
let def_self_ty = sig.input(0).skip_binder();
|
||||
let rcvr_ty = self.node_ty(rcvr.hir_id);
|
||||
// Get the evaluated type *after* calling the method call, so that the influence
|
||||
// of the arguments can be reflected in the receiver type. The receiver
|
||||
// expression has the type *before* theis analysis is done.
|
||||
let ty = match self.lookup_probe_for_diagnostic(
|
||||
segment.ident,
|
||||
rcvr_ty,
|
||||
expr,
|
||||
probe::ProbeScope::TraitsInScope,
|
||||
None,
|
||||
) {
|
||||
Ok(pick) => pick.self_ty,
|
||||
Err(_) => rcvr_ty,
|
||||
};
|
||||
// Remove one layer of references to account for `&mut self` and
|
||||
// `&self`, so that we can compare it against the binding.
|
||||
let (ty, def_self_ty) = match (ty.kind(), def_self_ty.kind()) {
|
||||
(ty::Ref(_, ty, a), ty::Ref(_, self_ty, b)) if a == b => (*ty, *self_ty),
|
||||
_ => (ty, def_self_ty),
|
||||
};
|
||||
let mut param_args = FxHashMap::default();
|
||||
let mut param_expected = FxHashMap::default();
|
||||
let mut param_found = FxHashMap::default();
|
||||
if self.can_eq(self.param_env, ty, found).is_ok() {
|
||||
// We only point at the first place where the found type was inferred.
|
||||
for (i, param_ty) in sig.inputs().skip_binder().iter().skip(1).enumerate() {
|
||||
if def_self_ty.contains(*param_ty) && let ty::Param(_) = param_ty.kind() {
|
||||
// We found an argument that references a type parameter in `Self`,
|
||||
// so we assume that this is the argument that caused the found
|
||||
// type, which we know already because of `can_eq` above was first
|
||||
// inferred in this method call.
|
||||
let arg = &args[i];
|
||||
let arg_ty = self.node_ty(arg.hir_id);
|
||||
err.span_label(
|
||||
arg.span,
|
||||
&format!(
|
||||
"this is of type `{arg_ty}`, which causes `{ident}` to be \
|
||||
inferred as `{ty}`",
|
||||
),
|
||||
);
|
||||
param_args.insert(param_ty, (arg, arg_ty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Here we find, for a type param `T`, the type that `T` is in the current
|
||||
// method call *and* in the original expected type. That way, we can see if we
|
||||
// can give any structured suggestion for the function argument.
|
||||
let mut c = CollectAllMismatches {
|
||||
infcx: &self.infcx,
|
||||
param_env: self.param_env,
|
||||
errors: vec![],
|
||||
};
|
||||
let _ = c.relate(def_self_ty, ty);
|
||||
for error in c.errors {
|
||||
if let TypeError::Sorts(error) = error {
|
||||
param_found.insert(error.expected, error.found);
|
||||
}
|
||||
}
|
||||
c.errors = vec![];
|
||||
let _ = c.relate(def_self_ty, expected);
|
||||
for error in c.errors {
|
||||
if let TypeError::Sorts(error) = error {
|
||||
param_expected.insert(error.expected, error.found);
|
||||
}
|
||||
}
|
||||
for (param, (arg, arg_ty)) in param_args.iter() {
|
||||
let Some(expected) = param_expected.get(param) else { continue; };
|
||||
let Some(found) = param_found.get(param) else { continue; };
|
||||
if self.can_eq(self.param_env, *arg_ty, *found).is_err() { continue; }
|
||||
self.emit_coerce_suggestions(err, arg, *found, *expected, None, None);
|
||||
}
|
||||
|
||||
let ty = eraser.fold_ty(ty);
|
||||
if ty.references_error() {
|
||||
break;
|
||||
}
|
||||
if ty != prev
|
||||
&& param_args.is_empty()
|
||||
&& self.can_eq(self.param_env, ty, found).is_ok()
|
||||
{
|
||||
// We only point at the first place where the found type was inferred.
|
||||
err.span_label(
|
||||
segment.ident.span,
|
||||
with_forced_trimmed_paths!(format!(
|
||||
"here the type of `{ident}` is inferred to be `{ty}`",
|
||||
)),
|
||||
);
|
||||
break;
|
||||
} else if !param_args.is_empty() {
|
||||
break;
|
||||
}
|
||||
prev = ty;
|
||||
} else {
|
||||
let ty = eraser.fold_ty(self.node_ty(binding.hir_id));
|
||||
if ty.references_error() {
|
||||
break;
|
||||
}
|
||||
if ty != prev
|
||||
&& let Some(span) = prev_span
|
||||
&& self.can_eq(self.param_env, ty, found).is_ok()
|
||||
{
|
||||
// We only point at the first place where the found type was inferred.
|
||||
// We use the *previous* span because if the type is known *here* it means
|
||||
// it was *evaluated earlier*. We don't do this for method calls because we
|
||||
// evaluate the method's self type eagerly, but not in any other case.
|
||||
err.span_label(
|
||||
span,
|
||||
with_forced_trimmed_paths!(format!(
|
||||
"here the type of `{ident}` is inferred to be `{ty}`",
|
||||
)),
|
||||
);
|
||||
break;
|
||||
}
|
||||
prev = ty;
|
||||
}
|
||||
if binding.hir_id == expr.hir_id {
|
||||
// Do not look at expressions that come after the expression we were originally
|
||||
// evaluating and had a type error.
|
||||
break;
|
||||
}
|
||||
prev_span = Some(binding.span);
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
fn annotate_expected_due_to_let_ty(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
|
|
|||
|
|
@ -798,18 +798,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
full_call_span,
|
||||
format!("arguments to this {} are incorrect", call_name),
|
||||
);
|
||||
if let (Some(callee_ty), hir::ExprKind::MethodCall(_, rcvr, _, _)) =
|
||||
(callee_ty, &call_expr.kind)
|
||||
{
|
||||
// Type that would have accepted this argument if it hadn't been inferred earlier.
|
||||
// FIXME: We leave an inference variable for now, but it'd be nice to get a more
|
||||
// specific type to increase the accuracy of the diagnostic.
|
||||
let expected = self.infcx.next_ty_var(TypeVariableOrigin {
|
||||
kind: TypeVariableOriginKind::MiscVariable,
|
||||
span: full_call_span,
|
||||
});
|
||||
self.point_at_expr_source_of_inferred_type(&mut err, rcvr, expected, callee_ty);
|
||||
}
|
||||
// Call out where the function is defined
|
||||
self.label_fn_like(
|
||||
&mut err,
|
||||
|
|
|
|||
|
|
@ -67,9 +67,6 @@ LL | x == 5
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/assignment-in-if.rs:44:18
|
||||
|
|
||||
LL | if y = (Foo { foo: x }) {
|
||||
| - here the type of `x` is inferred to be `usize`
|
||||
...
|
||||
LL | if x == x && x = x && x == x {
|
||||
| ------ ^ expected `bool`, found `usize`
|
||||
| |
|
||||
|
|
@ -78,9 +75,6 @@ LL | if x == x && x = x && x == x {
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/assignment-in-if.rs:44:22
|
||||
|
|
||||
LL | if y = (Foo { foo: x }) {
|
||||
| - here the type of `x` is inferred to be `usize`
|
||||
...
|
||||
LL | if x == x && x = x && x == x {
|
||||
| ^ expected `bool`, found `usize`
|
||||
|
||||
|
|
@ -98,9 +92,6 @@ LL | if x == x && x == x && x == x {
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/assignment-in-if.rs:51:28
|
||||
|
|
||||
LL | if y = (Foo { foo: x }) {
|
||||
| - here the type of `x` is inferred to be `usize`
|
||||
...
|
||||
LL | if x == x && x == x && x = x {
|
||||
| ---------------- ^ expected `bool`, found `usize`
|
||||
| |
|
||||
|
|
|
|||
|
|
@ -17,9 +17,6 @@ LL | fn bar(_: Vec<i32>) {}
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/point-at-inference-2.rs:9:9
|
||||
|
|
||||
LL | baz(&v);
|
||||
| - here the type of `v` is inferred to be `Vec<&i32>`
|
||||
LL | baz(&v);
|
||||
LL | bar(v);
|
||||
| --- ^ expected `i32`, found `&i32`
|
||||
| |
|
||||
|
|
@ -36,8 +33,6 @@ LL | fn bar(_: Vec<i32>) {}
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/point-at-inference-2.rs:12:9
|
||||
|
|
||||
LL | baz(&v);
|
||||
| - here the type of `v` is inferred to be `Vec<&i32>`
|
||||
LL | bar(v);
|
||||
| --- ^ expected `i32`, found `&i32`
|
||||
| |
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
fn main() {
|
||||
let mut v = Vec::new();
|
||||
v.push(0i32);
|
||||
//~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
|
||||
v.push(0);
|
||||
v.push(1i32); //~ ERROR mismatched types
|
||||
//~^ NOTE expected `i32`, found `u32`
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
fn main() {
|
||||
let mut v = Vec::new();
|
||||
v.push(0i32);
|
||||
//~^ NOTE this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
|
||||
v.push(0);
|
||||
v.push(1u32); //~ ERROR mismatched types
|
||||
//~^ NOTE expected `i32`, found `u32`
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/point-at-inference-3.rs:7:12
|
||||
--> $DIR/point-at-inference-3.rs:6:12
|
||||
|
|
||||
LL | v.push(0i32);
|
||||
| ---- this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
|
||||
...
|
||||
LL | v.push(1u32);
|
||||
| ---- ^^^^ expected `i32`, found `u32`
|
||||
| |
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
// run-rustfix
|
||||
fn bar(_: Vec<i32>) {}
|
||||
fn baz(_: &impl std::any::Any) {}
|
||||
fn main() {
|
||||
let v = vec![1, 2, 3, 4, 5];
|
||||
let mut foo = vec![];
|
||||
baz(&foo);
|
||||
for i in &v {
|
||||
foo.push(*i);
|
||||
}
|
||||
baz(&foo);
|
||||
bar(foo); //~ ERROR E0308
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
// run-rustfix
|
||||
fn bar(_: Vec<i32>) {}
|
||||
fn baz(_: &impl std::any::Any) {}
|
||||
fn main() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
error[E0308]: mismatched types
|
||||
--> $DIR/point-at-inference.rs:12:9
|
||||
--> $DIR/point-at-inference.rs:11:9
|
||||
|
|
||||
LL | foo.push(i);
|
||||
| - this is of type `&{integer}`, which causes `foo` to be inferred as `Vec<&{integer}>`
|
||||
...
|
||||
LL | bar(foo);
|
||||
| --- ^^^ expected `i32`, found `&{integer}`
|
||||
| |
|
||||
|
|
@ -12,14 +9,10 @@ LL | bar(foo);
|
|||
= note: expected struct `Vec<i32>`
|
||||
found struct `Vec<&{integer}>`
|
||||
note: function defined here
|
||||
--> $DIR/point-at-inference.rs:2:4
|
||||
--> $DIR/point-at-inference.rs:1:4
|
||||
|
|
||||
LL | fn bar(_: Vec<i32>) {}
|
||||
| ^^^ -----------
|
||||
help: consider dereferencing the borrow
|
||||
|
|
||||
LL | foo.push(*i);
|
||||
| +
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue