//! Tests specific to declarative macros, aka macros by example. This covers
//! both stable `macro_rules!` macros as well as unstable `macro` macros.
mod tt_conversion;
mod matching;
mod meta_syntax;
use expect_test::expect;
use crate::macro_expansion_tests::check;
#[test]
fn mbe_smoke_test() {
check(
r#"
macro_rules! impl_froms {
($e:ident: $($v:ident),*) => {
$(
impl From<$v> for $e {
fn from(it: $v) -> $e { $e::$v(it) }
}
)*
}
}
impl_froms!(TokenTree: Leaf, Subtree);
"#,
expect![[r#"
macro_rules! impl_froms {
($e:ident: $($v:ident),*) => {
$(
impl From<$v> for $e {
fn from(it: $v) -> $e { $e::$v(it) }
}
)*
}
}
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)
}
}
"#]],
);
}
#[test]
fn expansion_does_not_parse_as_expression() {
check(
r#"
macro_rules! stmts {
() => { let _ = 0; }
}
fn f() { let _ = stmts!(); }
"#,
expect![[r#"
macro_rules! stmts {
() => { let _ = 0; }
}
fn f() { let _ = /* error: could not convert tokens */; }
"#]],
)
}
#[test]
fn wrong_nesting_level() {
check(
r#"
macro_rules! m {
($($i:ident);*) => ($i)
}
m!{a}
"#,
expect![[r#"
macro_rules! m {
($($i:ident);*) => ($i)
}
/* error: expected simple binding, found nested binding `i` */
"#]],
);
}
#[test]
fn match_by_first_token_literally() {
check(
r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
(= $i:ident) => ( fn $i() {} );
(+ $i:ident) => ( struct $i; )
}
m! { foo }
m! { = bar }
m! { + Baz }
"#,
expect![[r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
(= $i:ident) => ( fn $i() {} );
(+ $i:ident) => ( struct $i; )
}
mod foo {}
fn bar() {}
struct Baz;
"#]],
);
}
#[test]
fn match_by_last_token_literally() {
check(
r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
($i:ident =) => ( fn $i() {} );
($i:ident +) => ( struct $i; )
}
m! { foo }
m! { bar = }
m! { Baz + }
"#,
expect![[r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
($i:ident =) => ( fn $i() {} );
($i:ident +) => ( struct $i; )
}
mod foo {}
fn bar() {}
struct Baz;
"#]],
);
}
#[test]
fn match_by_ident() {
check(
r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
(spam $i:ident) => ( fn $i() {} );
(eggs $i:ident) => ( struct $i; )
}
m! { foo }
m! { spam bar }
m! { eggs Baz }
"#,
expect![[r#"
macro_rules! m {
($i:ident) => ( mod $i {} );
(spam $i:ident) => ( fn $i() {} );
(eggs $i:ident) => ( struct $i; )
}
mod foo {}
fn bar() {}
struct Baz;
"#]],
);
}
#[test]
fn match_by_separator_token() {
check(
r#"
macro_rules! m {
($($i:ident),*) => ($(mod $i {} )*);
($($i:ident)#*) => ($(fn $i() {} )*);
($i:ident ,# $ j:ident) => ( struct $i; struct $ j; )
}
m! { foo, bar }
m! { foo# bar }
m! { Foo,# Bar }
"#,
expect![[r##"
macro_rules! m {
($($i:ident),*) => ($(mod $i {} )*);
($($i:ident)#*) => ($(fn $i() {} )*);
($i:ident ,# $ j:ident) => ( struct $i; struct $ j; )
}
mod foo {}
mod bar {}
fn foo() {}
fn bar() {}
struct Foo;
struct Bar;
"##]],
);
}
#[test]
fn test_match_group_pattern_with_multiple_defs() {
check(
r#"
macro_rules! m {
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
}
m! { foo, bar }
"#,
expect![[r#"
macro_rules! m {
($($i:ident),*) => ( impl Bar { $(fn $i() {})* } );
}
impl Bar {
fn foo() {}
fn bar() {}
}
"#]],
);
}
#[test]
fn test_match_group_pattern_with_multiple_statement() {
check(
r#"
macro_rules! m {
($($i:ident),*) => ( fn baz() { $($i ();)* } );
}
m! { foo, bar }
"#,
expect![[r#"
macro_rules! m {
($($i:ident),*) => ( fn baz() { $($i ();)* } );
}
fn baz() {
foo();
bar();
}
"#]],
)
}
#[test]
fn test_match_group_pattern_with_multiple_statement_without_semi() {
check(
r#"
macro_rules! m {
($($i:ident),*) => ( fn baz() { $($i() );* } );
}
m! { foo, bar }
"#,
expect![[r#"
macro_rules! m {
($($i:ident),*) => ( fn baz() { $($i() );* } );
}
fn baz() {
foo();
bar()
}
"#]],
)
}
#[test]
fn test_match_group_empty_fixed_token() {
check(
r#"
macro_rules! m {
($($i:ident)* #abc) => ( fn baz() { $($i ();)* } );
}
m!{#abc}
"#,
expect![[r##"
macro_rules! m {
($($i:ident)* #abc) => ( fn baz() { $($i ();)* } );
}
fn baz() {}
"##]],
)
}
#[test]
fn test_match_group_in_subtree() {
check(
r#"
macro_rules! m {
(fn $name:ident { $($i:ident)* } ) => ( fn $name() { $($i ();)* } );
}
m! { fn baz { a b } }
"#,
expect![[r#"
macro_rules! m {
(fn $name:ident { $($i:ident)* } ) => ( fn $name() { $($i ();)* } );
}
fn baz() {
a();
b();
}
"#]],
)
}
#[test]
fn test_expr_order() {
check(
r#"
macro_rules! m {
($ i:expr) => { fn bar() { $ i * 3; } }
}
// +tree
m! { 1 + 2 }
"#,
expect![[r#"
macro_rules! m {
($ i:expr) => { fn bar() { $ i * 3; } }
}
fn bar() {
1+2*3;
}
// [email protected]
// [email protected]
// [email protected] "fn"
// [email protected]
// [email protected] "bar"
// [email protected]
// [email protected] "("
// [email protected] ")"
// [email protected]
// [email protected]
// [email protected] "{"
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected] "1"
// [email protected] "+"
// [email protected]
// [email protected] "2"
// [email protected] "*"
// [email protected]
// [email protected] "3"
// [email protected] ";"
// [email protected] "}"
"#]],
)
}
#[test]
fn test_match_group_with_multichar_sep() {
check(
r#"
macro_rules! m {
(fn $name:ident { $($i:literal)* }) => ( fn $name() -> bool { $($i)&&* } );
}
m! (fn baz { true false } );
"#,
expect![[r#"
macro_rules! m {
(fn $name:ident { $($i:literal)* }) => ( fn $name() -> bool { $($i)&&* } );
}
fn baz() -> bool {
true && false
}
"#]],
);
check(
r#"
macro_rules! m {
(fn $name:ident { $($i:literal)&&* }) => ( fn $name() -> bool { $($i)&&* } );
}
m! (fn baz { true && false } );
"#,
expect![[r#"
macro_rules! m {
(fn $name:ident { $($i:literal)&&* }) => ( fn $name() -> bool { $($i)&&* } );
}
fn baz() -> bool {
true && false
}
"#]],
);
}
#[test]
fn test_match_group_zero_match() {
check(
r#"
macro_rules! m { ( $($i:ident)* ) => (); }
m!();
"#,
expect![[r#"
macro_rules! m { ( $($i:ident)* ) => (); }
"#]],
);
}
#[test]
fn test_match_group_in_group() {
check(
r#"
macro_rules! m {
[ $( ( $($i:ident)* ) )* ] => [ x![$( ( $($i)* ) )*]; ]
}
m! ( (a b) );
"#,
expect![[r#"
macro_rules! m {
[ $( ( $($i:ident)* ) )* ] => [ x![$( ( $($i)* ) )*]; ]
}
x![(a b)];
"#]],
)
}
#[test]
fn test_expand_to_item_list() {
check(
r#"
macro_rules! structs {
($($i:ident),*) => { $(struct $i { field: u32 } )* }
}
// +tree
structs!(Foo, Bar);
"#,
expect![[r#"
macro_rules! structs {
($($i:ident),*) => { $(struct $i { field: u32 } )* }
}
struct Foo {
field:u32
}
struct Bar {
field:u32
}
// [email protected]
// [email protected]
// [email protected] "struct"
// [email protected]
// [email protected] "Foo"
// [email protected]
// [email protected] "{"
// [email protected]
// [email protected]
// [email protected] "field"
// [email protected] ":"
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected] "u32"
// [email protected] "}"
// [email protected]
// [email protected] "struct"
// [email protected]
// [email protected] "Bar"
// [email protected]
// [email protected] "{"
// [email protected]
// [email protected]
// [email protected] "field"
// [email protected] ":"
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected] "u32"
// [email protected] "}"
"#]],
);
}
#[test]
fn test_two_idents() {
check(
r#"
macro_rules! m {
($i:ident, $j:ident) => { fn foo() { let a = $i; let b = $j; } }
}
m! { foo, bar }
"#,
expect![[r#"
macro_rules! m {
($i:ident, $j:ident) => { fn foo() { let a = $i; let b = $j; } }
}
fn foo() {
let a = foo;
let b = bar;
}
"#]],
);
}
#[test]
fn test_tt_to_stmts() {
check(
r#"
macro_rules! m {
() => {
let a = 0;
a = 10 + 1;
a
}
}
fn f() -> i32 {
// +tree
m!{}
}
"#,
expect![[r#"
macro_rules! m {
() => {
let a = 0;
a = 10 + 1;
a
}
}
fn f() -> i32 {
let a = 0;
a = 10+1;
a
// [email protected]
// [email protected]
// [email protected] "let"
// [email protected]
// [email protected]
// [email protected] "a"
// [email protected] "="
// [email protected]
// [email protected] "0"
// [email protected] ";"
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected] "a"
// [email protected] "="
// [email protected]
// [email protected]
// [email protected] "10"
// [email protected] "+"
// [email protected]
// [email protected] "1"
// [email protected] ";"
// [email protected]
// [email protected]
// [email protected]
// [email protected]
// [email protected] "a"
}
"#]],
);
}
#[test]
fn test_match_literal() {
check(
r#"
macro_rules! m {
('(') => { fn l_paren() {} }
}
m!['('];
"#,
expect![[r#"
macro_rules! m {
('(') => { fn l_paren() {} }
}
fn l_paren() {}
"#]],
);
}
#[test]
fn test_parse_macro_def_simple() {
cov_mark::check!(parse_macro_def_simple);
check(
r#"
macro m($id:ident) { fn $id() {} }
m!(bar);
"#,
expect![[r#"
macro m($id:ident) { fn $id() {} }
fn bar() {}
"#]],
);
}
#[test]
fn test_parse_macro_def_rules() {
cov_mark::check!(parse_macro_def_rules);
check(
r#"
macro m {
($id:ident) => { fn $id() {} }
}
m!(bar);
"#,
expect![[r#"
macro m {
($id:ident) => { fn $id() {} }
}
fn bar() {}
"#]],
);
}
#[test]
fn test_macro_2_0_panic_2015() {
check(
r#"
macro panic_2015 {
() => (),
(bar) => (),
}
panic_2015!(bar);
"#,
expect![[r#"
macro panic_2015 {
() => (),
(bar) => (),
}
"#]],
);
}
#[test]
fn test_path() {
check(
r#"
macro_rules! m {
($p:path) => { fn foo() { let a = $p; } }
}
m! { foo }
m! { bar::<u8>::baz::<u8> }
"#,
expect![[r#"
macro_rules! m {
($p:path) => { fn foo() { let a = $p; } }
}
fn foo() {
let a = foo;
}
fn foo() {
let a = bar::<u8>::baz::<u8> ;
}
"#]],
);
}
#[test]
fn test_two_paths() {
check(
r#"
macro_rules! m {
($i:path, $j:path) => { fn foo() { let a = $ i; let b = $j; } }
}
m! { foo, bar }
"#,
expect![[r#"
macro_rules! m {
($i:path, $j:path) => { fn foo() { let a = $ i; let b = $j; } }
}
fn foo() {
let a = foo;
let b = bar;
}
"#]],
);
}
#[test]
fn test_path_with_path() {
check(
r#"
macro_rules! m {
($p:path) => { fn foo() { let a = $p::bar; } }
}
m! { foo }
"#,
expect![[r#"
macro_rules! m {
($p:path) => { fn foo() { let a = $p::bar; } }
}
fn foo() {
let a = foo::bar;
}
"#]],
);
}
#[test]
fn test_expr() {
check(
r#"
macro_rules! m {
($e:expr) => { fn bar() { $e; } }
}
m! { 2 + 2 * baz(3).quux() }
"#,
expect![[r#"
macro_rules! m {
($e:expr) => { fn bar() { $e; } }
}
fn bar() {
2+2*baz(3).quux();
}
"#]],
)
}
#[test]
fn test_last_expr() {
check(
r#"
macro_rules! vec {
($($item:expr),*) => {{
let mut v = Vec::new();
$( v.push($item); )*
v
}};
}
fn f() {
vec![1,2,3];
}
"#,
expect![[r#"
macro_rules! vec {
($($item:expr),*) => {{
let mut v = Vec::new();
$( v.push($item); )*
v
}};
}
fn f() {
{
let mut v = Vec::new();
v.push(1);
v.push(2);
v.push(3);
v
};
}
"#]],
);
}
#[test]
fn test_expr_with_attr() {
check(
r#"
macro_rules! m { ($a:expr) => { x!(); } }
m!(#[allow(a)]());
"#,
expect![[r#"
macro_rules! m { ($a:expr) => { x!(); } }
x!();
"#]],
)
}