aboutsummaryrefslogtreecommitdiff
path: root/crates/mbe
diff options
context:
space:
mode:
Diffstat (limited to 'crates/mbe')
-rw-r--r--crates/mbe/src/lib.rs58
-rw-r--r--crates/mbe/src/mbe_expander/matcher.rs34
-rw-r--r--crates/mbe/src/mbe_expander/transcriber.rs47
-rw-r--r--crates/mbe/src/parser.rs84
-rw-r--r--crates/mbe/src/syntax_bridge.rs3
-rw-r--r--crates/mbe/src/tests.rs48
6 files changed, 171 insertions, 103 deletions
diff --git a/crates/mbe/src/lib.rs b/crates/mbe/src/lib.rs
index 3ad609a00..b3472879d 100644
--- a/crates/mbe/src/lib.rs
+++ b/crates/mbe/src/lib.rs
@@ -14,10 +14,10 @@ mod tests;
14 14
15use std::fmt; 15use std::fmt;
16 16
17pub use tt::{Delimiter, Punct}; 17pub use tt::{Delimiter, DelimiterKind, Punct};
18 18
19use crate::{ 19use crate::{
20 parser::{parse_pattern, Op}, 20 parser::{parse_pattern, parse_template, Op},
21 tt_iter::TtIter, 21 tt_iter::TtIter,
22}; 22};
23 23
@@ -78,8 +78,24 @@ pub struct MacroRules {
78 78
79#[derive(Clone, Debug, PartialEq, Eq)] 79#[derive(Clone, Debug, PartialEq, Eq)]
80struct Rule { 80struct Rule {
81 lhs: tt::Subtree, 81 lhs: MetaTemplate,
82 rhs: tt::Subtree, 82 rhs: MetaTemplate,
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
86struct MetaTemplate {
87 delimiter: Option<Delimiter>,
88 tokens: Vec<Result<Op, ExpandError>>,
89}
90
91impl<'a> MetaTemplate {
92 fn iter(&self) -> impl Iterator<Item = &Result<Op, ExpandError>> {
93 self.tokens.iter()
94 }
95
96 fn delimiter_kind(&self) -> Option<DelimiterKind> {
97 self.delimiter.map(|it| it.kind)
98 }
83} 99}
84 100
85#[derive(Clone, Copy, Debug, PartialEq, Eq)] 101#[derive(Clone, Copy, Debug, PartialEq, Eq)]
@@ -167,7 +183,7 @@ impl MacroRules {
167 rules.push(rule); 183 rules.push(rule);
168 if let Err(()) = src.expect_char(';') { 184 if let Err(()) = src.expect_char(';') {
169 if src.len() > 0 { 185 if src.len() > 0 {
170 return Err(ParseError::Expected("expected `:`".to_string())); 186 return Err(ParseError::Expected("expected `;`".to_string()));
171 } 187 }
172 break; 188 break;
173 } 189 }
@@ -201,23 +217,23 @@ impl MacroRules {
201 217
202impl Rule { 218impl Rule {
203 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> { 219 fn parse(src: &mut TtIter) -> Result<Rule, ParseError> {
204 let mut lhs = src 220 let lhs = src
205 .expect_subtree() 221 .expect_subtree()
206 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 222 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
207 .clone();
208 lhs.delimiter = None;
209 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?; 223 src.expect_char('=').map_err(|()| ParseError::Expected("expected `=`".to_string()))?;
210 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?; 224 src.expect_char('>').map_err(|()| ParseError::Expected("expected `>`".to_string()))?;
211 let mut rhs = src 225 let rhs = src
212 .expect_subtree() 226 .expect_subtree()
213 .map_err(|()| ParseError::Expected("expected subtree".to_string()))? 227 .map_err(|()| ParseError::Expected("expected subtree".to_string()))?;
214 .clone(); 228
215 rhs.delimiter = None; 229 let lhs = MetaTemplate { tokens: parse_pattern(&lhs), delimiter: None };
230 let rhs = MetaTemplate { tokens: parse_template(&rhs), delimiter: None };
231
216 Ok(crate::Rule { lhs, rhs }) 232 Ok(crate::Rule { lhs, rhs })
217 } 233 }
218} 234}
219 235
220fn to_parse_error(e: ExpandError) -> ParseError { 236fn to_parse_error(e: &ExpandError) -> ParseError {
221 let msg = match e { 237 let msg = match e {
222 ExpandError::InvalidRepeat => "invalid repeat".to_string(), 238 ExpandError::InvalidRepeat => "invalid repeat".to_string(),
223 _ => "invalid macro definition".to_string(), 239 _ => "invalid macro definition".to_string(),
@@ -225,22 +241,22 @@ fn to_parse_error(e: ExpandError) -> ParseError {
225 ParseError::Expected(msg) 241 ParseError::Expected(msg)
226} 242}
227 243
228fn validate(pattern: &tt::Subtree) -> Result<(), ParseError> { 244fn validate(pattern: &MetaTemplate) -> Result<(), ParseError> {
229 for op in parse_pattern(pattern) { 245 for op in pattern.iter() {
230 let op = op.map_err(to_parse_error)?; 246 let op = op.as_ref().map_err(|e| to_parse_error(&e))?;
231 247
232 match op { 248 match op {
233 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => validate(subtree)?, 249 Op::Subtree(subtree) => validate(&subtree)?,
234 Op::Repeat { subtree, separator, .. } => { 250 Op::Repeat { subtree, separator, .. } => {
235 // Checks that no repetition which could match an empty token 251 // Checks that no repetition which could match an empty token
236 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558 252 // https://github.com/rust-lang/rust/blob/a58b1ed44f5e06976de2bdc4d7dc81c36a96934f/src/librustc_expand/mbe/macro_rules.rs#L558
237 253
238 if separator.is_none() { 254 if separator.is_none() {
239 if parse_pattern(subtree).all(|child_op| { 255 if subtree.iter().all(|child_op| {
240 match child_op.map_err(to_parse_error) { 256 match child_op.as_ref().map_err(to_parse_error) {
241 Ok(Op::Var { kind, .. }) => { 257 Ok(Op::Var { kind, .. }) => {
242 // vis is optional 258 // vis is optional
243 if kind.map_or(false, |it| it == "vis") { 259 if kind.as_ref().map_or(false, |it| it == "vis") {
244 return true; 260 return true;
245 } 261 }
246 } 262 }
diff --git a/crates/mbe/src/mbe_expander/matcher.rs b/crates/mbe/src/mbe_expander/matcher.rs
index 44722c0f1..385b46601 100644
--- a/crates/mbe/src/mbe_expander/matcher.rs
+++ b/crates/mbe/src/mbe_expander/matcher.rs
@@ -2,10 +2,10 @@
2 2
3use crate::{ 3use crate::{
4 mbe_expander::{Binding, Bindings, Fragment}, 4 mbe_expander::{Binding, Bindings, Fragment},
5 parser::{parse_pattern, Op, RepeatKind, Separator}, 5 parser::{Op, RepeatKind, Separator},
6 subtree_source::SubtreeTokenSource, 6 subtree_source::SubtreeTokenSource,
7 tt_iter::TtIter, 7 tt_iter::TtIter,
8 ExpandError, 8 ExpandError, MetaTemplate,
9}; 9};
10 10
11use super::ExpandResult; 11use super::ExpandResult;
@@ -83,7 +83,7 @@ impl Match {
83// sense to try using it. Matching errors are added to the `Match`. It might 83// sense to try using it. Matching errors are added to the `Match`. It might
84// make sense to make pattern parsing a separate step? 84// make sense to make pattern parsing a separate step?
85 85
86pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match, ExpandError> { 86pub(super) fn match_(pattern: &MetaTemplate, src: &tt::Subtree) -> Result<Match, ExpandError> {
87 assert!(pattern.delimiter == None); 87 assert!(pattern.delimiter == None);
88 88
89 let mut res = Match::default(); 89 let mut res = Match::default();
@@ -101,12 +101,12 @@ pub(super) fn match_(pattern: &tt::Subtree, src: &tt::Subtree) -> Result<Match,
101 101
102fn match_subtree( 102fn match_subtree(
103 res: &mut Match, 103 res: &mut Match,
104 pattern: &tt::Subtree, 104 pattern: &MetaTemplate,
105 src: &mut TtIter, 105 src: &mut TtIter,
106) -> Result<(), ExpandError> { 106) -> Result<(), ExpandError> {
107 for op in parse_pattern(pattern) { 107 for op in pattern.iter() {
108 match op? { 108 match op.as_ref().map_err(|err| err.clone())? {
109 Op::TokenTree(tt::TokenTree::Leaf(lhs)) => { 109 Op::Leaf(lhs) => {
110 let rhs = match src.expect_leaf() { 110 let rhs = match src.expect_leaf() {
111 Ok(l) => l, 111 Ok(l) => l,
112 Err(()) => { 112 Err(()) => {
@@ -132,7 +132,7 @@ fn match_subtree(
132 } 132 }
133 } 133 }
134 } 134 }
135 Op::TokenTree(tt::TokenTree::Subtree(lhs)) => { 135 Op::Subtree(lhs) => {
136 let rhs = match src.expect_subtree() { 136 let rhs = match src.expect_subtree() {
137 Ok(s) => s, 137 Ok(s) => s,
138 Err(()) => { 138 Err(()) => {
@@ -150,7 +150,7 @@ fn match_subtree(
150 res.add_err(err!("leftover tokens")); 150 res.add_err(err!("leftover tokens"));
151 } 151 }
152 } 152 }
153 Op::Var { name, kind } => { 153 Op::Var { name, kind, .. } => {
154 let kind = match kind { 154 let kind = match kind {
155 Some(k) => k, 155 Some(k) => k,
156 None => { 156 None => {
@@ -172,7 +172,7 @@ fn match_subtree(
172 } 172 }
173 } 173 }
174 Op::Repeat { subtree, kind, separator } => { 174 Op::Repeat { subtree, kind, separator } => {
175 match_repeat(res, subtree, kind, separator, src)?; 175 match_repeat(res, subtree, *kind, separator, src)?;
176 } 176 }
177 } 177 }
178 } 178 }
@@ -372,9 +372,9 @@ impl<'a> TtIter<'a> {
372 372
373pub(super) fn match_repeat( 373pub(super) fn match_repeat(
374 res: &mut Match, 374 res: &mut Match,
375 pattern: &tt::Subtree, 375 pattern: &MetaTemplate,
376 kind: RepeatKind, 376 kind: RepeatKind,
377 separator: Option<Separator>, 377 separator: &Option<Separator>,
378 src: &mut TtIter, 378 src: &mut TtIter,
379) -> Result<(), ExpandError> { 379) -> Result<(), ExpandError> {
380 // Dirty hack to make macro-expansion terminate. 380 // Dirty hack to make macro-expansion terminate.
@@ -489,12 +489,12 @@ fn match_meta_var(kind: &str, input: &mut TtIter) -> ExpandResult<Option<Fragmen
489 result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) }) 489 result.map(|tt| if kind == "expr" { tt.map(Fragment::Ast) } else { tt.map(Fragment::Tokens) })
490} 490}
491 491
492fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &tt::Subtree) -> Result<(), ExpandError> { 492fn collect_vars(buf: &mut Vec<SmolStr>, pattern: &MetaTemplate) -> Result<(), ExpandError> {
493 for op in parse_pattern(pattern) { 493 for op in pattern.iter() {
494 match op? { 494 match op.as_ref().map_err(|e| e.clone())? {
495 Op::Var { name, .. } => buf.push(name.clone()), 495 Op::Var { name, .. } => buf.push(name.clone()),
496 Op::TokenTree(tt::TokenTree::Leaf(_)) => (), 496 Op::Leaf(_) => (),
497 Op::TokenTree(tt::TokenTree::Subtree(subtree)) => collect_vars(buf, subtree)?, 497 Op::Subtree(subtree) => collect_vars(buf, subtree)?,
498 Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?, 498 Op::Repeat { subtree, .. } => collect_vars(buf, subtree)?,
499 } 499 }
500 } 500 }
diff --git a/crates/mbe/src/mbe_expander/transcriber.rs b/crates/mbe/src/mbe_expander/transcriber.rs
index 57592dc92..57f3f104d 100644
--- a/crates/mbe/src/mbe_expander/transcriber.rs
+++ b/crates/mbe/src/mbe_expander/transcriber.rs
@@ -6,8 +6,8 @@ use syntax::SmolStr;
6use super::ExpandResult; 6use super::ExpandResult;
7use crate::{ 7use crate::{
8 mbe_expander::{Binding, Bindings, Fragment}, 8 mbe_expander::{Binding, Bindings, Fragment},
9 parser::{parse_template, Op, RepeatKind, Separator}, 9 parser::{Op, RepeatKind, Separator},
10 ExpandError, 10 ExpandError, MetaTemplate,
11}; 11};
12 12
13impl Bindings { 13impl Bindings {
@@ -50,7 +50,10 @@ impl Bindings {
50 } 50 }
51} 51}
52 52
53pub(super) fn transcribe(template: &tt::Subtree, bindings: &Bindings) -> ExpandResult<tt::Subtree> { 53pub(super) fn transcribe(
54 template: &MetaTemplate,
55 bindings: &Bindings,
56) -> ExpandResult<tt::Subtree> {
54 assert!(template.delimiter == None); 57 assert!(template.delimiter == None);
55 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() }; 58 let mut ctx = ExpandCtx { bindings: &bindings, nesting: Vec::new() };
56 let mut arena: Vec<tt::TokenTree> = Vec::new(); 59 let mut arena: Vec<tt::TokenTree> = Vec::new();
@@ -76,35 +79,35 @@ struct ExpandCtx<'a> {
76 79
77fn expand_subtree( 80fn expand_subtree(
78 ctx: &mut ExpandCtx, 81 ctx: &mut ExpandCtx,
79 template: &tt::Subtree, 82 template: &MetaTemplate,
80 arena: &mut Vec<tt::TokenTree>, 83 arena: &mut Vec<tt::TokenTree>,
81) -> ExpandResult<tt::Subtree> { 84) -> ExpandResult<tt::Subtree> {
82 // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation 85 // remember how many elements are in the arena now - when returning, we want to drain exactly how many elements we added. This way, the recursive uses of the arena get their own "view" of the arena, but will reuse the allocation
83 let start_elements = arena.len(); 86 let start_elements = arena.len();
84 let mut err = None; 87 let mut err = None;
85 for op in parse_template(template) { 88 for op in template.iter() {
86 let op = match op { 89 let op = match op {
87 Ok(op) => op, 90 Ok(op) => op,
88 Err(e) => { 91 Err(e) => {
89 err = Some(e); 92 err = Some(e.clone());
90 break; 93 break;
91 } 94 }
92 }; 95 };
93 match op { 96 match op {
94 Op::TokenTree(tt @ tt::TokenTree::Leaf(..)) => arena.push(tt.clone()), 97 Op::Leaf(tt) => arena.push(tt.clone().into()),
95 Op::TokenTree(tt::TokenTree::Subtree(tt)) => { 98 Op::Subtree(tt) => {
96 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, tt, arena); 99 let ExpandResult { value: tt, err: e } = expand_subtree(ctx, &tt, arena);
97 err = err.or(e); 100 err = err.or(e);
98 arena.push(tt.into()); 101 arena.push(tt.into());
99 } 102 }
100 Op::Var { name, .. } => { 103 Op::Var { name, id, .. } => {
101 let ExpandResult { value: fragment, err: e } = expand_var(ctx, name); 104 let ExpandResult { value: fragment, err: e } = expand_var(ctx, &name, *id);
102 err = err.or(e); 105 err = err.or(e);
103 push_fragment(arena, fragment); 106 push_fragment(arena, fragment);
104 } 107 }
105 Op::Repeat { subtree, kind, separator } => { 108 Op::Repeat { subtree, kind, separator } => {
106 let ExpandResult { value: fragment, err: e } = 109 let ExpandResult { value: fragment, err: e } =
107 expand_repeat(ctx, subtree, kind, separator, arena); 110 expand_repeat(ctx, subtree, *kind, separator, arena);
108 err = err.or(e); 111 err = err.or(e);
109 push_fragment(arena, fragment) 112 push_fragment(arena, fragment)
110 } 113 }
@@ -115,12 +118,10 @@ fn expand_subtree(
115 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err } 118 ExpandResult { value: tt::Subtree { delimiter: template.delimiter, token_trees: tts }, err }
116} 119}
117 120
118fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> { 121fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr, id: tt::TokenId) -> ExpandResult<Fragment> {
119 if v == "crate" { 122 if v == "crate" {
120 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path. 123 // We simply produce identifier `$crate` here. And it will be resolved when lowering ast to Path.
121 let tt = 124 let tt = tt::Leaf::from(tt::Ident { text: "$crate".into(), id }).into();
122 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: tt::TokenId::unspecified() })
123 .into();
124 ExpandResult::ok(Fragment::Tokens(tt)) 125 ExpandResult::ok(Fragment::Tokens(tt))
125 } else if !ctx.bindings.contains(v) { 126 } else if !ctx.bindings.contains(v) {
126 // Note that it is possible to have a `$var` inside a macro which is not bound. 127 // Note that it is possible to have a `$var` inside a macro which is not bound.
@@ -139,14 +140,8 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
139 let tt = tt::Subtree { 140 let tt = tt::Subtree {
140 delimiter: None, 141 delimiter: None,
141 token_trees: vec![ 142 token_trees: vec![
142 tt::Leaf::from(tt::Punct { 143 tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone, id }).into(),
143 char: '$', 144 tt::Leaf::from(tt::Ident { text: v.clone(), id }).into(),
144 spacing: tt::Spacing::Alone,
145 id: tt::TokenId::unspecified(),
146 })
147 .into(),
148 tt::Leaf::from(tt::Ident { text: v.clone(), id: tt::TokenId::unspecified() })
149 .into(),
150 ], 145 ],
151 } 146 }
152 .into(); 147 .into();
@@ -161,9 +156,9 @@ fn expand_var(ctx: &mut ExpandCtx, v: &SmolStr) -> ExpandResult<Fragment> {
161 156
162fn expand_repeat( 157fn expand_repeat(
163 ctx: &mut ExpandCtx, 158 ctx: &mut ExpandCtx,
164 template: &tt::Subtree, 159 template: &MetaTemplate,
165 kind: RepeatKind, 160 kind: RepeatKind,
166 separator: Option<Separator>, 161 separator: &Option<Separator>,
167 arena: &mut Vec<tt::TokenTree>, 162 arena: &mut Vec<tt::TokenTree>,
168) -> ExpandResult<Fragment> { 163) -> ExpandResult<Fragment> {
169 let mut buf: Vec<tt::TokenTree> = Vec::new(); 164 let mut buf: Vec<tt::TokenTree> = Vec::new();
diff --git a/crates/mbe/src/parser.rs b/crates/mbe/src/parser.rs
index c3fdd4040..77cc739b6 100644
--- a/crates/mbe/src/parser.rs
+++ b/crates/mbe/src/parser.rs
@@ -4,16 +4,17 @@
4use smallvec::SmallVec; 4use smallvec::SmallVec;
5use syntax::SmolStr; 5use syntax::SmolStr;
6 6
7use crate::{tt_iter::TtIter, ExpandError}; 7use crate::{tt_iter::TtIter, ExpandError, MetaTemplate};
8 8
9#[derive(Debug)] 9#[derive(Clone, Debug, PartialEq, Eq)]
10pub(crate) enum Op<'a> { 10pub(crate) enum Op {
11 Var { name: &'a SmolStr, kind: Option<&'a SmolStr> }, 11 Var { name: SmolStr, kind: Option<SmolStr>, id: tt::TokenId },
12 Repeat { subtree: &'a tt::Subtree, kind: RepeatKind, separator: Option<Separator> }, 12 Repeat { subtree: MetaTemplate, kind: RepeatKind, separator: Option<Separator> },
13 TokenTree(&'a tt::TokenTree), 13 Leaf(tt::Leaf),
14 Subtree(MetaTemplate),
14} 15}
15 16
16#[derive(Clone, Debug, PartialEq, Eq)] 17#[derive(Copy, Clone, Debug, PartialEq, Eq)]
17pub(crate) enum RepeatKind { 18pub(crate) enum RepeatKind {
18 ZeroOrMore, 19 ZeroOrMore,
19 OneOrMore, 20 OneOrMore,
@@ -45,16 +46,12 @@ impl PartialEq for Separator {
45 } 46 }
46} 47}
47 48
48pub(crate) fn parse_template( 49pub(crate) fn parse_template(template: &tt::Subtree) -> Vec<Result<Op, ExpandError>> {
49 template: &tt::Subtree, 50 parse_inner(&template, Mode::Template)
50) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> {
51 parse_inner(template, Mode::Template)
52} 51}
53 52
54pub(crate) fn parse_pattern( 53pub(crate) fn parse_pattern(pattern: &tt::Subtree) -> Vec<Result<Op, ExpandError>> {
55 pattern: &tt::Subtree, 54 parse_inner(&pattern, Mode::Pattern)
56) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> {
57 parse_inner(pattern, Mode::Pattern)
58} 55}
59 56
60#[derive(Clone, Copy)] 57#[derive(Clone, Copy)]
@@ -63,12 +60,13 @@ enum Mode {
63 Template, 60 Template,
64} 61}
65 62
66fn parse_inner(src: &tt::Subtree, mode: Mode) -> impl Iterator<Item = Result<Op<'_>, ExpandError>> { 63fn parse_inner(tt: &tt::Subtree, mode: Mode) -> Vec<Result<Op, ExpandError>> {
67 let mut src = TtIter::new(src); 64 let mut src = TtIter::new(&tt);
68 std::iter::from_fn(move || { 65 std::iter::from_fn(move || {
69 let first = src.next()?; 66 let first = src.next()?;
70 Some(next_op(first, &mut src, mode)) 67 Some(next_op(first, &mut src, mode))
71 }) 68 })
69 .collect()
72} 70}
73 71
74macro_rules! err { 72macro_rules! err {
@@ -83,37 +81,46 @@ macro_rules! bail {
83 }; 81 };
84} 82}
85 83
86fn next_op<'a>( 84fn next_op<'a>(first: &tt::TokenTree, src: &mut TtIter<'a>, mode: Mode) -> Result<Op, ExpandError> {
87 first: &'a tt::TokenTree,
88 src: &mut TtIter<'a>,
89 mode: Mode,
90) -> Result<Op<'a>, ExpandError> {
91 let res = match first { 85 let res = match first {
92 tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: '$', .. })) => { 86 tt::TokenTree::Leaf(leaf @ tt::Leaf::Punct(tt::Punct { char: '$', .. })) => {
93 // Note that the '$' itself is a valid token inside macro_rules. 87 // Note that the '$' itself is a valid token inside macro_rules.
94 let second = match src.next() { 88 let second = match src.next() {
95 None => return Ok(Op::TokenTree(first)), 89 None => return Ok(Op::Leaf(leaf.clone())),
96 Some(it) => it, 90 Some(it) => it,
97 }; 91 };
98 match second { 92 match second {
99 tt::TokenTree::Subtree(subtree) => { 93 tt::TokenTree::Subtree(subtree) => {
100 let (separator, kind) = parse_repeat(src)?; 94 let (separator, kind) = parse_repeat(src)?;
95 let delimiter = subtree.delimiter;
96 let tokens = parse_inner(&subtree, mode);
97 let subtree = MetaTemplate { tokens, delimiter };
101 Op::Repeat { subtree, separator, kind } 98 Op::Repeat { subtree, separator, kind }
102 } 99 }
103 tt::TokenTree::Leaf(leaf) => match leaf { 100 tt::TokenTree::Leaf(leaf) => match leaf {
104 tt::Leaf::Punct(_) => { 101 tt::Leaf::Punct(punct) => {
105 return Err(ExpandError::UnexpectedToken); 102 static UNDERSCORE: SmolStr = SmolStr::new_inline("_");
103
104 if punct.char != '_' {
105 return Err(ExpandError::UnexpectedToken);
106 }
107 let name = UNDERSCORE.clone();
108 let kind = eat_fragment_kind(src, mode)?;
109 let id = punct.id;
110 Op::Var { name, kind, id }
106 } 111 }
107 tt::Leaf::Ident(ident) => { 112 tt::Leaf::Ident(ident) => {
108 let name = &ident.text; 113 let name = ident.text.clone();
109 let kind = eat_fragment_kind(src, mode)?; 114 let kind = eat_fragment_kind(src, mode)?;
110 Op::Var { name, kind } 115 let id = ident.id;
116 Op::Var { name, kind, id }
111 } 117 }
112 tt::Leaf::Literal(lit) => { 118 tt::Leaf::Literal(lit) => {
113 if is_boolean_literal(lit) { 119 if is_boolean_literal(&lit) {
114 let name = &lit.text; 120 let name = lit.text.clone();
115 let kind = eat_fragment_kind(src, mode)?; 121 let kind = eat_fragment_kind(src, mode)?;
116 Op::Var { name, kind } 122 let id = lit.id;
123 Op::Var { name, kind, id }
117 } else { 124 } else {
118 bail!("bad var 2"); 125 bail!("bad var 2");
119 } 126 }
@@ -121,19 +128,22 @@ fn next_op<'a>(
121 }, 128 },
122 } 129 }
123 } 130 }
124 tt => Op::TokenTree(tt), 131 tt::TokenTree::Leaf(tt) => Op::Leaf(tt.clone()),
132 tt::TokenTree::Subtree(subtree) => {
133 let delimiter = subtree.delimiter;
134 let tokens = parse_inner(&subtree, mode);
135 let subtree = MetaTemplate { tokens, delimiter };
136 Op::Subtree(subtree)
137 }
125 }; 138 };
126 Ok(res) 139 Ok(res)
127} 140}
128 141
129fn eat_fragment_kind<'a>( 142fn eat_fragment_kind<'a>(src: &mut TtIter<'a>, mode: Mode) -> Result<Option<SmolStr>, ExpandError> {
130 src: &mut TtIter<'a>,
131 mode: Mode,
132) -> Result<Option<&'a SmolStr>, ExpandError> {
133 if let Mode::Pattern = mode { 143 if let Mode::Pattern = mode {
134 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?; 144 src.expect_char(':').map_err(|()| err!("bad fragment specifier 1"))?;
135 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?; 145 let ident = src.expect_ident().map_err(|()| err!("bad fragment specifier 1"))?;
136 return Ok(Some(&ident.text)); 146 return Ok(Some(ident.text.clone()));
137 }; 147 };
138 Ok(None) 148 Ok(None)
139} 149}
diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs
index 2bec7fd49..265c0d63d 100644
--- a/crates/mbe/src/syntax_bridge.rs
+++ b/crates/mbe/src/syntax_bridge.rs
@@ -313,7 +313,7 @@ trait TokenConvertor {
313 return; 313 return;
314 } 314 }
315 315
316 result.push(if k.is_punct() && k != UNDERSCORE { 316 result.push(if k.is_punct() {
317 assert_eq!(range.len(), TextSize::of('.')); 317 assert_eq!(range.len(), TextSize::of('.'));
318 let delim = match k { 318 let delim = match k {
319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])), 319 T!['('] => Some((tt::DelimiterKind::Parenthesis, T![')'])),
@@ -378,7 +378,6 @@ trait TokenConvertor {
378 let leaf: tt::Leaf = match k { 378 let leaf: tt::Leaf = match k {
379 T![true] | T![false] => make_leaf!(Ident), 379 T![true] | T![false] => make_leaf!(Ident),
380 IDENT => make_leaf!(Ident), 380 IDENT => make_leaf!(Ident),
381 UNDERSCORE => make_leaf!(Ident),
382 k if k.is_keyword() => make_leaf!(Ident), 381 k if k.is_keyword() => make_leaf!(Ident),
383 k if k.is_literal() => make_leaf!(Literal), 382 k if k.is_literal() => make_leaf!(Literal),
384 LIFETIME_IDENT => { 383 LIFETIME_IDENT => {
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 6cd0ed205..1d9afb4fb 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -761,6 +761,18 @@ fn test_last_expr() {
761} 761}
762 762
763#[test] 763#[test]
764fn test_expr_with_attr() {
765 parse_macro(
766 r#"
767macro_rules! m {
768 ($a:expr) => {0}
769}
770"#,
771 )
772 .assert_expand_items("m!(#[allow(a)]())", "0");
773}
774
775#[test]
764fn test_ty() { 776fn test_ty() {
765 parse_macro( 777 parse_macro(
766 r#" 778 r#"
@@ -1020,6 +1032,42 @@ fn test_underscore() {
1020} 1032}
1021 1033
1022#[test] 1034#[test]
1035fn test_underscore_not_greedily() {
1036 parse_macro(
1037 r#"
1038macro_rules! q {
1039 ($($a:ident)* _) => {0};
1040}
1041"#,
1042 )
1043 // `_` overlaps with `$a:ident` but rustc matches it under the `_` token
1044 .assert_expand_items(r#"q![a b c d _]"#, r#"0"#);
1045
1046 parse_macro(
1047 r#"
1048macro_rules! q {
1049 ($($a:expr => $b:ident)* _ => $c:expr) => {0};
1050}
1051"#,
1052 )
1053 // `_ => ou` overlaps with `$a:expr => $b:ident` but rustc matches it under `_ => $c:expr`
1054 .assert_expand_items(r#"q![a => b c => d _ => ou]"#, r#"0"#);
1055}
1056
1057#[test]
1058fn test_underscore_as_type() {
1059 parse_macro(
1060 r#"
1061macro_rules! q {
1062 ($a:ty) => {0};
1063}
1064"#,
1065 )
1066 // Underscore is a type
1067 .assert_expand_items(r#"q![_]"#, r#"0"#);
1068}
1069
1070#[test]
1023fn test_vertical_bar_with_pat() { 1071fn test_vertical_bar_with_pat() {
1024 parse_macro( 1072 parse_macro(
1025 r#" 1073 r#"