From 2f45cfc415626cfae5cba89c88a25fb3225486f7 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Thu, 13 Aug 2020 10:08:11 +0200 Subject: Rename ra_mbe -> mbe --- crates/ra_mbe/src/tests.rs | 1898 -------------------------------------------- 1 file changed, 1898 deletions(-) delete mode 100644 crates/ra_mbe/src/tests.rs (limited to 'crates/ra_mbe/src/tests.rs') diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs deleted file mode 100644 index 0796ceee1..000000000 --- a/crates/ra_mbe/src/tests.rs +++ /dev/null @@ -1,1898 +0,0 @@ -use std::fmt::Write; - -use ::parser::FragmentKind; -use syntax::{ast, AstNode, NodeOrToken, SyntaxKind::IDENT, SyntaxNode, WalkEvent, T}; -use test_utils::assert_eq_text; - -use super::*; - -mod rule_parsing { - use syntax::{ast, AstNode}; - - use crate::ast_to_token_tree; - - use super::*; - - #[test] - fn test_valid_arms() { - fn check(macro_body: &str) { - let m = parse_macro_arm(macro_body); - m.unwrap(); - } - - check("($i:ident) => ()"); - check("($($i:ident)*) => ($_)"); - check("($($true:ident)*) => ($true)"); - check("($($false:ident)*) => ($false)"); - check("($) => ($)"); - } - - #[test] - fn test_invalid_arms() { - fn check(macro_body: &str, err: &str) { - let m = parse_macro_arm(macro_body); - assert_eq!(m, Err(ParseError::Expected(String::from(err)))); - } - - check("invalid", "expected subtree"); - - check("$i:ident => ()", "expected subtree"); - check("($i:ident) ()", "expected `=`"); - check("($($i:ident)_) => ()", "invalid repeat"); - - check("($i) => ($i)", "invalid macro definition"); - check("($i:) => ($i)", "invalid macro definition"); - } - - fn parse_macro_arm(arm_definition: &str) -> Result { - let macro_definition = format!(" macro_rules! m {{ {} }} ", arm_definition); - let source_file = ast::SourceFile::parse(¯o_definition).ok().unwrap(); - let macro_definition = - source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - - let (definition_tt, _) = - ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); - crate::MacroRules::parse(&definition_tt) - } -} - -// Good first issue (although a slightly challenging one): -// -// * Pick a random test from here -// https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt -// * Port the test to rust and add it to this module -// * Make it pass :-) - -#[test] -fn test_token_id_shift() { - let expansion = parse_macro( - r#" -macro_rules! foobar { - ($e:ident) => { foo bar $e } -} -"#, - ) - .expand_tt("foobar!(baz);"); - - fn get_id(t: &tt::TokenTree) -> Option { - if let tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) = t { - return Some(ident.id.0); - } - None - } - - assert_eq!(expansion.token_trees.len(), 3); - // {($e:ident) => { foo bar $e }} - // 012345 67 8 9 T 12 - assert_eq!(get_id(&expansion.token_trees[0]), Some(9)); - assert_eq!(get_id(&expansion.token_trees[1]), Some(10)); - - // The input args of macro call include parentheses: - // (baz) - // So baz should be 12+1+1 - assert_eq!(get_id(&expansion.token_trees[2]), Some(14)); -} - -#[test] -fn test_token_map() { - let expanded = parse_macro( - r#" -macro_rules! foobar { - ($e:ident) => { fn $e() {} } -} -"#, - ) - .expand_tt("foobar!(baz);"); - - let (node, token_map) = token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap(); - let content = node.syntax_node().to_string(); - - let get_text = |id, kind| -> String { - content[token_map.range_by_token(id).unwrap().by_kind(kind).unwrap()].to_string() - }; - - assert_eq!(expanded.token_trees.len(), 4); - // {($e:ident) => { fn $e() {} }} - // 012345 67 8 9 T12 3 - - assert_eq!(get_text(tt::TokenId(9), IDENT), "fn"); - assert_eq!(get_text(tt::TokenId(12), T!['(']), "("); - assert_eq!(get_text(tt::TokenId(13), T!['{']), "{"); -} - -#[test] -fn test_convert_tt() { - parse_macro(r#" -macro_rules! impl_froms { - ($e:ident: $($v:ident),*) => { - $( - impl From<$v> for $e { - fn from(it: $v) -> $e { - $e::$v(it) - } - } - )* - } -} -"#) - .assert_expand_tt( - "impl_froms!(TokenTree: Leaf, Subtree);", - "impl From for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ - impl From for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" - ); -} - -#[test] -fn test_convert_tt2() { - parse_macro( - r#" -macro_rules! impl_froms { - ($e:ident: $($v:ident),*) => { - $( - impl From<$v> for $e { - fn from(it: $v) -> $e { - $e::$v(it) - } - } - )* - } -} -"#, - ) - .assert_expand( - "impl_froms!(TokenTree: Leaf, Subtree);", - r#" -SUBTREE $ - IDENT impl 20 - IDENT From 21 - PUNCH < [joint] 22 - IDENT Leaf 53 - PUNCH > [alone] 25 - IDENT for 26 - IDENT TokenTree 51 - SUBTREE {} 29 - IDENT fn 30 - IDENT from 31 - SUBTREE () 32 - IDENT it 33 - PUNCH : [alone] 34 - IDENT Leaf 53 - PUNCH - [joint] 37 - PUNCH > [alone] 38 - IDENT TokenTree 51 - SUBTREE {} 41 - IDENT TokenTree 51 - PUNCH : [joint] 44 - PUNCH : [joint] 45 - IDENT Leaf 53 - SUBTREE () 48 - IDENT it 49 - IDENT impl 20 - IDENT From 21 - PUNCH < [joint] 22 - IDENT Subtree 55 - PUNCH > [alone] 25 - IDENT for 26 - IDENT TokenTree 51 - SUBTREE {} 29 - IDENT fn 30 - IDENT from 31 - SUBTREE () 32 - IDENT it 33 - PUNCH : [alone] 34 - IDENT Subtree 55 - PUNCH - [joint] 37 - PUNCH > [alone] 38 - IDENT TokenTree 51 - SUBTREE {} 41 - IDENT TokenTree 51 - PUNCH : [joint] 44 - PUNCH : [joint] 45 - IDENT Subtree 55 - SUBTREE () 48 - IDENT it 49 -"#, - ); -} - -#[test] -fn test_lifetime_split() { - parse_macro( - r#" -macro_rules! foo { - ($($t:tt)*) => { $($t)*} -} -"#, - ) - .assert_expand( - r#"foo!(static bar: &'static str = "hello";);"#, - r#" -SUBTREE $ - IDENT static 17 - IDENT bar 18 - PUNCH : [alone] 19 - PUNCH & [alone] 20 - PUNCH ' [joint] 21 - IDENT static 22 - IDENT str 23 - PUNCH = [alone] 24 - LITERAL "hello" 25 - PUNCH ; [joint] 26 -"#, - ); -} - -#[test] -fn test_expr_order() { - let expanded = parse_macro( - r#" - macro_rules! foo { - ($ i:expr) => { - fn bar() { $ i * 2; } - } - } -"#, - ) - .expand_items("foo! { 1 + 1}"); - - let dump = format!("{:#?}", expanded); - assert_eq_text!( - dump.trim(), - r#"MACRO_ITEMS@0..15 - FN@0..15 - FN_KW@0..2 "fn" - NAME@2..5 - IDENT@2..5 "bar" - PARAM_LIST@5..7 - L_PAREN@5..6 "(" - R_PAREN@6..7 ")" - BLOCK_EXPR@7..15 - L_CURLY@7..8 "{" - EXPR_STMT@8..14 - BIN_EXPR@8..13 - BIN_EXPR@8..11 - LITERAL@8..9 - INT_NUMBER@8..9 "1" - PLUS@9..10 "+" - LITERAL@10..11 - INT_NUMBER@10..11 "1" - STAR@11..12 "*" - LITERAL@12..13 - INT_NUMBER@12..13 "2" - SEMICOLON@13..14 ";" - R_CURLY@14..15 "}""#, - ); -} - -#[test] -fn test_fail_match_pattern_by_first_token() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ident) => ( - mod $ i {} - ); - (= $ i:ident) => ( - fn $ i() {} - ); - (+ $ i:ident) => ( - struct $ i; - ) - } -"#, - ) - .assert_expand_items("foo! { foo }", "mod foo {}") - .assert_expand_items("foo! { = bar }", "fn bar () {}") - .assert_expand_items("foo! { + Baz }", "struct Baz ;"); -} - -#[test] -fn test_fail_match_pattern_by_last_token() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ident) => ( - mod $ i {} - ); - ($ i:ident =) => ( - fn $ i() {} - ); - ($ i:ident +) => ( - struct $ i; - ) - } -"#, - ) - .assert_expand_items("foo! { foo }", "mod foo {}") - .assert_expand_items("foo! { bar = }", "fn bar () {}") - .assert_expand_items("foo! { Baz + }", "struct Baz ;"); -} - -#[test] -fn test_fail_match_pattern_by_word_token() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ident) => ( - mod $ i {} - ); - (spam $ i:ident) => ( - fn $ i() {} - ); - (eggs $ i:ident) => ( - struct $ i; - ) - } -"#, - ) - .assert_expand_items("foo! { foo }", "mod foo {}") - .assert_expand_items("foo! { spam bar }", "fn bar () {}") - .assert_expand_items("foo! { eggs Baz }", "struct Baz ;"); -} - -#[test] -fn test_match_group_pattern_by_separator_token() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:ident),*) => ($ ( - mod $ i {} - )*); - ($ ($ i:ident)#*) => ($ ( - fn $ i() {} - )*); - ($ i:ident ,# $ j:ident) => ( - struct $ i; - struct $ j; - ) - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "mod foo {} mod bar {}") - .assert_expand_items("foo! { foo# bar }", "fn foo () {} fn bar () {}") - .assert_expand_items("foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); -} - -#[test] -fn test_match_group_pattern_with_multiple_defs() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:ident),*) => ( struct Bar { $ ( - fn $ i {} - )*} ); - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); -} - -#[test] -fn test_match_group_pattern_with_multiple_statement() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:ident),*) => ( fn baz { $ ( - $ i (); - )*} ); - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); -} - -#[test] -fn test_match_group_pattern_with_multiple_statement_without_semi() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:ident),*) => ( fn baz { $ ( - $i() - );*} ); - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "fn baz {foo () ;bar ()}"); -} - -#[test] -fn test_match_group_empty_fixed_token() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:ident)* #abc) => ( fn baz { $ ( - $ i (); - )*} ); - } -"#, - ) - .assert_expand_items("foo! {#abc}", "fn baz {}"); -} - -#[test] -fn test_match_group_in_subtree() { - parse_macro( - r#" - macro_rules! foo { - (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( - $ i (); - )*} ); - }"#, - ) - .assert_expand_items("foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); -} - -#[test] -fn test_match_group_with_multichar_sep() { - parse_macro( - r#" - macro_rules! foo { - (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); - }"#, - ) - .assert_expand_items("foo! (fn baz {true true} );", "fn baz () -> bool {true &&true}"); -} - -#[test] -fn test_match_group_zero_match() { - parse_macro( - r#" - macro_rules! foo { - ( $($i:ident)* ) => (); - }"#, - ) - .assert_expand_items("foo! ();", ""); -} - -#[test] -fn test_match_group_in_group() { - parse_macro( - r#" - macro_rules! foo { - { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); - }"#, - ) - .assert_expand_items("foo! ( (a b) );", "(a b)"); -} - -#[test] -fn test_expand_to_item_list() { - let tree = parse_macro( - " - macro_rules! structs { - ($($i:ident),*) => { - $(struct $i { field: u32 } )* - } - } - ", - ) - .expand_items("structs!(Foo, Bar);"); - assert_eq!( - format!("{:#?}", tree).trim(), - r#" -MACRO_ITEMS@0..40 - STRUCT@0..20 - STRUCT_KW@0..6 "struct" - NAME@6..9 - IDENT@6..9 "Foo" - RECORD_FIELD_LIST@9..20 - L_CURLY@9..10 "{" - RECORD_FIELD@10..19 - NAME@10..15 - IDENT@10..15 "field" - COLON@15..16 ":" - PATH_TYPE@16..19 - PATH@16..19 - PATH_SEGMENT@16..19 - NAME_REF@16..19 - IDENT@16..19 "u32" - R_CURLY@19..20 "}" - STRUCT@20..40 - STRUCT_KW@20..26 "struct" - NAME@26..29 - IDENT@26..29 "Bar" - RECORD_FIELD_LIST@29..40 - L_CURLY@29..30 "{" - RECORD_FIELD@30..39 - NAME@30..35 - IDENT@30..35 "field" - COLON@35..36 ":" - PATH_TYPE@36..39 - PATH@36..39 - PATH_SEGMENT@36..39 - NAME_REF@36..39 - IDENT@36..39 "u32" - R_CURLY@39..40 "}""# - .trim() - ); -} - -fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { - if let tt::TokenTree::Subtree(subtree) = tt { - return &subtree; - } - unreachable!("It is not a subtree"); -} -fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { - if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { - return lit; - } - unreachable!("It is not a literal"); -} - -fn to_punct(tt: &tt::TokenTree) -> &tt::Punct { - if let tt::TokenTree::Leaf(tt::Leaf::Punct(lit)) = tt { - return lit; - } - unreachable!("It is not a Punct"); -} - -#[test] -fn test_expand_literals_to_token_tree() { - let expansion = parse_macro( - r#" - macro_rules! literals { - ($i:ident) => { - { - let a = 'c'; - let c = 1000; - let f = 12E+99_f64; - let s = "rust1"; - } - } - } - "#, - ) - .expand_tt("literals!(foo);"); - let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; - - // [let] [a] [=] ['c'] [;] - assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); - // [let] [c] [=] [1000] [;] - assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); - // [let] [f] [=] [12E+99_f64] [;] - assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); - // [let] [s] [=] ["rust1"] [;] - assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); -} - -#[test] -fn test_attr_to_token_tree() { - let expansion = parse_to_token_tree_by_syntax( - r#" - #[derive(Copy)] - struct Foo; - "#, - ); - - assert_eq!(to_punct(&expansion.token_trees[0]).char, '#'); - assert_eq!( - to_subtree(&expansion.token_trees[1]).delimiter_kind(), - Some(tt::DelimiterKind::Bracket) - ); -} - -#[test] -fn test_two_idents() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ident, $ j:ident) => { - fn foo() { let a = $ i; let b = $j; } - } - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); -} - -#[test] -fn test_tt_to_stmts() { - let stmts = parse_macro( - r#" - macro_rules! foo { - () => { - let a = 0; - a = 10 + 1; - a - } - } -"#, - ) - .expand_statements("foo!{}"); - - assert_eq!( - format!("{:#?}", stmts).trim(), - r#"MACRO_STMTS@0..15 - LET_STMT@0..7 - LET_KW@0..3 "let" - IDENT_PAT@3..4 - NAME@3..4 - IDENT@3..4 "a" - EQ@4..5 "=" - LITERAL@5..6 - INT_NUMBER@5..6 "0" - SEMICOLON@6..7 ";" - EXPR_STMT@7..14 - BIN_EXPR@7..13 - PATH_EXPR@7..8 - PATH@7..8 - PATH_SEGMENT@7..8 - NAME_REF@7..8 - IDENT@7..8 "a" - EQ@8..9 "=" - BIN_EXPR@9..13 - LITERAL@9..11 - INT_NUMBER@9..11 "10" - PLUS@11..12 "+" - LITERAL@12..13 - INT_NUMBER@12..13 "1" - SEMICOLON@13..14 ";" - EXPR_STMT@14..15 - PATH_EXPR@14..15 - PATH@14..15 - PATH_SEGMENT@14..15 - NAME_REF@14..15 - IDENT@14..15 "a""#, - ); -} - -#[test] -fn test_match_literal() { - parse_macro( - r#" - macro_rules! foo { - ('(') => { - fn foo() {} - } - } -"#, - ) - .assert_expand_items("foo! ['('];", "fn foo () {}"); -} - -// The following tests are port from intellij-rust directly -// https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt - -#[test] -fn test_path() { - parse_macro( - r#" - macro_rules! foo { - ($ i:path) => { - fn foo() { let a = $ i; } - } - } -"#, - ) - .assert_expand_items("foo! { foo }", "fn foo () {let a = foo ;}") - .assert_expand_items( - "foo! { bar::::baz:: }", - "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", - ); -} - -#[test] -fn test_two_paths() { - parse_macro( - r#" - macro_rules! foo { - ($ i:path, $ j:path) => { - fn foo() { let a = $ i; let b = $j; } - } - } -"#, - ) - .assert_expand_items("foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); -} - -#[test] -fn test_path_with_path() { - parse_macro( - r#" - macro_rules! foo { - ($ i:path) => { - fn foo() { let a = $ i :: bar; } - } - } -"#, - ) - .assert_expand_items("foo! { foo }", "fn foo () {let a = foo :: bar ;}"); -} - -#[test] -fn test_expr() { - parse_macro( - r#" - macro_rules! foo { - ($ i:expr) => { - fn bar() { $ i; } - } - } -"#, - ) - .assert_expand_items( - "foo! { 2 + 2 * baz(3).quux() }", - "fn bar () {2 + 2 * baz (3) . quux () ;}", - ); -} - -#[test] -fn test_last_expr() { - parse_macro( - r#" - macro_rules! vec { - ($($item:expr),*) => { - { - let mut v = Vec::new(); - $( - v.push($item); - )* - v - } - }; - } -"#, - ) - .assert_expand_items( - "vec!(1,2,3);", - "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", - ); -} - -#[test] -fn test_ty() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ty) => ( - fn bar() -> $ i { unimplemented!() } - ) - } -"#, - ) - .assert_expand_items("foo! { Baz }", "fn bar () -> Baz < u8 > {unimplemented ! ()}"); -} - -#[test] -fn test_ty_with_complex_type() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ty) => ( - fn bar() -> $ i { unimplemented!() } - ) - } -"#, - ) - // Reference lifetime struct with generic type - .assert_expand_items( - "foo! { &'a Baz }", - "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", - ) - // extern "Rust" func type - .assert_expand_items( - r#"foo! { extern "Rust" fn() -> Ret }"#, - r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, - ); -} - -#[test] -fn test_pat_() { - parse_macro( - r#" - macro_rules! foo { - ($ i:pat) => { fn foo() { let $ i; } } - } -"#, - ) - .assert_expand_items("foo! { (a, b) }", "fn foo () {let (a , b) ;}"); -} - -#[test] -fn test_stmt() { - parse_macro( - r#" - macro_rules! foo { - ($ i:stmt) => ( - fn bar() { $ i; } - ) - } -"#, - ) - .assert_expand_items("foo! { 2 }", "fn bar () {2 ;}") - .assert_expand_items("foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); -} - -#[test] -fn test_single_item() { - parse_macro( - r#" - macro_rules! foo { - ($ i:item) => ( - $ i - ) - } -"#, - ) - .assert_expand_items("foo! {mod c {}}", "mod c {}"); -} - -#[test] -fn test_all_items() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:item)*) => ($ ( - $ i - )*) - } -"#, - ). - assert_expand_items( - r#" - foo! { - 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; - } -"#, - 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() { - parse_macro( - r#" - macro_rules! foo { - ($ i:block) => { fn foo() $ i } - } -"#, - ) - .assert_expand_statements("foo! { { 1; } }", "fn foo () {1 ;}"); -} - -#[test] -fn test_meta() { - parse_macro( - r#" - macro_rules! foo { - ($ i:meta) => ( - #[$ i] - fn bar() {} - ) - } -"#, - ) - .assert_expand_items( - r#"foo! { cfg(target_os = "windows") }"#, - r#"# [cfg (target_os = "windows")] fn bar () {}"#, - ); -} - -#[test] -fn test_meta_doc_comments() { - parse_macro( - r#" - macro_rules! foo { - ($(#[$ i:meta])+) => ( - $(#[$ i])+ - fn bar() {} - ) - } -"#, - ). - assert_expand_items( - r#"foo! { - /// Single Line Doc 1 - /** - MultiLines Doc - */ - }"#, - "# [doc = \" Single Line Doc 1\"] # [doc = \"\\\\n MultiLines Doc\\\\n \"] fn bar () {}", - ); -} - -#[test] -fn test_tt_block() { - parse_macro( - r#" - macro_rules! foo { - ($ i:tt) => { fn foo() $ i } - } - "#, - ) - .assert_expand_items(r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); -} - -#[test] -fn test_tt_group() { - parse_macro( - r#" - macro_rules! foo { - ($($ i:tt)*) => { $($ i)* } - } - "#, - ) - .assert_expand_items(r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); -} - -#[test] -fn test_tt_composite() { - parse_macro( - r#" - macro_rules! foo { - ($i:tt) => { 0 } - } - "#, - ) - .assert_expand_items(r#"foo! { => }"#, r#"0"#); -} - -#[test] -fn test_tt_composite2() { - let node = parse_macro( - r#" - macro_rules! foo { - ($($tt:tt)*) => { abs!(=> $($tt)*) } - } - "#, - ) - .expand_items(r#"foo!{#}"#); - - let res = format!("{:#?}", &node); - assert_eq_text!( - res.trim(), - r###"MACRO_ITEMS@0..10 - MACRO_CALL@0..10 - PATH@0..3 - PATH_SEGMENT@0..3 - NAME_REF@0..3 - IDENT@0..3 "abs" - BANG@3..4 "!" - TOKEN_TREE@4..10 - L_PAREN@4..5 "(" - EQ@5..6 "=" - R_ANGLE@6..7 ">" - WHITESPACE@7..8 " " - POUND@8..9 "#" - R_PAREN@9..10 ")""### - ); -} - -#[test] -fn test_lifetime() { - parse_macro( - r#" - macro_rules! foo { - ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } - } -"#, - ) - .assert_expand_items(r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); -} - -#[test] -fn test_literal() { - parse_macro( - r#" - macro_rules! foo { - ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; - } -"#, - ) - .assert_expand_items(r#"foo!(u8 0);"#, r#"const VALUE : u8 = 0 ;"#); -} - -#[test] -fn test_boolean_is_ident() { - parse_macro( - r#" - macro_rules! foo { - ($lit0:literal, $lit1:literal) => { const VALUE: (bool,bool) = ($lit0,$lit1); }; - } -"#, - ) - .assert_expand( - r#"foo!(true,false);"#, - r#" -SUBTREE $ - IDENT const 14 - IDENT VALUE 15 - PUNCH : [alone] 16 - SUBTREE () 17 - IDENT bool 18 - PUNCH , [alone] 19 - IDENT bool 20 - PUNCH = [alone] 21 - SUBTREE () 22 - IDENT true 29 - PUNCH , [joint] 25 - IDENT false 31 - PUNCH ; [alone] 28 -"#, - ); -} - -#[test] -fn test_vis() { - parse_macro( - r#" - macro_rules! foo { - ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; - } -"#, - ) - .assert_expand_items(r#"foo!(pub foo);"#, r#"pub fn foo () {}"#) - // test optional cases - .assert_expand_items(r#"foo!(foo);"#, r#"fn foo () {}"#); -} - -#[test] -fn test_inner_macro_rules() { - parse_macro( - r#" -macro_rules! foo { - ($a:ident, $b:ident, $c:tt) => { - - macro_rules! bar { - ($bi:ident) => { - fn $bi() -> u8 {$c} - } - } - - bar!($a); - fn $b() -> u8 {$c} - } -} -"#, - ). - assert_expand_items( - r#"foo!(x,y, 1);"#, - r#"macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"#, - ); -} - -// The following tests are based on real world situations -#[test] -fn test_vec() { - let fixture = parse_macro( - r#" - macro_rules! vec { - ($($item:expr),*) => { - { - let mut v = Vec::new(); - $( - v.push($item); - )* - v - } - }; -} -"#, - ); - fixture - .assert_expand_items(r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#) - .assert_expand_items( - r#"vec![1u32,2];"#, - r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, - ); - - let tree = fixture.expand_expr(r#"vec![1u32,2];"#); - - assert_eq!( - format!("{:#?}", tree).trim(), - r#"BLOCK_EXPR@0..45 - L_CURLY@0..1 "{" - LET_STMT@1..20 - LET_KW@1..4 "let" - IDENT_PAT@4..8 - MUT_KW@4..7 "mut" - NAME@7..8 - IDENT@7..8 "v" - EQ@8..9 "=" - CALL_EXPR@9..19 - PATH_EXPR@9..17 - PATH@9..17 - PATH@9..12 - PATH_SEGMENT@9..12 - NAME_REF@9..12 - IDENT@9..12 "Vec" - COLON2@12..14 "::" - PATH_SEGMENT@14..17 - NAME_REF@14..17 - IDENT@14..17 "new" - ARG_LIST@17..19 - L_PAREN@17..18 "(" - R_PAREN@18..19 ")" - SEMICOLON@19..20 ";" - EXPR_STMT@20..33 - METHOD_CALL_EXPR@20..32 - PATH_EXPR@20..21 - PATH@20..21 - PATH_SEGMENT@20..21 - NAME_REF@20..21 - IDENT@20..21 "v" - DOT@21..22 "." - NAME_REF@22..26 - IDENT@22..26 "push" - ARG_LIST@26..32 - L_PAREN@26..27 "(" - LITERAL@27..31 - INT_NUMBER@27..31 "1u32" - R_PAREN@31..32 ")" - SEMICOLON@32..33 ";" - EXPR_STMT@33..43 - METHOD_CALL_EXPR@33..42 - PATH_EXPR@33..34 - PATH@33..34 - PATH_SEGMENT@33..34 - NAME_REF@33..34 - IDENT@33..34 "v" - DOT@34..35 "." - NAME_REF@35..39 - IDENT@35..39 "push" - ARG_LIST@39..42 - L_PAREN@39..40 "(" - LITERAL@40..41 - INT_NUMBER@40..41 "2" - R_PAREN@41..42 ")" - SEMICOLON@42..43 ";" - PATH_EXPR@43..44 - PATH@43..44 - PATH_SEGMENT@43..44 - NAME_REF@43..44 - IDENT@43..44 "v" - R_CURLY@44..45 "}""# - ); -} - -#[test] -fn test_winapi_struct() { - // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 - - parse_macro( - r#" -macro_rules! STRUCT { - ($(#[$attrs:meta])* struct $name:ident { - $($field:ident: $ftype:ty,)+ - }) => ( - #[repr(C)] #[derive(Copy)] $(#[$attrs])* - pub struct $name { - $(pub $field: $ftype,)+ - } - impl Clone for $name { - #[inline] - fn clone(&self) -> $name { *self } - } - #[cfg(feature = "impl-default")] - impl Default for $name { - #[inline] - fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } - } - ); -} -"#, - ). - // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs - assert_expand_items(r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, - "# [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 ()}}}" - ) - .assert_expand_items(r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, - "# [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 ()}}}" - ); -} - -#[test] -fn test_int_base() { - parse_macro( - r#" -macro_rules! int_base { - ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { - #[stable(feature = "rust1", since = "1.0.0")] - impl fmt::$Trait for $T { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - $Radix.fmt_int(*self as $U, f) - } - } - } -} -"#, - ).assert_expand_items(r#" int_base!{Binary for isize as usize -> Binary}"#, - "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" - ); -} - -#[test] -fn test_generate_pattern_iterators() { - // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs - parse_macro( - r#" -macro_rules! generate_pattern_iterators { - { double ended; with $(#[$common_stability_attribute:meta])*, - $forward_iterator:ident, - $reverse_iterator:ident, $iterty:ty - } => { - fn foo(){} - } -} -"#, - ).assert_expand_items( - r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str );"#, - "fn foo () {}", - ); -} - -#[test] -fn test_impl_fn_for_zst() { - // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs - parse_macro( - r#" -macro_rules! impl_fn_for_zst { - { $( $( #[$attr: meta] )* - struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = - |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty -$body: block; )+ - } => { - $( - $( #[$attr] )* - struct $Name; - - impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { - #[inline] - extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { - $body - } - } - - impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { - #[inline] - extern "rust-call" fn call_mut( - &mut self, - ($( $arg, )*): ($( $ArgTy, )*) - ) -> $ReturnTy { - Fn::call(&*self, ($( $arg, )*)) - } - } - - impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { - type Output = $ReturnTy; - - #[inline] - extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { - Fn::call(&self, ($( $arg, )*)) - } - } - )+ -} - } -"#, - ).assert_expand_items(r#" -impl_fn_for_zst ! { - # [ derive ( Clone ) ] - struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { - c . escape_debug_ext ( false ) - } ; - - # [ derive ( Clone ) ] - struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { - c . escape_unicode ( ) - } ; - # [ derive ( Clone ) ] - struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { - c . escape_default ( ) - } ; - } -"#, - "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}" - ); -} - -#[test] -fn test_impl_nonzero_fmt() { - // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 - parse_macro( - r#" - macro_rules! impl_nonzero_fmt { - ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { - fn foo () {} - } - } -"#, - ).assert_expand_items( - r#"impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"#, - "fn foo () {}", - ); -} - -#[test] -fn test_cfg_if_items() { - // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 - parse_macro( - r#" - macro_rules! __cfg_if_items { - (($($not:meta,)*) ; ) => {}; - (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { - __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } - } - } -"#, - ).assert_expand_items( - r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, - "__cfg_if_items ! {(rustdoc ,) ;}", - ); -} - -#[test] -fn test_cfg_if_main() { - // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 - parse_macro( - r#" - macro_rules! cfg_if { - ($( - if #[cfg($($meta:meta),*)] { $($it:item)* } - ) else * else { - $($it2:item)* - }) => { - __cfg_if_items! { - () ; - $( ( ($($meta),*) ($($it)*) ), )* - ( () ($($it2)*) ), - } - }; - - // Internal macro to Apply a cfg attribute to a list of items - (@__apply $m:meta, $($it:item)*) => { - $(#[$m] $it)* - }; - } -"#, - ).assert_expand_items(r#" -cfg_if ! { - if # [ cfg ( target_env = "msvc" ) ] { - // no extra unwinder support needed - } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { - // no unwinder on the system! - } else { - mod libunwind ; - pub use libunwind :: * ; - } - } -"#, - "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}" - ).assert_expand_items( - r#" -cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = "solaris" , target_os = "illumos" ) ) ) ) ) ) , } -"#, - "", - ); -} - -#[test] -fn test_proptest_arbitrary() { - // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16 - parse_macro( - r#" -macro_rules! arbitrary { - ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty; - $args: ident => $logic: expr) => { - impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { - type Parameters = $params; - type Strategy = $strat; - fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { - $logic - } - } - }; - -}"#, - ).assert_expand_items(r#"arbitrary ! ( [ A : Arbitrary ] - Vec < A > , - VecStrategy < A :: Strategy > , - RangedParams1 < A :: Parameters > ; - args => { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) } - ) ;"#, - "impl $crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}" - ); -} - -#[test] -fn test_old_ridl() { - // This is from winapi 2.8, which do not have a link from github - // - let expanded = parse_macro( - r#" -#[macro_export] -macro_rules! RIDL { - (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) - {$( - fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty - ),+} - ) => { - impl $interface { - $(pub unsafe fn $method(&mut self) -> $rtr { - ((*self.lpVtbl).$method)(self $(,$p)*) - })+ - } - }; -}"#, - ).expand_tt(r#" - RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { - fn GetDataSize(&mut self) -> UINT - }}"#); - - assert_eq!(expanded.to_string(), "impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}}"); -} - -#[test] -fn test_quick_error() { - let expanded = parse_macro( - r#" -macro_rules! quick_error { - - (SORT [enum $name:ident $( #[$meta:meta] )*] - items [$($( #[$imeta:meta] )* - => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] - {$( $ifuncs:tt )*} )* ] - buf [ ] - queue [ ] - ) => { - quick_error!(ENUMINITION [enum $name $( #[$meta] )*] - body [] - queue [$( - $( #[$imeta] )* - => - $iitem: $imode [$( $ivar: $ityp ),*] - )*] - ); -}; - -} -"#, - ) - .expand_tt( - r#" -quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ - => One : UNIT [] {} - => Two : TUPLE [s :String] {display ("two: {}" , s) from ()} - ] buf [] queue []) ; -"#, - ); - - assert_eq!(expanded.to_string(), "quick_error ! (ENUMINITION [enum Wrapped # [derive (Debug)]] body [] queue [=> One : UNIT [] => Two : TUPLE [s : String]]) ;"); -} - -#[test] -fn test_empty_repeat_vars_in_empty_repeat_vars() { - parse_macro( - r#" -macro_rules! delegate_impl { - ([$self_type:ident, $self_wrap:ty, $self_map:ident] - pub trait $name:ident $(: $sup:ident)* $(+ $more_sup:ident)* { - - // "Escaped" associated types. Stripped before making the `trait` - // itself, but forwarded when delegating impls. - $( - @escape [type $assoc_name_ext:ident] - // Associated types. Forwarded. - )* - $( - @section type - $( - $(#[$_assoc_attr:meta])* - type $assoc_name:ident $(: $assoc_bound:ty)*; - )+ - )* - // Methods. Forwarded. Using $self_map!(self) around the self argument. - // Methods must use receiver `self` or explicit type like `self: &Self` - // &self and &mut self are _not_ supported. - $( - @section self - $( - $(#[$_method_attr:meta])* - fn $method_name:ident(self $(: $self_selftype:ty)* $(,$marg:ident : $marg_ty:ty)*) -> $mret:ty; - )+ - )* - // Arbitrary tail that is ignored when forwarding. - $( - @section nodelegate - $($tail:tt)* - )* - }) => { - impl<> $name for $self_wrap where $self_type: $name { - $( - $( - fn $method_name(self $(: $self_selftype)* $(,$marg: $marg_ty)*) -> $mret { - $self_map!(self).$method_name($($marg),*) - } - )* - )* - } - } -} -"#, - ).assert_expand_items( - r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, - "impl <> Data for & \'a mut G where G : Data {}", - ); -} - -#[test] -fn expr_interpolation() { - let expanded = parse_macro( - r#" - macro_rules! id { - ($expr:expr) => { - map($expr) - } - } - "#, - ) - .expand_expr("id!(x + foo);"); - - assert_eq!(expanded.to_string(), "map(x+foo)"); -} - -pub(crate) struct MacroFixture { - rules: MacroRules, -} - -impl MacroFixture { - pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { - self.try_expand_tt(invocation).unwrap() - } - - fn try_expand_tt(&self, invocation: &str) -> Result { - let source_file = ast::SourceFile::parse(invocation).tree(); - let macro_invocation = - source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - - let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()) - .ok_or_else(|| ExpandError::ConversionError)?; - - self.rules.expand(&invocation_tt).result() - } - - fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { - assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); - } - - fn expand_items(&self, invocation: &str) -> SyntaxNode { - let expanded = self.expand_tt(invocation); - token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() - } - - fn expand_statements(&self, invocation: &str) -> SyntaxNode { - let expanded = self.expand_tt(invocation); - token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node() - } - - fn expand_expr(&self, invocation: &str) -> SyntaxNode { - let expanded = self.expand_tt(invocation); - token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() - } - - fn assert_expand_tt(&self, invocation: &str, expected: &str) { - let expansion = self.expand_tt(invocation); - assert_eq!(expansion.to_string(), expected); - } - - fn assert_expand(&self, invocation: &str, expected: &str) { - let expansion = self.expand_tt(invocation); - let actual = format!("{:?}", expansion); - test_utils::assert_eq_text!(&actual.trim(), &expected.trim()); - } - - fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { - self.assert_expansion(FragmentKind::Items, invocation, expected); - self - } - - fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture { - self.assert_expansion(FragmentKind::Statements, invocation, expected); - self - } - - fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { - let expanded = self.expand_tt(invocation); - assert_eq!(expanded.to_string(), expected); - - let expected = expected.replace("$crate", "C_C__C"); - - // wrap the given text to a macro call - let expected = { - let wrapped = format!("wrap_macro!( {} )", expected); - let wrapped = ast::SourceFile::parse(&wrapped); - let wrapped = - wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); - let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; - wrapped.delimiter = None; - wrapped - }; - - let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); - let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); - - let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); - let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); - - let expected_tree = expected_tree.replace("C_C__C", "$crate"); - assert_eq!( - expanded_tree, expected_tree, - "\nleft:\n{}\nright:\n{}", - expanded_tree, expected_tree, - ); - } -} - -fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { - let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); - let macro_definition = - source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); - - let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); - - let parsed = parse_to_token_tree( - &ra_fixture[macro_definition.token_tree().unwrap().syntax().text_range()], - ) - .unwrap() - .0; - assert_eq!(definition_tt, parsed); - - definition_tt -} - -pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { - let definition_tt = parse_macro_to_tt(ra_fixture); - let rules = MacroRules::parse(&definition_tt).unwrap(); - MacroFixture { rules } -} - -pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { - let definition_tt = parse_macro_to_tt(ra_fixture); - - match MacroRules::parse(&definition_tt) { - Ok(_) => panic!("Expect error"), - Err(err) => err, - } -} - -pub(crate) fn parse_to_token_tree_by_syntax(ra_fixture: &str) -> tt::Subtree { - let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); - let tt = syntax_node_to_token_tree(source_file.syntax()).unwrap().0; - - let parsed = parse_to_token_tree(ra_fixture).unwrap().0; - assert_eq!(tt, parsed); - - parsed -} - -fn debug_dump_ignore_spaces(node: &syntax::SyntaxNode) -> String { - let mut level = 0; - let mut buf = String::new(); - macro_rules! indent { - () => { - for _ in 0..level { - buf.push_str(" "); - } - }; - } - - for event in node.preorder_with_tokens() { - match event { - WalkEvent::Enter(element) => { - match element { - NodeOrToken::Node(node) => { - indent!(); - writeln!(buf, "{:?}", node.kind()).unwrap(); - } - NodeOrToken::Token(token) => match token.kind() { - syntax::SyntaxKind::WHITESPACE => {} - _ => { - indent!(); - writeln!(buf, "{:?}", token.kind()).unwrap(); - } - }, - } - level += 1; - } - WalkEvent::Leave(_) => level -= 1, - } - } - - buf -} - -#[test] -fn test_issue_2520() { - let macro_fixture = parse_macro( - r#" - macro_rules! my_macro { - { - ( $( - $( [] $sname:ident : $stype:ty )? - $( [$expr:expr] $nname:ident : $ntype:ty )? - ),* ) - } => { - Test { - $( - $( $sname, )? - )* - } - }; - } - "#, - ); - - macro_fixture.assert_expand_items( - r#"my_macro ! { - ([] p1 : u32 , [|_| S0K0] s : S0K0 , [] k0 : i32) - }"#, - "Test {p1 , k0 ,}", - ); -} - -#[test] -fn test_issue_3861() { - let macro_fixture = parse_macro( - r#" - macro_rules! rgb_color { - ($p:expr, $t: ty) => { - pub fn new() { - let _ = 0 as $t << $p; - } - }; - } - "#, - ); - - macro_fixture.expand_items(r#"rgb_color!(8 + 8, u32);"#); -} - -#[test] -fn test_repeat_bad_var() { - // FIXME: the second rule of the macro should be removed and an error about - // `$( $c )+` raised - parse_macro( - r#" - macro_rules! foo { - ($( $b:ident )+) => { - $( $c )+ - }; - ($( $b:ident )+) => { - $( $b )+ - } - } - "#, - ) - .assert_expand_items("foo!(b0 b1);", "b0 b1"); -} - -#[test] -fn test_no_space_after_semi_colon() { - let expanded = parse_macro( - r#" - macro_rules! with_std { ($($i:item)*) => ($(#[cfg(feature = "std")]$i)*) } - "#, - ) - .expand_items(r#"with_std! {mod m;mod f;}"#); - - let dump = format!("{:#?}", expanded); - assert_eq_text!( - dump.trim(), - r###"MACRO_ITEMS@0..52 - MODULE@0..26 - ATTR@0..21 - POUND@0..1 "#" - L_BRACK@1..2 "[" - PATH@2..5 - PATH_SEGMENT@2..5 - NAME_REF@2..5 - IDENT@2..5 "cfg" - TOKEN_TREE@5..20 - L_PAREN@5..6 "(" - IDENT@6..13 "feature" - EQ@13..14 "=" - STRING@14..19 "\"std\"" - R_PAREN@19..20 ")" - R_BRACK@20..21 "]" - MOD_KW@21..24 "mod" - NAME@24..25 - IDENT@24..25 "m" - SEMICOLON@25..26 ";" - MODULE@26..52 - ATTR@26..47 - POUND@26..27 "#" - L_BRACK@27..28 "[" - PATH@28..31 - PATH_SEGMENT@28..31 - NAME_REF@28..31 - IDENT@28..31 "cfg" - TOKEN_TREE@31..46 - L_PAREN@31..32 "(" - IDENT@32..39 "feature" - EQ@39..40 "=" - STRING@40..45 "\"std\"" - R_PAREN@45..46 ")" - R_BRACK@46..47 "]" - MOD_KW@47..50 "mod" - NAME@50..51 - IDENT@50..51 "f" - SEMICOLON@51..52 ";""###, - ); -} - -// https://github.com/rust-lang/rust/blob/master/src/test/ui/issues/issue-57597.rs -#[test] -fn test_rustc_issue_57597() { - fn test_error(fixture: &str) { - assert_eq!(parse_macro_error(fixture), ParseError::RepetitionEmtpyTokenTree); - } - - test_error("macro_rules! foo { ($($($i:ident)?)+) => {}; }"); - test_error("macro_rules! foo { ($($($i:ident)?)*) => {}; }"); - test_error("macro_rules! foo { ($($($i:ident)?)?) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)?)?)?) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)*)?)?) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)?)*)?) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)?)?)*) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)*)*)?) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)?)*)*) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)?)*)+) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)+)?)*) => {}; }"); - test_error("macro_rules! foo { ($($($($i:ident)+)*)?) => {}; }"); -} - -#[test] -fn test_expand_bad_literal() { - parse_macro( - r#" - macro_rules! foo { ($i:literal) => {}; } - "#, - ) - .assert_expand_err(r#"foo!(&k");"#, &ExpandError::BindingError("".into())); -} - -#[test] -fn test_empty_comments() { - parse_macro( - r#" - macro_rules! one_arg_macro { ($fmt:expr) => (); } - "#, - ) - .assert_expand_err( - r#"one_arg_macro!(/**/)"#, - &ExpandError::BindingError("expected Expr".into()), - ); -} -- cgit v1.2.3