diff --git a/crates/hir_def/src/macro_expansion_tests.rs b/crates/hir_def/src/macro_expansion_tests.rs index e892ab52686f..66874a674d82 100644 --- a/crates/hir_def/src/macro_expansion_tests.rs +++ b/crates/hir_def/src/macro_expansion_tests.rs @@ -20,7 +20,7 @@ use stdx::format_to; use syntax::{ ast::{self, edit::IndentLevel}, AstNode, - SyntaxKind::{self, IDENT, LIFETIME_IDENT}, + SyntaxKind::{EOF, IDENT, LIFETIME_IDENT}, SyntaxNode, T, }; @@ -109,7 +109,7 @@ fn reindent(indent: IndentLevel, pp: String) -> String { fn pretty_print_macro_expansion(expn: SyntaxNode) -> String { let mut res = String::new(); - let mut prev_kind = SyntaxKind::EOF; + let mut prev_kind = EOF; let mut indent_level = 0; for token in iter::successors(expn.first_token(), |t| t.next_token()) { let curr_kind = token.kind(); @@ -128,6 +128,11 @@ fn pretty_print_macro_expansion(expn: SyntaxNode) -> String { (T![>], _) if curr_kind.is_keyword() => " ", (T![->], _) | (_, T![->]) => " ", (T![&&], _) | (_, T![&&]) => " ", + (T![,], _) => " ", + (T![fn], T!['(']) => "", + (T![']'], _) if curr_kind.is_keyword() => " ", + (T![']'], T![#]) => "\n", + _ if prev_kind.is_keyword() => " ", _ => "", }; diff --git a/crates/hir_def/src/macro_expansion_tests/mbe.rs b/crates/hir_def/src/macro_expansion_tests/mbe.rs index 958f2a1c3317..df93298a3561 100644 --- a/crates/hir_def/src/macro_expansion_tests/mbe.rs +++ b/crates/hir_def/src/macro_expansion_tests/mbe.rs @@ -403,15 +403,15 @@ fn test_match_group_in_group() { check( r#" macro_rules! m { - [ $( ( $($i:ident)* ) )* ] => [ x![$( ( $($i)* ) )*]; ] + [ $( ( $($i:ident)* ) )* ] => [ ok![$( ( $($i)* ) )*]; ] } m! ( (a b) ); "#, expect![[r#" macro_rules! m { - [ $( ( $($i:ident)* ) )* ] => [ x![$( ( $($i)* ) )*]; ] + [ $( ( $($i:ident)* ) )* ] => [ ok![$( ( $($i)* ) )*]; ] } -x![(a b)]; +ok![(a b)]; "#]], ) } @@ -769,12 +769,353 @@ fn f() { fn test_expr_with_attr() { check( r#" -macro_rules! m { ($a:expr) => { x!(); } } +macro_rules! m { ($a:expr) => { ok!(); } } m!(#[allow(a)]()); "#, expect![[r#" -macro_rules! m { ($a:expr) => { x!(); } } -x!(); +macro_rules! m { ($a:expr) => { ok!(); } } +ok!(); "#]], ) } + +#[test] +fn test_ty() { + check( + r#" +macro_rules! m { + ($t:ty) => ( fn bar() -> $t {} ) +} +m! { Baz } +"#, + expect![[r#" +macro_rules! m { + ($t:ty) => ( fn bar() -> $t {} ) +} +fn bar() -> Baz {} +"#]], + ) +} + +#[test] +fn test_ty_with_complex_type() { + check( + r#" +macro_rules! m { + ($t:ty) => ( fn bar() -> $ t {} ) +} + +m! { &'a Baz } + +m! { extern "Rust" fn() -> Ret } +"#, + expect![[r#" +macro_rules! m { + ($t:ty) => ( fn bar() -> $ t {} ) +} + +fn bar() -> & 'a Baz {} + +fn bar() -> extern "Rust"fn() -> Ret {} +"#]], + ); +} + +#[test] +fn test_pat_() { + check( + r#" +macro_rules! m { + ($p:pat) => { fn foo() { let $p; } } +} +m! { (a, b) } +"#, + expect![[r#" +macro_rules! m { + ($p:pat) => { fn foo() { let $p; } } +} +fn foo() { + let (a, b); +} +"#]], + ); +} + +#[test] +fn test_stmt() { + check( + r#" +macro_rules! m { + ($s:stmt) => ( fn bar() { $s; } ) +} +m! { 2 } +m! { let a = 0 } +"#, + expect![[r#" +macro_rules! m { + ($s:stmt) => ( fn bar() { $s; } ) +} +fn bar() { + 2; +} +fn bar() { + let a = 0; +} +"#]], + ) +} + +#[test] +fn test_single_item() { + check( + r#" +macro_rules! m { ($i:item) => ( $i ) } +m! { mod c {} } +"#, + expect![[r#" +macro_rules! m { ($i:item) => ( $i ) } +mod c {} +"#]], + ) +} + +#[test] +fn test_all_items() { + check( + r#" +macro_rules! m { ($($i:item)*) => ($($i )*) } +m! { + 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; +} +"#, + expect![[r#" +macro_rules! m { ($($i:item)*) => ($($i )*) } +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; +"#]], + ); +} + +#[test] +fn test_block() { + check( + r#" +macro_rules! m { ($b:block) => { fn foo() $b } } +m! { { 1; } } +"#, + expect![[r#" +macro_rules! m { ($b:block) => { fn foo() $b } } +fn foo() { + 1; +} +"#]], + ); +} + +#[test] +fn test_meta() { + check( + r#" +macro_rules! m { + ($m:meta) => ( #[$m] fn bar() {} ) +} +m! { cfg(target_os = "windows") } +m! { hello::world } +"#, + expect![[r##" +macro_rules! m { + ($m:meta) => ( #[$m] fn bar() {} ) +} +#[cfg(target_os = "windows")] fn bar() {} +#[hello::world] fn bar() {} +"##]], + ); +} + +#[test] +fn test_meta_doc_comments() { + cov_mark::check!(test_meta_doc_comments); + check( + r#" +macro_rules! m { + ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +m! { + /// Single Line Doc 1 + /** + MultiLines Doc + */ +} +"#, + expect![[r##" +macro_rules! m { + ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +#[doc = " Single Line Doc 1"] +#[doc = "\n MultiLines Doc\n "] fn bar() {} +"##]], + ); +} + +#[test] +fn test_meta_extended_key_value_attributes() { + check( + r#" +macro_rules! m { + (#[$m:meta]) => ( #[$m] fn bar() {} ) +} +m! { #[doc = concat!("The `", "bla", "` lang item.")] } +"#, + expect![[r##" +macro_rules! m { + (#[$m:meta]) => ( #[$m] fn bar() {} ) +} +#[doc = concat!("The `", "bla", "` lang item.")] fn bar() {} +"##]], + ); +} + +#[test] +fn test_meta_doc_comments_non_latin() { + check( + r#" +macro_rules! m { + ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +m! { + /// 錦瑟無端五十弦,一弦一柱思華年。 + /** + 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 + */ +} +"#, + expect![[r##" +macro_rules! m { + ($(#[$ m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +#[doc = " 錦瑟無端五十弦,一弦一柱思華年。"] +#[doc = "\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\n "] fn bar() {} +"##]], + ); +} + +#[test] +fn test_meta_doc_comments_escaped_characters() { + check( + r#" +macro_rules! m { + ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +m! { + /// \ " ' +} +"#, + expect![[r##" +macro_rules! m { + ($(#[$m:meta])+) => ( $(#[$m])+ fn bar() {} ) +} +#[doc = " \\ \" \'"] fn bar() {} +"##]], + ); +} + +#[test] +fn test_tt_block() { + check( + r#" +macro_rules! m { ($tt:tt) => { fn foo() $tt } } +m! { { 1; } } +"#, + expect![[r#" +macro_rules! m { ($tt:tt) => { fn foo() $tt } } +fn foo() { + 1; +} +"#]], + ); +} + +#[test] +fn test_tt_group() { + check( + r#" +macro_rules! m { ($($tt:tt)*) => { $($tt)* } } +m! { fn foo() {} }" +"#, + expect![[r#" +macro_rules! m { ($($tt:tt)*) => { $($tt)* } } +fn foo() {}" +"#]], + ); +} + +#[test] +fn test_tt_composite() { + check( + r#" +macro_rules! m { ($tt:tt) => { ok!(); } } +m! { => } +m! { = > } +"#, + expect![[r#" +macro_rules! m { ($tt:tt) => { ok!(); } } +ok!(); +/* error: leftover tokens */ok!(); +"#]], + ); +} + +#[test] +fn test_tt_composite2() { + check( + r#" +macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } +m! {#} +"#, + expect![[r##" +macro_rules! m { ($($tt:tt)*) => { abs!(=> $($tt)*); } } +abs!( = > #); +"##]], + ); +} + +#[test] +fn test_tt_with_composite_without_space() { + // Test macro input without any spaces + // See https://github.com/rust-analyzer/rust-analyzer/issues/6692 + check( + r#" +macro_rules! m { ($ op:tt, $j:path) => ( ok!(); ) } +m!(==,Foo::Bool) +"#, + expect![[r#" +macro_rules! m { ($ op:tt, $j:path) => ( ok!(); ) } +ok!(); +"#]], + ); +} 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 index 3450cda3fda2..291f99330f1e 100644 --- a/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs +++ b/crates/hir_def/src/macro_expansion_tests/mbe/tt_conversion.rs @@ -53,11 +53,11 @@ macro_rules! m { } } fn f() { - let_ = 'c'; - let_ = 1000; - let_ = 12E+99_f64; - let_ = "rust1"; - let_ = -92; + let _ = 'c'; + let _ = 1000; + let _ = 12E+99_f64; + let _ = "rust1"; + let _ = -92; } "#]], ); diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index f3c9af1ccb4b..6bd57bb568bf 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -307,6 +307,7 @@ fn doc_comment_text(comment: &ast::Comment) -> SmolStr { } fn convert_doc_comment(token: &syntax::SyntaxToken) -> Option> { + cov_mark::hit!(test_meta_doc_comments); let comment = ast::Comment::cast(token.clone())?; let doc = comment.kind().doc?; diff --git a/crates/mbe/src/tests/expand.rs b/crates/mbe/src/tests/expand.rs index 01b553352268..15a718402911 100644 --- a/crates/mbe/src/tests/expand.rs +++ b/crates/mbe/src/tests/expand.rs @@ -101,315 +101,6 @@ fn test_attr_to_token_tree() { ); } -#[test] -fn test_ty() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ty) => ( - fn bar() -> $ i { unimplemented!() } - ) - } -"#, - ) - .assert_expand_items("foo! { Baz }", "fn bar () -> Baz < u8 > {unimplemented ! ()}"); -} - -#[test] -fn test_ty_with_complex_type() { - parse_macro( - r#" - macro_rules! foo { - ($ i:ty) => ( - fn bar() -> $ i { unimplemented!() } - ) - } -"#, - ) - // Reference lifetime struct with generic type - .assert_expand_items( - "foo! { &'a Baz }", - "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", - ) - // extern "Rust" func type - .assert_expand_items( - r#"foo! { extern "Rust" fn() -> Ret }"#, - r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, - ); -} - -#[test] -fn test_pat_() { - parse_macro( - r#" - macro_rules! foo { - ($ i:pat) => { fn foo() { let $ i; } } - } -"#, - ) - .assert_expand_items("foo! { (a, b) }", "fn foo () {let (a , b) ;}"); -} - -#[test] -fn test_stmt() { - parse_macro( - r#" - macro_rules! foo { - ($ i:stmt) => ( - fn bar() { $ i; } - ) - } -"#, - ) - .assert_expand_items("foo! { 2 }", "fn bar () {2 ;}") - .assert_expand_items("foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); -} - -#[test] -fn test_single_item() { - parse_macro( - r#" - macro_rules! foo { - ($ i:item) => ( - $ i - ) - } -"#, - ) - .assert_expand_items("foo! {mod c {}}", "mod c {}"); -} - -#[test] -fn test_all_items() { - parse_macro( - r#" - macro_rules! foo { - ($ ($ i:item)*) => ($ ( - $ i - )*) - } -"#, - ). - assert_expand_items( - r#" - 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; - } -"#, - 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 ;"#, - ); -} - -#[test] -fn test_block() { - parse_macro( - r#" - macro_rules! foo { - ($ i:block) => { fn foo() $ i } - } -"#, - ) - .assert_expand_statements("foo! { { 1; } }", "fn foo () {1 ;}"); -} - -#[test] -fn test_meta() { - parse_macro( - r#" - macro_rules! foo { - ($ i:meta) => ( - #[$ i] - fn bar() {} - ) - } -"#, - ) - .assert_expand_items( - r#"foo! { cfg(target_os = "windows") }"#, - r#"# [cfg (target_os = "windows")] fn bar () {}"#, - ) - .assert_expand_items(r#"foo! { hello::world }"#, r#"# [hello :: world] fn bar () {}"#); -} - -#[test] -fn test_meta_doc_comments() { - parse_macro( - r#" - macro_rules! foo { - ($(#[$ i:meta])+) => ( - $(#[$ i])+ - fn bar() {} - ) - } -"#, - ). - assert_expand_items( - r#"foo! { - /// Single Line Doc 1 - /** - MultiLines Doc - */ - }"#, - "# [doc = \" Single Line Doc 1\"] # [doc = \"\\n MultiLines Doc\\n \"] fn bar () {}", - ); -} - -#[test] -fn test_meta_extended_key_value_attributes() { - parse_macro( - r#" -macro_rules! foo { - (#[$i:meta]) => ( - #[$ i] - fn bar() {} - ) -} -"#, - ) - .assert_expand_items( - r#"foo! { #[doc = concat!("The `", "bla", "` lang item.")] }"#, - r#"# [doc = concat ! ("The `" , "bla" , "` lang item.")] fn bar () {}"#, - ); -} - -#[test] -fn test_meta_doc_comments_non_latin() { - parse_macro( - r#" - macro_rules! foo { - ($(#[$ i:meta])+) => ( - $(#[$ i])+ - fn bar() {} - ) - } -"#, - ). - assert_expand_items( - r#"foo! { - /// 錦瑟無端五十弦,一弦一柱思華年。 - /** - 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 - */ - }"#, - "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\n \"] fn bar () {}", - ); -} - -#[test] -fn test_meta_doc_comments_escaped_characters() { - parse_macro( - r#" - macro_rules! foo { - ($(#[$ i:meta])+) => ( - $(#[$ i])+ - fn bar() {} - ) - } -"#, - ) - .assert_expand_items( - r#"foo! { - /// \ " ' - }"#, - r#"# [doc = " \\ \" \'"] fn bar () {}"#, - ); -} - -#[test] -fn test_tt_block() { - parse_macro( - r#" - macro_rules! foo { - ($ i:tt) => { fn foo() $ i } - } - "#, - ) - .assert_expand_items(r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); -} - -#[test] -fn test_tt_group() { - parse_macro( - r#" - macro_rules! foo { - ($($ i:tt)*) => { $($ i)* } - } - "#, - ) - .assert_expand_items(r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); -} - -#[test] -fn test_tt_composite() { - parse_macro( - r#" - macro_rules! foo { - ($i:tt) => { 0 } - } - "#, - ) - .assert_expand_items(r#"foo! { => }"#, r#"0"#); -} - -#[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!( - 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 ")""###, - res.trim() - ); -} - -#[test] -fn test_tt_with_composite_without_space() { - parse_macro( - r#" - macro_rules! foo { - ($ op:tt, $j:path) => ( - 0 - ) - } -"#, - ) - // Test macro input without any spaces - // See https://github.com/rust-analyzer/rust-analyzer/issues/6692 - .assert_expand_items("foo!(==,Foo::Bool)", "0"); -} - #[test] fn test_underscore() { parse_macro(