Switch to a bitflags MacroKinds to support macros with more than one kind

Review everything that uses `MacroKind`, and switch anything that could
refer to more than one kind to use `MacroKinds`.

Add a new `SyntaxExtensionKind::MacroRules` for `macro_rules!` macros,
using the concrete `MacroRulesMacroExpander` type, and have it track
which kinds it can handle. Eliminate the separate optional `attr_ext`,
now that a `SyntaxExtension` can handle multiple macro kinds.

This also avoids the need to downcast when calling methods on
`MacroRulesMacroExpander`, such as `get_unused_rule`.

Integrate macro kind checking into name resolution's
`sub_namespace_match`, so that we only find a macro if it's the right
type, and eliminate the special-case hack for attributes.
This commit is contained in:
Josh Triplett
2025-08-07 19:21:16 -07:00
parent 6355cd39c8
commit 0b855bcdc9
19 changed files with 212 additions and 119 deletions

View File

@@ -1,7 +1,6 @@
//! A bunch of methods and structures more or less related to resolving macros and
//! interface provided by `Resolver` to macro expander.
use std::any::Any;
use std::cell::Cell;
use std::mem;
use std::sync::Arc;
@@ -13,13 +12,13 @@ use rustc_expand::base::{
Annotatable, DeriveResolution, Indeterminate, ResolverExpand, SyntaxExtension,
SyntaxExtensionKind,
};
use rustc_expand::compile_declarative_macro;
use rustc_expand::expand::{
AstFragment, AstFragmentKind, Invocation, InvocationKind, SupportsMacroExpansion,
};
use rustc_expand::{MacroRulesMacroExpander, compile_declarative_macro};
use rustc_hir::StabilityLevel;
use rustc_hir::attrs::{CfgEntry, StrippedCfgItem};
use rustc_hir::def::{self, DefKind, Namespace, NonMacroAttrKind};
use rustc_hir::def::{self, DefKind, MacroKinds, Namespace, NonMacroAttrKind};
use rustc_hir::def_id::{CrateNum, DefId, LocalDefId};
use rustc_middle::middle::stability;
use rustc_middle::ty::{RegisteredTools, TyCtxt};
@@ -86,22 +85,19 @@ pub(crate) type MacroRulesScopeRef<'ra> = &'ra Cell<MacroRulesScope<'ra>>;
/// one for attribute-like macros (attributes, derives).
/// We ignore resolutions from one sub-namespace when searching names in scope for another.
pub(crate) fn sub_namespace_match(
candidate: Option<MacroKind>,
candidate: Option<MacroKinds>,
requirement: Option<MacroKind>,
) -> bool {
#[derive(PartialEq)]
enum SubNS {
Bang,
AttrLike,
}
let sub_ns = |kind| match kind {
MacroKind::Bang => SubNS::Bang,
MacroKind::Attr | MacroKind::Derive => SubNS::AttrLike,
};
let candidate = candidate.map(sub_ns);
let requirement = requirement.map(sub_ns);
// "No specific sub-namespace" means "matches anything" for both requirements and candidates.
candidate.is_none() || requirement.is_none() || candidate == requirement
let (Some(candidate), Some(requirement)) = (candidate, requirement) else {
return true;
};
match requirement {
MacroKind::Bang => candidate.contains(MacroKinds::BANG),
MacroKind::Attr | MacroKind::Derive => {
candidate.intersects(MacroKinds::ATTR | MacroKinds::DERIVE)
}
}
}
// We don't want to format a path using pretty-printing,
@@ -323,6 +319,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
parent_scope.expansion,
span,
fast_print_path(path),
kind,
def_id,
def_id.map(|def_id| self.macro_def_scope(def_id).nearest_parent_mod()),
),
@@ -356,11 +353,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> {
}
let def_id = self.local_def_id(node_id);
let m = &self.local_macro_map[&def_id];
let SyntaxExtensionKind::LegacyBang(ref ext) = m.ext.kind else {
continue;
};
let ext: &dyn Any = ext.as_ref();
let Some(m) = ext.downcast_ref::<MacroRulesMacroExpander>() else {
let SyntaxExtensionKind::MacroRules(ref m) = m.ext.kind else {
continue;
};
for arm_i in unused_arms.iter() {
@@ -633,7 +626,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
self.check_stability_and_deprecation(&ext, path, node_id);
let unexpected_res = if ext.macro_kind() != kind {
let unexpected_res = if !ext.macro_kinds().contains(kind.into()) {
Some((kind.article(), kind.descr_expected()))
} else if matches!(res, Res::Def(..)) {
match supports_macro_expansion {
@@ -665,7 +658,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Suggest moving the macro out of the derive() if the macro isn't Derive
if !path.span.from_expansion()
&& kind == MacroKind::Derive
&& ext.macro_kind() != MacroKind::Derive
&& !ext.macro_kinds().contains(MacroKinds::DERIVE)
{
err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span });
err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str });
@@ -842,10 +835,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
}
_ => None,
},
None => self.get_macro(res).map(|macro_data| match kind {
Some(MacroKind::Attr) if let Some(ref ext) = macro_data.attr_ext => Arc::clone(ext),
_ => Arc::clone(&macro_data.ext),
}),
None => self.get_macro(res).map(|macro_data| Arc::clone(&macro_data.ext)),
};
Ok((ext, res))
}
@@ -1114,7 +1104,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
&& let Some(binding) = binding
// This is a `macro_rules` itself, not some import.
&& let NameBindingKind::Res(res) = binding.kind
&& let Res::Def(DefKind::Macro(MacroKind::Bang), def_id) = res
&& let Res::Def(DefKind::Macro(kinds), def_id) = res
&& kinds.contains(MacroKinds::BANG)
// And the `macro_rules` is defined inside the attribute's module,
// so it cannot be in scope unless imported.
&& self.tcx.is_descendant_of(def_id, mod_def_id.to_def_id())
@@ -1161,8 +1152,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// Reserve some names that are not quite covered by the general check
// performed on `Resolver::builtin_attrs`.
if ident.name == sym::cfg || ident.name == sym::cfg_attr {
let macro_kind = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kind());
if macro_kind.is_some() && sub_namespace_match(macro_kind, Some(MacroKind::Attr)) {
let macro_kinds = self.get_macro(res).map(|macro_data| macro_data.ext.macro_kinds());
if macro_kinds.is_some() && sub_namespace_match(macro_kinds, Some(MacroKind::Attr)) {
self.dcx()
.emit_err(errors::NameReservedInAttributeNamespace { span: ident.span, ident });
}
@@ -1181,7 +1172,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
node_id: NodeId,
edition: Edition,
) -> MacroData {
let (mut ext, mut attr_ext, mut nrules) = compile_declarative_macro(
let (mut ext, mut nrules) = compile_declarative_macro(
self.tcx.sess,
self.tcx.features(),
macro_def,
@@ -1198,14 +1189,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
// The macro is a built-in, replace its expander function
// while still taking everything else from the source code.
ext.kind = builtin_ext_kind.clone();
attr_ext = None;
nrules = 0;
} else {
self.dcx().emit_err(errors::CannotFindBuiltinMacroWithName { span, ident });
}
}
MacroData { ext: Arc::new(ext), attr_ext, nrules, macro_rules: macro_def.macro_rules }
MacroData { ext: Arc::new(ext), nrules, macro_rules: macro_def.macro_rules }
}
fn path_accessible(