Skip to content

Commit

Permalink
Rollup merge of rust-lang#137565 - compiler-errors:macro-ex, r=estebank
Browse files Browse the repository at this point in the history
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
  • Loading branch information
workingjubilee authored Mar 5, 2025
2 parents df5ded4 + 09e5846 commit 9b2a60f
Show file tree
Hide file tree
Showing 17 changed files with 225 additions and 15 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -620,6 +620,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 ->
Expand Down
2 changes: 2 additions & 0 deletions compiler/rustc_hir_analysis/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,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)]
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
35 changes: 23 additions & 12 deletions compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -3282,29 +3282,38 @@ 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)
&& let Some(ty) = arg.as_type()
{
(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
Expand All @@ -3317,7 +3326,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
args,
vec![],
mod_id,
id,
expr.hir_id,
)
})
.map(|mut field_path| {
Expand All @@ -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!(
Expand Down Expand Up @@ -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;
}
Expand Down
24 changes: 21 additions & 3 deletions compiler/rustc_hir_typeck/src/method/suggest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
Expand All @@ -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) => {
Expand All @@ -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,
Expand All @@ -230,7 +238,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
span,
&mut err,
&mut sources,
Some(sugg_span),
Some(expr_span),
);
err.emit()
}
Expand All @@ -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()
}
Expand All @@ -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}",
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_resolve/src/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
31 changes: 31 additions & 0 deletions compiler/rustc_span/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
23 changes: 23 additions & 0 deletions tests/ui/associated-types/ident-from-macro-expansion.rs
Original file line number Diff line number Diff line change
@@ -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() {}
22 changes: 22 additions & 0 deletions tests/ui/associated-types/ident-from-macro-expansion.stderr
Original file line number Diff line number Diff line change
@@ -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`.
6 changes: 6 additions & 0 deletions tests/ui/hygiene/generate-mod.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down
6 changes: 6 additions & 0 deletions tests/ui/hygiene/globs.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
|
Expand All @@ -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
|
Expand Down
3 changes: 3 additions & 0 deletions tests/ui/macros/macro-parameter-span.stderr
Original file line number Diff line number Diff line change
@@ -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

Expand Down
3 changes: 3 additions & 0 deletions tests/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
18 changes: 18 additions & 0 deletions tests/ui/methods/ident-from-macro-expansion.rs
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 9b2a60f

Please sign in to comment.