mirror of
https://github.com/rust-lang/rust.git
synced 2026-04-27 18:57:42 +03:00
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`.
This commit is contained in:
@@ -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 =
|
||||
|
||||
@@ -490,6 +490,7 @@ pub(crate) struct MacroArgsBadDelim {
|
||||
pub span: Span,
|
||||
#[subdiagnostic]
|
||||
pub sugg: MacroArgsBadDelimSugg,
|
||||
pub rule_kw: Symbol,
|
||||
}
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
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 @@ pub fn get_unused_rule(&self, rule_i: usize) -> Option<(&Ident, MultiSpan)> {
|
||||
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));
|
||||
|
||||
@@ -556,6 +556,8 @@ pub fn internal(&self, feature: Symbol) -> bool {
|
||||
(incomplete, loop_match, "1.90.0", Some(132306)),
|
||||
/// Allow `macro_rules!` attribute rules
|
||||
(unstable, macro_attr, "CURRENT_RUSTC_VERSION", Some(83527)),
|
||||
/// Allow `macro_rules!` derive rules
|
||||
(unstable, macro_derive, "CURRENT_RUSTC_VERSION", Some(143549)),
|
||||
/// Give access to additional metadata about declarative macro meta-variables.
|
||||
(unstable, macro_metavar_expr, "1.61.0", Some(83527)),
|
||||
/// Provides a way to concatenate identifiers using metavariable expressions.
|
||||
|
||||
@@ -1315,6 +1315,7 @@
|
||||
macro_attr,
|
||||
macro_attributes_in_derive_output,
|
||||
macro_concat,
|
||||
macro_derive,
|
||||
macro_escape,
|
||||
macro_export,
|
||||
macro_lifetime_matcher,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
#![crate_type = "lib"]
|
||||
|
||||
macro_rules! MyDerive { derive() {} => {} }
|
||||
//~^ ERROR `macro_rules!` derives are unstable
|
||||
@@ -0,0 +1,13 @@
|
||||
error[E0658]: `macro_rules!` derives are unstable
|
||||
--> $DIR/feature-gate-macro-derive.rs:3:1
|
||||
|
|
||||
LL | macro_rules! MyDerive { derive() {} => {} }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: see issue #83527 <https://github.com/rust-lang/rust/issues/83527> for more information
|
||||
= help: add `#![feature(macro_attr)]` to the crate attributes to enable
|
||||
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
|
||||
|
||||
error: aborting due to 1 previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0658`.
|
||||
@@ -14,10 +14,10 @@ macro_rules! attr_incomplete_4 { attr() {} => }
|
||||
//~^ ERROR macro definition ended unexpectedly
|
||||
|
||||
macro_rules! attr_noparens_1 { attr{} {} => {} }
|
||||
//~^ ERROR macro attribute argument matchers require parentheses
|
||||
//~^ ERROR `attr` rule argument matchers require parentheses
|
||||
|
||||
macro_rules! attr_noparens_2 { attr[] {} => {} }
|
||||
//~^ ERROR macro attribute argument matchers require parentheses
|
||||
//~^ ERROR `attr` rule argument matchers require parentheses
|
||||
|
||||
macro_rules! attr_noparens_3 { attr _ {} => {} }
|
||||
//~^ ERROR invalid macro matcher
|
||||
|
||||
@@ -22,7 +22,7 @@ error: macro definition ended unexpectedly
|
||||
LL | macro_rules! attr_incomplete_4 { attr() {} => }
|
||||
| ^ expected right-hand side of macro rule
|
||||
|
||||
error: macro attribute argument matchers require parentheses
|
||||
error: `attr` rule argument matchers require parentheses
|
||||
--> $DIR/macro-attr-bad.rs:16:36
|
||||
|
|
||||
LL | macro_rules! attr_noparens_1 { attr{} {} => {} }
|
||||
@@ -34,7 +34,7 @@ LL - macro_rules! attr_noparens_1 { attr{} {} => {} }
|
||||
LL + macro_rules! attr_noparens_1 { attr() {} => {} }
|
||||
|
|
||||
|
||||
error: macro attribute argument matchers require parentheses
|
||||
error: `attr` rule argument matchers require parentheses
|
||||
--> $DIR/macro-attr-bad.rs:19:36
|
||||
|
|
||||
LL | macro_rules! attr_noparens_2 { attr[] {} => {} }
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
macro_rules! attr {
|
||||
attr[$($args:tt)*] { $($body:tt)* } => {
|
||||
//~^ ERROR: macro attribute argument matchers require parentheses
|
||||
//~^ ERROR: `attr` rule argument matchers require parentheses
|
||||
//~v ERROR: attr:
|
||||
compile_error!(concat!(
|
||||
"attr: args=\"",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
error: macro attribute argument matchers require parentheses
|
||||
error: `attr` rule argument matchers require parentheses
|
||||
--> $DIR/macro-attr-recovery.rs:5:9
|
||||
|
|
||||
LL | attr[$($args:tt)*] { $($body:tt)* } => {
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
#![crate_type = "lib"]
|
||||
#![feature(macro_derive)]
|
||||
|
||||
macro_rules! derive_incomplete_1 { derive }
|
||||
//~^ ERROR macro definition ended unexpectedly
|
||||
//~| NOTE expected `()` after `derive`
|
||||
|
||||
macro_rules! derive_incomplete_2 { derive() }
|
||||
//~^ ERROR macro definition ended unexpectedly
|
||||
//~| NOTE expected macro derive body
|
||||
|
||||
macro_rules! derive_incomplete_3 { derive() {} }
|
||||
//~^ ERROR expected `=>`
|
||||
//~| NOTE expected `=>`
|
||||
|
||||
macro_rules! derive_incomplete_4 { derive() {} => }
|
||||
//~^ ERROR macro definition ended unexpectedly
|
||||
//~| NOTE expected right-hand side of macro rule
|
||||
|
||||
macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||
//~^ ERROR `derive` rule argument matchers require parentheses
|
||||
|
||||
macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||
//~^ ERROR `derive` rule argument matchers require parentheses
|
||||
|
||||
macro_rules! derive_noparens_3 { derive _ {} => {} }
|
||||
//~^ ERROR `derive` must be followed by `()`
|
||||
|
||||
macro_rules! derive_args_1 { derive($x:ident) ($y:ident) => {} }
|
||||
//~^ ERROR `derive` rules do not accept arguments
|
||||
|
||||
macro_rules! derive_args_2 { derive() => {} }
|
||||
//~^ ERROR expected macro derive body, got `=>`
|
||||
|
||||
macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||
//~^ ERROR `derive` rules do not accept arguments
|
||||
//~| ERROR expected macro derive body, got `=>`
|
||||
//~| NOTE need `()` after this `derive`
|
||||
|
||||
macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
|
||||
//~^ ERROR duplicate matcher binding
|
||||
//~| NOTE duplicate binding
|
||||
//~| NOTE previous binding
|
||||
@@ -0,0 +1,90 @@
|
||||
error: macro definition ended unexpectedly
|
||||
--> $DIR/macro-derive-bad.rs:4:42
|
||||
|
|
||||
LL | macro_rules! derive_incomplete_1 { derive }
|
||||
| ^ expected `()` after `derive`
|
||||
|
||||
error: macro definition ended unexpectedly
|
||||
--> $DIR/macro-derive-bad.rs:8:44
|
||||
|
|
||||
LL | macro_rules! derive_incomplete_2 { derive() }
|
||||
| ^ expected macro derive body
|
||||
|
||||
error: expected `=>`, found end of macro arguments
|
||||
--> $DIR/macro-derive-bad.rs:12:47
|
||||
|
|
||||
LL | macro_rules! derive_incomplete_3 { derive() {} }
|
||||
| ^ expected `=>`
|
||||
|
||||
error: macro definition ended unexpectedly
|
||||
--> $DIR/macro-derive-bad.rs:16:50
|
||||
|
|
||||
LL | macro_rules! derive_incomplete_4 { derive() {} => }
|
||||
| ^ expected right-hand side of macro rule
|
||||
|
||||
error: `derive` rule argument matchers require parentheses
|
||||
--> $DIR/macro-derive-bad.rs:20:40
|
||||
|
|
||||
LL | macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||
| ^^
|
||||
|
|
||||
help: the delimiters should be `(` and `)`
|
||||
|
|
||||
LL - macro_rules! derive_noparens_1 { derive{} {} => {} }
|
||||
LL + macro_rules! derive_noparens_1 { derive() {} => {} }
|
||||
|
|
||||
|
||||
error: `derive` rule argument matchers require parentheses
|
||||
--> $DIR/macro-derive-bad.rs:23:40
|
||||
|
|
||||
LL | macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||
| ^^
|
||||
|
|
||||
help: the delimiters should be `(` and `)`
|
||||
|
|
||||
LL - macro_rules! derive_noparens_2 { derive[] {} => {} }
|
||||
LL + macro_rules! derive_noparens_2 { derive() {} => {} }
|
||||
|
|
||||
|
||||
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||
--> $DIR/macro-derive-bad.rs:26:41
|
||||
|
|
||||
LL | macro_rules! derive_noparens_3 { derive _ {} => {} }
|
||||
| ^
|
||||
|
||||
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||
--> $DIR/macro-derive-bad.rs:29:36
|
||||
|
|
||||
LL | macro_rules! derive_args_1 { derive($x:ident) ($y:ident) => {} }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: expected macro derive body, got `=>`
|
||||
--> $DIR/macro-derive-bad.rs:32:39
|
||||
|
|
||||
LL | macro_rules! derive_args_2 { derive() => {} }
|
||||
| ^^
|
||||
|
||||
error: `derive` rules do not accept arguments; `derive` must be followed by `()`
|
||||
--> $DIR/macro-derive-bad.rs:35:36
|
||||
|
|
||||
LL | macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||
| ^^^^^^^^^^
|
||||
|
||||
error: expected macro derive body, got `=>`
|
||||
--> $DIR/macro-derive-bad.rs:35:47
|
||||
|
|
||||
LL | macro_rules! derive_args_3 { derive($x:ident) => {} }
|
||||
| ------ ^^
|
||||
| |
|
||||
| need `()` after this `derive`
|
||||
|
||||
error: duplicate matcher binding
|
||||
--> $DIR/macro-derive-bad.rs:40:54
|
||||
|
|
||||
LL | macro_rules! derive_dup_matcher { derive() {$x:ident $x:ident} => {} }
|
||||
| -------- ^^^^^^^^ duplicate binding
|
||||
| |
|
||||
| previous binding
|
||||
|
||||
error: aborting due to 12 previous errors
|
||||
|
||||
Reference in New Issue
Block a user