diff options
Diffstat (limited to 'crates/mbe/src/tests.rs')
-rw-r--r-- | crates/mbe/src/tests.rs | 296 |
1 files changed, 210 insertions, 86 deletions
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index bd2977ebd..5c641ebf2 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs | |||
@@ -6,7 +6,7 @@ use syntax::{ | |||
6 | SyntaxKind::{ERROR, IDENT}, | 6 | SyntaxKind::{ERROR, IDENT}, |
7 | SyntaxNode, WalkEvent, T, | 7 | SyntaxNode, WalkEvent, T, |
8 | }; | 8 | }; |
9 | use test_utils::assert_eq_text; | 9 | use test_utils::{assert_eq_text, mark}; |
10 | 10 | ||
11 | use super::*; | 11 | use super::*; |
12 | 12 | ||
@@ -33,19 +33,18 @@ mod rule_parsing { | |||
33 | 33 | ||
34 | #[test] | 34 | #[test] |
35 | fn test_invalid_arms() { | 35 | fn test_invalid_arms() { |
36 | fn check(macro_body: &str, err: &str) { | 36 | fn check(macro_body: &str, err: ParseError) { |
37 | let m = parse_macro_arm(macro_body); | 37 | let m = parse_macro_arm(macro_body); |
38 | assert_eq!(m, Err(ParseError::Expected(String::from(err)))); | 38 | assert_eq!(m, Err(err.into())); |
39 | } | 39 | } |
40 | check("invalid", ParseError::Expected("expected subtree".into())); | ||
40 | 41 | ||
41 | check("invalid", "expected subtree"); | 42 | check("$i:ident => ()", ParseError::Expected("expected subtree".into())); |
43 | check("($i:ident) ()", ParseError::Expected("expected `=`".into())); | ||
44 | check("($($i:ident)_) => ()", ParseError::InvalidRepeat); | ||
42 | 45 | ||
43 | check("$i:ident => ()", "expected subtree"); | 46 | check("($i) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); |
44 | check("($i:ident) ()", "expected `=`"); | 47 | check("($i:) => ($i)", ParseError::UnexpectedToken("bad fragment specifier 1".into())); |
45 | check("($($i:ident)_) => ()", "invalid repeat"); | ||
46 | |||
47 | check("($i) => ($i)", "invalid macro definition"); | ||
48 | check("($i:) => ($i)", "invalid macro definition"); | ||
49 | } | 48 | } |
50 | 49 | ||
51 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { | 50 | fn parse_macro_arm(arm_definition: &str) -> Result<crate::MacroRules, ParseError> { |
@@ -458,6 +457,17 @@ fn test_match_group_with_multichar_sep() { | |||
458 | } | 457 | } |
459 | 458 | ||
460 | #[test] | 459 | #[test] |
460 | fn test_match_group_with_multichar_sep2() { | ||
461 | parse_macro( | ||
462 | r#" | ||
463 | macro_rules! foo { | ||
464 | (fn $name:ident {$($i:literal)&&*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
465 | }"#, | ||
466 | ) | ||
467 | .assert_expand_items("foo! (fn baz {true && true} );", "fn baz () -> bool {true &&true}"); | ||
468 | } | ||
469 | |||
470 | #[test] | ||
461 | fn test_match_group_zero_match() { | 471 | fn test_match_group_zero_match() { |
462 | parse_macro( | 472 | parse_macro( |
463 | r#" | 473 | r#" |
@@ -675,6 +685,36 @@ fn test_match_literal() { | |||
675 | .assert_expand_items("foo! ['('];", "fn foo () {}"); | 685 | .assert_expand_items("foo! ['('];", "fn foo () {}"); |
676 | } | 686 | } |
677 | 687 | ||
688 | #[test] | ||
689 | fn test_parse_macro_def_simple() { | ||
690 | mark::check!(parse_macro_def_simple); | ||
691 | |||
692 | parse_macro2( | ||
693 | r#" | ||
694 | macro foo($id:ident) { | ||
695 | fn $id() {} | ||
696 | } | ||
697 | "#, | ||
698 | ) | ||
699 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
700 | } | ||
701 | |||
702 | #[test] | ||
703 | fn test_parse_macro_def_rules() { | ||
704 | mark::check!(parse_macro_def_rules); | ||
705 | |||
706 | parse_macro2( | ||
707 | r#" | ||
708 | macro foo { | ||
709 | ($id:ident) => { | ||
710 | fn $id() {} | ||
711 | } | ||
712 | } | ||
713 | "#, | ||
714 | ) | ||
715 | .assert_expand_items("foo!(bar);", "fn bar () {}"); | ||
716 | } | ||
717 | |||
678 | // The following tests are port from intellij-rust directly | 718 | // The following tests are port from intellij-rust directly |
679 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | 719 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt |
680 | 720 | ||
@@ -941,6 +981,29 @@ fn test_meta_doc_comments() { | |||
941 | } | 981 | } |
942 | 982 | ||
943 | #[test] | 983 | #[test] |
984 | fn test_meta_doc_comments_non_latin() { | ||
985 | parse_macro( | ||
986 | r#" | ||
987 | macro_rules! foo { | ||
988 | ($(#[$ i:meta])+) => ( | ||
989 | $(#[$ i])+ | ||
990 | fn bar() {} | ||
991 | ) | ||
992 | } | ||
993 | "#, | ||
994 | ). | ||
995 | assert_expand_items( | ||
996 | r#"foo! { | ||
997 | /// 錦瑟無端五十弦,一弦一柱思華年。 | ||
998 | /** | ||
999 | 莊生曉夢迷蝴蝶,望帝春心託杜鵑。 | ||
1000 | */ | ||
1001 | }"#, | ||
1002 | "# [doc = \" 錦瑟無端五十弦,一弦一柱思華年。\"] # [doc = \"\\\\n 莊生曉夢迷蝴蝶,望帝春心託杜鵑。\\\\n \"] fn bar () {}", | ||
1003 | ); | ||
1004 | } | ||
1005 | |||
1006 | #[test] | ||
944 | fn test_tt_block() { | 1007 | fn test_tt_block() { |
945 | parse_macro( | 1008 | parse_macro( |
946 | r#" | 1009 | r#" |
@@ -1215,6 +1278,18 @@ macro_rules! m { | |||
1215 | .is_some()); | 1278 | .is_some()); |
1216 | } | 1279 | } |
1217 | 1280 | ||
1281 | #[test] | ||
1282 | fn test_match_is_not_greedy() { | ||
1283 | parse_macro( | ||
1284 | r#" | ||
1285 | macro_rules! foo { | ||
1286 | ($($i:ident $(,)*),*) => {}; | ||
1287 | } | ||
1288 | "#, | ||
1289 | ) | ||
1290 | .assert_expand_items(r#"foo!(a,b);"#, r#""#); | ||
1291 | } | ||
1292 | |||
1218 | // The following tests are based on real world situations | 1293 | // The following tests are based on real world situations |
1219 | #[test] | 1294 | #[test] |
1220 | fn test_vec() { | 1295 | fn test_vec() { |
@@ -1699,95 +1774,122 @@ pub(crate) struct MacroFixture { | |||
1699 | rules: MacroRules, | 1774 | rules: MacroRules, |
1700 | } | 1775 | } |
1701 | 1776 | ||
1702 | impl MacroFixture { | 1777 | pub(crate) struct MacroFixture2 { |
1703 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { | 1778 | rules: MacroDef, |
1704 | self.try_expand_tt(invocation).unwrap() | 1779 | } |
1705 | } | ||
1706 | |||
1707 | fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> { | ||
1708 | let source_file = ast::SourceFile::parse(invocation).tree(); | ||
1709 | let macro_invocation = | ||
1710 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1711 | |||
1712 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()) | ||
1713 | .ok_or_else(|| ExpandError::ConversionError)?; | ||
1714 | 1780 | ||
1715 | self.rules.expand(&invocation_tt).result() | 1781 | macro_rules! impl_fixture { |
1716 | } | 1782 | ($name:ident) => { |
1783 | impl $name { | ||
1784 | pub(crate) fn expand_tt(&self, invocation: &str) -> tt::Subtree { | ||
1785 | self.try_expand_tt(invocation).unwrap() | ||
1786 | } | ||
1717 | 1787 | ||
1718 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { | 1788 | fn try_expand_tt(&self, invocation: &str) -> Result<tt::Subtree, ExpandError> { |
1719 | assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); | 1789 | let source_file = ast::SourceFile::parse(invocation).tree(); |
1720 | } | 1790 | let macro_invocation = |
1791 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1721 | 1792 | ||
1722 | fn expand_items(&self, invocation: &str) -> SyntaxNode { | 1793 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()) |
1723 | let expanded = self.expand_tt(invocation); | 1794 | .ok_or_else(|| ExpandError::ConversionError)?; |
1724 | token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() | ||
1725 | } | ||
1726 | 1795 | ||
1727 | fn expand_statements(&self, invocation: &str) -> SyntaxNode { | 1796 | self.rules.expand(&invocation_tt).result() |
1728 | let expanded = self.expand_tt(invocation); | 1797 | } |
1729 | token_tree_to_syntax_node(&expanded, FragmentKind::Statements).unwrap().0.syntax_node() | ||
1730 | } | ||
1731 | 1798 | ||
1732 | fn expand_expr(&self, invocation: &str) -> SyntaxNode { | 1799 | #[allow(unused)] |
1733 | let expanded = self.expand_tt(invocation); | 1800 | fn assert_expand_err(&self, invocation: &str, err: &ExpandError) { |
1734 | token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() | 1801 | assert_eq!(self.try_expand_tt(invocation).as_ref(), Err(err)); |
1735 | } | 1802 | } |
1736 | 1803 | ||
1737 | fn assert_expand_tt(&self, invocation: &str, expected: &str) { | 1804 | #[allow(unused)] |
1738 | let expansion = self.expand_tt(invocation); | 1805 | fn expand_items(&self, invocation: &str) -> SyntaxNode { |
1739 | assert_eq!(expansion.to_string(), expected); | 1806 | let expanded = self.expand_tt(invocation); |
1740 | } | 1807 | token_tree_to_syntax_node(&expanded, FragmentKind::Items).unwrap().0.syntax_node() |
1808 | } | ||
1741 | 1809 | ||
1742 | fn assert_expand(&self, invocation: &str, expected: &str) { | 1810 | #[allow(unused)] |
1743 | let expansion = self.expand_tt(invocation); | 1811 | fn expand_statements(&self, invocation: &str) -> SyntaxNode { |
1744 | let actual = format!("{:?}", expansion); | 1812 | let expanded = self.expand_tt(invocation); |
1745 | test_utils::assert_eq_text!(&expected.trim(), &actual.trim()); | 1813 | token_tree_to_syntax_node(&expanded, FragmentKind::Statements) |
1746 | } | 1814 | .unwrap() |
1815 | .0 | ||
1816 | .syntax_node() | ||
1817 | } | ||
1747 | 1818 | ||
1748 | fn assert_expand_items(&self, invocation: &str, expected: &str) -> &MacroFixture { | 1819 | #[allow(unused)] |
1749 | self.assert_expansion(FragmentKind::Items, invocation, expected); | 1820 | fn expand_expr(&self, invocation: &str) -> SyntaxNode { |
1750 | self | 1821 | let expanded = self.expand_tt(invocation); |
1751 | } | 1822 | token_tree_to_syntax_node(&expanded, FragmentKind::Expr).unwrap().0.syntax_node() |
1823 | } | ||
1752 | 1824 | ||
1753 | fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &MacroFixture { | 1825 | #[allow(unused)] |
1754 | self.assert_expansion(FragmentKind::Statements, invocation, expected); | 1826 | fn assert_expand_tt(&self, invocation: &str, expected: &str) { |
1755 | self | 1827 | let expansion = self.expand_tt(invocation); |
1756 | } | 1828 | assert_eq!(expansion.to_string(), expected); |
1829 | } | ||
1757 | 1830 | ||
1758 | fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { | 1831 | #[allow(unused)] |
1759 | let expanded = self.expand_tt(invocation); | 1832 | fn assert_expand(&self, invocation: &str, expected: &str) { |
1760 | assert_eq!(expanded.to_string(), expected); | 1833 | let expansion = self.expand_tt(invocation); |
1761 | 1834 | let actual = format!("{:?}", expansion); | |
1762 | let expected = expected.replace("$crate", "C_C__C"); | 1835 | test_utils::assert_eq_text!(&expected.trim(), &actual.trim()); |
1763 | 1836 | } | |
1764 | // wrap the given text to a macro call | ||
1765 | let expected = { | ||
1766 | let wrapped = format!("wrap_macro!( {} )", expected); | ||
1767 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
1768 | let wrapped = | ||
1769 | wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1770 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
1771 | wrapped.delimiter = None; | ||
1772 | wrapped | ||
1773 | }; | ||
1774 | 1837 | ||
1775 | let expanded_tree = token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); | 1838 | fn assert_expand_items(&self, invocation: &str, expected: &str) -> &$name { |
1776 | let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); | 1839 | self.assert_expansion(FragmentKind::Items, invocation, expected); |
1840 | self | ||
1841 | } | ||
1777 | 1842 | ||
1778 | let expected_tree = token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); | 1843 | #[allow(unused)] |
1779 | let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); | 1844 | fn assert_expand_statements(&self, invocation: &str, expected: &str) -> &$name { |
1845 | self.assert_expansion(FragmentKind::Statements, invocation, expected); | ||
1846 | self | ||
1847 | } | ||
1780 | 1848 | ||
1781 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | 1849 | fn assert_expansion(&self, kind: FragmentKind, invocation: &str, expected: &str) { |
1782 | assert_eq!( | 1850 | let expanded = self.expand_tt(invocation); |
1783 | expanded_tree, expected_tree, | 1851 | assert_eq!(expanded.to_string(), expected); |
1784 | "\nleft:\n{}\nright:\n{}", | 1852 | |
1785 | expanded_tree, expected_tree, | 1853 | let expected = expected.replace("$crate", "C_C__C"); |
1786 | ); | 1854 | |
1787 | } | 1855 | // wrap the given text to a macro call |
1856 | let expected = { | ||
1857 | let wrapped = format!("wrap_macro!( {} )", expected); | ||
1858 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
1859 | let wrapped = wrapped | ||
1860 | .tree() | ||
1861 | .syntax() | ||
1862 | .descendants() | ||
1863 | .find_map(ast::TokenTree::cast) | ||
1864 | .unwrap(); | ||
1865 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
1866 | wrapped.delimiter = None; | ||
1867 | wrapped | ||
1868 | }; | ||
1869 | |||
1870 | let expanded_tree = | ||
1871 | token_tree_to_syntax_node(&expanded, kind).unwrap().0.syntax_node(); | ||
1872 | let expanded_tree = debug_dump_ignore_spaces(&expanded_tree).trim().to_string(); | ||
1873 | |||
1874 | let expected_tree = | ||
1875 | token_tree_to_syntax_node(&expected, kind).unwrap().0.syntax_node(); | ||
1876 | let expected_tree = debug_dump_ignore_spaces(&expected_tree).trim().to_string(); | ||
1877 | |||
1878 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | ||
1879 | assert_eq!( | ||
1880 | expanded_tree, expected_tree, | ||
1881 | "\nleft:\n{}\nright:\n{}", | ||
1882 | expanded_tree, expected_tree, | ||
1883 | ); | ||
1884 | } | ||
1885 | } | ||
1886 | }; | ||
1788 | } | 1887 | } |
1789 | 1888 | ||
1790 | fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { | 1889 | impl_fixture!(MacroFixture); |
1890 | impl_fixture!(MacroFixture2); | ||
1891 | |||
1892 | fn parse_macro_rules_to_tt(ra_fixture: &str) -> tt::Subtree { | ||
1791 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | 1893 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); |
1792 | let macro_definition = | 1894 | let macro_definition = |
1793 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); | 1895 | source_file.syntax().descendants().find_map(ast::MacroRules::cast).unwrap(); |
@@ -1804,14 +1906,36 @@ fn parse_macro_to_tt(ra_fixture: &str) -> tt::Subtree { | |||
1804 | definition_tt | 1906 | definition_tt |
1805 | } | 1907 | } |
1806 | 1908 | ||
1909 | fn parse_macro_def_to_tt(ra_fixture: &str) -> tt::Subtree { | ||
1910 | let source_file = ast::SourceFile::parse(ra_fixture).ok().unwrap(); | ||
1911 | let macro_definition = | ||
1912 | source_file.syntax().descendants().find_map(ast::MacroDef::cast).unwrap(); | ||
1913 | |||
1914 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.body().unwrap()).unwrap(); | ||
1915 | |||
1916 | let parsed = | ||
1917 | parse_to_token_tree(&ra_fixture[macro_definition.body().unwrap().syntax().text_range()]) | ||
1918 | .unwrap() | ||
1919 | .0; | ||
1920 | assert_eq!(definition_tt, parsed); | ||
1921 | |||
1922 | definition_tt | ||
1923 | } | ||
1924 | |||
1807 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { | 1925 | pub(crate) fn parse_macro(ra_fixture: &str) -> MacroFixture { |
1808 | let definition_tt = parse_macro_to_tt(ra_fixture); | 1926 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); |
1809 | let rules = MacroRules::parse(&definition_tt).unwrap(); | 1927 | let rules = MacroRules::parse(&definition_tt).unwrap(); |
1810 | MacroFixture { rules } | 1928 | MacroFixture { rules } |
1811 | } | 1929 | } |
1812 | 1930 | ||
1931 | pub(crate) fn parse_macro2(ra_fixture: &str) -> MacroFixture2 { | ||
1932 | let definition_tt = parse_macro_def_to_tt(ra_fixture); | ||
1933 | let rules = MacroDef::parse(&definition_tt).unwrap(); | ||
1934 | MacroFixture2 { rules } | ||
1935 | } | ||
1936 | |||
1813 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { | 1937 | pub(crate) fn parse_macro_error(ra_fixture: &str) -> ParseError { |
1814 | let definition_tt = parse_macro_to_tt(ra_fixture); | 1938 | let definition_tt = parse_macro_rules_to_tt(ra_fixture); |
1815 | 1939 | ||
1816 | match MacroRules::parse(&definition_tt) { | 1940 | match MacroRules::parse(&definition_tt) { |
1817 | Ok(_) => panic!("Expect error"), | 1941 | Ok(_) => panic!("Expect error"), |