about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs37
-rw-r--r--compiler/rustc_span/src/lib.rs37
-rw-r--r--tests/ui/argument-suggestions/extra_arguments.rs26
-rw-r--r--tests/ui/argument-suggestions/extra_arguments.stderr148
-rw-r--r--tests/ui/issues/issue-48364.stderr1
5 files changed, 198 insertions, 51 deletions
diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
index 817115012a4..004c8affcf0 100644
--- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
+++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs
@@ -519,7 +519,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         // 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);
+            let normalized_span = span.find_ancestor_inside_same_ctxt(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.
@@ -929,7 +929,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     };
                     labels.push((provided_span, format!("unexpected argument{provided_ty_name}")));
                     let mut span = provided_span;
-                    if span.can_be_used_for_suggestions() {
+                    if span.can_be_used_for_suggestions()
+                        && error_span.can_be_used_for_suggestions()
+                    {
                         if arg_idx.index() > 0
                         && let Some((_, prev)) = provided_arg_tys
                             .get(ProvidedIdx::from_usize(arg_idx.index() - 1)
@@ -1220,22 +1222,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         };
         if let Some(suggestion_text) = suggestion_text {
             let source_map = self.sess().source_map();
-            let (mut suggestion, suggestion_span) =
-                if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) {
-                    ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
-                } else {
-                    (
-                        format!(
-                            "{}(",
-                            source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
-                                fn_def_id.map_or("".to_string(), |fn_def_id| {
-                                    tcx.item_name(fn_def_id).to_string()
-                                })
+            let (mut suggestion, suggestion_span) = if let Some(call_span) =
+                full_call_span.find_ancestor_inside_same_ctxt(error_span)
+            {
+                ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi()))
+            } else {
+                (
+                    format!(
+                        "{}(",
+                        source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| {
+                            fn_def_id.map_or("".to_string(), |fn_def_id| {
+                                tcx.item_name(fn_def_id).to_string()
                             })
-                        ),
-                        error_span,
-                    )
-                };
+                        })
+                    ),
+                    error_span,
+                )
+            };
             let mut needs_comma = false;
             for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() {
                 if needs_comma {
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index afee5c0fe2b..c24b8d9ec17 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -686,6 +686,12 @@ impl Span {
     }
 
     /// Walk down the expansion ancestors to find a span that's contained within `outer`.
+    ///
+    /// The span returned by this method may have a different [`SyntaxContext`] as `outer`.
+    /// If you need to extend the span, use [`find_ancestor_inside_same_ctxt`] instead,
+    /// because joining spans with different syntax contexts can create unexpected results.
+    ///
+    /// [`find_ancestor_inside_same_ctxt`]: Self::find_ancestor_inside_same_ctxt
     pub fn find_ancestor_inside(mut self, outer: Span) -> Option<Span> {
         while !outer.contains(self) {
             self = self.parent_callsite()?;
@@ -693,11 +699,34 @@ impl Span {
         Some(self)
     }
 
-    /// Like `find_ancestor_inside`, but specifically for when spans might not
-    /// overlaps. Take care when using this, and prefer `find_ancestor_inside`
-    /// when you know that the spans are nested (modulo macro expansion).
+    /// Walk down the expansion ancestors to find a span with the same [`SyntaxContext`] as
+    /// `other`.
+    ///
+    /// Like [`find_ancestor_inside_same_ctxt`], but specifically for when spans might not
+    /// overlap. Take care when using this, and prefer [`find_ancestor_inside`] or
+    /// [`find_ancestor_inside_same_ctxt`] when you know that the spans are nested (modulo
+    /// macro expansion).
+    ///
+    /// [`find_ancestor_inside`]: Self::find_ancestor_inside
+    /// [`find_ancestor_inside_same_ctxt`]: Self::find_ancestor_inside_same_ctxt
     pub fn find_ancestor_in_same_ctxt(mut self, other: Span) -> Option<Span> {
-        while !Span::eq_ctxt(self, other) {
+        while !self.eq_ctxt(other) {
+            self = self.parent_callsite()?;
+        }
+        Some(self)
+    }
+
+    /// Walk down the expansion ancestors to find a span that's contained within `outer` and
+    /// has the same [`SyntaxContext`] as `outer`.
+    ///
+    /// This method is the combination of [`find_ancestor_inside`] and
+    /// [`find_ancestor_in_same_ctxt`] and should be preferred when extending the returned span.
+    /// If you do not need to modify the span, use [`find_ancestor_inside`] instead.
+    ///
+    /// [`find_ancestor_inside`]: Self::find_ancestor_inside
+    /// [`find_ancestor_in_same_ctxt`]: Self::find_ancestor_in_same_ctxt
+    pub fn find_ancestor_inside_same_ctxt(mut self, outer: Span) -> Option<Span> {
+        while !outer.contains(self) || !self.eq_ctxt(outer) {
             self = self.parent_callsite()?;
         }
         Some(self)
diff --git a/tests/ui/argument-suggestions/extra_arguments.rs b/tests/ui/argument-suggestions/extra_arguments.rs
index 1442062326d..4f2f3517ddd 100644
--- a/tests/ui/argument-suggestions/extra_arguments.rs
+++ b/tests/ui/argument-suggestions/extra_arguments.rs
@@ -1,12 +1,18 @@
 fn empty() {}
-fn one_arg(_a: i32) {}
+fn one_arg<T>(_a: T) {}
 fn two_arg_same(_a: i32, _b: i32) {}
 fn two_arg_diff(_a: i32, _b: &str) {}
 
 macro_rules! foo {
-    ($x:expr) => {
+    ($x:expr, ~) => {
         empty($x, 1); //~ ERROR function takes
-    }
+    };
+    ($x:expr, $y:expr) => {
+        empty($x, $y); //~ ERROR function takes
+    };
+    (~, $y:expr) => {
+        empty(1, $y); //~ ERROR function takes
+    };
 }
 
 fn main() {
@@ -39,5 +45,17 @@ fn main() {
     1,
     ""
   );
-  foo!(1);
+
+  // Check with macro expansions
+  foo!(1, ~);
+  foo!(~, 1);
+  foo!(1, 1);
+  one_arg(1, panic!()); //~ ERROR function takes
+  one_arg(panic!(), 1); //~ ERROR function takes
+  one_arg(stringify!($e), 1); //~ ERROR function takes
+
+  // Not a macro, but this also has multiple spans with equal source code,
+  // but different expansion contexts.
+  // https://github.com/rust-lang/rust/issues/114255
+  one_arg(for _ in 1.. {}, 1); //~ ERROR function takes
 }
diff --git a/tests/ui/argument-suggestions/extra_arguments.stderr b/tests/ui/argument-suggestions/extra_arguments.stderr
index 11c71099743..5ad8e35920a 100644
--- a/tests/ui/argument-suggestions/extra_arguments.stderr
+++ b/tests/ui/argument-suggestions/extra_arguments.stderr
@@ -1,5 +1,5 @@
 error[E0061]: this function takes 0 arguments but 1 argument was supplied
-  --> $DIR/extra_arguments.rs:13:3
+  --> $DIR/extra_arguments.rs:19:3
    |
 LL |   empty("");
    |   ^^^^^ --
@@ -14,7 +14,7 @@ LL | fn empty() {}
    |    ^^^^^
 
 error[E0061]: this function takes 0 arguments but 2 arguments were supplied
-  --> $DIR/extra_arguments.rs:14:3
+  --> $DIR/extra_arguments.rs:20:3
    |
 LL |   empty(1, 1);
    |   ^^^^^ -  - unexpected argument of type `{integer}`
@@ -33,7 +33,7 @@ LL +   empty();
    |
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
-  --> $DIR/extra_arguments.rs:16:3
+  --> $DIR/extra_arguments.rs:22:3
    |
 LL |   one_arg(1, 1);
    |   ^^^^^^^  ---
@@ -44,11 +44,11 @@ LL |   one_arg(1, 1);
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
-LL | fn one_arg(_a: i32) {}
-   |    ^^^^^^^ -------
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 2 arguments were supplied
-  --> $DIR/extra_arguments.rs:17:3
+  --> $DIR/extra_arguments.rs:23:3
    |
 LL |   one_arg(1, "");
    |   ^^^^^^^  ----
@@ -59,11 +59,11 @@ LL |   one_arg(1, "");
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
-LL | fn one_arg(_a: i32) {}
-   |    ^^^^^^^ -------
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
 
 error[E0061]: this function takes 1 argument but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:18:3
+  --> $DIR/extra_arguments.rs:24:3
    |
 LL |   one_arg(1, "", 1.0);
    |   ^^^^^^^    --  --- unexpected argument of type `{float}`
@@ -73,8 +73,8 @@ LL |   one_arg(1, "", 1.0);
 note: function defined here
   --> $DIR/extra_arguments.rs:2:4
    |
-LL | fn one_arg(_a: i32) {}
-   |    ^^^^^^^ -------
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
 help: remove the extra arguments
    |
 LL -   one_arg(1, "", 1.0);
@@ -82,7 +82,7 @@ LL +   one_arg(1);
    |
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:20:3
+  --> $DIR/extra_arguments.rs:26:3
    |
 LL |   two_arg_same(1, 1, 1);
    |   ^^^^^^^^^^^^     ---
@@ -97,7 +97,7 @@ LL | fn two_arg_same(_a: i32, _b: i32) {}
    |    ^^^^^^^^^^^^ -------  -------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:21:3
+  --> $DIR/extra_arguments.rs:27:3
    |
 LL |   two_arg_same(1, 1, 1.0);
    |   ^^^^^^^^^^^^     -----
@@ -112,7 +112,7 @@ LL | fn two_arg_same(_a: i32, _b: i32) {}
    |    ^^^^^^^^^^^^ -------  -------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:23:3
+  --> $DIR/extra_arguments.rs:29:3
    |
 LL |   two_arg_diff(1, 1, "");
    |   ^^^^^^^^^^^^  ---
@@ -127,7 +127,7 @@ LL | fn two_arg_diff(_a: i32, _b: &str) {}
    |    ^^^^^^^^^^^^ -------  --------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:24:3
+  --> $DIR/extra_arguments.rs:30:3
    |
 LL |   two_arg_diff(1, "", "");
    |   ^^^^^^^^^^^^      ----
@@ -142,7 +142,7 @@ LL | fn two_arg_diff(_a: i32, _b: &str) {}
    |    ^^^^^^^^^^^^ -------  --------
 
 error[E0061]: this function takes 2 arguments but 4 arguments were supplied
-  --> $DIR/extra_arguments.rs:25:3
+  --> $DIR/extra_arguments.rs:31:3
    |
 LL |   two_arg_diff(1, 1, "", "");
    |   ^^^^^^^^^^^^    -      -- unexpected argument of type `&'static str`
@@ -161,7 +161,7 @@ LL +   two_arg_diff(1, "");
    |
 
 error[E0061]: this function takes 2 arguments but 4 arguments were supplied
-  --> $DIR/extra_arguments.rs:26:3
+  --> $DIR/extra_arguments.rs:32:3
    |
 LL |   two_arg_diff(1, "", 1, "");
    |   ^^^^^^^^^^^^        -  -- unexpected argument of type `&'static str`
@@ -180,7 +180,7 @@ LL +   two_arg_diff(1, "");
    |
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:29:3
+  --> $DIR/extra_arguments.rs:35:3
    |
 LL |   two_arg_same(1, 1,     "");
    |   ^^^^^^^^^^^^     --------
@@ -195,7 +195,7 @@ LL | fn two_arg_same(_a: i32, _b: i32) {}
    |    ^^^^^^^^^^^^ -------  -------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:30:3
+  --> $DIR/extra_arguments.rs:36:3
    |
 LL |   two_arg_diff(1, 1,     "");
    |   ^^^^^^^^^^^^  ---
@@ -210,7 +210,7 @@ LL | fn two_arg_diff(_a: i32, _b: &str) {}
    |    ^^^^^^^^^^^^ -------  --------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:31:3
+  --> $DIR/extra_arguments.rs:37:3
    |
 LL |     two_arg_same(
    |     ^^^^^^^^^^^^
@@ -230,7 +230,7 @@ LL | fn two_arg_same(_a: i32, _b: i32) {}
    |    ^^^^^^^^^^^^ -------  -------
 
 error[E0061]: this function takes 2 arguments but 3 arguments were supplied
-  --> $DIR/extra_arguments.rs:37:3
+  --> $DIR/extra_arguments.rs:43:3
    |
 LL |     two_arg_diff(
    |     ^^^^^^^^^^^^
@@ -254,11 +254,10 @@ error[E0061]: this function takes 0 arguments but 2 arguments were supplied
 LL |         empty($x, 1);
    |         ^^^^^     - unexpected argument of type `{integer}`
 ...
-LL |   foo!(1);
-   |   -------
+LL |   foo!(1, ~);
+   |   ----------
    |   |    |
    |   |    unexpected argument of type `{integer}`
-   |   |    help: remove the extra argument
    |   in this macro invocation
    |
 note: function defined here
@@ -268,6 +267,105 @@ LL | fn empty() {}
    |    ^^^^^
    = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 16 previous errors
+error[E0061]: this function takes 0 arguments but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:14:9
+   |
+LL |         empty(1, $y);
+   |         ^^^^^ - unexpected argument of type `{integer}`
+...
+LL |   foo!(~, 1);
+   |   ----------
+   |   |       |
+   |   |       unexpected argument of type `{integer}`
+   |   in this macro invocation
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:1:4
+   |
+LL | fn empty() {}
+   |    ^^^^^
+   = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0061]: this function takes 0 arguments but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:11:9
+   |
+LL |         empty($x, $y);
+   |         ^^^^^
+...
+LL |   foo!(1, 1);
+   |   ----------
+   |   |    |  |
+   |   |    |  unexpected argument of type `{integer}`
+   |   |    unexpected argument of type `{integer}`
+   |   in this macro invocation
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:1:4
+   |
+LL | fn empty() {}
+   |    ^^^^^
+   = note: this error originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:53:3
+   |
+LL |   one_arg(1, panic!());
+   |   ^^^^^^^  ----------
+   |            | |
+   |            | unexpected argument
+   |            help: remove the extra argument
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:2:4
+   |
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:54:3
+   |
+LL |   one_arg(panic!(), 1);
+   |   ^^^^^^^         ---
+   |                   | |
+   |                   | unexpected argument of type `{integer}`
+   |                   help: remove the extra argument
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:2:4
+   |
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:55:3
+   |
+LL |   one_arg(stringify!($e), 1);
+   |   ^^^^^^^               ---
+   |                         | |
+   |                         | unexpected argument of type `{integer}`
+   |                         help: remove the extra argument
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:2:4
+   |
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
+
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+  --> $DIR/extra_arguments.rs:60:3
+   |
+LL |   one_arg(for _ in 1.. {}, 1);
+   |   ^^^^^^^                ---
+   |                          | |
+   |                          | unexpected argument of type `{integer}`
+   |                          help: remove the extra argument
+   |
+note: function defined here
+  --> $DIR/extra_arguments.rs:2:4
+   |
+LL | fn one_arg<T>(_a: T) {}
+   |    ^^^^^^^    -----
+
+error: aborting due to 22 previous errors
 
 For more information about this error, try `rustc --explain E0061`.
diff --git a/tests/ui/issues/issue-48364.stderr b/tests/ui/issues/issue-48364.stderr
index cac4af6a7f3..3f2e1b83ad5 100644
--- a/tests/ui/issues/issue-48364.stderr
+++ b/tests/ui/issues/issue-48364.stderr
@@ -10,7 +10,6 @@ LL |     b"".starts_with(stringify!(foo))
               found reference `&'static str`
 note: method defined here
   --> $SRC_DIR/core/src/slice/mod.rs:LL:COL
-   = note: this error originates in the macro `stringify` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: aborting due to previous error