about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-04-14 21:11:10 +0200
committerGitHub <noreply@github.com>2023-04-14 21:11:10 +0200
commit7bfccb3d7f413821b1fbeb044a2d35b3bf4a035e (patch)
tree5133abdbb6639c789b68f8c0fb97353e9d3a26cd
parent660c966ff941ddf995d3251df32508b383cd4cee (diff)
parent5cc475742151cd458091ca3c67598ddece3ee39b (diff)
downloadrust-7bfccb3d7f413821b1fbeb044a2d35b3bf4a035e.tar.gz
rust-7bfccb3d7f413821b1fbeb044a2d35b3bf4a035e.zip
Rollup merge of #108687 - compiler-errors:reformulate-point_at_expr_source_of_inferred_type, r=oli-obk
Reformulate `point_at_expr_source_of_inferred_type` to be more accurate

Be more accurate when deducing where along the several usages of a binding it is constrained to be some type that is incompatible with an expectation.

This also renames the method to `note_source_of_type_mismatch_constraint` because I prefer that name, though I guess I can revert that. (Also drive-by rename `note_result_coercion` -> `suggest_coercing_result_via_try_operator`, because it's suggesting, not noting!)

This PR is (probably?) best reviewed per commit, but it does regress a bit only to fix it later on, so it could also be reviewed as a whole if that makes the final results more clear.

r? `@estebank`
-rw-r--r--compiler/rustc_hir_typeck/src/demand.rs406
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs24
-rw-r--r--tests/ui/type/type-check/assignment-in-if.stderr9
-rw-r--r--tests/ui/type/type-check/point-at-inference-3.fixed3
-rw-r--r--tests/ui/type/type-check/point-at-inference-3.rs3
-rw-r--r--tests/ui/type/type-check/point-at-inference-3.stderr6
-rw-r--r--tests/ui/type/type-check/point-at-inference-4.rs3
-rw-r--r--tests/ui/type/type-check/point-at-inference-4.stderr11
-rw-r--r--tests/ui/type/type-check/point-at-inference.stderr4
-rw-r--r--tests/ui/typeck/bad-type-in-vec-contains.stderr1
-rw-r--r--tests/ui/typeck/bad-type-in-vec-push.stderr2
-rw-r--r--tests/ui/typeck/issue-107775.stderr6
12 files changed, 257 insertions, 221 deletions
diff --git a/compiler/rustc_hir_typeck/src/demand.rs b/compiler/rustc_hir_typeck/src/demand.rs
index a4c3be1d177..13442c31649 100644
--- a/compiler/rustc_hir_typeck/src/demand.rs
+++ b/compiler/rustc_hir_typeck/src/demand.rs
@@ -1,6 +1,5 @@
 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;
@@ -13,15 +12,13 @@ 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, TypeVisitableExt};
+use rustc_middle::ty::fold::BottomUpFolder;
+use rustc_middle::ty::print::with_no_trimmed_paths;
+use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut, TypeFoldable};
 use rustc_span::symbol::{sym, Symbol};
-use rustc_span::{BytePos, Span};
+use rustc_span::{BytePos, Span, DUMMY_SP};
 use rustc_target::abi::FieldIdx;
 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;
@@ -62,9 +59,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             || self.suggest_into(err, expr, expr_ty, expected)
             || self.suggest_floating_point_literal(err, expr, expected)
             || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
-            || self.note_result_coercion(err, expr, expected, expr_ty);
+            || self.suggest_coercing_result_via_try_operator(err, expr, expected, expr_ty);
+
         if !suggested {
-            self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span);
+            self.note_source_of_type_mismatch_constraint(
+                err,
+                expr,
+                TypeMismatchSource::Ty(expected),
+            );
         }
     }
 
@@ -218,37 +220,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         (expected, Some(err))
     }
 
