diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index 9d4744838be6..814586fd66d9 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs @@ -731,4 +731,95 @@ MACRO_ITEMS@[0; 40) } "#, r#"extern crate a ; mod b ; mod c {} use d ; const E : i32 = 0 ; static F : i32 = 0 ; impl G {} struct H ; enum I {Foo} trait J {} fn h () {} extern {} type T = u8 ;"#); } + + #[test] + fn test_block() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ i:block) => { fn foo() $ i } + } +"#, + ); + assert_expansion(&rules, "foo! { { 1; } }", "fn foo () {1 ;}"); + } + + #[test] + fn test_meta() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ i:meta) => ( + #[$ i] + fn bar() {} + ) + } +"#, + ); + assert_expansion( + &rules, + r#"foo! { cfg(target_os = "windows") }"#, + r#"# [cfg (target_os = "windows")] fn bar () {}"#, + ); + } + + #[test] + fn test_tt_block() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ i:tt) => { fn foo() $ i } + } +"#, + ); + assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); + } + + #[test] + fn test_tt_group() { + let rules = create_rules( + r#" + macro_rules! foo { + ($($ i:tt)*) => { $($ i)* } + } +"#, + ); + assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); + } + + #[test] + fn test_lifetime() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } + } +"#, + ); + assert_expansion(&rules, r#"foo!{'a}"#, r#"struct Ref < 'a > {s : & 'a str}"#); + } + + #[test] + fn test_literal() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; + } +"#, + ); + assert_expansion(&rules, r#"foo!(u8 0)"#, r#"const VALUE : u8 = 0 ;"#); + } + + #[test] + fn test_vis() { + let rules = create_rules( + r#" + macro_rules! foo { + ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; + } +"#, + ); + assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); + } } diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index acba42809ed9..01e29b556a97 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs @@ -161,11 +161,43 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result { + let block = + input.eat_block().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert(text.clone(), Binding::Simple(block.into())); + } + "meta" => { + let meta = + input.eat_meta().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert(text.clone(), Binding::Simple(meta.into())); + } + "tt" => { + let token = input.eat().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert(text.clone(), Binding::Simple(token.into())); + } "item" => { let item = input.eat_item().ok_or(ExpandError::UnexpectedToken)?.clone(); res.inner.insert(text.clone(), Binding::Simple(item.into())); } + "lifetime" => { + let lifetime = + input.eat_lifetime().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert(text.clone(), Binding::Simple(lifetime.into())); + } + "literal" => { + let literal = + input.eat_literal().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert( + text.clone(), + Binding::Simple(tt::Leaf::from(literal).into()), + ); + } + "vis" => { + let vis = input.eat_vis().ok_or(ExpandError::UnexpectedToken)?.clone(); + res.inner.insert(text.clone(), Binding::Simple(vis.into())); + } + _ => return Err(ExpandError::UnexpectedToken), } } diff --git a/crates/ra_mbe/src/subtree_parser.rs b/crates/ra_mbe/src/subtree_parser.rs index 195e4c3ac655..528aa0f8a0c8 100644 --- a/crates/ra_mbe/src/subtree_parser.rs +++ b/crates/ra_mbe/src/subtree_parser.rs @@ -46,10 +46,22 @@ impl<'a> Parser<'a> { self.parse(|src, sink| ra_parser::parse_stmt(src, sink, false)) } + pub fn parse_block(self) -> Option { + self.parse(ra_parser::parse_block) + } + + pub fn parse_meta(self) -> Option { + self.parse(ra_parser::parse_meta) + } + pub fn parse_item(self) -> Option { self.parse(ra_parser::parse_item) } + pub fn parse_vis(self) -> Option { + self.parse(ra_parser::parse_vis) + } + fn parse(self, f: F) -> Option where F: FnOnce(&dyn TokenSource, &mut dyn TreeSink), diff --git a/crates/ra_mbe/src/syntax_bridge.rs b/crates/ra_mbe/src/syntax_bridge.rs index 5844d3f12c8a..38a4810299a9 100644 --- a/crates/ra_mbe/src/syntax_bridge.rs +++ b/crates/ra_mbe/src/syntax_bridge.rs @@ -157,7 +157,10 @@ fn convert_tt( ); } } else { - let child = if token.kind().is_keyword() || token.kind() == IDENT { + let child: tt::TokenTree = if token.kind().is_keyword() + || token.kind() == IDENT + || token.kind() == LIFETIME + { let relative_range = token.range() - global_offset; let id = token_map.alloc(relative_range); let text = token.text().clone(); diff --git a/crates/ra_mbe/src/tt_cursor.rs b/crates/ra_mbe/src/tt_cursor.rs index 484437b0ecf7..741b5ea1cc7b 100644 --- a/crates/ra_mbe/src/tt_cursor.rs +++ b/crates/ra_mbe/src/tt_cursor.rs @@ -41,6 +41,13 @@ impl<'a> TtCursor<'a> { } } + pub(crate) fn at_literal(&mut self) -> Option<&'a tt::Literal> { + match self.current() { + Some(tt::TokenTree::Leaf(tt::Leaf::Literal(i))) => Some(i), + _ => None, + } + } + pub(crate) fn bump(&mut self) { self.pos += 1; } @@ -79,6 +86,13 @@ impl<'a> TtCursor<'a> { }) } + pub(crate) fn eat_literal(&mut self) -> Option<&'a tt::Literal> { + self.at_literal().map(|i| { + self.bump(); + i + }) + } + pub(crate) fn eat_path(&mut self) -> Option { let parser = Parser::new(&mut self.pos, self.subtree); parser.parse_path() @@ -104,11 +118,37 @@ impl<'a> TtCursor<'a> { parser.parse_stmt() } + pub(crate) fn eat_block(&mut self) -> Option { + let parser = Parser::new(&mut self.pos, self.subtree); + parser.parse_block() + } + + pub(crate) fn eat_meta(&mut self) -> Option { + let parser = Parser::new(&mut self.pos, self.subtree); + parser.parse_meta() + } + pub(crate) fn eat_item(&mut self) -> Option { let parser = Parser::new(&mut self.pos, self.subtree); parser.parse_item() } + pub(crate) fn eat_lifetime(&mut self) -> Option { + // check if it start from "`" + if let Some(ident) = self.at_ident() { + if ident.text.chars().next()? != '\'' { + return None; + } + } + + self.eat_ident().cloned().map(|ident| tt::Leaf::from(ident).into()) + } + + pub(crate) fn eat_vis(&mut self) -> Option { + let parser = Parser::new(&mut self.pos, self.subtree); + parser.parse_vis() + } + pub(crate) fn expect_char(&mut self, char: char) -> Result<(), ParseError> { if self.at_char(char) { self.bump(); diff --git a/crates/ra_parser/src/grammar.rs b/crates/ra_parser/src/grammar.rs index e1762633e495..67eae749d5f6 100644 --- a/crates/ra_parser/src/grammar.rs +++ b/crates/ra_parser/src/grammar.rs @@ -95,6 +95,37 @@ pub(crate) fn stmt(p: &mut Parser, with_semi: bool) { expressions::stmt(p, with_semi) } +pub(crate) fn block(p: &mut Parser) { + expressions::block(p); +} + +// Parse a meta item , which excluded [], e.g : #[ MetaItem ] +pub(crate) fn meta_item(p: &mut Parser) { + fn is_delimiter(p: &mut Parser) -> bool { + match p.current() { + L_CURLY | L_PAREN | L_BRACK => true, + _ => false, + } + } + + if is_delimiter(p) { + items::token_tree(p); + return; + } + + let m = p.start(); + while !p.at(EOF) { + if is_delimiter(p) { + items::token_tree(p); + break; + } else { + p.bump(); + } + } + + m.complete(p, TOKEN_TREE); +} + pub(crate) fn item(p: &mut Parser) { items::item_or_macro(p, true, items::ItemFlavor::Mod) } @@ -136,7 +167,7 @@ impl BlockLike { } } -fn opt_visibility(p: &mut Parser) -> bool { +pub(crate) fn opt_visibility(p: &mut Parser) -> bool { match p.current() { PUB_KW => { let m = p.start(); diff --git a/crates/ra_parser/src/lib.rs b/crates/ra_parser/src/lib.rs index 0ea942b6e022..970d699c01b2 100644 --- a/crates/ra_parser/src/lib.rs +++ b/crates/ra_parser/src/lib.rs @@ -93,11 +93,27 @@ pub fn parse_stmt(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink, parse_from_tokens(token_source, tree_sink, |p| grammar::stmt(p, with_semi)); } +/// Parse given tokens into the given sink as a block +pub fn parse_block(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { + parse_from_tokens(token_source, tree_sink, grammar::block); +} + +pub fn parse_meta(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { + parse_from_tokens(token_source, tree_sink, grammar::meta_item); +} + /// Parse given tokens into the given sink as an item pub fn parse_item(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { parse_from_tokens(token_source, tree_sink, grammar::item); } +/// Parse given tokens into the given sink as an visibility qualifier +pub fn parse_vis(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { + parse_from_tokens(token_source, tree_sink, |p| { + grammar::opt_visibility(p); + }); +} + pub fn parse_macro_items(token_source: &dyn TokenSource, tree_sink: &mut dyn TreeSink) { parse_from_tokens(token_source, tree_sink, grammar::macro_items); }