2019-12-19 15:17:22 +00:00
use std ::fmt ::Write ;
2019-11-09 10:14:10 +00:00
use ra_parser ::FragmentKind ;
2019-12-19 15:17:22 +00:00
use ra_syntax ::{ ast , AstNode , NodeOrToken , SyntaxKind ::IDENT , SyntaxNode , WalkEvent , T } ;
2019-09-10 11:27:08 +00:00
use test_utils ::assert_eq_text ;
2019-05-03 13:38:00 +00:00
use super ::* ;
2019-09-16 23:54:22 +00:00
mod rule_parsing {
use ra_syntax ::{ ast , AstNode } ;
use super ::* ;
use crate ::ast_to_token_tree ;
#[ 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) " ) ;
2020-03-03 17:03:44 +00:00
check ( " ($) => ($) " ) ;
2019-09-16 23:54:22 +00:00
}
#[ 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 < crate ::MacroRules , ParseError > {
let macro_definition = format! ( " macro_rules! m {{ {} }} " , arm_definition ) ;
let source_file = ast ::SourceFile ::parse ( & macro_definition ) . ok ( ) . unwrap ( ) ;
let macro_definition =
source_file . syntax ( ) . descendants ( ) . find_map ( ast ::MacroCall ::cast ) . unwrap ( ) ;
let ( definition_tt , _ ) =
ast_to_token_tree ( & macro_definition . token_tree ( ) . unwrap ( ) ) . unwrap ( ) ;
crate ::MacroRules ::parse ( & definition_tt )
}
}
2019-05-03 13:38:00 +00:00
// 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 :-)
2019-11-04 15:22:18 +00:00
#[ test ]
fn test_token_id_shift ( ) {
2019-12-19 15:17:22 +00:00
let expansion = parse_macro (
r #"
2019-11-04 15:22:18 +00:00
macro_rules ! foobar {
( $e :ident ) = > { foo bar $e }
}
2019-12-19 15:17:22 +00:00
" #,
)
. expand_tt ( " foobar!(baz); " ) ;
2019-11-04 15:22:18 +00:00
fn get_id ( t : & tt ::TokenTree ) -> Option < u32 > {
if let tt ::TokenTree ::Leaf ( tt ::Leaf ::Ident ( ident ) ) = t {
return Some ( ident . id . 0 ) ;
}
None
}
assert_eq! ( expansion . token_trees . len ( ) , 3 ) ;
2019-12-12 17:41:44 +00:00
// {($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 ) ) ;
2019-11-04 15:22:18 +00:00
}
2019-12-13 19:39:15 +00:00
#[ test ]
fn test_token_map ( ) {
2019-12-19 15:17:22 +00:00
let expanded = parse_macro (
r #"
2019-12-13 19:39:15 +00:00
macro_rules ! foobar {
( $e :ident ) = > { fn $e ( ) { } }
}
2019-12-19 15:17:22 +00:00
" #,
)
. expand_tt ( " foobar!(baz); " ) ;
let ( node , token_map ) = token_tree_to_syntax_node ( & expanded , FragmentKind ::Items ) . unwrap ( ) ;
let content = node . syntax_node ( ) . to_string ( ) ;
2019-12-13 19:39:15 +00:00
let get_text = | id , kind | -> String {
2019-12-18 03:36:10 +00:00
content [ token_map . range_by_token ( id ) . unwrap ( ) . by_kind ( kind ) . unwrap ( ) ] . to_string ( )
2019-12-13 19:39:15 +00:00
} ;
2019-12-19 15:17:22 +00:00
assert_eq! ( expanded . token_trees . len ( ) , 4 ) ;
2019-12-13 19:39:15 +00:00
// {($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! [ '{' ] ) , " { " ) ;
}
2019-05-03 13:38:00 +00:00
#[ test ]
fn test_convert_tt ( ) {
2019-12-19 15:17:22 +00:00
parse_macro ( r #"
2019-05-03 13:38:00 +00:00
macro_rules ! impl_froms {
( $e :ident : $( $v :ident ) , * ) = > {
$(
impl From < $v > for $e {
fn from ( it : $v ) -> $e {
$e ::$v ( it )
}
}
) *
}
}
2019-12-19 15:17:22 +00:00
" #)
. assert_expand_tt (
" impl_froms!(TokenTree: Leaf, Subtree); " ,
" impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \
impl From < Subtree > for TokenTree { fn from ( it : Subtree ) -> TokenTree { TokenTree ::Subtree ( it ) } } "
) ;
2019-05-03 13:38:00 +00:00
}
2020-04-09 15:48:08 +00:00
#[ 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
" #,
) ;
}
2020-04-18 18:45:17 +00:00
#[ 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
" #,
) ;
}
2019-09-19 10:19:37 +00:00
#[ test ]
fn test_expr_order ( ) {
2019-12-19 15:17:22 +00:00
let expanded = parse_macro (
2019-09-19 10:19:37 +00:00
r #"
macro_rules ! foo {
( $ i :expr ) = > {
fn bar ( ) { $ i * 2 ; }
2019-05-03 13:38:00 +00:00
}
}
2019-09-19 10:19:37 +00:00
" #,
2019-12-19 15:17:22 +00:00
)
. expand_items ( " foo! { 1 + 1} " ) ;
2019-05-03 13:38:00 +00:00
2019-12-19 15:17:22 +00:00
let dump = format! ( " {:#?} " , expanded ) ;
2019-09-19 10:19:37 +00:00
assert_eq_text! (
dump . trim ( ) ,
2020-04-24 21:51:02 +00:00
r #" MACRO_ITEMS@0..15
FN_DEF @ 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
2020-05-01 23:18:19 +00:00
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 " } " " #,
2019-09-19 10:19:37 +00:00
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_fail_match_pattern_by_first_token ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ident ) = > (
mod $ i { }
) ;
( = $ i :ident ) = > (
fn $ i ( ) { }
) ;
( + $ i :ident ) = > (
struct $ i ;
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo } " , " mod foo {} " )
. assert_expand_items ( " foo! { = bar } " , " fn bar () {} " )
. assert_expand_items ( " foo! { + Baz } " , " struct Baz ; " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_fail_match_pattern_by_last_token ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ident ) = > (
mod $ i { }
) ;
( $ i :ident = ) = > (
fn $ i ( ) { }
) ;
( $ i :ident + ) = > (
struct $ i ;
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo } " , " mod foo {} " )
. assert_expand_items ( " foo! { bar = } " , " fn bar () {} " )
. assert_expand_items ( " foo! { Baz + } " , " struct Baz ; " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_fail_match_pattern_by_word_token ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ident ) = > (
mod $ i { }
) ;
( spam $ i :ident ) = > (
fn $ i ( ) { }
) ;
( eggs $ i :ident ) = > (
struct $ i ;
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo } " , " mod foo {} " )
. assert_expand_items ( " foo! { spam bar } " , " fn bar () {} " )
. assert_expand_items ( " foo! { eggs Baz } " , " struct Baz ; " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_pattern_by_separator_token ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :ident ) , * ) = > ( $ (
mod $ i { }
) * ) ;
( $ ( $ i :ident ) #* ) = > ( $ (
fn $ i ( ) { }
) * ) ;
( $ i :ident , # $ j :ident ) = > (
struct $ i ;
struct $ j ;
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. 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 ; " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_pattern_with_multiple_defs ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :ident ) , * ) = > ( struct Bar { $ (
fn $ i { }
2019-05-28 14:39:01 +00:00
) * } ) ;
2019-05-03 13:38:00 +00:00
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo, bar } " , " struct Bar {fn foo {} fn bar {}} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_pattern_with_multiple_statement ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :ident ) , * ) = > ( fn baz { $ (
$ i ( ) ;
2019-05-28 14:39:01 +00:00
) * } ) ;
2019-05-03 13:38:00 +00:00
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo, bar } " , " fn baz {foo () ; bar () ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_pattern_with_multiple_statement_without_semi ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :ident ) , * ) = > ( fn baz { $ (
$i ( )
2019-05-28 14:39:01 +00:00
) ; * } ) ;
2019-05-03 13:38:00 +00:00
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo, bar } " , " fn baz {foo () ;bar ()} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_empty_fixed_token ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :ident ) * #abc ) = > ( fn baz { $ (
$ i ( ) ;
2019-05-28 14:39:01 +00:00
) * } ) ;
2019-05-03 13:38:00 +00:00
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! {#abc} " , " fn baz {} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_in_subtree ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! foo {
2019-05-03 13:38:00 +00:00
( fn $name :ident { $( $i :ident ) * } ) = > ( fn $name ( ) { $ (
$ i ( ) ;
2019-05-28 14:39:01 +00:00
) * } ) ;
2019-05-03 13:38:00 +00:00
} " #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! {fn baz {a b} } " , " fn baz () {a () ; b () ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_with_multichar_sep ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! foo {
( fn $name :ident { $( $i :literal ) * } ) = > ( fn $name ( ) -> bool { $( $i ) & & * } ) ;
2019-05-03 13:38:00 +00:00
} " #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! (fn baz {true true} ); " , " fn baz () -> bool {true &&true} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_zero_match ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! foo {
( $( $i :ident ) * ) = > ( ) ;
2019-05-03 13:38:00 +00:00
} " #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! (); " , " " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_match_group_in_group ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! foo {
2019-05-03 13:38:00 +00:00
{ $( ( $( $i :ident ) * ) ) * } = > ( $( ( $( $i ) * ) ) * ) ;
} " #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! ( (a b) ); " , " (a b) " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_expand_to_item_list ( ) {
2019-12-19 15:17:22 +00:00
let tree = parse_macro (
2019-05-03 13:38:00 +00:00
"
macro_rules ! structs {
( $( $i :ident ) , * ) = > {
$( struct $i { field : u32 } ) *
}
}
" ,
2019-12-19 15:17:22 +00:00
)
. expand_items ( " structs!(Foo, Bar); " ) ;
2019-05-03 13:38:00 +00:00
assert_eq! (
2019-11-09 10:14:10 +00:00
format! ( " {:#?} " , tree ) . trim ( ) ,
2019-05-03 13:38:00 +00:00
r #"
2020-04-24 21:51:02 +00:00
MACRO_ITEMS @ 0 .. 40
STRUCT_DEF @ 0 .. 20
STRUCT_KW @ 0 .. 6 " struct "
NAME @ 6 .. 9
IDENT @ 6 .. 9 " Foo "
RECORD_FIELD_DEF_LIST @ 9 .. 20
L_CURLY @ 9 .. 10 " { "
RECORD_FIELD_DEF @ 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_DEF @ 20 .. 40
STRUCT_KW @ 20 .. 26 " struct "
NAME @ 26 .. 29
IDENT @ 26 .. 29 " Bar "
RECORD_FIELD_DEF_LIST @ 29 .. 40
L_CURLY @ 29 .. 30 " { "
RECORD_FIELD_DEF @ 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 " } " " #
2019-05-03 13:38:00 +00:00
. trim ( )
) ;
}
2020-03-19 12:40:25 +00:00
fn to_subtree ( tt : & tt ::TokenTree ) -> & tt ::Subtree {
if let tt ::TokenTree ::Subtree ( subtree ) = tt {
return & subtree ;
2019-05-03 13:38:00 +00:00
}
2020-03-19 12:40:25 +00:00
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 " ) ;
}
2019-05-03 13:38:00 +00:00
2020-03-19 12:40:25 +00:00
fn to_punct ( tt : & tt ::TokenTree ) -> & tt ::Punct {
if let tt ::TokenTree ::Leaf ( tt ::Leaf ::Punct ( lit ) ) = tt {
return lit ;
2019-05-03 13:38:00 +00:00
}
2020-03-19 12:40:25 +00:00
unreachable! ( " It is not a Punct " ) ;
}
2019-05-03 13:38:00 +00:00
2020-03-19 12:40:25 +00:00
#[ test ]
fn test_expand_literals_to_token_tree ( ) {
2019-12-19 15:17:22 +00:00
let expansion = parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! literals {
( $i :ident ) = > {
{
let a = 'c' ;
let c = 1000 ;
let f = 12E+99_ f64 ;
let s = " rust1 " ;
}
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. expand_tt ( " literals!(foo); " ) ;
2019-05-03 13:38:00 +00:00
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 \" " ) ;
}
2020-03-19 12:40:25 +00:00
#[ 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 )
) ;
}
2019-05-03 13:38:00 +00:00
#[ test ]
fn test_two_idents ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ident , $ j :ident ) = > {
fn foo ( ) { let a = $ i ; let b = $j ; }
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo, bar } " , " fn foo () {let a = foo ; let b = bar ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_tt_to_stmts ( ) {
2019-12-19 15:17:22 +00:00
let stmts = parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( ) = > {
let a = 0 ;
a = 10 + 1 ;
a
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. expand_statements ( " foo!{} " ) ;
2019-05-03 13:38:00 +00:00
assert_eq! (
2019-11-09 10:14:10 +00:00
format! ( " {:#?} " , stmts ) . trim ( ) ,
2020-04-24 21:51:02 +00:00
r #" MACRO_STMTS@0..15
LET_STMT @ 0 .. 7
LET_KW @ 0 .. 3 " let "
BIND_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 " " #,
2019-05-03 13:38:00 +00:00
) ;
}
2019-05-20 10:29:02 +00:00
#[ test ]
fn test_match_literal ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-20 10:29:02 +00:00
r #"
macro_rules ! foo {
( '(' ) = > {
fn foo ( ) { }
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! ['(']; " , " fn foo () {} " ) ;
2019-05-20 10:29:02 +00:00
}
2019-05-03 13:38:00 +00:00
// 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 ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :path ) = > {
fn foo ( ) { let a = $ i ; }
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo } " , " fn foo () {let a = foo ;} " )
. assert_expand_items (
2019-05-03 13:38:00 +00:00
" foo! { bar::<u8>::baz::<u8> } " ,
" fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;} " ,
) ;
}
#[ test ]
fn test_two_paths ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :path , $ j :path ) = > {
fn foo ( ) { let a = $ i ; let b = $j ; }
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo, bar } " , " fn foo () {let a = foo ; let b = bar ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_path_with_path ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :path ) = > {
fn foo ( ) { let a = $ i :: bar ; }
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { foo } " , " fn foo () {let a = foo :: bar ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_expr ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :expr ) = > {
2019-05-28 14:39:01 +00:00
fn bar ( ) { $ i ; }
2019-05-03 13:38:00 +00:00
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items (
2019-05-03 13:38:00 +00:00
" foo! { 2 + 2 * baz(3).quux() } " ,
" fn bar () {2 + 2 * baz (3) . quux () ;} " ,
) ;
}
#[ test ]
fn test_last_expr ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! vec {
( $( $item :expr ) , * ) = > {
{
let mut v = Vec ::new ( ) ;
$(
v . push ( $item ) ;
) *
v
}
} ;
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items (
2019-05-28 15:46:11 +00:00
" vec!(1,2,3); " ,
2019-05-03 13:38:00 +00:00
" {let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v} " ,
) ;
}
#[ test ]
fn test_ty ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ty ) = > (
fn bar ( ) -> $ i { unimplemented! ( ) }
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { Baz<u8> } " , " fn bar () -> Baz < u8 > {unimplemented ! ()} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_ty_with_complex_type ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :ty ) = > (
fn bar ( ) -> $ i { unimplemented! ( ) }
)
}
" #,
2019-12-19 15:17:22 +00:00
)
2019-05-03 13:38:00 +00:00
// Reference lifetime struct with generic type
2019-12-19 15:17:22 +00:00
. assert_expand_items (
2019-05-03 13:38:00 +00:00
" foo! { &'a Baz<u8> } " ,
" fn bar () -> & 'a Baz < u8 > {unimplemented ! ()} " ,
2019-12-19 15:17:22 +00:00
)
2019-05-03 13:38:00 +00:00
// extern "Rust" func type
2019-12-19 15:17:22 +00:00
. assert_expand_items (
2019-05-03 13:38:00 +00:00
r # "foo! { extern "Rust" fn() -> Ret }"# ,
r # "fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"# ,
) ;
}
#[ test ]
fn test_pat_ ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :pat ) = > { fn foo ( ) { let $ i ; } }
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { (a, b) } " , " fn foo () {let (a , b) ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_stmt ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :stmt ) = > (
fn bar ( ) { $ i ; }
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! { 2 } " , " fn bar () {2 ;} " )
. assert_expand_items ( " foo! { let a = 0 } " , " fn bar () {let a = 0 ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_single_item ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :item ) = > (
$ i
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( " foo! {mod c {}} " , " mod c {} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_all_items ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ ( $ i :item ) * ) = > ( $ (
$ i
) * )
}
" #,
2019-12-19 15:17:22 +00:00
) .
assert_expand_items (
2019-11-09 10:14:10 +00:00
r #"
2019-05-03 13:38:00 +00:00
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 ;
}
2019-11-09 10:14:10 +00:00
" #,
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 ;"# ,
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_block ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :block ) = > { fn foo ( ) $ i }
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_statements ( " foo! { { 1; } } " , " fn foo () {1 ;} " ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_meta ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :meta ) = > (
#[ $ i ]
fn bar ( ) { }
)
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items (
2019-05-03 13:38:00 +00:00
r # "foo! { cfg(target_os = "windows") }"# ,
r # "# [cfg (target_os = "windows")] fn bar () {}"# ,
) ;
}
2019-05-04 07:00:16 +00:00
#[ test ]
fn test_meta_doc_comments ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-04 07:00:16 +00:00
r #"
macro_rules ! foo {
( $( #[ $ i:meta ] ) + ) = > (
$( #[ $ i ] ) +
fn bar ( ) { }
)
}
" #,
2019-12-19 15:17:22 +00:00
) .
assert_expand_items (
2019-05-28 14:39:01 +00:00
r #" foo! {
2019-05-04 07:00:16 +00:00
/// Single Line Doc 1
2019-05-28 14:39:01 +00:00
/**
2019-05-04 07:00:16 +00:00
MultiLines Doc
* /
} " #,
2019-05-28 15:46:11 +00:00
" # [doc = \" Single Line Doc 1 \" ] # [doc = \" \\ \\ n MultiLines Doc \\ \\ n \" ] fn bar () {} " ,
2019-05-04 07:00:16 +00:00
) ;
}
2019-05-03 13:38:00 +00:00
#[ test ]
fn test_tt_block ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ i :tt ) = > { fn foo ( ) $ i }
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( r # "foo! { { 1; } }"# , r # "fn foo () {1 ;}"# ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_tt_group ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $( $ i :tt ) * ) = > { $( $ i ) * }
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( r # "foo! { fn foo() {} }"# , r # "fn foo () {}"# ) ;
2019-05-03 13:38:00 +00:00
}
2020-03-04 14:29:55 +00:00
#[ test ]
fn test_tt_composite ( ) {
parse_macro (
r #"
macro_rules ! foo {
( $i :tt ) = > { 0 }
}
" #,
)
. assert_expand_items ( r # "foo! { => }"# , r # "0"# ) ;
}
2020-03-04 15:38:58 +00:00
#[ 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 ( ) ,
2020-04-24 21:51:02 +00:00
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 " ) " " ###
2020-03-04 15:38:58 +00:00
) ;
}
2019-05-03 13:38:00 +00:00
#[ test ]
fn test_lifetime ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ lt :lifetime ) = > { struct Ref < $ lt > { s : & $ lt str } }
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( r # "foo!{'a}"# , r # "struct Ref <'a > {s : &'a str}"# ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_literal ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ type :ty $ lit :literal ) = > { const VALUE : $ type = $ lit ; } ;
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( r # "foo!(u8 0);"# , r # "const VALUE : u8 = 0 ;"# ) ;
2019-05-03 13:38:00 +00:00
}
2020-04-18 19:24:17 +00:00
#[ 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
" #,
) ;
}
2019-05-03 13:38:00 +00:00
#[ test ]
fn test_vis ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! foo {
( $ vis :vis $ name :ident ) = > { $ vis fn $ name ( ) { } } ;
}
" #,
2019-12-19 15:17:22 +00:00
)
. assert_expand_items ( r # "foo!(pub foo);"# , r # "pub fn foo () {}"# )
// test optional cases
. assert_expand_items ( r # "foo!(foo);"# , r # "fn foo () {}"# ) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_inner_macro_rules ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
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 }
}
2019-05-28 14:39:01 +00:00
}
2019-05-03 13:38:00 +00:00
" #,
2019-12-19 15:17:22 +00:00
) .
assert_expand_items (
2019-05-03 13:38:00 +00:00
r # "foo!(x,y, 1);"# ,
2019-05-03 15:23:21 +00:00
r # "macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"# ,
2019-05-03 13:38:00 +00:00
) ;
}
// The following tests are based on real world situations
#[ test ]
fn test_vec ( ) {
2019-12-19 15:17:22 +00:00
let fixture = parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! vec {
( $( $item :expr ) , * ) = > {
{
let mut v = Vec ::new ( ) ;
$(
v . push ( $item ) ;
) *
v
}
} ;
}
" #,
) ;
2019-12-19 15:17:22 +00:00
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}"# ,
) ;
2019-05-03 13:38:00 +00:00
2019-12-19 15:17:22 +00:00
let tree = fixture . expand_expr ( r # "vec![1u32,2];"# ) ;
2019-09-19 10:19:37 +00:00
2019-05-03 13:38:00 +00:00
assert_eq! (
2019-11-09 10:14:10 +00:00
format! ( " {:#?} " , tree ) . trim ( ) ,
2020-04-24 21:51:02 +00:00
r #" BLOCK_EXPR@0..45
2020-05-01 23:18:19 +00:00
L_CURLY @ 0 .. 1 " { "
LET_STMT @ 1 .. 20
LET_KW @ 1 .. 4 " let "
BIND_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 " } " " #
2019-05-03 13:38:00 +00:00
) ;
}
#[ test ]
fn test_winapi_struct ( ) {
// from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
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 ( ) } }
}
) ;
}
" #,
2019-12-19 15:17:22 +00:00
) .
2019-05-03 13:38:00 +00:00
// from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs
2019-12-19 15:17:22 +00:00
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 ()}}} "
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_int_base ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
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 )
}
}
}
2019-05-28 14:39:01 +00:00
}
2019-05-03 13:38:00 +00:00
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items ( r # " int_base!{Binary for isize as usize -> Binary}"# ,
2019-05-03 13:38:00 +00:00
" # [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)}} "
2019-12-19 15:17:22 +00:00
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_generate_pattern_iterators ( ) {
// from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! generate_pattern_iterators {
2019-05-03 13:38:00 +00:00
{ double ended ; with $( #[ $common_stability_attribute:meta ] ) * ,
$forward_iterator :ident ,
$reverse_iterator :ident , $iterty :ty
} = > {
fn foo ( ) { }
}
}
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items (
2019-11-09 10:14:10 +00:00
r # "generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str );"# ,
" fn foo () {} " ,
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_impl_fn_for_zst ( ) {
// from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
2019-05-28 14:39:01 +00:00
macro_rules ! impl_fn_for_zst {
2019-05-03 13:38:00 +00:00
{ $( $( #[ $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 , ) * ) )
}
}
) +
}
}
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items ( r #"
2019-05-28 14:39:01 +00:00
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 ( )
} ;
2019-05-03 13:38:00 +00:00
}
2019-05-28 14:39:01 +00:00
" #,
2019-12-19 15:17:22 +00:00
" # [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 ,))}} "
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_impl_nonzero_fmt ( ) {
// from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! impl_nonzero_fmt {
( #[ $stability: meta ] ( $( $Trait : ident ) , + ) for $Ty : ident ) = > {
fn foo ( ) { }
}
}
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items (
2019-11-09 10:14:10 +00:00
r # "impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"# ,
" fn foo () {} " ,
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_cfg_if_items ( ) {
// from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! __cfg_if_items {
( ( $( $not :meta , ) * ) ; ) = > { } ;
( ( $( $not :meta , ) * ) ; ( ( $( $m :meta ) , * ) ( $( $it :item ) * ) ) , $( $rest :tt ) * ) = > {
__cfg_if_items! { ( $( $not , ) * $( $m , ) * ) ; $( $rest ) * }
}
}
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items (
2019-11-09 10:14:10 +00:00
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 ,) ;} " ,
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_cfg_if_main ( ) {
// from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
r #"
macro_rules ! cfg_if {
( $(
if #[ cfg($($meta:meta),*) ] { $( $it :item ) * }
) else * else {
$( $it2 :item ) *
} ) = > {
__cfg_if_items! {
( ) ;
$( ( ( $( $meta ) , * ) ( $( $it ) * ) ) , ) *
( ( ) ( $( $it2 ) * ) ) ,
}
2019-05-03 17:14:25 +00:00
} ;
// Internal macro to Apply a cfg attribute to a list of items
( @ __apply $m :meta , $( $it :item ) * ) = > {
$( #[ $m ] $it ) *
} ;
2019-05-03 13:38:00 +00:00
}
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items ( r #"
2019-05-28 14:39:01 +00:00
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 :: * ;
}
}
" #,
2019-12-19 15:17:22 +00:00
" __cfg_if_items ! {() ; ((target_env = \" msvc \" ) ()) , ((all (target_arch = \" wasm32 \" , not (target_os = \" emscripten \" ))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,} "
) . assert_expand_items (
2019-11-09 10:14:10 +00:00
r #"
2019-05-28 14:39:01 +00:00
cfg_if ! { @ __apply cfg ( all ( not ( any ( not ( any ( target_os = " solaris " , target_os = " illumos " ) ) ) ) ) ) , }
2019-05-03 17:14:25 +00:00
" #,
2019-11-09 10:14:10 +00:00
" " ,
2019-05-03 17:14:25 +00:00
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_proptest_arbitrary ( ) {
// from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16
2019-12-19 15:17:22 +00:00
parse_macro (
2019-05-03 13:38:00 +00:00
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
}
}
2019-05-28 14:39:01 +00:00
} ;
2019-05-03 13:38:00 +00:00
} " #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items ( r #" arbitrary ! ( [ A : Arbitrary ]
2019-05-28 14:39:01 +00:00
Vec < A > ,
VecStrategy < A :: Strategy > ,
RangedParams1 < A :: Parameters > ;
args = > { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) }
2019-05-03 13:38:00 +00:00
) ; " #,
2019-12-19 15:17:22 +00:00
" impl <A : Arbitrary > $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)}}} "
) ;
2019-05-03 13:38:00 +00:00
}
#[ test ]
fn test_old_ridl ( ) {
// This is from winapi 2.8, which do not have a link from github
//
2019-12-19 15:17:22 +00:00
let expanded = parse_macro (
2019-05-03 13:38:00 +00:00
r #"
#[ macro_export ]
2019-05-28 14:39:01 +00:00
macro_rules ! RIDL {
2019-05-03 13:38:00 +00:00
( 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 ) * )
} ) +
2019-05-28 14:39:01 +00:00
}
2019-05-03 13:38:00 +00:00
} ;
} " #,
2019-12-19 15:17:22 +00:00
) . expand_tt ( r #"
RIDL ! { interface ID3D11Asynchronous ( ID3D11AsynchronousVtbl ) : ID3D11DeviceChild ( ID3D11DeviceChildVtbl ) {
fn GetDataSize ( & mut self ) -> UINT
} } " #);
2019-05-03 13:38:00 +00:00
assert_eq! ( expanded . to_string ( ) , " impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}} " ) ;
}
#[ test ]
fn test_quick_error ( ) {
2019-12-19 15:17:22 +00:00
let expanded = parse_macro (
2019-05-03 13:38:00 +00:00
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! ( ENUM_DEFINITION [ enum $name $( #[ $meta ] ) * ]
body [ ]
queue [ $(
2019-05-28 14:39:01 +00:00
$( #[ $imeta ] ) *
= >
$iitem : $imode [ $( $ivar : $ityp ) , * ]
2019-05-03 13:38:00 +00:00
) * ]
2019-05-28 14:39:01 +00:00
) ;
2019-05-03 13:38:00 +00:00
} ;
}
" #,
2019-12-19 15:17:22 +00:00
)
. expand_tt (
2019-05-03 13:38:00 +00:00
r #"
quick_error ! ( SORT [ enum Wrapped # [ derive ( Debug ) ] ] items [
2019-05-28 14:39:01 +00:00
= > One : UNIT [ ] { }
2019-05-03 13:38:00 +00:00
= > Two : TUPLE [ s :String ] { display ( " two: {} " , s ) from ( ) }
] buf [ ] queue [ ] ) ;
" #,
) ;
assert_eq! ( expanded . to_string ( ) , " quick_error ! (ENUM_DEFINITION [enum Wrapped # [derive (Debug)]] body [] queue [=> One : UNIT [] => Two : TUPLE [s : String]]) ; " ) ;
}
2019-05-05 02:21:26 +00:00
#[ test ]
fn test_empty_repeat_vars_in_empty_repeat_vars ( ) {
2019-12-19 15:17:22 +00:00
parse_macro (
2019-11-09 10:14:10 +00:00
r #"
2019-05-05 02:21:26 +00:00
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 ) , * )
}
) *
) *
}
}
}
2019-11-09 10:14:10 +00:00
" #,
2019-12-19 15:17:22 +00:00
) . assert_expand_items (
2019-11-09 10:14:10 +00:00
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 {} " ,
) ;
2019-05-05 02:21:26 +00:00
}
2019-09-19 10:19:37 +00:00
2019-12-19 13:43:19 +00:00
#[ test ]
fn expr_interpolation ( ) {
2019-12-19 15:17:22 +00:00
let expanded = parse_macro (
2019-12-19 13:43:19 +00:00
r #"
macro_rules ! id {
( $expr :expr ) = > {
map ( $expr )
}
}
" #,
2019-12-19 15:17:22 +00:00
)
. expand_expr ( " id!(x + foo); " ) ;
2019-12-19 13:43:19 +00:00
assert_eq! ( expanded . to_string ( ) , " map(x+foo) " ) ;
}
2019-12-19 15:17:22 +00:00
pub ( crate ) struct MacroFixture {
rules : MacroRules ,
2019-09-19 10:19:37 +00:00
}
2019-12-19 15:17:22 +00:00
impl MacroFixture {
pub ( crate ) fn expand_tt ( & self , invocation : & str ) -> tt ::Subtree {
2020-02-20 21:42:24 +00:00
self . try_expand_tt ( invocation ) . unwrap ( )
}
fn try_expand_tt ( & self , invocation : & str ) -> Result < tt ::Subtree , ExpandError > {
let source_file = ast ::SourceFile ::parse ( invocation ) . tree ( ) ;
2019-12-19 15:17:22 +00:00
let macro_invocation =
source_file . syntax ( ) . descendants ( ) . find_map ( ast ::MacroCall ::cast ) . unwrap ( ) ;
2019-09-19 10:19:37 +00:00
2020-03-20 19:04:11 +00:00
let ( invocation_tt , _ ) = ast_to_token_tree ( & macro_invocation . token_tree ( ) . unwrap ( ) )
. ok_or_else ( | | ExpandError ::ConversionError ) ? ;
2019-09-19 10:19:37 +00:00
2020-03-16 11:22:10 +00:00
self . rules . expand ( & invocation_tt ) . result ( )
2020-02-20 21:42:24 +00:00
}
fn assert_expand_err ( & self , invocation : & str , err : & ExpandError ) {
assert_eq! ( self . try_expand_tt ( invocation ) . as_ref ( ) , Err ( err ) ) ;
2019-12-19 15:17:22 +00:00
}
2019-12-13 19:39:15 +00:00
2019-12-19 15:17:22 +00:00
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 ( )
}
2019-12-13 19:39:15 +00:00
2019-12-19 15:17:22 +00:00
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 ( )
}
2019-12-13 19:39:15 +00:00
2019-12-19 15:17:22 +00:00
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 ( )
}
2019-12-13 19:39:15 +00:00
2019-12-19 15:17:22 +00:00
fn assert_expand_tt ( & self , invocation : & str , expected : & str ) {
let expansion = self . expand_tt ( invocation ) ;
assert_eq! ( expansion . to_string ( ) , expected ) ;
}
2019-09-19 10:19:37 +00:00
2020-04-09 15:48:08 +00:00
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 ( ) ) ;
}
2019-12-19 15:17:22 +00:00
fn assert_expand_items ( & self , invocation : & str , expected : & str ) -> & MacroFixture {
self . assert_expansion ( FragmentKind ::Items , invocation , expected ) ;
self
}
2019-09-19 10:19:37 +00:00
2019-12-19 15:17:22 +00:00
fn assert_expand_statements ( & self , invocation : & str , expected : & str ) -> & MacroFixture {
self . assert_expansion ( FragmentKind ::Statements , invocation , expected ) ;
self
}
2019-09-19 10:19:37 +00:00
2019-12-19 15:17:22 +00:00
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
} ;
2019-09-19 10:19:37 +00:00
2019-12-19 15:17:22 +00:00
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 ( ) ;
2019-09-19 10:19:37 +00:00
2019-12-19 15:17:22 +00:00
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 ( ) ;
2019-09-19 10:19:37 +00:00
2019-12-19 15:17:22 +00:00
let expected_tree = expected_tree . replace ( " C_C__C " , " $crate " ) ;
assert_eq! (
expanded_tree , expected_tree ,
" \n left: \n {} \n right: \n {} " ,
expanded_tree , expected_tree ,
) ;
}
2019-09-19 10:19:37 +00:00
}
2020-04-25 15:09:20 +00:00
fn parse_macro_to_tt ( ra_fixture : & str ) -> tt ::Subtree {
2020-03-08 16:13:04 +00:00
let source_file = ast ::SourceFile ::parse ( ra_fixture ) . ok ( ) . unwrap ( ) ;
2020-02-22 11:14:46 +00:00
let macro_definition =
2019-12-19 15:17:22 +00:00
source_file . syntax ( ) . descendants ( ) . find_map ( ast ::MacroCall ::cast ) . unwrap ( ) ;
let ( definition_tt , _ ) = ast_to_token_tree ( & macro_definition . token_tree ( ) . unwrap ( ) ) . unwrap ( ) ;
2020-03-08 16:13:04 +00:00
let parsed = parse_to_token_tree (
& ra_fixture [ macro_definition . token_tree ( ) . unwrap ( ) . syntax ( ) . text_range ( ) ] ,
)
. unwrap ( )
. 0 ;
assert_eq! ( definition_tt , parsed ) ;
2020-04-25 15:09:20 +00:00
definition_tt
}
pub ( crate ) fn parse_macro ( ra_fixture : & str ) -> MacroFixture {
let definition_tt = parse_macro_to_tt ( ra_fixture ) ;
2019-12-19 15:17:22 +00:00
let rules = MacroRules ::parse ( & definition_tt ) . unwrap ( ) ;
MacroFixture { rules }
}
2019-09-19 10:19:37 +00:00
2020-04-25 15:09:20 +00:00
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 ,
}
}
2020-03-19 12:40:25 +00:00
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
}
2019-12-19 15:17:22 +00:00
fn debug_dump_ignore_spaces ( node : & ra_syntax ::SyntaxNode ) -> String {
2019-09-19 10:19:37 +00:00
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 ( ) {
ra_syntax ::SyntaxKind ::WHITESPACE = > { }
_ = > {
indent! ( ) ;
writeln! ( buf , " {:?} " , token . kind ( ) ) . unwrap ( ) ;
}
} ,
}
level + = 1 ;
}
WalkEvent ::Leave ( _ ) = > level - = 1 ,
}
}
buf
}
2019-12-28 21:26:24 +00:00
#[ 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 ,} " ,
) ;
}
2020-04-08 10:34:20 +00:00
#[ 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);"# ) ;
}
2019-12-28 21:26:24 +00:00
#[ test ]
fn test_repeat_bad_var ( ) {
2019-12-30 16:12:20 +00:00
// FIXME: the second rule of the macro should be removed and an error about
// `$( $c )+` raised
2019-12-28 21:26:24 +00:00
parse_macro (
r #"
macro_rules ! foo {
( $( $b :ident ) + ) = > {
$( $c ) +
} ;
( $( $b :ident ) + ) = > {
$( $b ) +
}
}
" #,
)
. assert_expand_items ( " foo!(b0 b1); " , " b0 b1 " ) ;
}
2020-02-20 21:42:24 +00:00
2020-03-05 20:32:08 +00:00
#[ 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 ( ) ,
2020-04-24 21:51:02 +00:00
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 " ; " " ###,
2020-03-05 20:32:08 +00:00
) ;
}
2020-04-25 15:09:20 +00:00
// 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)+)*)?) => {}; } " ) ;
}
2020-02-20 21:42:24 +00:00
#[ test ]
fn test_expand_bad_literal ( ) {
parse_macro (
r #"
macro_rules ! foo { ( $i :literal ) = > { } ; }
" #,
)
2020-03-20 21:20:28 +00:00
. assert_expand_err ( r # "foo!(&k");"# , & ExpandError ::BindingError ( " " . into ( ) ) ) ;
2020-02-20 21:42:24 +00:00
}
2020-04-25 09:37:34 +00:00
#[ test ]
fn test_empty_comments ( ) {
parse_macro (
r #"
2020-04-25 10:49:46 +00:00
macro_rules ! one_arg_macro { ( $fmt :expr ) = > ( ) ; }
2020-04-25 09:37:34 +00:00
" #,
)
. assert_expand_err (
r # "one_arg_macro!(/**/)"# ,
& ExpandError ::BindingError ( " expected Expr " . into ( ) ) ,
) ;
}