-    pub fn point_at_expr_source_of_inferred_type(
+    /// Notes the point at which a variable is constrained to some type incompatible
+    /// with some expectation given by `source`.
+    pub fn note_source_of_type_mismatch_constraint(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'_>,
-        found: Ty<'tcx>,
-        expected: Ty<'tcx>,
-        mismatch_span: Span,
+        source: TypeMismatchSource<'tcx>,
     ) -> bool {
-        let map = self.tcx.hir();
+        let hir = 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;
-        }
+        let hir::def::Res::Local(local_hir_id) = p.res else { return false; };
+        let hir::Node::Pat(pat) = hir.get(local_hir_id) else { return false; };
+        let (init_ty_hir_id, init) = match hir.get_parent(pat.hir_id) {
+            hir::Node::Local(hir::Local { ty: Some(ty), init, .. }) => (ty.hir_id, *init),
+            hir::Node::Local(hir::Local { init: Some(init), .. }) => (init.hir_id, Some(*init)),
+            _ => return false,
+        };
+        let Some(init_ty) = self.node_ty_opt(init_ty_hir_id) else { return false; };
 
         // Locate all the usages of the relevant binding.
-        struct FindExprs<'hir> {
+        struct FindExprs<'tcx> {
             hir_id: hir::HirId,
-            uses: Vec<&'hir hir::Expr<'hir>>,
+            uses: Vec<&'tcx hir::Expr<'tcx>>,
         }
-        impl<'v> Visitor<'v> for FindExprs<'v> {
-            fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
+        impl<'tcx> Visitor<'tcx> for FindExprs<'tcx> {
+            fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
                 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
@@ -259,180 +258,205 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             }
         }
 
-        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);
+        let mut expr_finder = FindExprs { hir_id: local_hir_id, uses: init.into_iter().collect() };
+        let body =
+            hir.body(hir.maybe_body_owned_by(self.body_id).expect("expected item to have body"));
         expr_finder.visit_expr(body.value);
-        // Hack to make equality checks on types with inference variables and regions useful.
-        let mut eraser = BottomUpFolder {
+
+        use rustc_infer::infer::type_variable::*;
+        use rustc_middle::infer::unify_key::*;
+        // Replaces all of the variables in the given type with a fresh inference variable.
+        let mut fudger = BottomUpFolder {
             tcx: self.tcx,
+            ty_op: |ty| {
+                if let ty::Infer(infer) = ty.kind() {
+                    match infer {
+                        ty::InferTy::TyVar(_) => self.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::MiscVariable,
+                            span: DUMMY_SP,
+                        }),
+                        ty::InferTy::IntVar(_) => self.next_int_var(),
+                        ty::InferTy::FloatVar(_) => self.next_float_var(),
+                        _ => bug!(),
+                    }
+                } else {
+                    ty
+                }
+            },
             lt_op: |_| self.tcx.lifetimes.re_erased,
-            ct_op: |c| c,
-            ty_op: |t| match *t.kind() {
-                ty::Infer(ty::TyVar(_)) => self.tcx.mk_ty_var(ty::TyVid::from_u32(0)),
-                ty::Infer(ty::IntVar(_)) => self.tcx.mk_int_var(ty::IntVid { index: 0 }),
-                ty::Infer(ty::FloatVar(_)) => self.tcx.mk_float_var(ty::FloatVid { index: 0 }),
-                _ => t,
+            ct_op: |ct| {
+                if let ty::ConstKind::Infer(_) = ct.kind() {
+                    self.next_const_var(
+                        ct.ty(),
+                        ConstVariableOrigin {
+                            kind: ConstVariableOriginKind::MiscVariable,
+                            span: DUMMY_SP,
+                        },
+                    )
+                } else {
+                    ct
+                }
             },
         };
