diff options
| author | Josh Triplett <josh@joshtriplett.org> | 2025-08-09 15:16:19 -0700 |
|---|---|---|
| committer | Josh Triplett <josh@joshtriplett.org> | 2025-08-14 13:53:57 -0700 |
| commit | 8fb98ef36846f74c8a642e856808ad5600ae4110 (patch) | |
| tree | b99fb21a9f7d1a9be132370d3526e40c7416f4fe /compiler/rustc_expand | |
| parent | 2c1ac85679678dfe5cce7ea8037735b0349ceaf3 (diff) | |
| download | rust-8fb98ef36846f74c8a642e856808ad5600ae4110.tar.gz rust-8fb98ef36846f74c8a642e856808ad5600ae4110.zip | |
mbe: Parse macro `derive` rules
This handles various kinds of errors, but does not allow applying the derive yet. This adds the feature gate `macro_derive`.
Diffstat (limited to 'compiler/rustc_expand')
| -rw-r--r-- | compiler/rustc_expand/messages.ftl | 2 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/errors.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_expand/src/mbe/macro_rules.rs | 59 |
3 files changed, 55 insertions, 7 deletions
diff --git a/compiler/rustc_expand/messages.ftl b/compiler/rustc_expand/messages.ftl index 1f8f3be6809..61ba716d082 100644 --- a/compiler/rustc_expand/messages.ftl +++ b/compiler/rustc_expand/messages.ftl @@ -70,7 +70,7 @@ expand_invalid_fragment_specifier = invalid fragment specifier `{$fragment}` .help = {$help} -expand_macro_args_bad_delim = macro attribute argument matchers require parentheses +expand_macro_args_bad_delim = `{$rule_kw}` rule argument matchers require parentheses expand_macro_args_bad_delim_sugg = the delimiters should be `(` and `)` expand_macro_body_stability = diff --git a/compiler/rustc_expand/src/errors.rs b/compiler/rustc_expand/src/errors.rs index e58269991fc..ba9d76970f0 100644 --- a/compiler/rustc_expand/src/errors.rs +++ b/compiler/rustc_expand/src/errors.rs @@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim { pub span: Span, #[subdiagnostic] pub sugg: MacroArgsBadDelimSugg, + pub rule_kw: Symbol, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 334f57f9d62..ae2c96d3840 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -27,7 +27,7 @@ use rustc_session::Session; use rustc_session::parse::{ParseSess, feature_err}; use rustc_span::edition::Edition; use rustc_span::hygiene::Transparency; -use rustc_span::{Ident, Span, kw, sym}; +use rustc_span::{Ident, Span, Symbol, kw, sym}; use tracing::{debug, instrument, trace, trace_span}; use super::diagnostics::failed_to_match_macro; @@ -138,6 +138,9 @@ pub(super) enum MacroRule { body_span: Span, rhs: mbe::TokenTree, }, + /// A derive rule, for use with `#[m]` + #[expect(unused)] + Derive { body: Vec<MatcherLoc>, body_span: Span, rhs: mbe::TokenTree }, } pub struct MacroRulesMacroExpander { @@ -157,6 +160,7 @@ impl MacroRulesMacroExpander { MacroRule::Attr { args_span, body_span, ref rhs, .. } => { (MultiSpan::from_spans(vec![args_span, body_span]), rhs) } + MacroRule::Derive { body_span, ref rhs, .. } => (MultiSpan::from_span(body_span), rhs), }; if has_compile_error_macro(rhs) { None } else { Some((&self.name, span)) } } @@ -569,7 +573,7 @@ pub fn compile_declarative_macro( let mut rules = Vec::new(); while p.token != token::Eof { - let args = if p.eat_keyword_noexpect(sym::attr) { + let (args, is_derive) = if p.eat_keyword_noexpect(sym::attr) { kinds |= MacroKinds::ATTR; if !features.macro_attr() { feature_err(sess, sym::macro_attr, span, "`macro_rules!` attributes are unstable") @@ -579,16 +583,46 @@ pub fn compile_declarative_macro( return dummy_syn_ext(guar); } let args = p.parse_token_tree(); - check_args_parens(sess, &args); + check_args_parens(sess, sym::attr, &args); let args = parse_one_tt(args, RulePart::Pattern, sess, node_id, features, edition); check_emission(check_lhs(sess, node_id, &args)); if let Some(guar) = check_no_eof(sess, &p, "expected macro attr body") { return dummy_syn_ext(guar); } - Some(args) + (Some(args), false) + } else if p.eat_keyword_noexpect(sym::derive) { + kinds |= MacroKinds::DERIVE; + let derive_keyword_span = p.prev_token.span; + if !features.macro_derive() { + feature_err(sess, sym::macro_attr, span, "`macro_rules!` derives are unstable") + .emit(); + } + if let Some(guar) = check_no_eof(sess, &p, "expected `()` after `derive`") { + return dummy_syn_ext(guar); + } + let args = p.parse_token_tree(); + check_args_parens(sess, sym::derive, &args); + let args_empty_result = check_args_empty(sess, &args); + let args_not_empty = args_empty_result.is_err(); + check_emission(args_empty_result); + if let Some(guar) = check_no_eof(sess, &p, "expected macro derive body") { + return dummy_syn_ext(guar); + } + // If the user has `=>` right after the `()`, they might have forgotten the empty + // parentheses. + if p.token == token::FatArrow { + let mut err = sess + .dcx() + .struct_span_err(p.token.span, "expected macro derive body, got `=>`"); + if args_not_empty { + err.span_label(derive_keyword_span, "need `()` after this `derive`"); + } + return dummy_syn_ext(err.emit()); + } + (None, true) } else { kinds |= MacroKinds::BANG; - None + (None, false) }; let lhs_tt = p.parse_token_tree(); let lhs_tt = parse_one_tt(lhs_tt, RulePart::Pattern, sess, node_id, features, edition); @@ -619,6 +653,8 @@ pub fn compile_declarative_macro( let args = mbe::macro_parser::compute_locs(&delimited.tts); let body_span = lhs_span; rules.push(MacroRule::Attr { args, args_span, body: lhs, body_span, rhs: rhs_tt }); + } else if is_derive { + rules.push(MacroRule::Derive { body: lhs, body_span: lhs_span, rhs: rhs_tt }); } else { rules.push(MacroRule::Func { lhs, lhs_span, rhs: rhs_tt }); } @@ -665,7 +701,7 @@ fn check_no_eof(sess: &Session, p: &Parser<'_>, msg: &'static str) -> Option<Err None } -fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) { +fn check_args_parens(sess: &Session, rule_kw: Symbol, args: &tokenstream::TokenTree) { // This does not handle the non-delimited case; that gets handled separately by `check_lhs`. if let tokenstream::TokenTree::Delimited(dspan, _, delim, _) = args && *delim != Delimiter::Parenthesis @@ -673,10 +709,21 @@ fn check_args_parens(sess: &Session, args: &tokenstream::TokenTree) { sess.dcx().emit_err(errors::MacroArgsBadDelim { span: dspan.entire(), sugg: errors::MacroArgsBadDelimSugg { open: dspan.open, close: dspan.close }, + rule_kw, }); } } +fn check_args_empty(sess: &Session, args: &tokenstream::TokenTree) -> Result<(), ErrorGuaranteed> { + match args { + tokenstream::TokenTree::Delimited(.., delimited) if delimited.is_empty() => Ok(()), + _ => { + let msg = "`derive` rules do not accept arguments; `derive` must be followed by `()`"; + Err(sess.dcx().span_err(args.span(), msg)) + } + } +} + fn check_lhs(sess: &Session, node_id: NodeId, lhs: &mbe::TokenTree) -> Result<(), ErrorGuaranteed> { let e1 = check_lhs_nt_follows(sess, node_id, lhs); let e2 = check_lhs_no_empty_seq(sess, slice::from_ref(lhs)); |
