about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs23
-rw-r--r--compiler/rustc_span/src/lib.rs23
-rw-r--r--tests/ui/methods/ident-from-macro-expansion.rs18
-rw-r--r--tests/ui/methods/ident-from-macro-expansion.stderr21
4 files changed, 82 insertions, 3 deletions
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 18218a7a0a6..41bd3933fee 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -158,7 +158,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             self.typeck_results.borrow_mut().used_trait_imports.insert(import_id);
         }
 
-        let (span, sugg_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
+        let (span, expr_span, source, item_name, args) = match self.tcx.hir_node(call_id) {
             hir::Node::Expr(&hir::Expr {
                 kind: hir::ExprKind::MethodCall(segment, rcvr, args, _),
                 span,
@@ -194,6 +194,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             node => unreachable!("{node:?}"),
         };
 
+        // Try to get the span of the identifier within the expression's syntax context (if that's different).
+        let within_macro_span = span.within_macro(expr_span);
+
         // Avoid suggestions when we don't know what's going on.
         if let Err(guar) = rcvr_ty.error_reported() {
             return guar;
@@ -207,10 +210,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 call_id,
                 source,
                 args,
-                sugg_span,
+                expr_span,
                 &mut no_match_data,
                 expected,
                 trait_missing_method,
+                within_macro_span,
             ),
 
             MethodError::Ambiguity(mut sources) => {
@@ -221,6 +225,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     "multiple applicable items in scope"
                 );
                 err.span_label(item_name.span, format!("multiple `{item_name}` found"));
+                if let Some(within_macro_span) = within_macro_span {
+                    err.span_label(within_macro_span, "due to this macro variable");
+                }
 
                 self.note_candidates_on_method_error(
                     rcvr_ty,
@@ -230,7 +237,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     span,
                     &mut err,
                     &mut sources,
-                    Some(sugg_span),
+                    Some(expr_span),
                 );
                 err.emit()
             }
@@ -252,6 +259,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     .span_if_local(def_id)
                     .unwrap_or_else(|| self.tcx.def_span(def_id));
                 err.span_label(sp, format!("private {kind} defined here"));
+                if let Some(within_macro_span) = within_macro_span {
+                    err.span_label(within_macro_span, "due to this macro variable");
+                }
                 self.suggest_valid_traits(&mut err, item_name, out_of_scope_traits, true);
                 err.emit()
             }
@@ -268,6 +278,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 if !needs_mut {
                     err.span_label(bound_span, "this has a `Sized` requirement");
                 }
+                if let Some(within_macro_span) = within_macro_span {
+                    err.span_label(within_macro_span, "due to this macro variable");
+                }
                 if !candidates.is_empty() {
                     let help = format!(
                         "{an}other candidate{s} {were} found in the following trait{s}",
@@ -581,6 +594,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         no_match_data: &mut NoMatchData<'tcx>,
         expected: Expectation<'tcx>,
         trait_missing_method: bool,
+        within_macro_span: Option<Span>,
     ) -> ErrorGuaranteed {
         let mode = no_match_data.mode;
         let tcx = self.tcx;
@@ -721,6 +735,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         if tcx.sess.source_map().is_multiline(sugg_span) {
             err.span_label(sugg_span.with_hi(span.lo()), "");
         }
+        if let Some(within_macro_span) = within_macro_span {
+            err.span_label(within_macro_span, "due to this macro variable");
+        }
 
         if short_ty_str.len() < ty_str.len() && ty_str.len() > 10 {
             ty_str = short_ty_str;
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index bca9323a50d..d23e1825e6e 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -1057,6 +1057,29 @@ impl Span {
         }
     }
 
+    /// Returns the `Span` within the syntax context of "within". This is useful when
+    /// "self" is an expansion from a macro variable, since this can be used for
+    /// providing extra macro expansion context for certain errors.
+    ///
+    /// ```text
+    /// macro_rules! m {
+    ///     ($ident:ident) => { ($ident,) }
+    /// }
+    ///
+    /// m!(outer_ident);
+    /// ```
+    ///
+    /// If "self" is the span of the outer_ident, and "within" is the span of the `($ident,)`
+    /// expr, then this will return the span of the `$ident` macro variable.
+    pub fn within_macro(self, within: Span) -> Option<Span> {
+        match Span::prepare_to_combine(self, within) {
+            Ok((self_, _, parent)) if self_.lo != self.lo() && self.hi() != self_.hi => {
+                Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent))
+            }
+            _ => None,
+        }
+    }
+
     pub fn from_inner(self, inner: InnerSpan) -> Span {
         let span = self.data();
         Span::new(
diff --git a/tests/ui/methods/ident-from-macro-expansion.rs b/tests/ui/methods/ident-from-macro-expansion.rs
new file mode 100644
index 00000000000..38d2fee0e53
--- /dev/null
+++ b/tests/ui/methods/ident-from-macro-expansion.rs
@@ -0,0 +1,18 @@
+macro_rules! dot {
+    ($id:ident) => {
+        ().$id();
+    }
+}
+
+macro_rules! dispatch {
+    ($id:ident) => {
+        <()>::$id();
+    }
+}
+
+fn main() {
+    dot!(hello);
+    //~^ ERROR no method named `hello` found for unit type `()` in the current scope
+    dispatch!(hello);
+    //~^ ERROR no function or associated item named `hello` found for unit type `()` in the current scope
+}
diff --git a/tests/ui/methods/ident-from-macro-expansion.stderr b/tests/ui/methods/ident-from-macro-expansion.stderr
new file mode 100644
index 00000000000..b596ce29f6f
--- /dev/null
+++ b/tests/ui/methods/ident-from-macro-expansion.stderr
@@ -0,0 +1,21 @@
+error[E0599]: no method named `hello` found for unit type `()` in the current scope
+  --> $DIR/ident-from-macro-expansion.rs:14:10
+   |
+LL |         ().$id();
+   |            --- due to this macro variable
+...
+LL |     dot!(hello);
+   |          ^^^^^ method not found in `()`
+
+error[E0599]: no function or associated item named `hello` found for unit type `()` in the current scope
+  --> $DIR/ident-from-macro-expansion.rs:16:15
+   |
+LL |         <()>::$id();
+   |               --- due to this macro variable
+...
+LL |     dispatch!(hello);
+   |               ^^^^^ function or associated item not found in `()`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0599`.