-        let mut prev = eraser.fold_ty(ty);
-        let mut prev_span: Option<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).subst_identity();
-                let def_self_ty = sig.input(0).skip_binder();
-                let param_tys = sig.inputs().skip_binder().iter().skip(1);
-                // If there's an arity mismatch, pointing out the call as the source of an inference
-                // can be misleading, so we skip it.
-                if param_tys.len() != args.len() {
-                    continue;
-                }
-                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* this analysis is done.
-                let ty = match self.lookup_probe_for_diagnostic(
-                    segment.ident,
-                    rcvr_ty,
-                    expr,
-                    probe::ProbeScope::TraitsInScope,
-                    None,
-                ) {
-                    Ok(pick) => eraser.fold_ty(pick.self_ty),
-                    Err(_) => rcvr_ty,
+
+        let expected_ty = match source {
+            TypeMismatchSource::Ty(expected_ty) => expected_ty,
+            // Try to deduce what the possible value of `expr` would be if the
+            // incompatible arg were compatible. For example, given `Vec<i32>`
+            // and `vec.push(1u32)`, we ideally want to deduce that the type of
+            // `vec` *should* have been `Vec<u32>`. This will allow us to then
+            // run the subsequent code with this expectation, finding out exactly
+            // when this type diverged from our expectation.
+            TypeMismatchSource::Arg { call_expr, incompatible_arg: idx } => {
+                let hir::ExprKind::MethodCall(segment, _, args, _) = call_expr.kind else {
+                    return false;
                 };
-                // 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 Some(arg_ty) = self.node_ty_opt(args[idx].hir_id) else {
+                    return false;
                 };
-                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) {
-                    // We only point at the first place where the found type was inferred.
-                    for (param_ty, arg) in param_tys.zip(args) {
-                        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_ty = self.node_ty(arg.hir_id);
-                            if !arg.span.overlaps(mismatch_span) {
-                                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));
-                        }
-                    }
+                let possible_rcvr_ty = expr_finder.uses.iter().find_map(|binding| {
+                    let possible_rcvr_ty = self.node_ty_opt(binding.hir_id)?;
+                    // Fudge the receiver, so we can do new inference on it.
+                    let possible_rcvr_ty = possible_rcvr_ty.fold_with(&mut fudger);
+                    let method = self
+                        .lookup_method(
+                            possible_rcvr_ty,
+                            segment,
+                            DUMMY_SP,
+                            call_expr,
+                            binding,
+                            args,
+                        )
+                        .ok()?;
+                    // Unify the method signature with our incompatible arg, to
+                    // do inference in the *opposite* direction and to find out
+                    // what our ideal rcvr ty would look like.
+                    let _ = self
+                        .at(&ObligationCause::dummy(), self.param_env)
+                        .eq(DefineOpaqueTypes::No, method.sig.inputs()[idx + 1], arg_ty)
+                        .ok()?;
+                    self.select_obligations_where_possible(|errs| {
+                        // Yeet the errors, we're already reporting errors.
+                        errs.clear();
+                    });
+                    Some(self.resolve_vars_if_possible(possible_rcvr_ty))
+                });
+                if let Some(rcvr_ty) = possible_rcvr_ty {
+                    rcvr_ty
+                } else {
+                    return false;
                 }
+            }
+        };
 
