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:
@@ -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(¯o_data.ext),
|
||||
}),
|
||||
None => self.get_macro(res).map(|macro_data| Arc::clone(¯o_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(
|
||||
|
||||
Reference in New Issue
Block a user