about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-03-06 12:22:13 -0500
committerGitHub <noreply@github.com>2025-03-06 12:22:13 -0500
commitaab7b145d0e1c0d297fe2b678e327daffb62e909 (patch)
treefd8e3a30affebcd3f950548874d943f6c692b1ad
parentcc5d90cbad746560c2e1a6a71777c096f7a7c575 (diff)
parent09e584671b439dc8a9d57cc900cc26668a69ddea (diff)
downloadrust-aab7b145d0e1c0d297fe2b678e327daffb62e909.tar.gz
rust-aab7b145d0e1c0d297fe2b678e327daffb62e909.zip
Rollup merge of #137565 - compiler-errors:macro-ex, r=estebank
Try to point of macro expansion from resolver and method errors if it involves macro var

In the case that a macro caller passes an identifier into a macro generating a path or method expression, point out that identifier in the context of the *macro* so it's a bit more clear how the macro is involved in causing the error.

r? ``````````@estebank`````````` or reassign
-rw-r--r--compiler/rustc_hir_analysis/messages.ftl2
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs2
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs3
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs24
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs8
-rw-r--r--compiler/rustc_span/src/lib.rs31
-rw-r--r--tests/ui/associated-types/ident-from-macro-expansion.rs23
-rw-r--r--tests/ui/associated-types/ident-from-macro-expansion.stderr22
-rw-r--r--tests/ui/hygiene/generate-mod.stderr6
-rw-r--r--tests/ui/hygiene/globs.stderr6
-rw-r--r--tests/ui/macros/macro-parameter-span.stderr3
-rw-r--r--tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr3
-rw-r--r--tests/ui/methods/ident-from-macro-expansion.rs18
-rw-r--r--tests/ui/methods/ident-from-macro-expansion.stderr21
-rw-r--r--tests/ui/structs/ident-from-macro-expansion.rs19
-rw-r--r--tests/ui/structs/ident-from-macro-expansion.stderr14
17 files changed, 225 insertions, 15 deletions
diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl
index d16aba197fe..06c39996a12 100644
--- a/compiler/rustc_hir_analysis/messages.ftl
+++ b/compiler/rustc_hir_analysis/messages.ftl
@@ -617,6 +617,8 @@ hir_analysis_variances_of = {$variances}
 hir_analysis_where_clause_on_main = `main` function is not allowed to have a `where` clause
     .label = `main` cannot have a `where` clause
 
+hir_analysis_within_macro = due to this macro variable
+
 hir_analysis_wrong_number_of_generic_arguments_to_intrinsic =
     intrinsic has wrong number of {$descr} parameters: found {$found}, expected {$expected}
     .label = expected {$expected} {$descr} {$expected ->
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
index f3e1e89812c..e04d6e2c2ae 100644
--- a/compiler/rustc_hir_analysis/src/errors.rs
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -82,6 +82,8 @@ pub(crate) struct AssocItemNotFound<'a> {
     pub label: Option<AssocItemNotFoundLabel<'a>>,
     #[subdiagnostic]
     pub sugg: Option<AssocItemNotFoundSugg<'a>>,
+    #[label(hir_analysis_within_macro)]
+    pub within_macro_span: Option<Span>,
 }
 
 #[derive(Subdiagnostic)]
diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
index d51fd7f7e78..ace5e34b382 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -151,6 +151,9 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             qself: &qself_str,
             label: None,
             sugg: None,
+            // Try to get the span of the identifier within the path's syntax context
+            // (if that's different).
+            within_macro_span: assoc_name.span.within_macro(span, tcx.sess.source_map()),
         };
 
         if is_dummy {
diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs
index cbe677171a0..2a89a03e0aa 100644
--- a/compiler/rustc_hir_typeck/src/expr.rs
+++ b/compiler/rustc_hir_typeck/src/expr.rs
@@ -3069,7 +3069,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
             ident, base, expr, base_ty
         );
-        let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
+        let mut err = self.no_such_field_err(ident, base_ty, expr);
 
         match *base_ty.peel_refs().kind() {
             ty::Array(_, len) => {
@@ -3282,18 +3282,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         );
     }
 
-    fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'_> {
+    fn no_such_field_err(
+        &self,
+        field: Ident,
+        base_ty: Ty<'tcx>,
+        expr: &hir::Expr<'tcx>,
+    ) -> Diag<'_> {
         let span = field.span;
-        debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
+        debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, base_ty);
 
