diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index 216c6a4574..99c405fb91 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1293,19 +1293,53 @@ ok!(); } #[test] -fn test_vertical_bar_with_pat() { +fn test_vertical_bar_with_pat_param() { check( r#" -macro_rules! m { (|$pat:pat| ) => { ok!(); } } +macro_rules! m { (|$pat:pat_param| ) => { ok!(); } } m! { |x| } "#, expect![[r#" -macro_rules! m { (|$pat:pat| ) => { ok!(); } } +macro_rules! m { (|$pat:pat_param| ) => { ok!(); } } ok!(); "#]], ); } +#[test] +fn test_new_std_matches() { + check( + r#" +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +fn main() { + matches!(0, 0 | 1 if true); +} + "#, + expect![[r#" +macro_rules! matches { + ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => { + match $expression { + $pattern $(if $guard)? => true, + _ => false + } + }; +} +fn main() { + match 0 { + 0|1if true =>true , _=>false + }; +} + "#]], + ); +} + #[test] fn test_dollar_crate_lhs_is_not_meta() { check( diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs index bed04b3a34..43c5bb9273 100644 --- a/crates/hir-expand/src/db.rs +++ b/crates/hir-expand/src/db.rs @@ -2,7 +2,7 @@ use std::sync::Arc; -use base_db::{salsa, SourceDatabase}; +use base_db::{salsa, Edition, SourceDatabase}; use either::Either; use limit::Limit; use mbe::syntax_node_to_token_tree; @@ -406,13 +406,14 @@ fn macro_def( ) -> Result, mbe::ParseError> { match id.kind { MacroDefKind::Declarative(ast_id) => { + let is_2021 = db.crate_graph()[id.krate].edition >= Edition::Edition2021; let (mac, def_site_token_map) = match ast_id.to_node(db) { ast::Macro::MacroRules(macro_rules) => { let arg = macro_rules .token_tree() .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt)?; + let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021)?; (mac, def_site_token_map) } ast::Macro::MacroDef(macro_def) => { @@ -420,7 +421,7 @@ fn macro_def( .body() .ok_or_else(|| mbe::ParseError::Expected("expected a token tree".into()))?; let (tt, def_site_token_map) = mbe::syntax_node_to_token_tree(arg.syntax()); - let mac = mbe::DeclarativeMacro::parse_macro2(&tt)?; + let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021)?; (mac, def_site_token_map) } }; diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs index 141f77abb8..212e0a02db 100644 --- a/crates/mbe/src/benchmark.rs +++ b/crates/mbe/src/benchmark.rs @@ -20,7 +20,10 @@ fn benchmark_parse_macro_rules() { let rules = macro_rules_fixtures_tt(); let hash: usize = { let _pt = bench("mbe parse macro rules"); - rules.values().map(|it| DeclarativeMacro::parse_macro_rules(it).unwrap().rules.len()).sum() + rules + .values() + .map(|it| DeclarativeMacro::parse_macro_rules(it, true).unwrap().rules.len()) + .sum() }; assert_eq!(hash, 1144); } @@ -50,7 +53,7 @@ fn benchmark_expand_macro_rules() { fn macro_rules_fixtures() -> FxHashMap { macro_rules_fixtures_tt() .into_iter() - .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt).unwrap())) + .map(|(id, tt)| (id, DeclarativeMacro::parse_macro_rules(&tt, true).unwrap())) .collect() } diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs index 6b7f4a22d2..8e2181e977 100644 --- a/crates/mbe/src/expander.rs +++ b/crates/mbe/src/expander.rs @@ -13,10 +13,11 @@ use crate::{parser::MetaVarKind, tt, ExpandError, ExpandResult}; pub(crate) fn expand_rules( rules: &[crate::Rule], input: &tt::Subtree, + is_2021: bool, ) -> ExpandResult { let mut match_: Option<(matcher::Match, &crate::Rule)> = None; for rule in rules { - let new_match = matcher::match_(&rule.lhs, input); + let new_match = matcher::match_(&rule.lhs, input, is_2021); if new_match.err.is_none() { // If we find a rule that applies without errors, we're done. diff --git a/crates/mbe/src/expander/matcher.rs b/crates/mbe/src/expander/matcher.rs index 6fe8c005cf..0ab5c8c3c9 100644 --- a/crates/mbe/src/expander/matcher.rs +++ b/crates/mbe/src/expander/matcher.rs @@ -111,8 +111,8 @@ impl Match { } /// Matching errors are added to the `Match`. -pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree) -> Match { - let mut res = match_loop(pattern, input); +pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree, is_2021: bool) -> Match { + let mut res = match_loop(pattern, input, is_2021); res.bound_count = count(res.bindings.bindings()); return res; @@ -354,6 +354,7 @@ struct MatchState<'t> { /// - `eof_items`: the set of items that would be valid if this was the EOF. /// - `bb_items`: the set of items that are waiting for the black-box parser. /// - `error_items`: the set of items in errors, used for error-resilient parsing +#[inline] fn match_loop_inner<'t>( src: TtIter<'t>, stack: &[TtIter<'t>], @@ -364,6 +365,7 @@ fn match_loop_inner<'t>( next_items: &mut Vec>, eof_items: &mut SmallVec<[MatchState<'t>; 1]>, error_items: &mut SmallVec<[MatchState<'t>; 1]>, + is_2021: bool, ) { macro_rules! try_push { ($items: expr, $it:expr) => { @@ -474,7 +476,7 @@ fn match_loop_inner<'t>( OpDelimited::Op(Op::Var { kind, name, .. }) => { if let &Some(kind) = kind { let mut fork = src.clone(); - let match_res = match_meta_var(kind, &mut fork); + let match_res = match_meta_var(kind, &mut fork, is_2021); match match_res.err { None => { // Some meta variables are optional (e.g. vis) @@ -583,7 +585,7 @@ fn match_loop_inner<'t>( } } -fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { +fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree, is_2021: bool) -> Match { let mut src = TtIter::new(src); let mut stack: SmallVec<[TtIter<'_>; 1]> = SmallVec::new(); let mut res = Match::default(); @@ -622,6 +624,7 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { &mut next_items, &mut eof_items, &mut error_items, + is_2021, ); stdx::always!(cur_items.is_empty()); @@ -731,14 +734,17 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree) -> Match { } } -fn match_meta_var(kind: MetaVarKind, input: &mut TtIter<'_>) -> ExpandResult> { +fn match_meta_var( + kind: MetaVarKind, + input: &mut TtIter<'_>, + is_2021: bool, +) -> ExpandResult> { let fragment = match kind { MetaVarKind::Path => parser::PrefixEntryPoint::Path, MetaVarKind::Ty => parser::PrefixEntryPoint::Ty, - // FIXME: These two should actually behave differently depending on the edition. - // - // https://doc.rust-lang.org/edition-guide/rust-2021/or-patterns-macro-rules.html - MetaVarKind::Pat | MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, + MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop, + MetaVarKind::Pat => parser::PrefixEntryPoint::Pat, + MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat, MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt, MetaVarKind::Block => parser::PrefixEntryPoint::Block, MetaVarKind::Meta => parser::PrefixEntryPoint::MetaItem, diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs index a043e8222d..d20641062c 100644 --- a/crates/mbe/src/lib.rs +++ b/crates/mbe/src/lib.rs @@ -107,6 +107,9 @@ pub struct DeclarativeMacro { rules: Vec, /// Highest id of the token we have in TokenMap shift: Shift, + // This is used for correctly determining the behavior of the pat fragment + // FIXME: This should be tracked by hygiene of the fragment identifier! + is_2021: bool, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -190,7 +193,10 @@ pub enum Origin { impl DeclarativeMacro { /// The old, `macro_rules! m {}` flavor. - pub fn parse_macro_rules(tt: &tt::Subtree) -> Result { + pub fn parse_macro_rules( + tt: &tt::Subtree, + is_2021: bool, + ) -> Result { // Note: this parsing can be implemented using mbe machinery itself, by // matching against `$($lhs:tt => $rhs:tt);*` pattern, but implementing // manually seems easier. @@ -211,11 +217,11 @@ impl DeclarativeMacro { validate(lhs)?; } - Ok(DeclarativeMacro { rules, shift: Shift::new(tt) }) + Ok(DeclarativeMacro { rules, shift: Shift::new(tt), is_2021 }) } /// The new, unstable `macro m {}` flavor. - pub fn parse_macro2(tt: &tt::Subtree) -> Result { + pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result { let mut src = TtIter::new(tt); let mut rules = Vec::new(); @@ -244,14 +250,14 @@ impl DeclarativeMacro { validate(lhs)?; } - Ok(DeclarativeMacro { rules, shift: Shift::new(tt) }) + Ok(DeclarativeMacro { rules, shift: Shift::new(tt), is_2021 }) } pub fn expand(&self, tt: &tt::Subtree) -> ExpandResult { // apply shift let mut tt = tt.clone(); self.shift.shift_all(&mut tt); - expander::expand_rules(&self.rules, &tt) + expander::expand_rules(&self.rules, &tt, self.is_2021) } pub fn map_id_down(&self, id: tt::TokenId) -> tt::TokenId { diff --git a/crates/parser/src/grammar.rs b/crates/parser/src/grammar.rs index e2fb1d1ace..1814e0e54c 100644 --- a/crates/parser/src/grammar.rs +++ b/crates/parser/src/grammar.rs @@ -66,6 +66,10 @@ pub(crate) mod entry { patterns::pattern_single(p); } + pub(crate) fn pat_top(p: &mut Parser<'_>) { + patterns::pattern_top(p); + } + pub(crate) fn ty(p: &mut Parser<'_>) { types::type_(p); } diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index 8c5aed0232..1aba1f7674 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -131,6 +131,7 @@ pub enum PrefixEntryPoint { Block, Stmt, Pat, + PatTop, Ty, Expr, Path, @@ -145,6 +146,7 @@ impl PrefixEntryPoint { PrefixEntryPoint::Block => grammar::entry::prefix::block, PrefixEntryPoint::Stmt => grammar::entry::prefix::stmt, PrefixEntryPoint::Pat => grammar::entry::prefix::pat, + PrefixEntryPoint::PatTop => grammar::entry::prefix::pat_top, PrefixEntryPoint::Ty => grammar::entry::prefix::ty, PrefixEntryPoint::Expr => grammar::entry::prefix::expr, PrefixEntryPoint::Path => grammar::entry::prefix::path,