about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2023-04-24 20:36:45 +0000
committerbors <bors@rust-lang.org>2023-04-24 20:36:45 +0000
commit707382c21dff9f7a00dc91fadbd1363838b7d71b (patch)
treed74b6e106e935c1db2e173ecaf321965379fe609
parent0c0025f4cd14a61eb279efaa76dad4b672233f75 (diff)
parentd1ca5055253a5dd4871b37f2813b6094203b81b2 (diff)
Auto merge of #14652 - Veykril:pat2021, r=Veykril
fix: Fix pat fragment handling in 2021 edition

Fixes https://github.com/rust-lang/rust-analyzer/issues/9055

The fix isn't that great, but we are kind of forced to do it the quick and hacky way right now since std has changed the `matches` macro to make use of this now. And for a proper fix we need to track hygiene for identifiers which is a long way off anyways
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs40
-rw-r--r--crates/hir-expand/src/db.rs7
-rw-r--r--crates/mbe/src/benchmark.rs7
-rw-r--r--crates/mbe/src/expander.rs3
-rw-r--r--crates/mbe/src/expander/matcher.rs24
-rw-r--r--crates/mbe/src/lib.rs16
-rw-r--r--crates/parser/src/grammar.rs4
-rw-r--r--crates/parser/src/lib.rs2
8 files changed, 80 insertions, 23 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 216c6a45746..99c405fb917 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1293,20 +1293,54 @@ 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(
         r#"
diff --git a/crates/hir-expand/src/db.rs b/crates/hir-expand/src/db.rs
index c8de6954ee7..bb957e3ffa0 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<Arc<TokenExpander>, 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 141f77abb8b..212e0a02dbc 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<String, DeclarativeMacro> {
     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 6b7f4a22d29..8e2181e977e 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<tt::Subtree> {
     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 6fe8c005cf5..0ab5c8c3c9e 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<MatchState<'t>>,
     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<Option<Fragment>> {
+fn match_meta_var(
+    kind: MetaVarKind,
+    input: &mut TtIter<'_>,
+    is_2021: bool,
+) -> ExpandResult<Option<Fragment>> {
     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 a043e8222dc..d20641062c1 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -107,6 +107,9 @@ pub struct DeclarativeMacro {
     rules: Vec<Rule>,
     /// 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<DeclarativeMacro, ParseError> {
+    pub fn parse_macro_rules(
+        tt: &tt::Subtree,
+        is_2021: bool,
+    ) -> Result<DeclarativeMacro, ParseError> {
         // 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<DeclarativeMacro, ParseError> {
+    pub fn parse_macro2(tt: &tt::Subtree, is_2021: bool) -> Result<DeclarativeMacro, ParseError> {
         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<tt::Subtree> {
         // 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 e2fb1d1aced..1814e0e54c5 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 8c5aed0232b..1aba1f7674f 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,