From cd3204d1a272dc4739a5f86546620287941aa6e7 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Wed, 20 Jul 2022 03:05:14 +0000
Subject: [PATCH] Normalize the arg spans to be within the call span

---
 .../rustc_typeck/src/check/fn_ctxt/checks.rs  | 26 +++++++++++++------
 src/test/ui/inference/deref-suggestion.rs     |  3 ++-
 src/test/ui/inference/deref-suggestion.stderr | 24 +++++++----------
 3 files changed, 30 insertions(+), 23 deletions(-)

diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
index e3db70845ddf1..64f931aca95f4 100644
--- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
+++ b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs
@@ -481,6 +481,17 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         self.set_tainted_by_errors();
         let tcx = self.tcx;
 
+        // Get the argument span in the context of the call span so that
+        // suggestions and labels are (more) correct when an arg is a
+        // macro invocation.
+        let normalize_span = |span: Span| -> Span {
+            let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span);
+            // Sometimes macros mess up the spans, so do not normalize the
+            // arg span to equal the error span, because that's less useful
+            // than pointing out the arg expr in the wrong context.
+            if normalized_span.source_equal(error_span) { span } else { normalized_span }
+        };
+
         // Precompute the provided types and spans, since that's all we typically need for below
         let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args
             .iter()
@@ -490,7 +501,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .borrow()
                     .expr_ty_adjusted_opt(*expr)
                     .unwrap_or_else(|| tcx.ty_error());
-                (self.resolve_vars_if_possible(ty), expr.span)
+                (self.resolve_vars_if_possible(ty), normalize_span(expr.span))
             })
             .collect();
         let callee_expr = match &call_expr.peel_blocks().kind {
@@ -600,11 +611,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 // Take some care with spans, so we don't suggest wrapping a macro's
                 // innards in parenthesis, for example.
                 if satisfied
-                    && let Some(lo) =
-                        provided_args[mismatch_idx.into()].span.find_ancestor_inside(error_span)
-                    && let Some(hi) = provided_args[(mismatch_idx + tys.len() - 1).into()]
-                        .span
-                        .find_ancestor_inside(error_span)
+                    && let Some((_, lo)) =
+                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx))
+                    && let Some((_, hi)) =
+                        provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1))
                 {
                     let mut err;
                     if tys.len() == 1 {
@@ -612,7 +622,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         // so don't do anything special here.
                         err = self.report_and_explain_type_error(
                             TypeTrace::types(
-                                &self.misc(lo),
+                                &self.misc(*lo),
                                 true,
                                 formal_and_expected_inputs[mismatch_idx.into()].1,
                                 provided_arg_tys[mismatch_idx.into()].0,
@@ -1052,7 +1062,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 let suggestion_text = if let Some(provided_idx) = provided_idx
                     && let (_, provided_span) = provided_arg_tys[*provided_idx]
                     && let Ok(arg_text) =
-                        source_map.span_to_snippet(provided_span.source_callsite())
+                        source_map.span_to_snippet(provided_span)
                 {
                     arg_text
                 } else {
diff --git a/src/test/ui/inference/deref-suggestion.rs b/src/test/ui/inference/deref-suggestion.rs
index 4fd695585ba06..0d8e7289dc8a2 100644
--- a/src/test/ui/inference/deref-suggestion.rs
+++ b/src/test/ui/inference/deref-suggestion.rs
@@ -1,5 +1,5 @@
 macro_rules! borrow {
-    ($x:expr) => { &$x } //~ ERROR mismatched types
+    ($x:expr) => { &$x }
 }
 
 fn foo(_: String) {}
@@ -32,6 +32,7 @@ fn main() {
     foo(&mut "aaa".to_owned());
     //~^ ERROR mismatched types
     foo3(borrow!(0));
+    //~^ ERROR mismatched types
     foo4(&0);
     assert_eq!(3i32, &3i32);
     //~^ ERROR mismatched types
diff --git a/src/test/ui/inference/deref-suggestion.stderr b/src/test/ui/inference/deref-suggestion.stderr
index e763e17e51786..d729f2d682a61 100644
--- a/src/test/ui/inference/deref-suggestion.stderr
+++ b/src/test/ui/inference/deref-suggestion.stderr
@@ -70,13 +70,10 @@ LL +     foo("aaa".to_owned());
    |
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:2:20
+  --> $DIR/deref-suggestion.rs:34:10
    |
-LL |     ($x:expr) => { &$x }
-   |                    ^^^ expected `u32`, found `&{integer}`
-...
 LL |     foo3(borrow!(0));
-   |     ---- ---------- in this macro invocation
+   |     ---- ^^^^^^^^^^ expected `u32`, found `&{integer}`
    |     |
    |     arguments to this function are incorrect
    |
@@ -85,10 +82,9 @@ note: function defined here
    |
 LL | fn foo3(_: u32) {}
    |    ^^^^ ------
-   = note: this error originates in the macro `borrow` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:36:5
+  --> $DIR/deref-suggestion.rs:37:5
    |
 LL |     assert_eq!(3i32, &3i32);
    |     ^^^^^^^^^^^^^^^^^^^^^^^ expected `i32`, found `&i32`
@@ -96,7 +92,7 @@ LL |     assert_eq!(3i32, &3i32);
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:39:17
+  --> $DIR/deref-suggestion.rs:40:17
    |
 LL |     let s = S { u };
    |                 ^
@@ -105,7 +101,7 @@ LL |     let s = S { u };
    |                 help: consider borrowing here: `u: &u`
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:41:20
+  --> $DIR/deref-suggestion.rs:42:20
    |
 LL |     let s = S { u: u };
    |                    ^
@@ -114,7 +110,7 @@ LL |     let s = S { u: u };
    |                    help: consider borrowing here: `&u`
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:44:17
+  --> $DIR/deref-suggestion.rs:45:17
    |
 LL |     let r = R { i };
    |                 ^ expected `u32`, found `&{integer}`
@@ -125,7 +121,7 @@ LL |     let r = R { i: *i };
    |                 ++++
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:46:20
+  --> $DIR/deref-suggestion.rs:47:20
    |
 LL |     let r = R { i: i };
    |                    ^ expected `u32`, found `&{integer}`
@@ -136,7 +132,7 @@ LL |     let r = R { i: *i };
    |                    +
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:55:9
+  --> $DIR/deref-suggestion.rs:56:9
    |
 LL |         b
    |         ^ expected `i32`, found `&{integer}`
@@ -147,7 +143,7 @@ LL |         *b
    |         +
 
 error[E0308]: mismatched types
-  --> $DIR/deref-suggestion.rs:63:9
+  --> $DIR/deref-suggestion.rs:64:9
    |
 LL |         b
    |         ^ expected `i32`, found `&{integer}`
@@ -158,7 +154,7 @@ LL |         *b
    |         +
 
 error[E0308]: `if` and `else` have incompatible types
-  --> $DIR/deref-suggestion.rs:68:12
+  --> $DIR/deref-suggestion.rs:69:12
    |
 LL |        let val = if true {
    |   _______________-