diff options
Diffstat (limited to 'crates/ra_mbe/src/mbe_expander.rs')
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 42 |
1 files changed, 37 insertions, 5 deletions
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 1453a106d..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 |
@@ -458,6 +462,33 @@ fn expand_tt( | |||
458 | // FIXME: Properly handle $crate token | 462 | // FIXME: Properly handle $crate token |
459 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 463 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
460 | .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() | ||
461 | } else { | 492 | } else { |
462 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); | 493 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
463 | ctx.var_expanded = true; | 494 | ctx.var_expanded = true; |
@@ -484,11 +515,12 @@ mod tests { | |||
484 | 515 | ||
485 | #[test] | 516 | #[test] |
486 | fn test_expand_rule() { | 517 | fn test_expand_rule() { |
487 | assert_err( | 518 | // FIXME: The missing $var check should be in parsing phase |
488 | "($i:ident) => ($j)", | 519 | // assert_err( |
489 | "foo!{a}", | 520 | // "($i:ident) => ($j)", |
490 | ExpandError::BindingError(String::from("could not find binding `j`")), | 521 | // "foo!{a}", |
491 | ); | 522 | // ExpandError::BindingError(String::from("could not find binding `j`")), |
523 | // ); | ||
492 | 524 | ||
493 | assert_err( | 525 | assert_err( |
494 | "($($i:ident);*) => ($i)", | 526 | "($($i:ident);*) => ($i)", |