-        let mut err = self.dcx().create_err(NoFieldOnType { span, ty: expr_t, field });
-        if expr_t.references_error() {
+        let mut err = self.dcx().create_err(NoFieldOnType { span, ty: base_ty, field });
+        if base_ty.references_error() {
             err.downgrade_to_delayed_bug();
         }
 
+        if let Some(within_macro_span) = span.within_macro(expr.span, self.tcx.sess.source_map()) {
+            err.span_label(within_macro_span, "due to this macro variable");
+        }
+
         // try to add a suggestion in case the field is a nested field of a field of the Adt
-        let mod_id = self.tcx.parent_module(id).to_def_id();
-        let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
+        let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
+        let (ty, unwrap) = if let ty::Adt(def, args) = base_ty.kind()
             && (self.tcx.is_diagnostic_item(sym::Result, def.did())
                 || self.tcx.is_diagnostic_item(sym::Option, def.did()))
             && let Some(arg) = args.get(0)
@@ -3301,10 +3310,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
         {
             (ty, "unwrap().")
         } else {
-            (expr_t, "")
+            (base_ty, "")
         };
         for (found_fields, args) in
-            self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, id)
+            self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
         {
             let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
             let mut candidate_fields: Vec<_> = found_fields
@@ -3317,7 +3326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                         args,
                         vec![],
                         mod_id,
-                        id,
+                        expr.hir_id,
                     )
                 })
                 .map(|mut field_path| {
@@ -3328,7 +3337,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
             candidate_fields.sort();
 
             let len = candidate_fields.len();
-            if len > 0 {
+            // Don't suggest `.field` if the base expr is from a different
+            // syntax context than the field.
+            if len > 0 && expr.span.eq_ctxt(field.span) {
                 err.span_suggestions(
                     field.span.shrink_to_lo(),
                     format!(
@@ -3963,7 +3974,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                 _ => (),
             };
 
-            self.no_such_field_err(field, container, expr.hir_id).emit();
+            self.no_such_field_err(field, container, expr).emit();
 
             break;
         }
diff --git a/compiler/rustc_hir_typeck/src/method/suggest.rs b/compiler/rustc_hir_typeck/src/method/suggest.rs
index 18218a7a0a6..cb1e89fb9e5 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,10 @@ 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, self.tcx.sess.source_map());
+
         // Avoid suggestions when we don't know what's going on.
         if let Err(guar) = rcvr_ty.error_reported() {
             return guar;
@@ -207,10 +211,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 +226,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 +238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
                     span,
                     &mut err,
                     &mut sources,
-                    Some(sugg_span),
+                    Some(expr_span),
                 );
                 err.emit()
             }
@@ -252,6 +260,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 +279,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 +595,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 +736,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_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index 42be92c0f8f..4a100ec0214 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -429,6 +429,14 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
         let mut err = self.r.dcx().struct_span_err(base_error.span, base_error.msg.clone());
         err.code(code);
 
+        // Try to get the span of the identifier within the path's syntax context
+        // (if that's different).
+        if let Some(within_macro_span) =
+            base_error.span.within_macro(span, self.r.tcx.sess.source_map())
+        {
+            err.span_label(within_macro_span, "due to this macro variable");
+        }
+
         self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);
         self.suggest_at_operator_in_slice_pat_with_range(&mut err, path);
         self.suggest_swapping_misplaced_self_ty_and_trait(&mut err, source, res, base_error.span);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index bca9323a50d..798e186a94b 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -1057,6 +1057,37 @@ 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, sm: &SourceMap) -> Option<Span> {
+        match Span::prepare_to_combine(self, within) {
+            // Only return something if it doesn't overlap with the original span,
+            // and the span isn't "imported" (i.e. from unavailable sources).
+            // FIXME: This does limit the usefulness of the error when the macro is
+            // from a foreign crate; we could also take into account `-Zmacro-backtrace`,
+            // which doesn't redact this span (but that would mean passing in even more
+            // args to this function, lol).
+            Ok((self_, _, parent))
+                if self_.hi < self.lo() || self.hi() < self_.lo && !sm.is_imported(within) =>
+            {
+                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/associated-types/ident-from-macro-expansion.rs b/tests/ui/associated-types/ident-from-macro-expansion.rs
new file mode 100644
index 00000000000..6aabe457140
--- /dev/null
+++ b/tests/ui/associated-types/ident-from-macro-expansion.rs
@@ -0,0 +1,23 @@
+trait Trait {}
+impl Trait for () {}
+
+macro_rules! fully_qualified {
+    ($id:ident) => {
+        <() as Trait>::$id
+    }
+}
+
+macro_rules! type_dependent {
+    ($t:ident, $id:ident) => {
+        T::$id
+    }
+}
+
+fn t<T: Trait>() {
+    let x: fully_qualified!(Assoc);
+    //~^ ERROR cannot find associated type `Assoc` in trait `Trait`
+    let x: type_dependent!(T, Assoc);
+    //~^ ERROR associated type `Assoc` not found for `T`
+}
+
+fn main() {}
diff --git a/tests/ui/associated-types/ident-from-macro-expansion.stderr b/tests/ui/associated-types/ident-from-macro-expansion.stderr
new file mode 100644
index 00000000000..dabf13c2c17
--- /dev/null
+++ b/tests/ui/associated-types/ident-from-macro-expansion.stderr
@@ -0,0 +1,22 @@
+error[E0576]: cannot find associated type `Assoc` in trait `Trait`
+  --> $DIR/ident-from-macro-expansion.rs:17:29
+   |
+LL |         <() as Trait>::$id
+   |                        --- due to this macro variable
+...
+LL |     let x: fully_qualified!(Assoc);
+   |                             ^^^^^ not found in `Trait`
+
+error[E0220]: associated type `Assoc` not found for `T`
+  --> $DIR/ident-from-macro-expansion.rs:19:31
+   |
+LL |         T::$id
+   |            --- due to this macro variable
+...
+LL |     let x: type_dependent!(T, Assoc);
+   |                               ^^^^^ associated type `Assoc` not found
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0220, E0576.
+For more information about an error, try `rustc --explain E0220`.
diff --git a/tests/ui/hygiene/generate-mod.stderr b/tests/ui/hygiene/generate-mod.stderr
index 32a2e145ca9..58b9c642dab 100644
--- a/tests/ui/hygiene/generate-mod.stderr
+++ b/tests/ui/hygiene/generate-mod.stderr
@@ -1,12 +1,18 @@
 error[E0412]: cannot find type `FromOutside` in this scope
   --> $DIR/generate-mod.rs:35:13
    |
+LL |     type A = $FromOutside;
+   |              ------------ due to this macro variable
+...
 LL |     genmod!(FromOutside, Outer);
    |             ^^^^^^^^^^^ not found in this scope
 
 error[E0412]: cannot find type `Outer` in this scope
   --> $DIR/generate-mod.rs:35:26
    |
+LL |     struct $Outer;
+   |            ------ due to this macro variable
+...
 LL |     genmod!(FromOutside, Outer);
    |                          ^^^^^ not found in this scope
 
diff --git a/tests/ui/hygiene/globs.stderr b/tests/ui/hygiene/globs.stderr
index 3f7a0ae7efa..31f25b182f1 100644
--- a/tests/ui/hygiene/globs.stderr
+++ b/tests/ui/hygiene/globs.stderr
@@ -50,6 +50,9 @@ error[E0425]: cannot find function `f` in this scope
 LL | n!(f);
    | ----- in this macro invocation
 ...
+LL |                     $j();
+   |                     -- due to this macro variable
+...
 LL |         n!(f);
    |            ^ not found in this scope
    |
@@ -63,6 +66,9 @@ error[E0425]: cannot find function `f` in this scope
 LL | n!(f);
    | ----- in this macro invocation
 ...
+LL |                     $j();
+   |                     -- due to this macro variable
+...
 LL |                 f
    |                 ^ not found in this scope
    |
diff --git a/tests/ui/macros/macro-parameter-span.stderr b/tests/ui/macros/macro-parameter-span.stderr
index 247750a8ad7..44c8c56dff9 100644
--- a/tests/ui/macros/macro-parameter-span.stderr
+++ b/tests/ui/macros/macro-parameter-span.stderr
@@ -1,6 +1,9 @@
 error[E0425]: cannot find value `x` in this scope
   --> $DIR/macro-parameter-span.rs:11:9
    |
+LL |         $id
+   |         --- due to this macro variable
+...
 LL |         x
    |         ^ not found in this scope
 
diff --git a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
index ce7694ecb1d..d9646760cea 100644
--- a/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
+++ b/tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
@@ -338,6 +338,9 @@ LL |     no_curly__no_rhs_dollar__no_round!(a);
 error[E0425]: cannot find value `a` in this scope
   --> $DIR/syntax-errors.rs:152:37
    |
+LL |     ( $i:ident ) => { count($i) };
+   |                             -- due to this macro variable
+...
 LL |     no_curly__rhs_dollar__no_round!(a);
    |                                     ^ not found in this scope
 
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`.
diff --git a/tests/ui/structs/ident-from-macro-expansion.rs b/tests/ui/structs/ident-from-macro-expansion.rs
new file mode 100644
index 00000000000..56d31a42561
--- /dev/null
+++ b/tests/ui/structs/ident-from-macro-expansion.rs
@@ -0,0 +1,19 @@
+struct Foo {
+    inner: Inner,
+}
+
+struct Inner {
+    y: i32,
+}
+
+macro_rules! access {
+    ($expr:expr, $ident:ident) => {
+        $expr.$ident
+    }
+}
+
+fn main() {
+    let k = Foo { inner: Inner { y: 0 } };
+    access!(k, y);
+    //~^ ERROR no field `y` on type `Foo`
+}
diff --git a/tests/ui/structs/ident-from-macro-expansion.stderr b/tests/ui/structs/ident-from-macro-expansion.stderr
new file mode 100644
index 00000000000..be2ab7c2e99
--- /dev/null
+++ b/tests/ui/structs/ident-from-macro-expansion.stderr
@@ -0,0 +1,14 @@
+error[E0609]: no field `y` on type `Foo`
+  --> $DIR/ident-from-macro-expansion.rs:17:16
+   |
+LL |         $expr.$ident
+   |               ------ due to this macro variable
+...
+LL |     access!(k, y);
+   |                ^ unknown field
+   |
+   = note: available field is: `inner`
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0609`.