diff --git a/Cargo.lock b/Cargo.lock index dbb76ada8377f..62ee27e4cca50 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3889,6 +3889,7 @@ dependencies = [ name = "rustc_hir" version = "0.0.0" dependencies = [ + "bitflags", "odht", "rustc_abi", "rustc_arena", diff --git a/compiler/rustc_ast_lowering/src/item.rs b/compiler/rustc_ast_lowering/src/item.rs index ac6fac4c08e02..599e9ce7c257b 100644 --- a/compiler/rustc_ast_lowering/src/item.rs +++ b/compiler/rustc_ast_lowering/src/item.rs @@ -464,14 +464,14 @@ impl<'hir> LoweringContext<'_, 'hir> { let body = Box::new(self.lower_delim_args(body)); let def_id = self.local_def_id(id); let def_kind = self.tcx.def_kind(def_id); - let DefKind::Macro(macro_kind) = def_kind else { + let DefKind::Macro(macro_kinds) = def_kind else { unreachable!( "expected DefKind::Macro for macro item, found {}", def_kind.descr(def_id.to_def_id()) ); }; let macro_def = self.arena.alloc(ast::MacroDef { body, macro_rules: *macro_rules }); - hir::ItemKind::Macro(ident, macro_def, macro_kind) + hir::ItemKind::Macro(ident, macro_def, macro_kinds) } ItemKind::Delegation(box delegation) => { let delegation_results = self.lower_delegation(delegation, id, false); diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index c234aa43c09c1..205aa47c1a02e 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -17,6 +17,7 @@ use rustc_errors::{DiagCtxtHandle, ErrorGuaranteed, PResult}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::{AttributeKind, CfgEntry, Deprecation}; +use rustc_hir::def::MacroKinds; use rustc_hir::{Stability, find_attr}; use rustc_lint_defs::{BufferedEarlyLint, RegisteredTools}; use rustc_parse::MACRO_ARGUMENTS; @@ -718,6 +719,9 @@ impl MacResult for DummyResult { /// A syntax extension kind. #[derive(Clone)] pub enum SyntaxExtensionKind { + /// A `macro_rules!` macro that can work as any `MacroKind` + MacroRules(Arc), + /// A token-based function-like macro. Bang( /// An expander with signature TokenStream -> TokenStream. @@ -772,9 +776,39 @@ pub enum SyntaxExtensionKind { ), /// A glob delegation. + /// + /// This is for delegated function implementations, and has nothing to do with glob imports. GlobDelegation(Arc), } +impl SyntaxExtensionKind { + /// Returns `Some(expander)` for a macro usable as a `LegacyBang`; otherwise returns `None` + /// + /// This includes a `MacroRules` with function-like rules. + pub fn as_legacy_bang(&self) -> Option<&(dyn TTMacroExpander + sync::DynSync + sync::DynSend)> { + match self { + SyntaxExtensionKind::LegacyBang(exp) => Some(exp.as_ref()), + SyntaxExtensionKind::MacroRules(exp) if exp.kinds().contains(MacroKinds::BANG) => { + Some(exp.as_ref()) + } + _ => None, + } + } + + /// Returns `Some(expander)` for a macro usable as an `Attr`; otherwise returns `None` + /// + /// This includes a `MacroRules` with `attr` rules. + pub fn as_attr(&self) -> Option<&(dyn AttrProcMacro + sync::DynSync + sync::DynSend)> { + match self { + SyntaxExtensionKind::Attr(exp) => Some(exp.as_ref()), + SyntaxExtensionKind::MacroRules(exp) if exp.kinds().contains(MacroKinds::ATTR) => { + Some(exp.as_ref()) + } + _ => None, + } + } +} + /// A struct representing a macro definition in "lowered" form ready for expansion. pub struct SyntaxExtension { /// A syntax extension kind. @@ -804,18 +838,19 @@ pub struct SyntaxExtension { } impl SyntaxExtension { - /// Returns which kind of macro calls this syntax extension. - pub fn macro_kind(&self) -> MacroKind { + /// Returns which kinds of macro call this syntax extension. + pub fn macro_kinds(&self) -> MacroKinds { match self.kind { SyntaxExtensionKind::Bang(..) | SyntaxExtensionKind::LegacyBang(..) - | SyntaxExtensionKind::GlobDelegation(..) => MacroKind::Bang, + | SyntaxExtensionKind::GlobDelegation(..) => MacroKinds::BANG, SyntaxExtensionKind::Attr(..) | SyntaxExtensionKind::LegacyAttr(..) - | SyntaxExtensionKind::NonMacroAttr => MacroKind::Attr, + | SyntaxExtensionKind::NonMacroAttr => MacroKinds::ATTR, SyntaxExtensionKind::Derive(..) | SyntaxExtensionKind::LegacyDerive(..) => { - MacroKind::Derive + MacroKinds::DERIVE } + SyntaxExtensionKind::MacroRules(ref m) => m.kinds(), } } @@ -1027,11 +1062,12 @@ impl SyntaxExtension { parent: LocalExpnId, call_site: Span, descr: Symbol, + kind: MacroKind, macro_def_id: Option, parent_module: Option, ) -> ExpnData { ExpnData::new( - ExpnKind::Macro(self.macro_kind(), descr), + ExpnKind::Macro(kind, descr), parent.to_expn_id(), call_site, self.span, diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e7ae44169688d..f675541e0793f 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -736,8 +736,8 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let (fragment_kind, span) = (invoc.fragment_kind, invoc.span()); ExpandResult::Ready(match invoc.kind { - InvocationKind::Bang { mac, span } => match ext { - SyntaxExtensionKind::Bang(expander) => { + InvocationKind::Bang { mac, span } => { + if let SyntaxExtensionKind::Bang(expander) = ext { match expander.expand(self.cx, span, mac.args.tokens.clone()) { Ok(tok_result) => { let fragment = @@ -755,8 +755,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } - } - SyntaxExtensionKind::LegacyBang(expander) => { + } else if let Some(expander) = ext.as_legacy_bang() { let tok_result = match expander.expand(self.cx, span, mac.args.tokens.clone()) { ExpandResult::Ready(tok_result) => tok_result, ExpandResult::Retry(_) => { @@ -776,11 +775,12 @@ impl<'a, 'b> MacroExpander<'a, 'b> { let guar = self.error_wrong_fragment_kind(fragment_kind, &mac, span); fragment_kind.dummy(span, guar) } + } else { + unreachable!(); } - _ => unreachable!(), - }, - InvocationKind::Attr { attr, pos, mut item, derives } => match ext { - SyntaxExtensionKind::Attr(expander) => { + } + InvocationKind::Attr { attr, pos, mut item, derives } => { + if let Some(expander) = ext.as_attr() { self.gate_proc_macro_input(&item); self.gate_proc_macro_attr_item(span, &item); let tokens = match &item { @@ -835,8 +835,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { } Err(guar) => return ExpandResult::Ready(fragment_kind.dummy(span, guar)), } - } - SyntaxExtensionKind::LegacyAttr(expander) => { + } else if let SyntaxExtensionKind::LegacyAttr(expander) = ext { match validate_attr::parse_meta(&self.cx.sess.psess, &attr) { Ok(meta) => { let item_clone = macro_stats.then(|| item.clone()); @@ -878,15 +877,15 @@ impl<'a, 'b> MacroExpander<'a, 'b> { fragment_kind.expect_from_annotatables(iter::once(item)) } } - } - SyntaxExtensionKind::NonMacroAttr => { + } else if let SyntaxExtensionKind::NonMacroAttr = ext { // `-Zmacro-stats` ignores these because they don't do any real expansion. self.cx.expanded_inert_attrs.mark(&attr); item.visit_attrs(|attrs| attrs.insert(pos, attr)); fragment_kind.expect_from_annotatables(iter::once(item)) + } else { + unreachable!(); } - _ => unreachable!(), - }, + } InvocationKind::Derive { path, item, is_const } => match ext { SyntaxExtensionKind::Derive(expander) | SyntaxExtensionKind::LegacyDerive(expander) => { diff --git a/compiler/rustc_expand/src/mbe/diagnostics.rs b/compiler/rustc_expand/src/mbe/diagnostics.rs index 5b9d56ee2bc32..80433b7be9103 100644 --- a/compiler/rustc_expand/src/mbe/diagnostics.rs +++ b/compiler/rustc_expand/src/mbe/diagnostics.rs @@ -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")); }; diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 25987a5036635..faeae1f494e65 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -357,10 +357,10 @@ enum NestedMacroState { /// The token `macro_rules` was processed. MacroRules, /// The tokens `macro_rules!` were processed. - MacroRulesNot, + MacroRulesBang, /// The tokens `macro_rules!` followed by a name were processed. The name may be either directly /// an identifier or a meta-variable (that hopefully would be instantiated by an identifier). - MacroRulesNotName, + MacroRulesBangName, /// The keyword `macro` was processed. Macro, /// The keyword `macro` followed by a name was processed. @@ -408,24 +408,24 @@ fn check_nested_occurrences( NestedMacroState::MacroRules, &TokenTree::Token(Token { kind: TokenKind::Bang, .. }), ) => { - state = NestedMacroState::MacroRulesNot; + state = NestedMacroState::MacroRulesBang; } ( - NestedMacroState::MacroRulesNot, + NestedMacroState::MacroRulesBang, &TokenTree::Token(Token { kind: TokenKind::Ident(..), .. }), ) => { - state = NestedMacroState::MacroRulesNotName; + state = NestedMacroState::MacroRulesBangName; } - (NestedMacroState::MacroRulesNot, &TokenTree::MetaVar(..)) => { - state = NestedMacroState::MacroRulesNotName; + (NestedMacroState::MacroRulesBang, &TokenTree::MetaVar(..)) => { + state = NestedMacroState::MacroRulesBangName; // We check that the meta-variable is correctly used. check_occurrences(psess, node_id, tt, macros, binders, ops, guar); } - (NestedMacroState::MacroRulesNotName, TokenTree::Delimited(.., del)) + (NestedMacroState::MacroRulesBangName, TokenTree::Delimited(.., del)) | (NestedMacroState::MacroName, TokenTree::Delimited(.., del)) if del.delim == Delimiter::Brace => { - let macro_rules = state == NestedMacroState::MacroRulesNotName; + let macro_rules = state == NestedMacroState::MacroRulesBangName; state = NestedMacroState::Empty; let rest = check_nested_macro(psess, node_id, macro_rules, &del.tts, &nested_macros, guar); diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 08b0efb74a05f..334f57f9d6259 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -15,6 +15,7 @@ use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan}; use rustc_feature::Features; use rustc_hir as hir; use rustc_hir::attrs::AttributeKind; +use rustc_hir::def::MacroKinds; use rustc_hir::find_attr; use rustc_lint_defs::BuiltinLintDiag; use rustc_lint_defs::builtin::{ @@ -144,6 +145,7 @@ pub struct MacroRulesMacroExpander { name: Ident, span: Span, transparency: Transparency, + kinds: MacroKinds, rules: Vec, } @@ -158,6 +160,10 @@ impl MacroRulesMacroExpander { }; if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) } } + + pub fn kinds(&self) -> MacroKinds { + self.kinds + } } impl TTMacroExpander for MacroRulesMacroExpander { @@ -540,13 +546,13 @@ pub fn compile_declarative_macro( span: Span, node_id: NodeId, edition: Edition, -) -> (SyntaxExtension, Option>, usize) { +) -> (SyntaxExtension, usize) { let mk_syn_ext = |kind| { let is_local = is_defined_in_current_crate(node_id); SyntaxExtension::new(sess, kind, span, Vec::new(), edition, ident.name, attrs, is_local) }; - let mk_bang_ext = |expander| mk_syn_ext(SyntaxExtensionKind::LegacyBang(expander)); - let dummy_syn_ext = |guar| (mk_bang_ext(Arc::new(DummyExpander(guar))), None, 0); + let dummy_syn_ext = + |guar| (mk_syn_ext(SyntaxExtensionKind::LegacyBang(Arc::new(DummyExpander(guar)))), 0); let macro_rules = macro_def.macro_rules; let exp_sep = if macro_rules { exp!(Semi) } else { exp!(Comma) }; @@ -559,12 +565,12 @@ pub fn compile_declarative_macro( let mut guar = None; let mut check_emission = |ret: Result<(), ErrorGuaranteed>| guar = guar.or(ret.err()); - let mut has_attr_rules = false; + let mut kinds = MacroKinds::empty(); let mut rules = Vec::new(); while p.token != token::Eof { let args = if p.eat_keyword_noexpect(sym::attr) { - has_attr_rules = true; + kinds |= MacroKinds::ATTR; if !features.macro_attr() { feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable") .emit(); @@ -581,6 +587,7 @@ pub fn compile_declarative_macro( } Some(args) } else { + kinds |= MacroKinds::BANG; None }; let lhs_tt = p.parse_token_tree(); @@ -627,6 +634,7 @@ pub fn compile_declarative_macro( let guar = sess.dcx().span_err(span, "macros must contain at least one rule"); return dummy_syn_ext(guar); } + assert!(!kinds.is_empty()); let transparency = find_attr!(attrs, AttributeKind::MacroTransparency(x) => *x) .unwrap_or(Transparency::fallback(macro_rules)); @@ -640,12 +648,8 @@ pub fn compile_declarative_macro( // Return the number of rules for unused rule linting, if this is a local macro. let nrules = if is_defined_in_current_crate(node_id) { rules.len() } else { 0 }; - let exp = Arc::new(MacroRulesMacroExpander { name: ident, span, node_id, transparency, rules }); - let opt_attr_ext = has_attr_rules.then(|| { - let exp = Arc::clone(&exp); - Arc::new(mk_syn_ext(SyntaxExtensionKind::Attr(exp))) - }); - (mk_bang_ext(exp), opt_attr_ext, nrules) + let exp = MacroRulesMacroExpander { name: ident, kinds, span, node_id, transparency, rules }; + (mk_syn_ext(SyntaxExtensionKind::MacroRules(Arc::new(exp))), nrules) } fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option { diff --git a/compiler/rustc_hir/Cargo.toml b/compiler/rustc_hir/Cargo.toml index 539d2e6f0b179..71496b7ec324a 100644 --- a/compiler/rustc_hir/Cargo.toml +++ b/compiler/rustc_hir/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +bitflags = "2.9.1" odht = { version = "0.3.1", features = ["nightly"] } rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_hir/src/def.rs b/compiler/rustc_hir/src/def.rs index 339d4e2eab7fa..79319e2426603 100644 --- a/compiler/rustc_hir/src/def.rs +++ b/compiler/rustc_hir/src/def.rs @@ -31,6 +31,53 @@ pub enum CtorKind { Const, } +/// A set of macro kinds, for macros that can have more than one kind +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Encodable, Decodable, Hash, Debug)] +#[derive(HashStable_Generic)] +pub struct MacroKinds(u8); +bitflags::bitflags! { + impl MacroKinds: u8 { + const BANG = 1 << 0; + const ATTR = 1 << 1; + const DERIVE = 1 << 2; + } +} + +impl From for MacroKinds { + fn from(kind: MacroKind) -> Self { + match kind { + MacroKind::Bang => Self::BANG, + MacroKind::Attr => Self::ATTR, + MacroKind::Derive => Self::DERIVE, + } + } +} + +impl MacroKinds { + /// Convert the MacroKinds to a static string. + /// + /// This hardcodes all the possibilities, in order to return a static string. + pub fn descr(self) -> &'static str { + match self { + // FIXME: change this to "function-like macro" and fix all tests + Self::BANG => "macro", + Self::ATTR => "attribute macro", + Self::DERIVE => "derive macro", + _ if self == (Self::ATTR | Self::BANG) => "attribute/function macro", + _ if self == (Self::DERIVE | Self::BANG) => "derive/function macro", + _ if self == (Self::ATTR | Self::DERIVE) => "attribute/derive macro", + _ if self.is_all() => "attribute/derive/function macro", + _ if self.is_empty() => "useless macro", + _ => unreachable!(), + } + } + + /// Return an indefinite article (a/an) for use with `descr()` + pub fn article(self) -> &'static str { + if self.contains(Self::ATTR) { "an" } else { "a" } + } +} + /// An attribute that is not a macro; e.g., `#[inline]` or `#[rustfmt::skip]`. #[derive(Clone, Copy, PartialEq, Eq, Encodable, Decodable, Hash, Debug, HashStable_Generic)] pub enum NonMacroAttrKind { @@ -101,7 +148,7 @@ pub enum DefKind { AssocConst, // Macro namespace - Macro(MacroKind), + Macro(MacroKinds), // Not namespaced (or they are, but we don't treat them so) ExternCrate, @@ -177,7 +224,7 @@ impl DefKind { DefKind::AssocConst => "associated constant", DefKind::TyParam => "type parameter", DefKind::ConstParam => "const parameter", - DefKind::Macro(macro_kind) => macro_kind.descr(), + DefKind::Macro(kinds) => kinds.descr(), DefKind::LifetimeParam => "lifetime parameter", DefKind::Use => "import", DefKind::ForeignMod => "foreign module", @@ -208,7 +255,7 @@ impl DefKind { | DefKind::Use | DefKind::InlineConst | DefKind::ExternCrate => "an", - DefKind::Macro(macro_kind) => macro_kind.article(), + DefKind::Macro(kinds) => kinds.article(), _ => "a", } } @@ -845,10 +892,10 @@ impl Res { ) } - pub fn macro_kind(self) -> Option { + pub fn macro_kinds(self) -> Option { match self { - Res::Def(DefKind::Macro(kind), _) => Some(kind), - Res::NonMacroAttr(..) => Some(MacroKind::Attr), + Res::Def(DefKind::Macro(kinds), _) => Some(kinds), + Res::NonMacroAttr(..) => Some(MacroKinds::ATTR), _ => None, } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 34db6f92d9288..8358a11e586ad 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -20,7 +20,6 @@ use rustc_data_structures::tagged_ptr::TaggedRef; use rustc_index::IndexVec; use rustc_macros::{Decodable, Encodable, HashStable_Generic}; use rustc_span::def_id::LocalDefId; -use rustc_span::hygiene::MacroKind; use rustc_span::source_map::Spanned; use rustc_span::{BytePos, DUMMY_SP, ErrorGuaranteed, Ident, Span, Symbol, kw, sym}; use rustc_target::asm::InlineAsmRegOrRegClass; @@ -30,7 +29,7 @@ use tracing::debug; use crate::LangItem; use crate::attrs::AttributeKind; -use crate::def::{CtorKind, DefKind, PerNS, Res}; +use crate::def::{CtorKind, DefKind, MacroKinds, PerNS, Res}; use crate::def_id::{DefId, LocalDefIdMap}; pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId}; use crate::intravisit::{FnKind, VisitorExt}; @@ -4156,7 +4155,7 @@ impl<'hir> Item<'hir> { expect_fn, (Ident, &FnSig<'hir>, &'hir Generics<'hir>, BodyId), ItemKind::Fn { ident, sig, generics, body, .. }, (*ident, sig, generics, *body); - expect_macro, (Ident, &ast::MacroDef, MacroKind), + expect_macro, (Ident, &ast::MacroDef, MacroKinds), ItemKind::Macro(ident, def, mk), (*ident, def, *mk); expect_mod, (Ident, &'hir Mod<'hir>), ItemKind::Mod(ident, m), (*ident, m); @@ -4335,7 +4334,7 @@ pub enum ItemKind<'hir> { has_body: bool, }, /// A MBE macro definition (`macro_rules!` or `macro`). - Macro(Ident, &'hir ast::MacroDef, MacroKind), + Macro(Ident, &'hir ast::MacroDef, MacroKinds), /// A module. Mod(Ident, &'hir Mod<'hir>), /// An external module, e.g. `extern { .. }`. diff --git a/compiler/rustc_lint/src/non_local_def.rs b/compiler/rustc_lint/src/non_local_def.rs index b877f909fc029..e6507fe6149f3 100644 --- a/compiler/rustc_lint/src/non_local_def.rs +++ b/compiler/rustc_lint/src/non_local_def.rs @@ -5,7 +5,7 @@ use rustc_hir::{Body, HirId, Item, ItemKind, Node, Path, TyKind}; use rustc_middle::ty::TyCtxt; use rustc_session::{declare_lint, impl_lint_pass}; use rustc_span::def_id::{DefId, LOCAL_CRATE}; -use rustc_span::{ExpnKind, MacroKind, Span, kw, sym}; +use rustc_span::{ExpnKind, Span, kw, sym}; use crate::lints::{NonLocalDefinitionsCargoUpdateNote, NonLocalDefinitionsDiag}; use crate::{LateContext, LateLintPass, LintContext, fluent_generated as fluent}; @@ -240,7 +240,7 @@ impl<'tcx> LateLintPass<'tcx> for NonLocalDefinitions { }, ) } - ItemKind::Macro(_, _macro, MacroKind::Bang) + ItemKind::Macro(_, _macro, _kinds) if cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) => { cx.emit_span_lint( diff --git a/compiler/rustc_metadata/src/rmeta/encoder.rs b/compiler/rustc_metadata/src/rmeta/encoder.rs index d42c8b947a48a..a7e7e9985f4d4 100644 --- a/compiler/rustc_metadata/src/rmeta/encoder.rs +++ b/compiler/rustc_metadata/src/rmeta/encoder.rs @@ -1981,7 +1981,7 @@ impl<'a, 'tcx> EncodeContext<'a, 'tcx> { def_key.disambiguated_data.data = DefPathData::MacroNs(name); let def_id = id.to_def_id(); - self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind)); + self.tables.def_kind.set_some(def_id.index, DefKind::Macro(macro_kind.into())); self.tables.proc_macro.set_some(def_id.index, macro_kind); self.encode_attrs(id); record!(self.tables.def_keys[def_id] <- def_key); diff --git a/compiler/rustc_metadata/src/rmeta/mod.rs b/compiler/rustc_metadata/src/rmeta/mod.rs index 99174e4ad2fd9..1f7d142d33000 100644 --- a/compiler/rustc_metadata/src/rmeta/mod.rs +++ b/compiler/rustc_metadata/src/rmeta/mod.rs @@ -11,7 +11,7 @@ use rustc_abi::{FieldIdx, ReprOptions, VariantIdx}; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::svh::Svh; use rustc_hir::attrs::StrippedCfgItem; -use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap}; +use rustc_hir::def::{CtorKind, DefKind, DocLinkResMap, MacroKinds}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIndex, DefPathHash, StableCrateId}; use rustc_hir::definitions::DefKey; use rustc_hir::lang_items::LangItem; diff --git a/compiler/rustc_metadata/src/rmeta/table.rs b/compiler/rustc_metadata/src/rmeta/table.rs index 0671aa203993a..2cb07a28a8ad2 100644 --- a/compiler/rustc_metadata/src/rmeta/table.rs +++ b/compiler/rustc_metadata/src/rmeta/table.rs @@ -81,7 +81,7 @@ impl FixedSizeEncoding for u64 { } macro_rules! fixed_size_enum { - ($ty:ty { $(($($pat:tt)*))* }) => { + ($ty:ty { $(($($pat:tt)*))* } $( unreachable { $(($($upat:tt)*))+ } )?) => { impl FixedSizeEncoding for Option<$ty> { type ByteArray = [u8;1]; @@ -103,12 +103,24 @@ macro_rules! fixed_size_enum { b[0] = match self { None => unreachable!(), $(Some($($pat)*) => 1 + ${index()},)* + $(Some($($($upat)*)|+) => unreachable!(),)? } } } } } +// Workaround; need const traits to construct bitflags in a const +macro_rules! const_macro_kinds { + ($($name:ident),+$(,)?) => (MacroKinds::from_bits_truncate($(MacroKinds::$name.bits())|+)) +} +const MACRO_KINDS_ATTR_BANG: MacroKinds = const_macro_kinds!(ATTR, BANG); +const MACRO_KINDS_DERIVE_BANG: MacroKinds = const_macro_kinds!(DERIVE, BANG); +const MACRO_KINDS_DERIVE_ATTR: MacroKinds = const_macro_kinds!(DERIVE, ATTR); +const MACRO_KINDS_DERIVE_ATTR_BANG: MacroKinds = const_macro_kinds!(DERIVE, ATTR, BANG); +// Ensure that we get a compilation error if MacroKinds gets extended without updating metadata. +const _: () = assert!(MACRO_KINDS_DERIVE_ATTR_BANG.is_all()); + fixed_size_enum! { DefKind { ( Mod ) @@ -151,10 +163,16 @@ fixed_size_enum! { ( Ctor(CtorOf::Struct, CtorKind::Const) ) ( Ctor(CtorOf::Variant, CtorKind::Fn) ) ( Ctor(CtorOf::Variant, CtorKind::Const) ) - ( Macro(MacroKind::Bang) ) - ( Macro(MacroKind::Attr) ) - ( Macro(MacroKind::Derive) ) + ( Macro(MacroKinds::BANG) ) + ( Macro(MacroKinds::ATTR) ) + ( Macro(MacroKinds::DERIVE) ) + ( Macro(MACRO_KINDS_ATTR_BANG) ) + ( Macro(MACRO_KINDS_DERIVE_ATTR) ) + ( Macro(MACRO_KINDS_DERIVE_BANG) ) + ( Macro(MACRO_KINDS_DERIVE_ATTR_BANG) ) ( SyntheticCoroutineBody ) + } unreachable { + ( Macro(_) ) } } diff --git a/compiler/rustc_resolve/messages.ftl b/compiler/rustc_resolve/messages.ftl index ceef558c0cf95..d5ff8a4b60970 100644 --- a/compiler/rustc_resolve/messages.ftl +++ b/compiler/rustc_resolve/messages.ftl @@ -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 diff --git a/compiler/rustc_resolve/src/build_reduced_graph.rs b/compiler/rustc_resolve/src/build_reduced_graph.rs index d18d0fc16a8e4..07395b68c7d4c 100644 --- a/compiler/rustc_resolve/src/build_reduced_graph.rs +++ b/compiler/rustc_resolve/src/build_reduced_graph.rs @@ -1235,7 +1235,8 @@ impl<'a, 'ra, 'tcx> BuildReducedGraphVisitor<'a, 'ra, 'tcx> { ItemKind::Fn(box ast::Fn { ident: fn_ident, .. }) => { match self.proc_macro_stub(item, *fn_ident) { Some((macro_kind, ident, span)) => { - let res = Res::Def(DefKind::Macro(macro_kind), def_id.to_def_id()); + let macro_kinds = macro_kind.into(); + let res = Res::Def(DefKind::Macro(macro_kinds), def_id.to_def_id()); let macro_data = MacroData::new(self.r.dummy_ext(macro_kind)); self.r.new_local_macro(def_id, macro_data); self.r.proc_macro_stubs.insert(def_id); diff --git a/compiler/rustc_resolve/src/def_collector.rs b/compiler/rustc_resolve/src/def_collector.rs index 7d51fef28d3ba..1e4513eb78707 100644 --- a/compiler/rustc_resolve/src/def_collector.rs +++ b/compiler/rustc_resolve/src/def_collector.rs @@ -149,9 +149,9 @@ impl<'a, 'ra, 'tcx> visit::Visitor<'a> for DefCollector<'a, 'ra, 'tcx> { let macro_data = self.resolver.compile_macro(def, *ident, &attrs, i.span, i.id, edition); - let macro_kind = macro_data.ext.macro_kind(); + let macro_kinds = macro_data.ext.macro_kinds(); opt_macro_data = Some(macro_data); - DefKind::Macro(macro_kind) + DefKind::Macro(macro_kinds) } ItemKind::GlobalAsm(..) => DefKind::GlobalAsm, ItemKind::Use(use_tree) => { diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 210ab72678c56..a78cf0287954c 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -13,7 +13,7 @@ use rustc_errors::{ use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::{AttributeKind, CfgEntry, StrippedCfgItem}; use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, NonMacroAttrKind, PerNS}; +use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds, NonMacroAttrKind, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::{PrimTy, Stability, StabilityLevel, find_attr}; use rustc_middle::bug; @@ -1491,11 +1491,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let Some(binding) = resolution.borrow().best_binding() else { continue; }; - let Res::Def(DefKind::Macro(MacroKind::Derive | MacroKind::Attr), def_id) = - binding.res() - else { + let Res::Def(DefKind::Macro(kinds), def_id) = binding.res() else { continue; }; + if !kinds.intersects(MacroKinds::ATTR | MacroKinds::DERIVE) { + continue; + } // By doing this all *imported* macros get added to the `macro_map` even if they // are *unused*, which makes the later suggestions find them and work. let _ = this.get_macro_by_def_id(def_id); @@ -1504,7 +1505,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }, ); - let is_expected = &|res: Res| res.macro_kind() == Some(macro_kind); + let is_expected = + &|res: Res| res.macro_kinds().is_some_and(|k| k.contains(macro_kind.into())); let suggestion = self.early_lookup_typo_candidate( ScopeSet::Macro(macro_kind), parent_scope, @@ -1553,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 }); @@ -1566,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 @@ -1601,13 +1602,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let desc = match binding.res() { - Res::Def(DefKind::Macro(MacroKind::Bang), _) => "a function-like macro".to_string(), - Res::Def(DefKind::Macro(MacroKind::Attr), _) | Res::NonMacroAttr(..) => { + Res::Def(DefKind::Macro(MacroKinds::BANG), _) => { + "a function-like macro".to_string() + } + Res::Def(DefKind::Macro(MacroKinds::ATTR), _) | Res::NonMacroAttr(..) => { format!("an attribute: `#[{ident}]`") } - Res::Def(DefKind::Macro(MacroKind::Derive), _) => { + Res::Def(DefKind::Macro(MacroKinds::DERIVE), _) => { format!("a derive macro: `#[derive({ident})]`") } + Res::Def(DefKind::Macro(kinds), _) => { + format!("{} {}", kinds.article(), kinds.descr()) + } Res::ToolMod => { // Don't confuse the user with tool modules. continue; @@ -1644,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 @@ -2748,9 +2759,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let binding_key = BindingKey::new(ident, MacroNS); let binding = self.resolution(crate_module, binding_key)?.binding()?; - let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else { + let Res::Def(DefKind::Macro(kinds), _) = binding.res() else { return None; }; + if !kinds.contains(MacroKinds::BANG) { + return None; + } let module_name = crate_module.kind.name().unwrap_or(kw::Crate); let import_snippet = match import.kind { ImportKind::Single { source, target, .. } if source != target => { diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 2747ba135ed0d..a1d62ba7a6834 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -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] diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 9efcef695b7b4..87b3fc76c9671 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -2,7 +2,7 @@ use Determinacy::*; use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; -use rustc_hir::def::{DefKind, Namespace, NonMacroAttrKind, PartialRes, PerNS}; +use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; use rustc_middle::bug; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; @@ -259,7 +259,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { { let ext = &self.get_macro_by_def_id(def_id).ext; if ext.builtin_name.is_none() - && ext.macro_kind() == MacroKind::Derive + && ext.macro_kinds() == MacroKinds::DERIVE && parent.expansion.outer_expn_is_descendant_of(*ctxt) { return Some((parent, derive_fallback_lint_id)); @@ -632,17 +632,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { match result { Ok((binding, flags)) => { - let binding_macro_kind = binding.macro_kind(); - // If we're looking for an attribute, that might be supported by a - // `macro_rules!` macro. - // FIXME: Replace this with tracking multiple macro kinds for one Def. - if !(sub_namespace_match(binding_macro_kind, macro_kind) - || (binding_macro_kind == Some(MacroKind::Bang) - && macro_kind == Some(MacroKind::Attr) - && this - .get_macro(binding.res()) - .is_some_and(|macro_data| macro_data.attr_ext.is_some()))) - { + if !sub_namespace_match(binding.macro_kinds(), macro_kind) { return None; } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index aca251da71d39..c8cab5a0fe9a1 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -19,14 +19,13 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def::Namespace::{self, *}; -use rustc_hir::def::{self, CtorKind, CtorOf, DefKind}; +use rustc_hir::def::{self, CtorKind, CtorOf, DefKind, MacroKinds}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId}; use rustc_hir::{MissingLifetimeKind, PrimTy}; use rustc_middle::ty; use rustc_session::{Session, lint}; use rustc_span::edit_distance::{edit_distance, find_best_match_for_name}; use rustc_span::edition::Edition; -use rustc_span::hygiene::MacroKind; use rustc_span::{DUMMY_SP, Ident, Span, Symbol, kw, sym}; use thin_vec::ThinVec; use tracing::debug; @@ -1850,12 +1849,12 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { match (res, source) { ( - Res::Def(DefKind::Macro(MacroKind::Bang), def_id), + Res::Def(DefKind::Macro(kinds), def_id), PathSource::Expr(Some(Expr { kind: ExprKind::Index(..) | ExprKind::Call(..), .. })) | PathSource::Struct(_), - ) => { + ) if kinds.contains(MacroKinds::BANG) => { // Don't suggest macro if it's unstable. let suggestable = def_id.is_local() || self.r.tcx.lookup_stability(def_id).is_none_or(|s| s.is_stable()); @@ -1880,7 +1879,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { err.note("if you want the `try` keyword, you need Rust 2018 or later"); } } - (Res::Def(DefKind::Macro(MacroKind::Bang), _), _) => { + (Res::Def(DefKind::Macro(kinds), _), _) if kinds.contains(MacroKinds::BANG) => { err.span_label(span, fallback_label.to_string()); } (Res::Def(DefKind::TyAlias, def_id), PathSource::Trait(_)) => { diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b43f71913d9ba..797f4f619e31d 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -53,7 +53,8 @@ use rustc_feature::BUILTIN_ATTRIBUTES; use rustc_hir::attrs::StrippedCfgItem; use rustc_hir::def::Namespace::{self, *}; use rustc_hir::def::{ - self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, NonMacroAttrKind, PartialRes, PerNS, + self, CtorOf, DefKind, DocLinkResMap, LifetimeRes, MacroKinds, NonMacroAttrKind, PartialRes, + PerNS, }; use rustc_hir::def_id::{CRATE_DEF_ID, CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalDefIdMap}; use rustc_hir::definitions::DisambiguatorState; @@ -969,8 +970,8 @@ impl<'ra> NameBindingData<'ra> { matches!(self.res(), Res::Def(DefKind::AssocConst | DefKind::AssocFn | DefKind::AssocTy, _)) } - fn macro_kind(&self) -> Option { - self.res().macro_kind() + fn macro_kinds(&self) -> Option { + self.res().macro_kinds() } // Suppose that we resolved macro invocation with `invoc_parent_expansion` to binding `binding` @@ -1030,14 +1031,13 @@ struct DeriveData { struct MacroData { ext: Arc, - attr_ext: Option>, nrules: usize, macro_rules: bool, } impl MacroData { fn new(ext: Arc) -> MacroData { - MacroData { ext, attr_ext: None, nrules: 0, macro_rules: false } + MacroData { ext, nrules: 0, macro_rules: false } } } diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 9173d0d3ea5ec..9f25635f1fd1a 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -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>; /// 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, + candidate: Option, requirement: Option, ) -> 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::() 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,8 @@ 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) + && ext.macro_kinds().contains(MacroKinds::ATTR) { err.remove_surrounding_derive = Some(RemoveSurroundingDerive { span: path.span }); err.add_as_non_derive = Some(AddAsNonDerive { macro_path: &path_str }); @@ -842,10 +836,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 +1105,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 +1153,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 +1173,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 +1190,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( diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 8c0f897c9926c..0d98c64bbde5f 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use rustc_data_structures::fx::FxHashSet; use rustc_hir as hir; use rustc_hir::Mutability; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdSet, LocalDefId, LocalModDefId}; use rustc_metadata::creader::{CStore, LoadedMacro}; use rustc_middle::ty::fast_reject::SimplifiedType; @@ -137,13 +137,16 @@ pub(crate) fn try_inline( clean::ConstantItem(Box::new(ct)) }) } - Res::Def(DefKind::Macro(kind), did) => { - let mac = build_macro(cx, did, name, kind); - - let type_kind = match kind { - MacroKind::Bang => ItemType::Macro, - MacroKind::Attr => ItemType::ProcAttribute, - MacroKind::Derive => ItemType::ProcDerive, + Res::Def(DefKind::Macro(kinds), did) => { + let mac = build_macro(cx, did, name, kinds); + + // FIXME: handle attributes and derives that aren't proc macros, and macros with + // multiple kinds + let type_kind = match kinds { + MacroKinds::BANG => ItemType::Macro, + MacroKinds::ATTR => ItemType::ProcAttribute, + MacroKinds::DERIVE => ItemType::ProcDerive, + _ => todo!("Handle macros with multiple kinds"), }; record_extern_fqn(cx, did, type_kind); mac @@ -749,22 +752,36 @@ fn build_macro( cx: &mut DocContext<'_>, def_id: DefId, name: Symbol, - macro_kind: MacroKind, + macro_kinds: MacroKinds, ) -> clean::ItemKind { match CStore::from_tcx(cx.tcx).load_macro_untracked(def_id, cx.tcx) { - LoadedMacro::MacroDef { def, .. } => match macro_kind { - MacroKind::Bang => clean::MacroItem(clean::Macro { + // FIXME: handle attributes and derives that aren't proc macros, and macros with multiple + // kinds + LoadedMacro::MacroDef { def, .. } => match macro_kinds { + MacroKinds::BANG => clean::MacroItem(clean::Macro { source: utils::display_macro_source(cx, name, &def), macro_rules: def.macro_rules, }), - MacroKind::Derive | MacroKind::Attr => { - clean::ProcMacroItem(clean::ProcMacro { kind: macro_kind, helpers: Vec::new() }) - } + MacroKinds::DERIVE => clean::ProcMacroItem(clean::ProcMacro { + kind: MacroKind::Derive, + helpers: Vec::new(), + }), + MacroKinds::ATTR => clean::ProcMacroItem(clean::ProcMacro { + kind: MacroKind::Attr, + helpers: Vec::new(), + }), + _ => todo!("Handle macros with multiple kinds"), }, - LoadedMacro::ProcMacro(ext) => clean::ProcMacroItem(clean::ProcMacro { - kind: ext.macro_kind(), - helpers: ext.helper_attrs, - }), + LoadedMacro::ProcMacro(ext) => { + // Proc macros can only have a single kind + let kind = match ext.macro_kinds() { + MacroKinds::BANG => MacroKind::Bang, + MacroKinds::ATTR => MacroKind::Attr, + MacroKinds::DERIVE => MacroKind::Derive, + _ => unreachable!(), + }; + clean::ProcMacroItem(clean::ProcMacro { kind, helpers: ext.helper_attrs }) + } } } diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 7194c2fcededa..5003857330da0 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -40,7 +40,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet, In use rustc_errors::codes::*; use rustc_errors::{FatalError, struct_span_code_err}; use rustc_hir::attrs::AttributeKind; -use rustc_hir::def::{CtorKind, DefKind, Res}; +use rustc_hir::def::{CtorKind, DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE, LocalDefId}; use rustc_hir::{LangItem, PredicateOrigin, find_attr}; use rustc_hir_analysis::hir_ty_lowering::FeedConstTy; @@ -2845,11 +2845,19 @@ fn clean_maybe_renamed_item<'tcx>( generics: clean_generics(generics, cx), fields: variant_data.fields().iter().map(|x| clean_field(x, cx)).collect(), }), - ItemKind::Macro(_, macro_def, MacroKind::Bang) => MacroItem(Macro { + // FIXME: handle attributes and derives that aren't proc macros, and macros with + // multiple kinds + ItemKind::Macro(_, macro_def, MacroKinds::BANG) => MacroItem(Macro { source: display_macro_source(cx, name, macro_def), macro_rules: macro_def.macro_rules, }), - ItemKind::Macro(_, _, macro_kind) => clean_proc_macro(item, &mut name, macro_kind, cx), + ItemKind::Macro(_, _, MacroKinds::ATTR) => { + clean_proc_macro(item, &mut name, MacroKind::Attr, cx) + } + ItemKind::Macro(_, _, MacroKinds::DERIVE) => { + clean_proc_macro(item, &mut name, MacroKind::Derive, cx) + } + ItemKind::Macro(_, _, _) => todo!("Handle macros with multiple kinds"), // proc macros can have a name set by attributes ItemKind::Fn { ref sig, generics, body: body_id, .. } => { clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx) diff --git a/src/librustdoc/formats/item_type.rs b/src/librustdoc/formats/item_type.rs index 3aba7a370adb2..1dba84aa44cc1 100644 --- a/src/librustdoc/formats/item_type.rs +++ b/src/librustdoc/formats/item_type.rs @@ -2,7 +2,7 @@ use std::fmt; -use rustc_hir::def::{CtorOf, DefKind}; +use rustc_hir::def::{CtorOf, DefKind, MacroKinds}; use rustc_span::hygiene::MacroKind; use serde::{Serialize, Serializer}; @@ -134,9 +134,10 @@ impl ItemType { DefKind::Trait => Self::Trait, DefKind::TyAlias => Self::TypeAlias, DefKind::TraitAlias => Self::TraitAlias, - DefKind::Macro(MacroKind::Bang) => ItemType::Macro, - DefKind::Macro(MacroKind::Attr) => ItemType::ProcAttribute, - DefKind::Macro(MacroKind::Derive) => ItemType::ProcDerive, + DefKind::Macro(MacroKinds::BANG) => ItemType::Macro, + DefKind::Macro(MacroKinds::ATTR) => ItemType::ProcAttribute, + DefKind::Macro(MacroKinds::DERIVE) => ItemType::ProcDerive, + DefKind::Macro(_) => todo!("Handle macros with multiple kinds"), DefKind::ForeignTy => Self::ForeignType, DefKind::Variant => Self::Variant, DefKind::Field => Self::StructField, diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs index bcb676cd1f17e..2616a47e5292f 100644 --- a/src/librustdoc/passes/collect_intra_doc_links.rs +++ b/src/librustdoc/passes/collect_intra_doc_links.rs @@ -13,7 +13,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet}; use rustc_data_structures::intern::Interned; use rustc_errors::{Applicability, Diag, DiagMessage}; use rustc_hir::def::Namespace::*; -use rustc_hir::def::{DefKind, Namespace, PerNS}; +use rustc_hir::def::{DefKind, MacroKinds, Namespace, PerNS}; use rustc_hir::def_id::{CRATE_DEF_ID, DefId, LOCAL_CRATE}; use rustc_hir::{Mutability, Safety}; use rustc_middle::ty::{Ty, TyCtxt}; @@ -25,7 +25,6 @@ use rustc_resolve::rustdoc::{ use rustc_session::config::CrateType; use rustc_session::lint::Lint; use rustc_span::BytePos; -use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Ident, Symbol, sym}; use smallvec::{SmallVec, smallvec}; use tracing::{debug, info, instrument, trace}; @@ -115,9 +114,11 @@ impl Res { let prefix = match kind { DefKind::Fn | DefKind::AssocFn => return Suggestion::Function, - DefKind::Macro(MacroKind::Bang) => return Suggestion::Macro, + // FIXME: handle macros with multiple kinds, and attribute/derive macros that aren't + // proc macros + DefKind::Macro(MacroKinds::BANG) => return Suggestion::Macro, - DefKind::Macro(MacroKind::Derive) => "derive", + DefKind::Macro(MacroKinds::DERIVE) => "derive", DefKind::Struct => "struct", DefKind::Enum => "enum", DefKind::Trait => "trait", @@ -881,9 +882,12 @@ fn trait_impls_for<'a>( fn is_derive_trait_collision(ns: &PerNS, ResolutionFailure<'_>>>) -> bool { if let (Ok(type_ns), Ok(macro_ns)) = (&ns.type_ns, &ns.macro_ns) { type_ns.iter().any(|(res, _)| matches!(res, Res::Def(DefKind::Trait, _))) - && macro_ns - .iter() - .any(|(res, _)| matches!(res, Res::Def(DefKind::Macro(MacroKind::Derive), _))) + && macro_ns.iter().any(|(res, _)| { + matches!( + res, + Res::Def(DefKind::Macro(kinds), _) if kinds.contains(MacroKinds::DERIVE) + ) + }) } else { false } @@ -1662,11 +1666,11 @@ impl Disambiguator { let suffixes = [ // If you update this list, please also update the relevant rustdoc book section! - ("!()", DefKind::Macro(MacroKind::Bang)), - ("!{}", DefKind::Macro(MacroKind::Bang)), - ("![]", DefKind::Macro(MacroKind::Bang)), + ("!()", DefKind::Macro(MacroKinds::BANG)), + ("!{}", DefKind::Macro(MacroKinds::BANG)), + ("![]", DefKind::Macro(MacroKinds::BANG)), ("()", DefKind::Fn), - ("!", DefKind::Macro(MacroKind::Bang)), + ("!", DefKind::Macro(MacroKinds::BANG)), ]; if let Some(idx) = link.find('@') { @@ -1685,7 +1689,7 @@ impl Disambiguator { safety: Safety::Safe, }), "function" | "fn" | "method" => Kind(DefKind::Fn), - "derive" => Kind(DefKind::Macro(MacroKind::Derive)), + "derive" => Kind(DefKind::Macro(MacroKinds::DERIVE)), "field" => Kind(DefKind::Field), "variant" => Kind(DefKind::Variant), "type" => NS(Namespace::TypeNS), diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs index 9058277d72eea..b2e4b59437504 100644 --- a/src/librustdoc/visit_ast.rs +++ b/src/librustdoc/visit_ast.rs @@ -5,7 +5,7 @@ use std::mem; use rustc_data_structures::fx::{FxHashSet, FxIndexMap}; use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{DefKind, MacroKinds, Res}; use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet}; use rustc_hir::intravisit::{Visitor, walk_body, walk_item}; use rustc_hir::{CRATE_HIR_ID, Node}; @@ -13,7 +13,6 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::TyCtxt; use rustc_span::Span; use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_span::hygiene::MacroKind; use rustc_span::symbol::{Symbol, kw, sym}; use tracing::debug; @@ -325,7 +324,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { let is_bang_macro = matches!( item, - Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, _, MacroKind::Bang), .. }) + Node::Item(&hir::Item { kind: hir::ItemKind::Macro(_, _, kinds), .. }) if kinds.contains(MacroKinds::BANG) ); if !self.view_item_stack.insert(res_did) && !is_bang_macro { @@ -406,7 +405,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> { // attribute can still be visible. || match item.kind { hir::ItemKind::Impl(..) => true, - hir::ItemKind::Macro(_, _, MacroKind::Bang) => { + hir::ItemKind::Macro(_, _, _) => { self.cx.tcx.has_attr(item.owner_id.def_id, sym::macro_export) } _ => false, diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs index 95e16aae40f9a..945bb84708f85 100644 --- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs +++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs @@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData}; use rustc_lint::{LateContext, LateLintPass}; use rustc_session::impl_lint_pass; -use rustc_span::MacroKind; use rustc_span::symbol::Symbol; declare_clippy_lint! { @@ -503,8 +502,8 @@ impl LateLintPass<'_> for ItemNameRepetitions { ); } - let is_macro_rule = matches!(item.kind, ItemKind::Macro(_, _, MacroKind::Bang)); - if both_are_public && item_camel.len() > mod_camel.len() && !is_macro_rule { + let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _)); + if both_are_public && item_camel.len() > mod_camel.len() && !is_macro { let matching = count_match_start(mod_camel, &item_camel); let rmatching = count_match_end(mod_camel, &item_camel); let nchars = mod_camel.chars().count(); diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs index 3828aff4164e8..902e8af7ec48d 100644 --- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs +++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs @@ -7,7 +7,6 @@ use rustc_lint::{LateContext, LateLintPass}; use rustc_middle::ty; use rustc_session::impl_lint_pass; use rustc_span::def_id::CRATE_DEF_ID; -use rustc_span::hygiene::MacroKind; declare_clippy_lint! { /// ### What it does @@ -89,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate { // We ignore macro exports. And `ListStem` uses, which aren't interesting. fn is_ignorable_export<'tcx>(item: &'tcx Item<'tcx>) -> bool { if let ItemKind::Use(path, kind) = item.kind { - let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(MacroKind::Bang), _))) + let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(_), _))) || kind == UseKind::ListStem; if ignore { return true; diff --git a/tests/ui/macros/macro-rules-attr-error.rs b/tests/ui/macros/macro-rules-attr-error.rs index 1c8bb251e20e1..81eadb6692f22 100644 --- a/tests/ui/macros/macro-rules-attr-error.rs +++ b/tests/ui/macros/macro-rules-attr-error.rs @@ -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() {} => {} } diff --git a/tests/ui/macros/macro-rules-attr-error.stderr b/tests/ui/macros/macro-rules-attr-error.stderr index 177b700938409..674d35091b68d 100644 --- a/tests/ui/macros/macro-rules-attr-error.stderr +++ b/tests/ui/macros/macro-rules-attr-error.stderr @@ -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: 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 2 previous errors +error: aborting due to 6 previous errors diff --git a/tests/ui/proc-macro/macro-namespace-reserved-2.stderr b/tests/ui/proc-macro/macro-namespace-reserved-2.stderr index 0471124061ef4..c8a7cc3ba9135 100644 --- a/tests/ui/proc-macro/macro-namespace-reserved-2.stderr +++ b/tests/ui/proc-macro/macro-namespace-reserved-2.stderr @@ -95,14 +95,6 @@ error: expected derive macro, found macro `crate::my_macro` | LL | #[derive(crate::my_macro)] | ^^^^^^^^^^^^^^^ not a derive macro - | -help: remove from the surrounding `derive()` - --> $DIR/macro-namespace-reserved-2.rs:50:10 - | -LL | #[derive(crate::my_macro)] - | ^^^^^^^^^^^^^^^ - = help: add as non-Derive macro - `#[crate::my_macro]` error: cannot find macro `my_macro_attr` in this scope --> $DIR/macro-namespace-reserved-2.rs:28:5