about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs85
-rw-r--r--crates/hir-expand/src/declarative.rs34
-rw-r--r--crates/mbe/src/benchmark.rs10
-rw-r--r--crates/mbe/src/expander.rs3
-rw-r--r--crates/mbe/src/expander/matcher.rs14
-rw-r--r--crates/mbe/src/lib.rs30
-rw-r--r--crates/mbe/src/parser.rs36
7 files changed, 158 insertions, 54 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 965f329acb9..c5c26e26bc0 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -1449,6 +1449,7 @@ ok!();
 #[test]
 fn test_new_std_matches() {
     check(
+        //- edition:2021
         r#"
 macro_rules! matches {
     ($expression:expr, $pattern:pat $(if $guard:expr)? $(,)?) => {
@@ -1481,6 +1482,90 @@ fn main() {
 }
 
 #[test]
+fn test_hygienic_pat() {
+    check(
+        r#"
+//- /new.rs crate:new deps:old edition:2015
+old::make!();
+fn main() {
+    matches!(0, 0 | 1 if true);
+}
+//- /old.rs crate:old edition:2021
+#[macro_export]
+macro_rules! make {
+    () => {
+        macro_rules! matches {
+            ($expression:expr, $pattern:pat if $guard:expr ) => {
+                match $expression {
+                    $pattern if $guard => true,
+                    _ => false
+                }
+            };
+        }
+    }
+}
+ "#,
+        expect![[r#"
+macro_rules !matches {
+    ($expression: expr, $pattern: pat if $guard: expr) = > {
+        match $expression {
+            $pattern if $guard = > true , _ = > false
+        }
+    }
+    ;
+}
+fn main() {
+    match 0 {
+        0|1 if true =>true , _=>false
+    };
+}
+"#]],
+    );
+    check(
+        r#"
+//- /new.rs crate:new deps:old edition:2021
+old::make!();
+fn main() {
+    matches/*+errors*/!(0, 0 | 1 if true);
+}
+//- /old.rs crate:old edition:2015
+#[macro_export]
+macro_rules! make {
+    () => {
+        macro_rules! matches {
+            ($expression:expr, $pattern:pat if $guard:expr ) => {
+                match $expression {
+                    $pattern if $guard => true,
+                    _ => false
+                }
+            };
+        }
+    }
+}
+ "#,
+        expect![[r#"
+macro_rules !matches {
+    ($expression: expr, $pattern: pat if $guard: expr) = > {
+        match $expression {
+            $pattern if $guard = > true , _ = > false
+        }
+    }
+    ;
+}
+fn main() {
+    /* error: unexpected token in input *//* parse error: expected expression */
+/* parse error: expected FAT_ARROW */
+/* parse error: expected `,` */
+/* parse error: expected pattern */
+match 0 {
+        0 if $guard=>true , _=>false
+    };
+}
+"#]],
+    );
+}
+
+#[test]
 fn test_dollar_crate_lhs_is_not_meta() {
     check(
         r#"
diff --git a/crates/hir-expand/src/declarative.rs b/crates/hir-expand/src/declarative.rs
index eceb20df5f8..9a0b218e6d1 100644
--- a/crates/hir-expand/src/declarative.rs
+++ b/crates/hir-expand/src/declarative.rs
@@ -2,7 +2,7 @@
 use std::sync::OnceLock;
 
 use base_db::{CrateId, VersionReq};
-use span::{Edition, MacroCallId, Span};
+use span::{MacroCallId, Span, SyntaxContextId};
 use syntax::{ast, AstNode};
 use triomphe::Arc;
 
@@ -10,7 +10,7 @@ use crate::{
     attrs::RawAttrs,
     db::ExpandDatabase,
     hygiene::{apply_mark, Transparency},
-    tt, AstId, ExpandError, ExpandResult,
+    tt, AstId, ExpandError, ExpandResult, Lookup,
 };
 
 /// Old-style `macro_rules` or the new macros 2.0
@@ -94,8 +94,6 @@ impl DeclarativeMacroExpander {
         def_crate: CrateId,
         id: AstId<ast::Macro>,
     ) -> Arc<DeclarativeMacroExpander> {
-        let crate_data = &db.crate_graph()[def_crate];
-        let is_2021 = crate_data.edition >= Edition::Edition2021;
         let (root, map) = crate::db::parse_with_map(db, id.file_id);
         let root = root.syntax_node();
 
@@ -133,6 +131,16 @@ impl DeclarativeMacroExpander {
             )
         });
 
+        let edition = |ctx: SyntaxContextId| {
+            let crate_graph = db.crate_graph();
+            if ctx.is_root() {
+                crate_graph[def_crate].edition
+            } else {
+                let data = db.lookup_intern_syntax_context(ctx);
+                // UNWRAP-SAFETY: Only the root context has no outer expansion
+                crate_graph[data.outer_expn.unwrap().lookup(db).def.krate].edition
+            }
+        };
         let (mac, transparency) = match id.to_ptr(db).to_node(&root) {
             ast::Macro::MacroRules(macro_rules) => (
                 match macro_rules.token_tree() {
@@ -145,12 +153,11 @@ impl DeclarativeMacroExpander {
                             ),
                         );
 
-                        mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021, new_meta_vars)
+                        mbe::DeclarativeMacro::parse_macro_rules(&tt, edition, new_meta_vars)
                     }
-                    None => mbe::DeclarativeMacro::from_err(
-                        mbe::ParseError::Expected("expected a token tree".into()),
-                        is_2021,
-                    ),
+                    None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
+                        "expected a token tree".into(),
+                    )),
                 },
                 transparency(&macro_rules).unwrap_or(Transparency::SemiTransparent),
             ),
@@ -163,12 +170,11 @@ impl DeclarativeMacroExpander {
                             map.span_for_range(macro_def.macro_token().unwrap().text_range()),
                         );
 
-                        mbe::DeclarativeMacro::parse_macro2(&tt, is_2021, new_meta_vars)
+                        mbe::DeclarativeMacro::parse_macro2(&tt, edition, new_meta_vars)
                     }
-                    None => mbe::DeclarativeMacro::from_err(
-                        mbe::ParseError::Expected("expected a token tree".into()),
-                        is_2021,
-                    ),
+                    None => mbe::DeclarativeMacro::from_err(mbe::ParseError::Expected(
+                        "expected a token tree".into(),
+                    )),
                 },
                 transparency(&macro_def).unwrap_or(Transparency::Opaque),
             ),
diff --git a/crates/mbe/src/benchmark.rs b/crates/mbe/src/benchmark.rs
index cacd5ed81a5..4d5531ae307 100644
--- a/crates/mbe/src/benchmark.rs
+++ b/crates/mbe/src/benchmark.rs
@@ -23,7 +23,11 @@ fn benchmark_parse_macro_rules() {
         let _pt = bench("mbe parse macro rules");
         rules
             .values()
-            .map(|it| DeclarativeMacro::parse_macro_rules(it, true, true).rules.len())
+            .map(|it| {
+                DeclarativeMacro::parse_macro_rules(it, |_| span::Edition::CURRENT, true)
+                    .rules
+                    .len()
+            })
             .sum()
     };
     assert_eq!(hash, 1144);
@@ -54,7 +58,9 @@ 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, true, true)))
+        .map(|(id, tt)| {
+            (id, DeclarativeMacro::parse_macro_rules(&tt, |_| span::Edition::CURRENT, true))
+        })
         .collect()
 }
 
