Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to point of macro expansion from resolver and method errors if it involves macro var #137565

Merged
merged 4 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading