Detect and report macro kind mismatches early, and more precisely

This eliminates the case in `failed_to_match_macro` to check for a
function-like invocation of a macro with no function-like rules.

Instead, macro kind mismatches now result in an unresolved macro, and we
detect this case in `unresolved_macro_suggestions`, which now carefully
distinguishes between a kind mismatch and other errors.

This also handles cases of forward-referenced attributes and cyclic
attributes.

Expand test coverage to include all of these cases.
This commit is contained in:
Josh Triplett 2025-08-08 22:49:57 -07:00
parent c81fcaca1c
commit ba231db3f3
6 changed files with 108 additions and 29 deletions

View file

@ -58,18 +58,6 @@ pub(super) fn failed_to_match_macro(
let Some(BestFailure { token, msg: label, remaining_matcher, .. }) = tracker.best_failure
else {
// FIXME: we should report this at macro resolution time, as we do for
// `resolve_macro_cannot_use_as_attr`. We can do that once we track multiple macro kinds for a
// Def.
if attr_args.is_none() && !rules.iter().any(|rule| matches!(rule, MacroRule::Func { .. })) {
let msg = format!("macro has no rules for function-like invocation `{name}!`");
let mut err = psess.dcx().struct_span_err(sp, msg);
if !def_head_span.is_dummy() {
let msg = "this macro has no rules for function-like invocation";
err.span_label(def_head_span, msg);
}
return (sp, err.emit());
}
return (sp, psess.dcx().span_delayed_bug(sp, "failed to match a macro"));
};

View file

@ -242,6 +242,9 @@ resolve_lowercase_self =
attempt to use a non-constant value in a constant
.suggestion = try using `Self`
resolve_macro_cannot_use_as_fn_like =
`{$ident}` exists, but has no rules for function-like invocation
resolve_macro_cannot_use_as_attr =
`{$ident}` exists, but has no `attr` rules

View file

@ -1555,11 +1555,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
if let Some((def_id, unused_ident)) = unused_macro {
let scope = self.local_macro_def_scopes[&def_id];
let parent_nearest = parent_scope.module.nearest_parent_mod();
if Some(parent_nearest) == scope.opt_def_id() {
let unused_macro_kinds = self.local_macro_map[def_id].ext.macro_kinds();
if !unused_macro_kinds.contains(macro_kind.into()) {
match macro_kind {
MacroKind::Bang => {
err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });
err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });
err.subdiagnostic(MacroRulesNot::Func { span: unused_ident.span, ident });
}
MacroKind::Attr => {
err.subdiagnostic(MacroRulesNot::Attr { span: unused_ident.span, ident });
@ -1568,14 +1568,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
err.subdiagnostic(MacroRulesNot::Derive { span: unused_ident.span, ident });
}
}
return;
}
}
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
err.subdiagnostic(AddedMacroUse);
return;
if Some(parent_nearest) == scope.opt_def_id() {
err.subdiagnostic(MacroDefinedLater { span: unused_ident.span });
err.subdiagnostic(MacroSuggMovePosition { span: ident.span, ident });
return;
}
}
if ident.name == kw::Default
@ -1651,6 +1650,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
err.subdiagnostic(note);
return;
}
if self.macro_names.contains(&ident.normalize_to_macros_2_0()) {
err.subdiagnostic(AddedMacroUse);
return;
}
}
/// Given an attribute macro that failed to be resolved, look for `derive` macros that could

View file

@ -672,6 +672,12 @@ pub(crate) struct MacroSuggMovePosition {
#[derive(Subdiagnostic)]
pub(crate) enum MacroRulesNot {
#[label(resolve_macro_cannot_use_as_fn_like)]
Func {
#[primary_span]
span: Span,
ident: Ident,
},
#[label(resolve_macro_cannot_use_as_attr)]
Attr {
#[primary_span]

View file

@ -7,9 +7,46 @@ macro_rules! local_attr {
//~^^ ERROR: local_attr
}
//~v NOTE: `fn_only` exists, but has no `attr` rules
macro_rules! fn_only {
{} => {}
}
//~v NOTE: `attr_only` exists, but has no rules for function-like invocation
macro_rules! attr_only {
attr() {} => {}
}
fn main() {
//~v NOTE: in this expansion of #[local_attr]
#[local_attr]
struct S;
local_attr!(arg); //~ ERROR: macro has no rules for function-like invocation
//~vv ERROR: cannot find macro `local_attr` in this scope
//~| NOTE: `local_attr` is in scope, but it is an attribute
local_attr!(arg);
//~v ERROR: cannot find attribute `fn_only` in this scope
#[fn_only]
struct S;
attr_only!(); //~ ERROR: cannot find macro `attr_only` in this scope
}
//~vv ERROR: cannot find attribute `forward_referenced_attr` in this scope
//~| NOTE: consider moving the definition of `forward_referenced_attr` before this call
#[forward_referenced_attr]
struct S;
//~v NOTE: a macro with the same name exists, but it appears later
macro_rules! forward_referenced_attr {
attr() {} => {}
}
//~vv ERROR: cannot find attribute `cyclic_attr` in this scope
//~| NOTE: consider moving the definition of `cyclic_attr` before this call
#[cyclic_attr]
//~v NOTE: a macro with the same name exists, but it appears later
macro_rules! cyclic_attr {
attr() {} => {}
}

View file

@ -9,14 +9,55 @@ LL | #[local_attr]
|
= note: this error originates in the attribute macro `local_attr` (in Nightly builds, run with -Z macro-backtrace for more info)
error: macro has no rules for function-like invocation `local_attr!`
--> $DIR/macro-rules-attr-error.rs:14:5
error: cannot find macro `local_attr` in this scope
--> $DIR/macro-rules-attr-error.rs:27:5
|
LL | macro_rules! local_attr {
| ----------------------- this macro has no rules for function-like invocation
...
LL | local_attr!(arg);
| ^^^^^^^^^^^^^^^^
| ^^^^^^^^^^
|
= note: `local_attr` is in scope, but it is an attribute: `#[local_attr]`
error: aborting due to 2 previous errors
error: cannot find attribute `fn_only` in this scope
--> $DIR/macro-rules-attr-error.rs:30:7
|
LL | macro_rules! fn_only {
| ------- `fn_only` exists, but has no `attr` rules
...
LL | #[fn_only]
| ^^^^^^^
error: cannot find macro `attr_only` in this scope
--> $DIR/macro-rules-attr-error.rs:33:5
|
LL | macro_rules! attr_only {
| --------- `attr_only` exists, but has no rules for function-like invocation
...
LL | attr_only!();
| ^^^^^^^^^
error: cannot find attribute `forward_referenced_attr` in this scope
--> $DIR/macro-rules-attr-error.rs:38:3
|
LL | #[forward_referenced_attr]
| ^^^^^^^^^^^^^^^^^^^^^^^ consider moving the definition of `forward_referenced_attr` before this call
|
note: a macro with the same name exists, but it appears later
--> $DIR/macro-rules-attr-error.rs:42:14
|
LL | macro_rules! forward_referenced_attr {
| ^^^^^^^^^^^^^^^^^^^^^^^
error: cannot find attribute `cyclic_attr` in this scope
--> $DIR/macro-rules-attr-error.rs:48:3
|
LL | #[cyclic_attr]
| ^^^^^^^^^^^ consider moving the definition of `cyclic_attr` before this call
|
note: a macro with the same name exists, but it appears later
--> $DIR/macro-rules-attr-error.rs:50:14
|
LL | macro_rules! cyclic_attr {
| ^^^^^^^^^^^
error: aborting due to 6 previous errors