diff options
Diffstat (limited to 'crates/ra_mbe/src')
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 110 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 30 | ||||
-rw-r--r-- | crates/ra_mbe/src/subtree_source.rs | 19 |
3 files changed, 115 insertions, 44 deletions
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index d2115bd67..9aad08db9 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -214,14 +214,15 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
214 | let tree = token_tree_to_macro_items(&expanded); | 214 | let tree = token_tree_to_macro_items(&expanded); |
215 | 215 | ||
216 | // Eat all white space by parse it back and forth | 216 | // Eat all white space by parse it back and forth |
217 | let expansion = ast::SourceFile::parse(expansion); | 217 | // Because $crate will seperate in two token , will do some special treatment here |
218 | let expansion = expansion.replace("$crate", "C_C__C"); | ||
219 | let expansion = ast::SourceFile::parse(&expansion); | ||
218 | let expansion = syntax_node_to_token_tree(expansion.syntax()).unwrap().0; | 220 | let expansion = syntax_node_to_token_tree(expansion.syntax()).unwrap().0; |
219 | let file = token_tree_to_macro_items(&expansion); | 221 | let file = token_tree_to_macro_items(&expansion); |
222 | let file = file.unwrap().syntax().debug_dump().trim().to_string(); | ||
223 | let file = file.replace("C_C__C", "$crate"); | ||
220 | 224 | ||
221 | assert_eq!( | 225 | assert_eq!(tree.unwrap().syntax().debug_dump().trim(), file,); |
222 | tree.unwrap().syntax().debug_dump().trim(), | ||
223 | file.unwrap().syntax().debug_dump().trim() | ||
224 | ); | ||
225 | } | 226 | } |
226 | 227 | ||
227 | #[test] | 228 | #[test] |
@@ -348,7 +349,36 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
348 | } | 349 | } |
349 | 350 | ||
350 | #[test] | 351 | #[test] |
351 | fn expand_to_item_list() { | 352 | fn test_match_group_empty_fixed_token() { |
353 | let rules = create_rules( | ||
354 | r#" | ||
355 | macro_rules! foo { | ||
356 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
357 | $ i (); | ||
358 | )*} ); | ||
359 | } | ||
360 | "#, | ||
361 | ); | ||
362 | |||
363 | assert_expansion(&rules, "foo! {#abc}", "fn baz {}"); | ||
364 | } | ||
365 | |||
366 | #[test] | ||
367 | fn test_match_group_in_subtree() { | ||
368 | let rules = create_rules( | ||
369 | r#" | ||
370 | macro_rules! foo { | ||
371 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
372 | $ i (); | ||
373 | )*} ); | ||
374 | }"#, | ||
375 | ); | ||
376 | |||
377 | assert_expansion(&rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
378 | } | ||
379 | |||
380 | #[test] | ||
381 | fn test_expand_to_item_list() { | ||
352 | let rules = create_rules( | 382 | let rules = create_rules( |
353 | " | 383 | " |
354 | macro_rules! structs { | 384 | macro_rules! structs { |
@@ -401,7 +431,7 @@ MACRO_ITEMS@[0; 40) | |||
401 | } | 431 | } |
402 | 432 | ||
403 | #[test] | 433 | #[test] |
404 | fn expand_literals_to_token_tree() { | 434 | fn test_expand_literals_to_token_tree() { |
405 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | 435 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { |
406 | if let tt::TokenTree::Subtree(subtree) = tt { | 436 | if let tt::TokenTree::Subtree(subtree) = tt { |
407 | return &subtree; | 437 | return &subtree; |
@@ -763,30 +793,29 @@ MACRO_ITEMS@[0; 40) | |||
763 | ); | 793 | ); |
764 | } | 794 | } |
765 | 795 | ||
766 | // #[test] | 796 | #[test] |
767 | // fn test_tt_block() { | 797 | // fn test_tt_block() { |
768 | // let rules = create_rules( | 798 | // let rules = create_rules( |
769 | // r#" | 799 | // r#" |
770 | // macro_rules! foo { | 800 | // macro_rules! foo { |
771 | // ($ i:tt) => { fn foo() $ i } | 801 | // ($ i:tt) => { fn foo() $ i } |
772 | // } | 802 | // } |
773 | // "#, | 803 | // "#, |
774 | // ); | 804 | // ); |
775 | // assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); | 805 | // assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); |
776 | // } | 806 | // } |
777 | 807 | ||
778 | // #[test] | 808 | // #[test] |
779 | // fn test_tt_group() { | 809 | // fn test_tt_group() { |
780 | // let rules = create_rules( | 810 | // let rules = create_rules( |
781 | // r#" | 811 | // r#" |
782 | // macro_rules! foo { | 812 | // macro_rules! foo { |
783 | // ($($ i:tt)*) => { $($ i)* } | 813 | // ($($ i:tt)*) => { $($ i)* } |
784 | // } | 814 | // } |
785 | // "#, | 815 | // "#, |
786 | // ); | 816 | // ); |
787 | // assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); | 817 | // assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); |
788 | // } | 818 | // } |
789 | |||
790 | #[test] | 819 | #[test] |
791 | fn test_lifetime() { | 820 | fn test_lifetime() { |
792 | let rules = create_rules( | 821 | let rules = create_rules( |
@@ -822,4 +851,39 @@ MACRO_ITEMS@[0; 40) | |||
822 | ); | 851 | ); |
823 | assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); | 852 | assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); |
824 | } | 853 | } |
854 | |||
855 | // The following tests are based on real world situations | ||
856 | #[test] | ||
857 | fn test_winapi_struct() { | ||
858 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 | ||
859 | |||
860 | let rules = create_rules( | ||
861 | r#" | ||
862 | macro_rules! STRUCT { | ||
863 | ($(#[$attrs:meta])* struct $name:ident { | ||
864 | $($field:ident: $ftype:ty,)+ | ||
865 | }) => ( | ||
866 | #[repr(C)] #[derive(Copy)] $(#[$attrs])* | ||
867 | pub struct $name { | ||
868 | $(pub $field: $ftype,)+ | ||
869 | } | ||
870 | impl Clone for $name { | ||
871 | #[inline] | ||
872 | fn clone(&self) -> $name { *self } | ||
873 | } | ||
874 | #[cfg(feature = "impl-default")] | ||
875 | impl Default for $name { | ||
876 | #[inline] | ||
877 | fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } | ||
878 | } | ||
879 | ); | ||
880 | } | ||
881 | "#, | ||
882 | ); | ||
883 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs | ||
884 | assert_expansion(&rules, r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, | ||
885 | "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
886 | assert_expansion(&rules, r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, | ||
887 | "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
888 | } | ||
825 | } | 889 | } |
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 86867111f..66ea76698 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -121,6 +121,10 @@ impl Bindings { | |||
121 | } | 121 | } |
122 | Ok(()) | 122 | Ok(()) |
123 | } | 123 | } |
124 | |||
125 | fn merge(&mut self, nested: Bindings) { | ||
126 | self.inner.extend(nested.inner); | ||
127 | } | ||
124 | } | 128 | } |
125 | 129 | ||
126 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { | 130 | fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, ExpandError> { |
@@ -236,7 +240,21 @@ fn match_lhs(pattern: &crate::Subtree, input: &mut TtCursor) -> Result<Bindings, | |||
236 | } | 240 | } |
237 | } | 241 | } |
238 | } | 242 | } |
239 | _ => {} | 243 | crate::TokenTree::Subtree(subtree) => { |
244 | let input_subtree = | ||
245 | input.eat_subtree().map_err(|_| ExpandError::UnexpectedToken)?; | ||
246 | if subtree.delimiter != input_subtree.delimiter { | ||
247 | return Err(ExpandError::UnexpectedToken); | ||
248 | } | ||
249 | |||
250 | let mut input = TtCursor::new(input_subtree); | ||
251 | let bindings = match_lhs(&subtree, &mut input)?; | ||
252 | if !input.is_eof() { | ||
253 | return Err(ExpandError::UnexpectedToken); | ||
254 | } | ||
255 | |||
256 | res.merge(bindings); | ||
257 | } | ||
240 | } | 258 | } |
241 | } | 259 | } |
242 | Ok(res) | 260 | Ok(res) |
@@ -287,7 +305,15 @@ fn expand_tt( | |||
287 | .into() | 305 | .into() |
288 | } | 306 | } |
289 | crate::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(), | 307 | crate::Leaf::Punct(punct) => tt::Leaf::from(punct.clone()).into(), |
290 | crate::Leaf::Var(v) => bindings.get(&v.text, nesting)?.clone(), | 308 | crate::Leaf::Var(v) => { |
309 | if v.text == "crate" { | ||
310 | // FIXME: Properly handle $crate token | ||
311 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | ||
312 | .into() | ||
313 | } else { | ||
314 | bindings.get(&v.text, nesting)?.clone() | ||
315 | } | ||
316 | } | ||
291 | crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(), | 317 | crate::Leaf::Literal(l) => tt::Leaf::from(tt::Literal { text: l.text.clone() }).into(), |
292 | }, | 318 | }, |
293 | }; | 319 | }; |
diff --git a/crates/ra_mbe/src/subtree_source.rs b/crates/ra_mbe/src/subtree_source.rs index 0a070b46a..16a053b49 100644 --- a/crates/ra_mbe/src/subtree_source.rs +++ b/crates/ra_mbe/src/subtree_source.rs | |||
@@ -379,8 +379,6 @@ where | |||
379 | { | 379 | { |
380 | if let Some((m, is_joint_to_next)) = iter.current_punct3(p) { | 380 | if let Some((m, is_joint_to_next)) = iter.current_punct3(p) { |
381 | if let Some((kind, text)) = match m { | 381 | if let Some((kind, text)) = match m { |
382 | ('<', '<', '=') => Some((SHLEQ, "<<=")), | ||
383 | ('>', '>', '=') => Some((SHREQ, ">>=")), | ||
384 | ('.', '.', '.') => Some((DOTDOTDOT, "...")), | 382 | ('.', '.', '.') => Some((DOTDOTDOT, "...")), |
385 | ('.', '.', '=') => Some((DOTDOTEQ, "..=")), | 383 | ('.', '.', '=') => Some((DOTDOTEQ, "..=")), |
386 | _ => None, | 384 | _ => None, |
@@ -391,23 +389,6 @@ where | |||
391 | 389 | ||
392 | if let Some((m, is_joint_to_next)) = iter.current_punct2(p) { | 390 | if let Some((m, is_joint_to_next)) = iter.current_punct2(p) { |
393 | if let Some((kind, text)) = match m { | 391 | if let Some((kind, text)) = match m { |
394 | ('<', '<') => Some((SHL, "<<")), | ||
395 | ('>', '>') => Some((SHR, ">>")), | ||
396 | |||
397 | ('|', '|') => Some((PIPEPIPE, "||")), | ||
398 | ('&', '&') => Some((AMPAMP, "&&")), | ||
399 | ('%', '=') => Some((PERCENTEQ, "%=")), | ||
400 | ('*', '=') => Some((STAREQ, "*=")), | ||
401 | ('/', '=') => Some((SLASHEQ, "/=")), | ||
402 | ('^', '=') => Some((CARETEQ, "^=")), | ||
403 | |||
404 | ('&', '=') => Some((AMPEQ, "&=")), | ||
405 | ('|', '=') => Some((PIPEEQ, "|=")), | ||
406 | ('-', '=') => Some((MINUSEQ, "-=")), | ||
407 | ('+', '=') => Some((PLUSEQ, "+=")), | ||
408 | ('>', '=') => Some((GTEQ, ">=")), | ||
409 | ('<', '=') => Some((LTEQ, "<=")), | ||
410 | |||
411 | ('-', '>') => Some((THIN_ARROW, "->")), | 392 | ('-', '>') => Some((THIN_ARROW, "->")), |
412 | ('!', '=') => Some((NEQ, "!=")), | 393 | ('!', '=') => Some((NEQ, "!=")), |
413 | ('=', '>') => Some((FAT_ARROW, "=>")), | 394 | ('=', '>') => Some((FAT_ARROW, "=>")), |