Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1f67b86

Browse files
committedFeb 25, 2025·
Also note struct access, and fix macro expansion from foreign crates
1 parent d42c2f4 commit 1f67b86

File tree

7 files changed

+71
-18
lines changed

7 files changed

+71
-18
lines changed
 

‎compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
153153
sugg: None,
154154
// Try to get the span of the identifier within the path's syntax context
155155
// (if that's different).
156-
within_macro_span: assoc_name.span.within_macro(span),
156+
within_macro_span: assoc_name.span.within_macro(span, tcx.sess.source_map()),
157157
};
158158

159159
if is_dummy {

‎compiler/rustc_hir_typeck/src/expr.rs

+22-12
Original file line numberDiff line numberDiff line change
@@ -3070,7 +3070,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
30703070
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
30713071
ident, base, expr, base_ty
30723072
);
3073-
let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
3073+
let mut err = self.no_such_field_err(ident, base_ty, expr);
30743074

30753075
match *base_ty.peel_refs().kind() {
30763076
ty::Array(_, len) => {
@@ -3283,32 +3283,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
32833283
);
32843284
}
32853285

3286-
fn no_such_field_err(&self, field: Ident, expr_t: Ty<'tcx>, id: HirId) -> Diag<'_> {
3286+
fn no_such_field_err(
3287+
&self,
3288+
field: Ident,
3289+
base_ty: Ty<'tcx>,
3290+
expr: &hir::Expr<'tcx>,
3291+
) -> Diag<'_> {
32873292
let span = field.span;
3288-
debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t);
3293+
debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, base_ty);
32893294

32903295
let mut err = type_error_struct!(
32913296
self.dcx(),
32923297
span,
3293-
expr_t,
3298+
base_ty,
32943299
E0609,
3295-
"no field `{field}` on type `{expr_t}`",
3300+
"no field `{field}` on type `{base_ty}`",
32963301
);
3302+
if let Some(within_macro_span) = span.within_macro(expr.span, self.tcx.sess.source_map()) {
3303+
err.span_label(within_macro_span, "due to this macro variable");
3304+
}
32973305

32983306
// try to add a suggestion in case the field is a nested field of a field of the Adt
3299-
let mod_id = self.tcx.parent_module(id).to_def_id();
3300-
let (ty, unwrap) = if let ty::Adt(def, args) = expr_t.kind()
3307+
let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id();
3308+
let (ty, unwrap) = if let ty::Adt(def, args) = base_ty.kind()
33013309
&& (self.tcx.is_diagnostic_item(sym::Result, def.did())
33023310
|| self.tcx.is_diagnostic_item(sym::Option, def.did()))
33033311
&& let Some(arg) = args.get(0)
33043312
&& let Some(ty) = arg.as_type()
33053313
{
33063314
(ty, "unwrap().")
33073315
} else {
3308-
(expr_t, "")
3316+
(base_ty, "")
33093317
};
33103318
for (found_fields, args) in
3311-
self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, id)
3319+
self.get_field_candidates_considering_privacy_for_diag(span, ty, mod_id, expr.hir_id)
33123320
{
33133321
let field_names = found_fields.iter().map(|field| field.name).collect::<Vec<_>>();
33143322
let mut candidate_fields: Vec<_> = found_fields
@@ -3321,7 +3329,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33213329
args,
33223330
vec![],
33233331
mod_id,
3324-
id,
3332+
expr.hir_id,
33253333
)
33263334
})
33273335
.map(|mut field_path| {
@@ -3332,7 +3340,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
33323340
candidate_fields.sort();
33333341

33343342
let len = candidate_fields.len();
3335-
if len > 0 {
3343+
// Don't suggest `.field` if the base expr is from a different
3344+
// syntax context than the field.
3345+
if len > 0 && expr.span.eq_ctxt(field.span) {
33363346
err.span_suggestions(
33373347
field.span.shrink_to_lo(),
33383348
format!(
@@ -3968,7 +3978,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
39683978
_ => (),
39693979
};
39703980

3971-
self.no_such_field_err(field, container, expr.hir_id).emit();
3981+
self.no_such_field_err(field, container, expr).emit();
39723982

39733983
break;
39743984
}

‎compiler/rustc_hir_typeck/src/method/suggest.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
196196

197197
// Try to get the span of the identifier within the expression's syntax context
198198
// (if that's different).
199-
let within_macro_span = span.within_macro(expr_span);
199+
let within_macro_span = span.within_macro(expr_span, self.tcx.sess.source_map());
200200

201201
// Avoid suggestions when we don't know what's going on.
202202
if let Err(guar) = rcvr_ty.error_reported() {

‎compiler/rustc_resolve/src/late/diagnostics.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -431,8 +431,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
431431

432432
// Try to get the span of the identifier within the path's syntax context
433433
// (if that's different).
434-
if let Some(within_macro_span) = base_error.span.within_macro(span) {
435-
err.span_label(within_macro_span, "within this macro");
434+
if let Some(within_macro_span) =
435+
base_error.span.within_macro(span, self.r.tcx.sess.source_map())
436+
{
437+
err.span_label(within_macro_span, "due to this macro variable");
436438
}
437439

438440
self.detect_missing_binding_available_from_pattern(&mut err, path, following_seg);

‎compiler/rustc_span/src/lib.rs

+10-2
Original file line numberDiff line numberDiff line change
@@ -1071,9 +1071,17 @@ impl Span {
10711071
///
10721072
/// If "self" is the span of the outer_ident, and "within" is the span of the `($ident,)`
10731073
/// expr, then this will return the span of the `$ident` macro variable.
1074-
pub fn within_macro(self, within: Span) -> Option<Span> {
1074+
pub fn within_macro(self, within: Span, sm: &SourceMap) -> Option<Span> {
10751075
match Span::prepare_to_combine(self, within) {
1076-
Ok((self_, _, parent)) if self_.lo != self.lo() && self.hi() != self_.hi => {
1076+
// Only return something if it doesn't overlap with the original span,
1077+
// and the span isn't "imported" (i.e. from unavailable sources).
1078+
// FIXME: This does limit the usefulness of the error when the macro is
1079+
// from a foreign crate; we could also take into account `-Zmacro-backtrace`,
1080+
// which doesn't redact this span (but that would mean passing in even more
1081+
// args to this function, lol).
1082+
Ok((self_, _, parent))
1083+
if self_.hi < self.lo() || self.hi() < self_.lo && !sm.is_imported(within) =>
1084+
{
10771085
Some(Span::new(self_.lo, self_.hi, self_.ctxt, parent))
10781086
}
10791087
_ => None,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
struct Foo {
2+
inner: Inner,
3+
}
4+
5+
struct Inner {
6+
y: i32,
7+
}
8+
9+
macro_rules! access {
10+
($expr:expr, $ident:ident) => {
11+
$expr.$ident
12+
}
13+
}
14+
15+
fn main() {
16+
let k = Foo { inner: Inner { y: 0 } };
17+
access!(k, y);
18+
//~^ ERROR no field `y` on type `Foo`
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
error[E0609]: no field `y` on type `Foo`
2+
--> $DIR/ident-from-macro-expansion.rs:17:16
3+
|
4+
LL | $expr.$ident
5+
| ------ due to this macro variable
6+
...
7+
LL | access!(k, y);
8+
| ^ unknown field
9+
|
10+
= note: available field is: `inner`
11+
12+
error: aborting due to 1 previous error
13+
14+
For more information about this error, try `rustc --explain E0609`.

0 commit comments

Comments
 (0)
Please sign in to comment.