//! Unlike rustc, rust-analyzer's syntax tree are not "made of" token trees.
//! Rather, token trees are an explicit bridge between the parser and
//! (procedural or declarative) macros.
//!
//! This module tests tt <-> syntax tree conversion specifically. In particular,
//! it, among other things, check that we convert `tt` to the right kind of
//! syntax node depending on the macro call-site.
use expect_test::expect;
use crate::macro_expansion_tests::check;
#[test]
fn round_trips_compound_tokens() {
check(
r#"
macro_rules! m {
() => { type qual: ::T = qual::T; }
}
m!();
"#,
expect![[r#"
macro_rules! m {
() => { type qual: ::T = qual::T; }
}
type qual: ::T = qual::T;
"#]],
)
}
#[test]
fn round_trips_literals() {
check(
r#"
macro_rules! m {
() => {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}
}
fn f() {
m!()
}
"#,
expect![[r#"
macro_rules! m {
() => {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}
}
fn f() {
let _ = 'c';
let _ = 1000;
let _ = 12E+99_f64;
let _ = 45E+1234_f128;
let _ = "rust1";
let _ = -92;
}
"#]],
);
}
#[test]
fn roundtrip_lifetime() {
check(
r#"
macro_rules! m {
($($t:tt)*) => { $($t)*}
}
m!(static bar: &'static str = "hello";);
"#,
expect![[r#"
macro_rules! m {
($($t:tt)*) => { $($t)*}
}
static bar: &'static str = "hello";
"#]],
);
}
#[test]
fn broken_parenthesis_sequence() {
check(
r#"
macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} }
fn f1() { m1!(x); }
fn f2() { m2!(x }
"#,
expect![[r#"
macro_rules! m1 { ($x:ident) => { ($x } }
macro_rules! m2 { ($x:ident) => {} }
fn f1() { (x); }
fn f2() { /* error: expected ident */ }
"#]],
)
}
#[test]
fn expansion_does_not_parse_as_expression() {
check(
r#"
macro_rules! stmts {
() => { fn foo() {} }
}
fn f() { let _ = stmts!/*+errors*/(); }
"#,
expect![[r#"
macro_rules! stmts {
() => { fn foo() {} }
}
fn f() { let _ = /* parse error: expected expression */
fn foo() {}; }
"#]],
)
}
#[test]
fn broken_pat() {
check(
r#"
macro_rules! m1 { () => (Some(x) left overs) }
macro_rules! m2 { () => ($) }
fn main() {
let m1!() = ();
let m2!/*+errors*/() = ();
}
"#,
expect![[r#"
macro_rules! m1 { () => (Some(x) left overs) }
macro_rules! m2 { () => ($) }
fn main() {
let Some(x)left overs = ();
let /* parse error: expected pattern */
$ = ();
}
"#]],
)
}
#[test]
fn float_literal_in_tt() {
check(
r#"
macro_rules! constant {
($( $ret:expr; )*) => {};
}
macro_rules! float_const_impl {
() => ( constant!(0.3; 3.3;); );
}
float_const_impl! {}
"#,
expect![[r#"
macro_rules! constant {
($( $ret:expr; )*) => {};
}
macro_rules! float_const_impl {
() => ( constant!(0.3; 3.3;); );
}
constant!(0.3;
3.3;
);
"#]],
);
}
#[test]
fn float_literal_in_output() {
check(
r#"
macro_rules! constant {
($e:expr ;) => {$e};
}
const _: () = constant!(0.0;);
const _: () = constant!(0.;);
const _: () = constant!(0e0;);
"#,
expect![[r#"
macro_rules! constant {
($e:expr ;) => {$e};
}
const _: () = 0.0;
const _: () = 0.;
const _: () = 0e0;
"#]],
);
}