From 40bfb29e503e36602aceba512609dd797ed88ac4 Mon Sep 17 00:00:00 2001 From: Tim Neumann Date: Thu, 2 Jun 2022 21:36:11 +0200 Subject: [PATCH] feat: Support `$$` in macros. The implementation mirrors what `rustc` currently does [1]. Part of #11952. [1]: https://github.com/rust-lang/rust/blob/0595ea1d12cf745e0a672d05341429ecb0917e66/compiler/rustc_expand/src/mbe/quoted.rs#L230-L241 --- .../hir-def/src/macro_expansion_tests/mbe.rs | 68 +++++++++++++++++++ .../macro_expansion_tests/mbe/meta_syntax.rs | 6 ++ crates/mbe/src/parser.rs | 8 +++ 3 files changed, 82 insertions(+) diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs index befef6547cb6..93d2f648ae29 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs @@ -1544,3 +1544,71 @@ struct Foo; "##]], ) } + +#[test] +fn test_dollar_dollar() { + check( + r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +register_struct!(Foo); +register_methods!(alpha, beta); +implement_methods!(1, 2, 3); +"#, + expect![[r#" +macro_rules! register_struct { ($Struct:ident) => { + macro_rules! register_methods { ($$($method:ident),*) => { + macro_rules! implement_methods { ($$$$($$val:expr),*) => { + struct $Struct; + impl $Struct { $$(fn $method() -> &'static [u32] { &[$$$$($$$$val),*] })*} + }} + }} +}} + +macro_rules !register_methods { + ($($method: ident), *) = > { + macro_rules!implement_methods { + ($$($val: expr), *) = > { + struct Foo; + impl Foo { + $(fn $method()-> & 'static[u32] { + &[$$($$val), *] + } + )* + } + } + } + } +} +macro_rules !implement_methods { + ($($val: expr), *) = > { + struct Foo; + impl Foo { + fn alpha()-> & 'static[u32] { + &[$($val), *] + } + fn beta()-> & 'static[u32] { + &[$($val), *] + } + } + } +} +struct Foo; +impl Foo { + fn alpha() -> & 'static[u32] { + &[1, 2, 3] + } + fn beta() -> & 'static[u32] { + &[1, 2, 3] + } +} +"#]], + ) +} diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs index 2de10ddbdf9b..636a66ad5351 100644 --- a/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs +++ b/crates/hir-def/src/macro_expansion_tests/mbe/meta_syntax.rs @@ -56,6 +56,9 @@ macro_rules! f2 { ($i:) => ($i) } f2!(); macro_rules! f3 { ($i:_) => () } f3!(); + +macro_rules! m1 { ($$i) => () } +m1!(); "#, expect![[r#" macro_rules! i1 { invalid } @@ -74,6 +77,9 @@ macro_rules! f2 { ($i:) => ($i) } /* error: invalid macro definition: missing fragment specifier */ macro_rules! f3 { ($i:_) => () } /* error: invalid macro definition: missing fragment specifier */ + +macro_rules! m1 { ($$i) => () } +/* error: invalid macro definition: `$$` is not allowed on the pattern side */ "#]], ) } diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs index 9be8d7085da0..6c7be5984188 100644 --- a/crates/mbe/src/parser.rs +++ b/crates/mbe/src/parser.rs @@ -135,6 +135,14 @@ fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Resul let id = lit.id; Op::Var { name, kind, id } } + tt::Leaf::Punct(punct @ tt::Punct { char: '$', .. }) => match mode { + Mode::Pattern => { + return Err(ParseError::unexpected( + "`$$` is not allowed on the pattern side", + )) + } + Mode::Template => Op::Leaf(tt::Leaf::Punct(*punct)), + }, tt::Leaf::Punct(_) | tt::Leaf::Literal(_) => { return Err(ParseError::expected("expected ident")) }