-                // 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![],
+        // If our expected_ty does not equal init_ty, then it *began* as incompatible.
+        // No need to note in this case...
+        if !self.can_eq(self.param_env, expected_ty, init_ty.fold_with(&mut fudger)) {
+            return false;
+        }
+
+        for window in expr_finder.uses.windows(2) {
+            // Bindings always update their recorded type after the fact, so we
+            // need to look at the *following* usage's type to see when the
+            // binding became incompatible.
+            let [binding, next_usage] = *window else { continue; };
+
+            // Don't go past the binding (always gonna be a nonsense label if so)
+            if binding.hir_id == expr.hir_id {
+                break;
+            }
+
+            let Some(next_use_ty) = self.node_ty_opt(next_usage.hir_id) else { continue; };
+
+            // If the type is not constrained in a way making it not possible to
+            // equate with `expected_ty` by this point, skip.
+            if self.can_eq(self.param_env, expected_ty, next_use_ty.fold_with(&mut fudger)) {
+                continue;
+            }
+
+            if let hir::Node::Expr(parent_expr) = hir.get_parent(binding.hir_id)
+                && let hir::ExprKind::MethodCall(segment, rcvr, args, _) = parent_expr.kind
+                && rcvr.hir_id == binding.hir_id
+            {
+                // If our binding became incompatible while it was a receiver
+                // to a method call, we may be able to make a better guess to
+                // the source of a type mismatch.
+                let Some(rcvr_ty) = self.node_ty_opt(rcvr.hir_id) else { continue; };
+                let rcvr_ty = rcvr_ty.fold_with(&mut fudger);
+                let Ok(method) =
+                    self.lookup_method(rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+                else {
+                    continue;
                 };
-                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) { 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)
+                let ideal_rcvr_ty = rcvr_ty.fold_with(&mut fudger);
+                let ideal_method = self
+                    .lookup_method(ideal_rcvr_ty, segment, DUMMY_SP, parent_expr, rcvr, args)
+                    .ok()
+                    .and_then(|method| {
+                        let _ = self.at(&ObligationCause::dummy(), self.param_env)
+                            .eq(DefineOpaqueTypes::No, ideal_rcvr_ty, expected_ty)
+                            .ok()?;
+                        Some(method)
+                    });
+
+                // Find what argument caused our rcvr to become incompatible
+                // with the expected ty.
+                for (idx, (expected_arg_ty, arg_expr)) in
+                    std::iter::zip(&method.sig.inputs()[1..], args).enumerate()
                 {
-                    // We only point at the first place where the found type was inferred.
-                    if !segment.ident.span.overlaps(mismatch_span) {
+                    let Some(arg_ty) = self.node_ty_opt(arg_expr.hir_id) else { continue; };
+                    let arg_ty = arg_ty.fold_with(&mut fudger);
+                    let _ = self.try_coerce(
+                        arg_expr,
+                        arg_ty,
+                        *expected_arg_ty,
+                        AllowTwoPhase::No,
+                        None,
+                    );
+                    self.select_obligations_where_possible(|errs| {
+                        // Yeet the errors, we're already reporting errors.
+                        errs.clear();
+                    });
+                    // If our rcvr, after inference due to unifying the signature
+                    // with the expected argument type, is still compatible with
+                    // the rcvr, then it must've not been the source of blame.
+                    if self.can_eq(self.param_env, rcvr_ty, expected_ty) {
+                        continue;
+                    }
+                    err.span_label(arg_expr.span, format!("this argument has type `{arg_ty}`..."));
                     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)
-                {
-                    // 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.
-                    if !span.overlaps(mismatch_span) {
-                        err.span_label(
-                            span,
-                            with_forced_trimmed_paths!(format!(
-                                "here the type of `{ident}` is inferred to be `{ty}`",
-                            )),
+                        binding.span,
+                        format!("... which causes `{ident}` to have type `{next_use_ty}`"),
+                    );
+                    // Using our "ideal" method signature, suggest a fix to this
+                    // blame arg, if possible. Don't do this if we're coming from
+                    // arg mismatch code, because we'll possibly suggest a mutually
+                    // incompatible fix at the original mismatch site.
+                    if matches!(source, TypeMismatchSource::Ty(_))
+                        && let Some(ideal_method) = ideal_method
+                    {
+                        self.emit_type_mismatch_suggestions(
+                            err,
+                            arg_expr,
+                            arg_ty,
+                            self.resolve_vars_if_possible(ideal_method.sig.inputs()[idx + 1]),
+                            None,
+                            None,
                         );
                     }
-                    break;
+                    return true;
                 }
-                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);
+            err.span_label(
+                binding.span,
+                format!("here the type of `{ident}` is inferred to be `{next_use_ty}`"),
+            );
+            return true;
         }
-        true
+
+        // We must've not found something that constrained the expr.
+        false
     }
 
     fn annotate_expected_due_to_let_ty(
@@ -708,7 +732,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
-    pub(crate) fn note_result_coercion(
+    pub(crate) fn suggest_coercing_result_via_try_operator(
         &self,
         err: &mut Diagnostic,
         expr: &hir::Expr<'tcx>,
@@ -2094,3 +2118,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         }
     }
 }
+
+pub enum TypeMismatchSource<'tcx> {
+    /// Expected the binding to have the given type, but it was found to have
+    /// a different type. Find out when that type first became incompatible.
+    Ty(Ty<'tcx>),
+    /// When we fail during method argument checking, try to find out if a previous
+    /// expression has constrained the method's receiver in a way that makes the
+    /// argument's type incompatible.
+    Arg { call_expr: &'tcx hir::Expr<'tcx>, incompatible_arg: usize },
+}
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index a009ae5d44e..ea1b52daaa5 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -472,7 +472,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         err_code: &str,
         fn_def_id: Option<DefId>,
         call_span: Span,
-        call_expr: &hir::Expr<'tcx>,
+        call_expr: &'tcx hir::Expr<'tcx>,
     ) {
         // Next, let's construct the error
         let (error_span, full_call_span, call_name, is_method) = match &call_expr.kind {
@@ -807,24 +807,20 @@ 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)
+
+            if let hir::ExprKind::MethodCall(_, rcvr, _, _) = call_expr.kind
+                && provided_idx.as_usize() == expected_idx.as_usize()
             {
-                // 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(
+                self.note_source_of_type_mismatch_constraint(
                     &mut err,
                     rcvr,
-                    expected,
-                    callee_ty,
-                    provided_arg_span,
+                    crate::demand::TypeMismatchSource::Arg {
+                        call_expr,
+                        incompatible_arg: provided_idx.as_usize(),
+                    },
                 );
             }
+
             // Call out where the function is defined
             self.label_fn_like(
                 &mut err,
diff --git a/tests/ui/type/type-check/assignment-in-if.stderr b/tests/ui/type/type-check/assignment-in-if.stderr
index de133e5599c..9f4558adab1 100644
--- a/tests/ui/type/type-check/assignment-in-if.stderr
+++ b/tests/ui/type/type-check/assignment-in-if.stderr
@@ -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`
    |        |
diff --git a/tests/ui/type/type-check/point-at-inference-3.fixed b/tests/ui/type/type-check/point-at-inference-3.fixed
index edd4adf8bd2..15a3b580568 100644
--- a/tests/ui/type/type-check/point-at-inference-3.fixed
+++ b/tests/ui/type/type-check/point-at-inference-3.fixed
@@ -2,7 +2,8 @@
 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>`
+    //~^ NOTE this argument has type `i32`...
+    //~| NOTE ... which causes `v` to have type `Vec<i32>`
     v.push(0);
     v.push(1i32); //~ ERROR mismatched types
     //~^ NOTE expected `i32`, found `u32`
diff --git a/tests/ui/type/type-check/point-at-inference-3.rs b/tests/ui/type/type-check/point-at-inference-3.rs
index 49d7b50075b..a48c4f9862f 100644
--- a/tests/ui/type/type-check/point-at-inference-3.rs
+++ b/tests/ui/type/type-check/point-at-inference-3.rs
@@ -2,7 +2,8 @@
 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>`
+    //~^ NOTE this argument has type `i32`...
+    //~| NOTE ... which causes `v` to have type `Vec<i32>`
     v.push(0);
     v.push(1u32); //~ ERROR mismatched types
     //~^ NOTE expected `i32`, found `u32`
diff --git a/tests/ui/type/type-check/point-at-inference-3.stderr b/tests/ui/type/type-check/point-at-inference-3.stderr
index 2c4907ed263..23876481236 100644
--- a/tests/ui/type/type-check/point-at-inference-3.stderr
+++ b/tests/ui/type/type-check/point-at-inference-3.stderr
@@ -1,8 +1,10 @@
 error[E0308]: mismatched types
-  --> $DIR/point-at-inference-3.rs:7:12
+  --> $DIR/point-at-inference-3.rs:8:12
    |
 LL |     v.push(0i32);
-   |            ---- this is of type `i32`, which causes `v` to be inferred as `Vec<i32>`
+   |     -      ---- this argument has type `i32`...
+   |     |
+   |     ... which causes `v` to have type `Vec<i32>`
 ...
 LL |     v.push(1u32);
    |       ---- ^^^^ expected `i32`, found `u32`
diff --git a/tests/ui/type/type-check/point-at-inference-4.rs b/tests/ui/type/type-check/point-at-inference-4.rs
index aea9b2c6c14..3deb234c275 100644
--- a/tests/ui/type/type-check/point-at-inference-4.rs
+++ b/tests/ui/type/type-check/point-at-inference-4.rs
@@ -11,8 +11,11 @@ fn main() {
     let s = S(None);
     s.infer(0i32);
     //~^ ERROR this method takes 2 arguments but 1 argument was supplied
+    //~| NOTE this argument has type `i32`...
+    //~| NOTE ... which causes `s` to have type `S<i32, _>`
     //~| NOTE an argument is missing
     //~| HELP provide the argument
+    //~| HELP change the type of the numeric literal from `i32` to `u32`
     let t: S<u32, _> = s;
     //~^ ERROR mismatched types
     //~| NOTE expected `S<u32, _>`, found `S<i32, _>`
diff --git a/tests/ui/type/type-check/point-at-inference-4.stderr b/tests/ui/type/type-check/point-at-inference-4.stderr
index 28833d2ed1c..5f7bb8b9367 100644
--- a/tests/ui/type/type-check/point-at-inference-4.stderr
+++ b/tests/ui/type/type-check/point-at-inference-4.stderr
@@ -15,8 +15,13 @@ LL |     s.infer(0i32, /* b */);
    |            ~~~~~~~~~~~~~~~
 
 error[E0308]: mismatched types
-  --> $DIR/point-at-inference-4.rs:16:24
+  --> $DIR/point-at-inference-4.rs:19:24
    |
+LL |     s.infer(0i32);
+   |     -       ---- this argument has type `i32`...
+   |     |
+   |     ... which causes `s` to have type `S<i32, _>`
+...
 LL |     let t: S<u32, _> = s;
    |            ---------   ^ expected `S<u32, _>`, found `S<i32, _>`
    |            |
@@ -24,6 +29,10 @@ LL |     let t: S<u32, _> = s;
    |
    = note: expected struct `S<u32, _>`
               found struct `S<i32, _>`
+help: change the type of the numeric literal from `i32` to `u32`
+   |
+LL |     s.infer(0u32);
+   |              ~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/type/type-check/point-at-inference.stderr b/tests/ui/type/type-check/point-at-inference.stderr
index a76b4f90c73..5fc94d4d1b6 100644
--- a/tests/ui/type/type-check/point-at-inference.stderr
+++ b/tests/ui/type/type-check/point-at-inference.stderr
@@ -2,7 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/point-at-inference.rs:12:9
    |
 LL |         foo.push(i);
-   |                  - this is of type `&{integer}`, which causes `foo` to be inferred as `Vec<&{integer}>`
+   |         ---      - this argument has type `&{integer}`...
+   |         |
+   |         ... which causes `foo` to have type `Vec<&{integer}>`
 ...
 LL |     bar(foo);
    |     --- ^^^ expected `Vec<i32>`, found `Vec<&{integer}>`
diff --git a/tests/ui/typeck/bad-type-in-vec-contains.stderr b/tests/ui/typeck/bad-type-in-vec-contains.stderr
index 0e03388d2d5..72533ab1fa3 100644
--- a/tests/ui/typeck/bad-type-in-vec-contains.stderr
+++ b/tests/ui/typeck/bad-type-in-vec-contains.stderr
@@ -7,7 +7,6 @@ LL |     primes.contains(3);
    |            |        expected `&_`, found integer
    |            |        help: consider borrowing here: `&3`
    |            arguments to this method are incorrect
-   |            here the type of `primes` is inferred to be `[_]`
    |
    = note: expected reference `&_`
                    found type `{integer}`
diff --git a/tests/ui/typeck/bad-type-in-vec-push.stderr b/tests/ui/typeck/bad-type-in-vec-push.stderr
index ae46050c91b..1d5337260fa 100644
--- a/tests/ui/typeck/bad-type-in-vec-push.stderr
+++ b/tests/ui/typeck/bad-type-in-vec-push.stderr
@@ -1,8 +1,6 @@
 error[E0308]: mismatched types
   --> $DIR/bad-type-in-vec-push.rs:11:17
    |
-LL |     vector.sort();
-   |     ------ here the type of `vector` is inferred to be `Vec<_>`
 LL |     result.push(vector);
    |            ---- ^^^^^^ expected integer, found `Vec<_>`
    |            |
diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr
index 9ee9c022c6e..b97e74b7e53 100644
--- a/tests/ui/typeck/issue-107775.stderr
+++ b/tests/ui/typeck/issue-107775.stderr
@@ -2,9 +2,9 @@ error[E0308]: mismatched types
   --> $DIR/issue-107775.rs:35:16
    |
 LL |         map.insert(1, Struct::do_something);
-   |                    -  -------------------- this is of type `fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}`, which causes `map` to be inferred as `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
-   |                    |
-   |                    this is of type `{integer}`, which causes `map` to be inferred as `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
+   |         ---           -------------------- this argument has type `fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}`...
+   |         |
+   |         ... which causes `map` to have type `HashMap<{integer}, fn(u8) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
 LL |         Self { map }
    |                ^^^ expected `HashMap<u16, fn(u8) -> Pin<...>>`, found `HashMap<{integer}, ...>`
    |