about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2025-02-24 21:37:01 +0000
committerMichael Goulet <michael@errs.io>2025-03-04 00:04:01 +0000
commit09e584671b439dc8a9d57cc900cc26668a69ddea (patch)
tree38763c999f24e795c21a0ccd88a763ba64cccbcf
parent06072468fe8de249a39d2fcc42cea51db2649a80 (diff)
downloadrust-09e584671b439dc8a9d57cc900cc26668a69ddea.tar.gz
rust-09e584671b439dc8a9d57cc900cc26668a69ddea.zip
Also note struct access, and fix macro expansion from foreign crates
-rw-r--r--compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs2
-rw-r--r--compiler/rustc_hir_typeck/src/expr.rs35
-rw-r--r--compiler/rustc_hir_typeck/src/method/suggest.rs2
-rw-r--r--compiler/rustc_resolve/src/late/diagnostics.rs6
-rw-r--r--compiler/rustc_span/src/lib.rs12
-rw-r--r--tests/ui/structs/ident-from-macro-expansion.rs19
-rw-r--r--tests/ui/structs/ident-from-macro-expansion.stderr14
7 files changed, 72 insertions, 18 deletions
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 156ddb8f2aa..6b9c1c0b309 100644
--- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
+++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
@@ -153,7 +153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
             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),
+            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 dec1779d92c..7f67e524fc4 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 09e1390cc3e..cb1e89fb9e5 100644
--- a/compiler/rustc_hir_typeck/src/method/suggest.rs
+++ b/compiler/rustc_hir_typeck/src/method/suggest.rs
@@ -196,7 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
 
         // 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);
+        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() {
diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs
index c86305e23d1..47b21f86641 100644
--- a/compiler/rustc_resolve/src/late/diagnostics.rs
+++ b/compiler/rustc_resolve/src/late/diagnostics.rs
@@ -431,8 +431,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
 
         // 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) {
-            err.span_label(within_macro_span, "within this macro");
+        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);
diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs
index d23e1825e6e..798e186a94b 100644
--- a/compiler/rustc_span/src/lib.rs
+++ b/compiler/rustc_span/src/lib.rs
@@ -1071,9 +1071,17 @@ impl Span {
     ///
     /// 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> {
+    pub fn within_macro(self, within: Span, sm: &SourceMap) -> Option<Span> {
         match Span::prepare_to_combine(self, within) {
-            Ok((self_, _, parent)) if self_.lo != self.lo() && self.hi() != self_.hi => {
+            // 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,
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`.