aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_mbe/src/mbe_expander.rs61
1 files changed, 56 insertions, 5 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs
index 66ea76698..00fb09a3b 100644
--- a/crates/ra_mbe/src/mbe_expander.rs
+++ b/crates/ra_mbe/src/mbe_expander.rs
@@ -221,11 +221,13 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
221 } 221 }
222 _ => return Err(ExpandError::UnexpectedToken), 222 _ => return Err(ExpandError::UnexpectedToken),
223 }, 223 },
224 crate::TokenTree::Repeat(crate::Repeat { subtree, kind: _, separator }) => { 224 crate::TokenTree::Repeat(crate::Repeat { subtree, kind, separator }) => {
225 // Dirty hack to make macro-expansion terminate. 225 // Dirty hack to make macro-expansion terminate.
226 // This should be replaced by a propper macro-by-example implementation 226 // This should be replaced by a propper macro-by-example implementation
227 let mut limit = 128; 227 let mut limit = 128;
228 let mut counter = 0;
228 while let Ok(nested) = match_lhs(subtree, input) { 229 while let Ok(nested) = match_lhs(subtree, input) {
230 counter += 1;
229 limit -= 1; 231 limit -= 1;
230 if limit == 0 { 232 if limit == 0 {
231 break; 233 break;
@@ -239,6 +241,17 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings,
239 } 241 }
240 } 242 }
241 } 243 }
244
245 match kind {
246 crate::RepeatKind::OneOrMore if counter == 0 => {
247 return Err(ExpandError::UnexpectedToken);
248 }
249 crate::RepeatKind::ZeroOrOne if counter > 1 => {
250 return Err(ExpandError::UnexpectedToken);
251 }
252
253 _ => {}
254 }
242 } 255 }
243 crate::TokenTree::Subtree(subtree) => { 256 crate::TokenTree::Subtree(subtree) => {
244 let input_subtree = 257 let input_subtree =
@@ -274,6 +287,20 @@ fn expand_subtree(
274 Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) 287 Ok(tt::Subtree { token_trees, delimiter: template.delimiter })
275} 288}
276 289
290/// Reduce single token subtree to single token
291/// In `tt` matcher case, all tt tokens will be braced by a Delimiter::None
292/// which makes all sort of problems.
293fn reduce_single_token(mut subtree: tt::Subtree) -> tt::TokenTree {
294 if subtree.delimiter != tt::Delimiter::None || subtree.token_trees.len() != 1 {
295 return subtree.into();
296 }
297
298 match subtree.token_trees.pop().unwrap() {
299 tt::TokenTree::Subtree(subtree) => reduce_single_token(subtree),
300 tt::TokenTree::Leaf(token) => token.into(),
301 }
302}
303
277fn expand_tt( 304fn expand_tt(
278 template: &crate::TokenTree, 305 template: &crate::TokenTree,
279 bindings: &Bindings, 306 bindings: &Bindings,
@@ -282,11 +309,13 @@ fn expand_tt(
282 let res: tt::TokenTree = match template { 309 let res: tt::TokenTree = match template {
283 crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(), 310 crate::TokenTree::Subtree(subtree) => expand_subtree(subtree, bindings, nesting)?.into(),
284 crate::TokenTree::Repeat(repeat) => { 311 crate::TokenTree::Repeat(repeat) => {
285 let mut token_trees = Vec::new(); 312 let mut token_trees: Vec<tt::TokenTree> = Vec::new();
286 nesting.push(0); 313 nesting.push(0);
287 // Dirty hack to make macro-expansion terminate. 314 // Dirty hack to make macro-expansion terminate.
288 // This should be replaced by a propper macro-by-example implementation 315 // This should be replaced by a propper macro-by-example implementation
289 let mut limit = 128; 316 let mut limit = 128;
317 let mut has_sep = false;
318
290 while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) { 319 while let Ok(t) = expand_subtree(&repeat.subtree, bindings, nesting) {
291 limit -= 1; 320 limit -= 1;
292 if limit == 0 { 321 if limit == 0 {
@@ -294,10 +323,26 @@ fn expand_tt(
294 } 323 }
295 let idx = nesting.pop().unwrap(); 324 let idx = nesting.pop().unwrap();
296 nesting.push(idx + 1); 325 nesting.push(idx + 1);
297 token_trees.push(t.into()) 326 token_trees.push(reduce_single_token(t).into());
327
328 if let Some(sep) = repeat.separator {
329 let punct =
330 tt::Leaf::from(tt::Punct { char: sep, spacing: tt::Spacing::Alone });
331 token_trees.push(punct.into());
332 has_sep = true;
333 }
298 } 334 }
299 nesting.pop().unwrap(); 335 nesting.pop().unwrap();
300 tt::Subtree { token_trees, delimiter: tt::Delimiter::None }.into() 336
337 // Dirty hack for remove the last sep
338 // if it is a "," undo the push
339 if has_sep && repeat.separator.unwrap() == ',' {
340 token_trees.pop();
341 }
342
343 // Check if it is a singel token subtree without any delimiter
344 // e.g {Delimiter:None> ['>'] /Delimiter:None>}
345 reduce_single_token(tt::Subtree { token_trees, delimiter: tt::Delimiter::None })
301 } 346 }
302 crate::TokenTree::Leaf(leaf) => match leaf { 347 crate::TokenTree::Leaf(leaf) => match leaf {
303 crate::Leaf::Ident(ident) => { 348 crate::Leaf::Ident(ident) => {
@@ -311,7 +356,13 @@ fn expand_tt(
311 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) 356 tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() })
312 .into() 357 .into()
313 } else { 358 } else {
314 bindings.get(&v.text, nesting)?.clone() 359 let tkn = bindings.get(&v.text, nesting)?.clone();
360
361 if let tt::TokenTree::Subtree(subtree) = tkn {
362 reduce_single_token(subtree)
363 } else {
364 tkn
365 }
315 } 366 }
316 } 367 }
317 crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(), 368 crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(),