Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs')
| -rw-r--r-- | crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs new file mode 100644 index 0000000000..84cc3f3872 --- /dev/null +++ b/crates/hir-def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -0,0 +1,150 @@ +//! 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 _ = "rust1"; + let _ = -92; + } +} +fn f() { + m!() +} +"#, + expect![[r#" +macro_rules! m { + () => { + let _ = 'c'; + let _ = 1000; + let _ = 12E+99_f64; + let _ = "rust1"; + let _ = -92; + } +} +fn f() { + let _ = 'c'; + let _ = 1000; + let _ = 12E+99_f64; + 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) => {} } + +m1!(); +m2!(x +"#, + expect![[r#" +macro_rules! m1 { ($x:ident) => { ($x } } +macro_rules! m2 { ($x:ident) => {} } + +/* error: invalid macro definition: expected subtree */ +/* error: Failed to lower macro args to token tree */ +"#]], + ) +} + +#[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 */ +$ = (); +} +"#]], + ) +} |