diff --git a/crates/mbe/src/expander.rs b/crates/mbe/src/expander.rs
index 9e365232656..2f2c0aa6ff5 100644
--- a/crates/mbe/src/expander.rs
+++ b/crates/mbe/src/expander.rs
@@ -15,13 +15,12 @@ pub(crate) fn expand_rules(
     rules: &[crate::Rule],
     input: &tt::Subtree<Span>,
     marker: impl Fn(&mut Span) + Copy,
-    is_2021: bool,
     new_meta_vars: bool,
     call_site: Span,
 ) -> ExpandResult<tt::Subtree<Span>> {
     let mut match_: Option<(matcher::Match, &crate::Rule)> = None;
     for rule in rules {
-        let new_match = matcher::match_(&rule.lhs, input, is_2021);
+        let new_match = matcher::match_(&rule.lhs, input);
 
         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 e14e13856fd..3170834d54f 100644
--- a/crates/mbe/src/expander/matcher.rs
+++ b/crates/mbe/src/expander/matcher.rs
@@ -108,8 +108,8 @@ impl Match {
 }
 
 /// Matching errors are added to the `Match`.
-pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>, is_2021: bool) -> Match {
-    let mut res = match_loop(pattern, input, is_2021);
+pub(super) fn match_(pattern: &MetaTemplate, input: &tt::Subtree<Span>) -> Match {
+    let mut res = match_loop(pattern, input);
     res.bound_count = count(res.bindings.bindings());
     return res;
 
@@ -362,7 +362,6 @@ 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,
     delim_span: tt::DelimSpan<Span>,
 ) {
     macro_rules! try_push {
@@ -474,7 +473,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, is_2021, delim_span);
+                    let match_res = match_meta_var(kind, &mut fork, delim_span);
                     match match_res.err {
                         None => {
                             // Some meta variables are optional (e.g. vis)
@@ -587,7 +586,7 @@ fn match_loop_inner<'t>(
     }
 }
 
-fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) -> Match {
+fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>) -> Match {
     let span = src.delimiter.delim_span();
     let mut src = TtIter::new(src);
     let mut stack: SmallVec<[TtIter<'_, Span>; 1]> = SmallVec::new();
@@ -627,7 +626,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) ->
             &mut next_items,
             &mut eof_items,
             &mut error_items,
-            is_2021,
             span,
         );
         stdx::always!(cur_items.is_empty());
@@ -741,7 +739,6 @@ fn match_loop(pattern: &MetaTemplate, src: &tt::Subtree<Span>, is_2021: bool) ->
 fn match_meta_var(
     kind: MetaVarKind,
     input: &mut TtIter<'_, Span>,
-    is_2021: bool,
     delim_span: DelimSpan<Span>,
 ) -> ExpandResult<Option<Fragment>> {
     let fragment = match kind {
@@ -751,8 +748,7 @@ fn match_meta_var(
             });
         }
         MetaVarKind::Ty => parser::PrefixEntryPoint::Ty,
-        MetaVarKind::Pat if is_2021 => parser::PrefixEntryPoint::PatTop,
-        MetaVarKind::Pat => parser::PrefixEntryPoint::Pat,
+        MetaVarKind::Pat => parser::PrefixEntryPoint::PatTop,
         MetaVarKind::PatParam => parser::PrefixEntryPoint::Pat,
         MetaVarKind::Stmt => parser::PrefixEntryPoint::Stmt,
         MetaVarKind::Block => parser::PrefixEntryPoint::Block,
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 2d68f6f6a53..3a853512660 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -17,7 +17,7 @@ mod tt_iter;
 #[cfg(test)]
 mod benchmark;
 
-use span::Span;
+use span::{Edition, Span, SyntaxContextId};
 use stdx::impl_from;
 
 use std::fmt;
@@ -129,9 +129,6 @@ impl fmt::Display for CountError {
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct DeclarativeMacro {
     rules: Box<[Rule]>,
-    // 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,
     err: Option<Box<ParseError>>,
 }
 
@@ -142,14 +139,14 @@ struct Rule {
 }
 
 impl DeclarativeMacro {
-    pub fn from_err(err: ParseError, is_2021: bool) -> DeclarativeMacro {
-        DeclarativeMacro { rules: Box::default(), is_2021, err: Some(Box::new(err)) }
+    pub fn from_err(err: ParseError) -> DeclarativeMacro {
+        DeclarativeMacro { rules: Box::default(), err: Some(Box::new(err)) }
     }
 
     /// The old, `macro_rules! m {}` flavor.
     pub fn parse_macro_rules(
         tt: &tt::Subtree<Span>,
-        is_2021: bool,
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then)
         new_meta_vars: bool,
     ) -> DeclarativeMacro {
@@ -161,7 +158,7 @@ impl DeclarativeMacro {
         let mut err = None;
 
         while src.len() > 0 {
-            let rule = match Rule::parse(&mut src, true, new_meta_vars) {
+            let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) {
                 Ok(it) => it,
                 Err(e) => {
                     err = Some(Box::new(e));
@@ -184,13 +181,13 @@ impl DeclarativeMacro {
             }
         }
 
-        DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err }
+        DeclarativeMacro { rules: rules.into_boxed_slice(), err }
     }
 
     /// The new, unstable `macro m {}` flavor.
     pub fn parse_macro2(
         tt: &tt::Subtree<Span>,
-        is_2021: bool,
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         // FIXME: Remove this once we drop support for rust 1.76 (defaults to true then)
         new_meta_vars: bool,
     ) -> DeclarativeMacro {
@@ -201,7 +198,7 @@ impl DeclarativeMacro {
         if tt::DelimiterKind::Brace == tt.delimiter.kind {
             cov_mark::hit!(parse_macro_def_rules);
             while src.len() > 0 {
-                let rule = match Rule::parse(&mut src, true, new_meta_vars) {
+                let rule = match Rule::parse(edition, &mut src, true, new_meta_vars) {
                     Ok(it) => it,
                     Err(e) => {
                         err = Some(Box::new(e));
@@ -220,7 +217,7 @@ impl DeclarativeMacro {
             }
         } else {
             cov_mark::hit!(parse_macro_def_simple);
-            match Rule::parse(&mut src, false, new_meta_vars) {
+            match Rule::parse(edition, &mut src, false, new_meta_vars) {
                 Ok(rule) => {
                     if src.len() != 0 {
                         err = Some(Box::new(ParseError::expected("remaining tokens in macro def")));
@@ -240,7 +237,7 @@ impl DeclarativeMacro {
             }
         }
 
-        DeclarativeMacro { rules: rules.into_boxed_slice(), is_2021, err }
+        DeclarativeMacro { rules: rules.into_boxed_slice(), err }
     }
 
     pub fn err(&self) -> Option<&ParseError> {
@@ -254,12 +251,13 @@ impl DeclarativeMacro {
         new_meta_vars: bool,
         call_site: Span,
     ) -> ExpandResult<tt::Subtree<Span>> {
-        expander::expand_rules(&self.rules, tt, marker, self.is_2021, new_meta_vars, call_site)
+        expander::expand_rules(&self.rules, tt, marker, new_meta_vars, call_site)
     }
 }
 
 impl Rule {
     fn parse(
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         src: &mut TtIter<'_, Span>,
         expect_arrow: bool,
         new_meta_vars: bool,
@@ -271,8 +269,8 @@ impl Rule {
         }
         let rhs = src.expect_subtree().map_err(|()| ParseError::expected("expected subtree"))?;
 
-        let lhs = MetaTemplate::parse_pattern(lhs)?;
-        let rhs = MetaTemplate::parse_template(rhs, new_meta_vars)?;
+        let lhs = MetaTemplate::parse_pattern(edition, lhs)?;
+        let rhs = MetaTemplate::parse_template(edition, rhs, new_meta_vars)?;
 
         Ok(crate::Rule { lhs, rhs })
     }
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index 560dcf61a82..eaf2fd8c273 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -2,7 +2,7 @@
 //! trees.
 
 use smallvec::{smallvec, SmallVec};
-use span::Span;
+use span::{Edition, Span, SyntaxContextId};
 use syntax::SmolStr;
 
 use crate::{tt_iter::TtIter, ParseError};
@@ -24,27 +24,36 @@ use crate::{tt_iter::TtIter, ParseError};
 pub(crate) struct MetaTemplate(pub(crate) Box<[Op]>);
 
 impl MetaTemplate {
-    pub(crate) fn parse_pattern(pattern: &tt::Subtree<Span>) -> Result<Self, ParseError> {
-        MetaTemplate::parse(pattern, Mode::Pattern, false)
+    pub(crate) fn parse_pattern(
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
+        pattern: &tt::Subtree<Span>,
+    ) -> Result<Self, ParseError> {
+        MetaTemplate::parse(edition, pattern, Mode::Pattern, false)
     }
 
     pub(crate) fn parse_template(
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
         template: &tt::Subtree<Span>,
         new_meta_vars: bool,
     ) -> Result<Self, ParseError> {
-        MetaTemplate::parse(template, Mode::Template, new_meta_vars)
+        MetaTemplate::parse(edition, template, Mode::Template, new_meta_vars)
     }
 
     pub(crate) fn iter(&self) -> impl Iterator<Item = &Op> {
         self.0.iter()
     }
 
-    fn parse(tt: &tt::Subtree<Span>, mode: Mode, new_meta_vars: bool) -> Result<Self, ParseError> {
+    fn parse(
+        edition: impl Copy + Fn(SyntaxContextId) -> Edition,
+        tt: &tt::Subtree<Span>,
+        mode: Mode,
+        new_meta_vars: bool,
+    ) -> Result<Self, ParseError> {
         let mut src = TtIter::new(tt);
 
         let mut res = Vec::new();
         while let Some(first) = src.peek_n(0) {
-            let op = next_op(first, &mut src, mode, new_meta_vars)?;
+            let op = next_op(edition, first, &mut src, mode, new_meta_vars)?;
             res.push(op);
         }
 
@@ -145,6 +154,7 @@ enum Mode {
 }
 
 fn next_op(
+    edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     first_peeked: &tt::TokenTree<Span>,
     src: &mut TtIter<'_, Span>,
     mode: Mode,
@@ -162,7 +172,7 @@ fn next_op(
                 tt::TokenTree::Subtree(subtree) => match subtree.delimiter.kind {
                     tt::DelimiterKind::Parenthesis => {
                         let (separator, kind) = parse_repeat(src)?;
-                        let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?;
+                        let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?;
                         Op::Repeat { tokens, separator, kind }
                     }
                     tt::DelimiterKind::Brace => match mode {
@@ -189,13 +199,13 @@ fn next_op(
                         Op::Ident(tt::Ident { text: "$crate".into(), span: ident.span })
                     }
                     tt::Leaf::Ident(ident) => {
-                        let kind = eat_fragment_kind(src, mode)?;
+                        let kind = eat_fragment_kind(edition, src, mode)?;
                         let name = ident.text.clone();
                         let id = ident.span;
                         Op::Var { name, kind, id }
                     }
                     tt::Leaf::Literal(lit) if is_boolean_literal(lit) => {
-                        let kind = eat_fragment_kind(src, mode)?;
+                        let kind = eat_fragment_kind(edition, src, mode)?;
                         let name = lit.text.clone();
                         let id = lit.span;
                         Op::Var { name, kind, id }
@@ -233,7 +243,7 @@ fn next_op(
 
         tt::TokenTree::Subtree(subtree) => {
             src.next().expect("first token already peeked");
-            let tokens = MetaTemplate::parse(subtree, mode, new_meta_vars)?;
+            let tokens = MetaTemplate::parse(edition, subtree, mode, new_meta_vars)?;
             Op::Subtree { tokens, delimiter: subtree.delimiter }
         }
     };
@@ -241,6 +251,7 @@ fn next_op(
 }
 
 fn eat_fragment_kind(
+    edition: impl Copy + Fn(SyntaxContextId) -> Edition,
     src: &mut TtIter<'_, Span>,
     mode: Mode,
 ) -> Result<Option<MetaVarKind>, ParseError> {
@@ -252,7 +263,10 @@ fn eat_fragment_kind(
         let kind = match ident.text.as_str() {
             "path" => MetaVarKind::Path,
             "ty" => MetaVarKind::Ty,
-            "pat" => MetaVarKind::Pat,
+            "pat" => match edition(ident.span.ctx) {
+                Edition::Edition2015 | Edition::Edition2018 => MetaVarKind::PatParam,
+                Edition::Edition2021 | Edition::Edition2024 => MetaVarKind::Pat,
+            },
             "pat_param" => MetaVarKind::PatParam,
             "stmt" => MetaVarKind::Stmt,
             "block" => MetaVarKind::Block,