diff options
author | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-03 16:36:19 +0100 |
---|---|---|
committer | bors[bot] <bors[bot]@users.noreply.github.com> | 2019-05-03 16:36:19 +0100 |
commit | 26b499811489f72ad5176d68d40c9a0748620417 (patch) | |
tree | 56c826ab4dd6cc5f531af919f91184e3aa50d248 /crates/ra_mbe/src/mbe_expander.rs | |
parent | b29f442c8b8601feec4dbc49f121801b153d83e1 (diff) | |
parent | 31909cc7d77fea5e08aaa9fd149338b243ec600f (diff) |
Merge #1228
1228: Add inner macro checker in mbe r=matklad a=edwin0cheng
This PR do the following things:
* Add an inner macro checker for allowing defining mbe in mbe. (It is a adhoc solution, we could eliminate it after we have a better mbe parser)
* Move all tests to an tests modules
* Filter empty tree while expanding mbe.
This is the final PR extracting from #1219
Co-authored-by: Edwin Cheng <[email protected]>
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 50 |
1 files changed, 45 insertions, 5 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 361b1e404..8f8a79855 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -84,6 +84,10 @@ enum Binding { | |||
84 | } | 84 | } |
85 | 85 | ||
86 | impl Bindings { | 86 | impl Bindings { |
87 | fn contains(&self, name: &SmolStr) -> bool { | ||
88 | self.inner.contains_key(name) | ||
89 | } | ||
90 | |||
87 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { | 91 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { |
88 | let mut b = self | 92 | let mut b = self |
89 | .inner | 93 | .inner |
@@ -329,6 +333,14 @@ fn expand_subtree( | |||
329 | .token_trees | 333 | .token_trees |
330 | .iter() | 334 | .iter() |
331 | .map(|it| expand_tt(it, ctx)) | 335 | .map(|it| expand_tt(it, ctx)) |
336 | .filter(|it| { | ||
337 | // Filter empty subtree | ||
338 | if let Ok(tt::TokenTree::Subtree(subtree)) = it { | ||
339 | subtree.delimiter != tt::Delimiter::None || !subtree.token_trees.is_empty() | ||
340 | } else { | ||
341 | true | ||
342 | } | ||
343 | }) | ||
332 | .collect::<Result<Vec<_>, ExpandError>>()?; | 344 | .collect::<Result<Vec<_>, ExpandError>>()?; |
333 | 345 | ||
334 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) | 346 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) |
@@ -450,6 +462,33 @@ fn expand_tt( | |||
450 | // FIXME: Properly handle $crate token | 462 | // FIXME: Properly handle $crate token |
451 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 463 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
452 | .into() | 464 | .into() |
465 | } else if !ctx.bindings.contains(&v.text) { | ||
466 | // Note that it is possible to have a `$var` inside a macro which is not bound. | ||
467 | // For example: | ||
468 | // ``` | ||
469 | // macro_rules! foo { | ||
470 | // ($a:ident, $b:ident, $c:tt) => { | ||
471 | // macro_rules! bar { | ||
472 | // ($bi:ident) => { | ||
473 | // fn $bi() -> u8 {$c} | ||
474 | // } | ||
475 | // } | ||
476 | // } | ||
477 | // ``` | ||
478 | // We just treat it a normal tokens | ||
479 | tt::Subtree { | ||
480 | delimiter: tt::Delimiter::None, | ||
481 | token_trees: vec![ | ||
482 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }) | ||
483 | .into(), | ||
484 | tt::Leaf::from(tt::Ident { | ||
485 | text: v.text.clone(), | ||
486 | id: TokenId::unspecified(), | ||
487 | }) | ||
488 | .into(), | ||
489 | ], | ||
490 | } | ||
491 | .into() | ||
453 | } else { | 492 | } else { |
454 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); | 493 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
455 | ctx.var_expanded = true; | 494 | ctx.var_expanded = true; |
@@ -476,11 +515,12 @@ mod tests { | |||
476 | 515 | ||
477 | #[test] | 516 | #[test] |
478 | fn test_expand_rule() { | 517 | fn test_expand_rule() { |
479 | assert_err( | 518 | // FIXME: The missing $var check should be in parsing phase |
480 | "($i:ident) => ($j)", | 519 | // assert_err( |
481 | "foo!{a}", | 520 | // "($i:ident) => ($j)", |
482 | ExpandError::BindingError(String::from("could not find binding `j`")), | 521 | // "foo!{a}", |
483 | ); | 522 | // ExpandError::BindingError(String::from("could not find binding `j`")), |
523 | // ); | ||
484 | 524 | ||
485 | assert_err( | 525 | assert_err( |
486 | "($($i:ident);*) => ($i)", | 526 | "($($i:ident);*) => ($i)", |