Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/mbe/src/tests.rs')
| -rw-r--r-- | crates/mbe/src/tests.rs | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs new file mode 100644 index 0000000000..e63ad113ff --- /dev/null +++ b/crates/mbe/src/tests.rs @@ -0,0 +1,332 @@ +//! Tests specific to declarative macros, aka macros by example. This covers +//! both stable `macro_rules!` macros as well as unstable `macro` macros. +// FIXME: Move more of the nameres independent tests from +// crates\hir-def\src\macro_expansion_tests\mod.rs to this +use expect_test::expect; +use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId}; +use stdx::format_to; +use tt::{TextRange, TextSize}; + +use crate::DeclarativeMacro; + +#[expect(deprecated)] +fn check_( + def_edition: Edition, + call_edition: Edition, + macro2: bool, + decl: &str, + arg: &str, + render_debug: bool, + expect: expect_test::Expect, + parse: parser::TopEntryPoint, +) { + let decl_tt = &syntax_bridge::parse_to_token_tree( + def_edition, + SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), def_edition), + ast_id: ErasedFileAstId::from_raw(0), + }, + SyntaxContextId::ROOT, + decl, + ) + .unwrap(); + let mac = if macro2 { + DeclarativeMacro::parse_macro2(None, decl_tt, |_| def_edition) + } else { + DeclarativeMacro::parse_macro_rules(decl_tt, |_| def_edition) + }; + let call_anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(1), call_edition), + ast_id: ErasedFileAstId::from_raw(0), + }; + let arg_tt = + syntax_bridge::parse_to_token_tree(call_edition, call_anchor, SyntaxContextId::ROOT, arg) + .unwrap(); + let res = mac.expand( + &arg_tt, + |_| (), + Span { + range: TextRange::up_to(TextSize::of(arg)), + anchor: call_anchor, + ctx: SyntaxContextId::ROOT, + }, + def_edition, + ); + let mut expect_res = String::new(); + if let Some(err) = res.err { + format_to!(expect_res, "{err:#?}\n\n",); + } + if render_debug { + format_to!(expect_res, "{:#?}\n\n", res.value.0); + } + let (node, _) = syntax_bridge::token_tree_to_syntax_node(&res.value.0, parse, def_edition); + format_to!( + expect_res, + "{}", + syntax_bridge::prettify_macro_expansion::prettify_macro_expansion( + node.syntax_node(), + &mut |it| it.clone() + ) + ); + expect.assert_eq(&expect_res); +} + +fn check( + def_edition: Edition, + call_edition: Edition, + decl: &str, + arg: &str, + expect: expect_test::Expect, +) { + check_( + def_edition, + call_edition, + false, + decl, + arg, + true, + expect, + parser::TopEntryPoint::SourceFile, + ); +} + +#[test] +fn token_mapping_smoke_test() { + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +( struct $ident:ident ) => { + struct $ident { + map: ::std::collections::HashSet<()>, + } +}; +"#, + r#" +struct MyTraitMap2 +"#, + expect![[r#" + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + IDENT struct 0:[email protected]#0 + IDENT MyTraitMap2 1:[email protected]#0 + SUBTREE {} 0:[email protected]#0 0:[email protected]#0 + IDENT map 0:[email protected]#0 + PUNCH : [alone] 0:[email protected]#0 + PUNCH : [joint] 0:[email protected]#0 + PUNCH : [alone] 0:[email protected]#0 + IDENT std 0:[email protected]#0 + PUNCH : [joint] 0:[email protected]#0 + PUNCH : [alone] 0:[email protected]#0 + IDENT collections 0:[email protected]#0 + PUNCH : [joint] 0:[email protected]#0 + PUNCH : [alone] 0:[email protected]#0 + IDENT HashSet 0:[email protected]#0 + PUNCH < [alone] 0:[email protected]#0 + SUBTREE () 0:[email protected]#0 0:[email protected]#0 + PUNCH > [joint] 0:[email protected]#0 + PUNCH , [alone] 0:[email protected]#0 + + struct MyTraitMap2 { + map: ::std::collections::HashSet<()>, + }"#]], + ); +} + +#[test] +fn token_mapping_floats() { + // Regression test for https://github.com/rust-lang/rust-analyzer/issues/12216 + // (and related issues) + check( + Edition::CURRENT, + Edition::CURRENT, + r#" +($($tt:tt)*) => { + $($tt)* +}; +"#, + r#" +fn main() { + 1; + 1.0; + ((1,),).0.0; + let x = 1; +} +"#, + expect![[r#" + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + IDENT fn 1:[email protected]#0 + IDENT main 1:[email protected]#0 + SUBTREE () 1:[email protected]#0 1:[email protected]#0 + SUBTREE {} 1:[email protected]#0 1:[email protected]#0 + LITERAL Integer 1 1:[email protected]#0 + PUNCH ; [alone] 1:[email protected]#0 + LITERAL Float 1.0 1:[email protected]#0 + PUNCH ; [alone] 1:[email protected]#0 + SUBTREE () 1:[email protected]#0 1:[email protected]#0 + SUBTREE () 1:[email protected]#0 1:[email protected]#0 + LITERAL Integer 1 1:[email protected]#0 + PUNCH , [alone] 1:[email protected]#0 + PUNCH , [alone] 1:[email protected]#0 + PUNCH . [alone] 1:[email protected]#0 + LITERAL Float 0.0 1:[email protected]#0 + PUNCH ; [alone] 1:[email protected]#0 + IDENT let 1:[email protected]#0 + IDENT x 1:[email protected]#0 + PUNCH = [alone] 1:[email protected]#0 + LITERAL Integer 1 1:[email protected]#0 + PUNCH ; [alone] 1:[email protected]#0 + + fn main(){ + 1; + 1.0; + ((1,),).0.0; + let x = 1; + }"#]], + ); +} + +#[test] +fn expr_2021() { + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, + const { 1 }, +"#, + expect![[r#" + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + IDENT _ 1:[email protected]#0 + PUNCH ; [joint] 0:[email protected]#0 + SUBTREE () 0:[email protected]#0 0:[email protected]#0 + IDENT const 1:[email protected]#0 + SUBTREE {} 1:[email protected]#0 1:[email protected]#0 + LITERAL Integer 1 1:[email protected]#0 + PUNCH ; [alone] 0:[email protected]#0 + + _; + (const { + 1 + });"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:[email protected]#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + PUNCH ; [alone] 0:[email protected]#0 + + ;"#]], + ); + check( + Edition::Edition2021, + Edition::Edition2024, + r#" +($($e:expr),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + const { 1 }, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:[email protected]#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + PUNCH ; [alone] 0:[email protected]#0 + + ;"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + 4, + "literal", + funcall(), + future.await, + break 'foo bar, +"#, + expect![[r#" + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + LITERAL Integer 4 1:[email protected]#0 + PUNCH ; [joint] 0:[email protected]#0 + LITERAL Str literal 1:[email protected]#0 + PUNCH ; [joint] 0:[email protected]#0 + SUBTREE () 0:[email protected]#0 0:[email protected]#0 + IDENT funcall 1:[email protected]#0 + SUBTREE () 1:[email protected]#0 1:[email protected]#0 + PUNCH ; [joint] 0:[email protected]#0 + SUBTREE () 0:[email protected]#0 0:[email protected]#0 + IDENT future 1:[email protected]#0 + PUNCH . [alone] 1:[email protected]#0 + IDENT await 1:[email protected]#0 + PUNCH ; [joint] 0:[email protected]#0 + SUBTREE () 0:[email protected]#0 0:[email protected]#0 + IDENT break 1:[email protected]#0 + PUNCH ' [joint] 1:[email protected]#0 + IDENT foo 1:[email protected]#0 + IDENT bar 1:[email protected]#0 + PUNCH ; [alone] 0:[email protected]#0 + + 4; + "literal"; + (funcall()); + (future.await); + (break 'foo bar);"#]], + ); + check( + Edition::Edition2024, + Edition::Edition2024, + r#" +($($e:expr_2021),* $(,)?) => { + $($e);* ; +}; +"#, + r#" + _, +"#, + expect![[r#" + ExpandError { + inner: ( + 1:[email protected]#0, + NoMatchingRule, + ), + } + + SUBTREE $$ 1:[email protected]#0 1:[email protected]#0 + PUNCH ; [alone] 0:[email protected]#0 + + ;"#]], + ); +} |