diff options
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 344 |
1 files changed, 159 insertions, 185 deletions
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs index d7482c63d..f34ab52e1 100644 --- a/crates/ra_mbe/src/tests.rs +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -1,4 +1,4 @@ | |||
1 | use ra_syntax::{ast, AstNode, NodeOrToken}; | 1 | use ra_syntax::{ast, AstNode, NodeOrToken, WalkEvent}; |
2 | use test_utils::assert_eq_text; | 2 | use test_utils::assert_eq_text; |
3 | 3 | ||
4 | use super::*; | 4 | use super::*; |
@@ -78,18 +78,8 @@ macro_rules! impl_froms { | |||
78 | impl_froms!(TokenTree: Leaf, Subtree); | 78 | impl_froms!(TokenTree: Leaf, Subtree); |
79 | "#; | 79 | "#; |
80 | 80 | ||
81 | let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); | 81 | let rules = create_rules(macro_definition); |
82 | let macro_definition = | 82 | let expansion = expand(&rules, macro_invocation); |
83 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
84 | |||
85 | let source_file = ast::SourceFile::parse(macro_invocation).ok().unwrap(); | ||
86 | let macro_invocation = | ||
87 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
88 | |||
89 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | ||
90 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | ||
91 | let rules = crate::MacroRules::parse(&definition_tt).unwrap(); | ||
92 | let expansion = rules.expand(&invocation_tt).unwrap(); | ||
93 | assert_eq!( | 83 | assert_eq!( |
94 | expansion.to_string(), | 84 | expansion.to_string(), |
95 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | 85 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ |
@@ -97,140 +87,48 @@ impl_froms!(TokenTree: Leaf, Subtree); | |||
97 | ) | 87 | ) |
98 | } | 88 | } |
99 | 89 | ||
100 | pub(crate) fn create_rules(macro_definition: &str) -> MacroRules { | 90 | #[test] |
101 | let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); | 91 | fn test_expr_order() { |
102 | let macro_definition = | 92 | let rules = create_rules( |
103 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | 93 | r#" |
104 | 94 | macro_rules! foo { | |
105 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | 95 | ($ i:expr) => { |
106 | crate::MacroRules::parse(&definition_tt).unwrap() | 96 | fn bar() { $ i * 2; } |
107 | } | ||
108 | |||
109 | pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { | ||
110 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); | ||
111 | let macro_invocation = | ||
112 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
113 | |||
114 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | ||
115 | |||
116 | rules.expand(&invocation_tt).unwrap() | ||
117 | } | ||
118 | |||
119 | pub(crate) fn expand_to_items(rules: &MacroRules, invocation: &str) -> ast::MacroItems { | ||
120 | let expanded = expand(rules, invocation); | ||
121 | token_tree_to_items(&expanded).unwrap().tree() | ||
122 | } | ||
123 | |||
124 | #[allow(unused)] | ||
125 | pub(crate) fn expand_to_stmts(rules: &MacroRules, invocation: &str) -> ast::MacroStmts { | ||
126 | let expanded = expand(rules, invocation); | ||
127 | token_tree_to_macro_stmts(&expanded).unwrap().tree() | ||
128 | } | ||
129 | |||
130 | pub(crate) fn expand_to_expr(rules: &MacroRules, invocation: &str) -> ast::Expr { | ||
131 | let expanded = expand(rules, invocation); | ||
132 | token_tree_to_expr(&expanded).unwrap().tree() | ||
133 | } | ||
134 | |||
135 | pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree { | ||
136 | // wrap the given text to a macro call | ||
137 | let wrapped = format!("wrap_macro!( {} )", text); | ||
138 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
139 | let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
140 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
141 | wrapped.delimiter = tt::Delimiter::None; | ||
142 | |||
143 | wrapped | ||
144 | } | ||
145 | |||
146 | pub(crate) enum MacroKind { | ||
147 | Items, | ||
148 | Stmts, | ||
149 | } | ||
150 | |||
151 | use ra_syntax::WalkEvent; | ||
152 | |||
153 | pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { | ||
154 | use std::fmt::Write; | ||
155 | |||
156 | let mut level = 0; | ||
157 | let mut buf = String::new(); | ||
158 | macro_rules! indent { | ||
159 | () => { | ||
160 | for _ in 0..level { | ||
161 | buf.push_str(" "); | ||
162 | } | ||
163 | }; | ||
164 | } | ||
165 | |||
166 | for event in node.preorder_with_tokens() { | ||
167 | match event { | ||
168 | WalkEvent::Enter(element) => { | ||
169 | match element { | ||
170 | NodeOrToken::Node(node) => { | ||
171 | indent!(); | ||
172 | writeln!(buf, "{:?}", node.kind()).unwrap(); | ||
173 | } | ||
174 | NodeOrToken::Token(token) => match token.kind() { | ||
175 | ra_syntax::SyntaxKind::WHITESPACE => {} | ||
176 | _ => { | ||
177 | indent!(); | ||
178 | writeln!(buf, "{:?}", token.kind()).unwrap(); | ||
179 | } | ||
180 | }, | ||
181 | } | ||
182 | level += 1; | ||
183 | } | 97 | } |
184 | WalkEvent::Leave(_) => level -= 1, | ||
185 | } | 98 | } |
186 | } | 99 | "#, |
187 | |||
188 | buf | ||
189 | } | ||
190 | |||
191 | pub(crate) fn assert_expansion( | ||
192 | kind: MacroKind, | ||
193 | rules: &MacroRules, | ||
194 | invocation: &str, | ||
195 | expected: &str, | ||
196 | ) -> tt::Subtree { | ||
197 | let expanded = expand(rules, invocation); | ||
198 | assert_eq!(expanded.to_string(), expected); | ||
199 | |||
200 | let expected = expected.replace("$crate", "C_C__C"); | ||
201 | |||
202 | // wrap the given text to a macro call | ||
203 | let expected = text_to_tokentree(&expected); | ||
204 | let (expanded_tree, expected_tree) = match kind { | ||
205 | MacroKind::Items => { | ||
206 | let expanded_tree = token_tree_to_items(&expanded).unwrap().tree(); | ||
207 | let expected_tree = token_tree_to_items(&expected).unwrap().tree(); | ||
208 | |||
209 | ( | ||
210 | debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), | ||
211 | debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(), | ||
212 | ) | ||
213 | } | ||
214 | |||
215 | MacroKind::Stmts => { | ||
216 | let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree(); | ||
217 | let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree(); | ||
218 | |||
219 | ( | ||
220 | debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), | ||
221 | debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(), | ||
222 | ) | ||
223 | } | ||
224 | }; | ||
225 | |||
226 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | ||
227 | assert_eq!( | ||
228 | expanded_tree, expected_tree, | ||
229 | "\nleft:\n{}\nright:\n{}", | ||
230 | expanded_tree, expected_tree, | ||
231 | ); | 100 | ); |
101 | let expanded = expand(&rules, "foo! { 1 + 1}"); | ||
102 | let tree = token_tree_to_items(&expanded).unwrap().tree(); | ||
232 | 103 | ||
233 | expanded | 104 | let dump = format!("{:#?}", tree.syntax()); |
105 | assert_eq_text!( | ||
106 | dump.trim(), | ||
107 | r#"MACRO_ITEMS@[0; 15) | ||
108 | FN_DEF@[0; 15) | ||
109 | FN_KW@[0; 2) "fn" | ||
110 | NAME@[2; 5) | ||
111 | IDENT@[2; 5) "bar" | ||
112 | PARAM_LIST@[5; 7) | ||
113 | L_PAREN@[5; 6) "(" | ||
114 | R_PAREN@[6; 7) ")" | ||
115 | BLOCK_EXPR@[7; 15) | ||
116 | BLOCK@[7; 15) | ||
117 | L_CURLY@[7; 8) "{" | ||
118 | EXPR_STMT@[8; 14) | ||
119 | BIN_EXPR@[8; 13) | ||
120 | BIN_EXPR@[8; 11) | ||
121 | LITERAL@[8; 9) | ||
122 | INT_NUMBER@[8; 9) "1" | ||
123 | PLUS@[9; 10) "+" | ||
124 | LITERAL@[10; 11) | ||
125 | INT_NUMBER@[10; 11) "1" | ||
126 | STAR@[11; 12) "*" | ||
127 | LITERAL@[12; 13) | ||
128 | INT_NUMBER@[12; 13) "2" | ||
129 | SEMI@[13; 14) ";" | ||
130 | R_CURLY@[14; 15) "}""#, | ||
131 | ); | ||
234 | } | 132 | } |
235 | 133 | ||
236 | #[test] | 134 | #[test] |
@@ -705,47 +603,6 @@ fn test_expr() { | |||
705 | } | 603 | } |
706 | 604 | ||
707 | #[test] | 605 | #[test] |
708 | fn test_expr_order() { | ||
709 | let rules = create_rules( | ||
710 | r#" | ||
711 | macro_rules! foo { | ||
712 | ($ i:expr) => { | ||
713 | fn bar() { $ i * 2; } | ||
714 | } | ||
715 | } | ||
716 | "#, | ||
717 | ); | ||
718 | let dump = format!("{:#?}", expand_to_items(&rules, "foo! { 1 + 1 }").syntax()); | ||
719 | assert_eq_text!( | ||
720 | dump.trim(), | ||
721 | r#"MACRO_ITEMS@[0; 15) | ||
722 | FN_DEF@[0; 15) | ||
723 | FN_KW@[0; 2) "fn" | ||
724 | NAME@[2; 5) | ||
725 | IDENT@[2; 5) "bar" | ||
726 | PARAM_LIST@[5; 7) | ||
727 | L_PAREN@[5; 6) "(" | ||
728 | R_PAREN@[6; 7) ")" | ||
729 | BLOCK_EXPR@[7; 15) | ||
730 | BLOCK@[7; 15) | ||
731 | L_CURLY@[7; 8) "{" | ||
732 | EXPR_STMT@[8; 14) | ||
733 | BIN_EXPR@[8; 13) | ||
734 | BIN_EXPR@[8; 11) | ||
735 | LITERAL@[8; 9) | ||
736 | INT_NUMBER@[8; 9) "1" | ||
737 | PLUS@[9; 10) "+" | ||
738 | LITERAL@[10; 11) | ||
739 | INT_NUMBER@[10; 11) "1" | ||
740 | STAR@[11; 12) "*" | ||
741 | LITERAL@[12; 13) | ||
742 | INT_NUMBER@[12; 13) "2" | ||
743 | SEMI@[13; 14) ";" | ||
744 | R_CURLY@[14; 15) "}""#, | ||
745 | ); | ||
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn test_last_expr() { | 606 | fn test_last_expr() { |
750 | let rules = create_rules( | 607 | let rules = create_rules( |
751 | r#" | 608 | r#" |
@@ -1061,8 +918,11 @@ fn test_vec() { | |||
1061 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, | 918 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, |
1062 | ); | 919 | ); |
1063 | 920 | ||
921 | let expansion = expand(&rules, r#"vec![1u32,2];"#); | ||
922 | let tree = token_tree_to_expr(&expansion).unwrap().tree(); | ||
923 | |||
1064 | assert_eq!( | 924 | assert_eq!( |
1065 | format!("{:#?}", expand_to_expr(&rules, r#"vec![1u32,2];"#).syntax()).trim(), | 925 | format!("{:#?}", tree.syntax()).trim(), |
1066 | r#"BLOCK_EXPR@[0; 45) | 926 | r#"BLOCK_EXPR@[0; 45) |
1067 | BLOCK@[0; 45) | 927 | BLOCK@[0; 45) |
1068 | L_CURLY@[0; 1) "{" | 928 | L_CURLY@[0; 1) "{" |
@@ -1502,3 +1362,117 @@ macro_rules! delegate_impl { | |||
1502 | 1362 | ||
1503 | assert_expansion(MacroKind::Items, &rules, r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, "impl <> Data for & \'a mut G where G : Data {}"); | 1363 | assert_expansion(MacroKind::Items, &rules, r#"delegate_impl ! {[G , & 'a mut G , deref] pub trait Data : GraphBase {@ section type type NodeWeight ;}}"#, "impl <> Data for & \'a mut G where G : Data {}"); |
1504 | } | 1364 | } |
1365 | |||
1366 | pub(crate) fn create_rules(macro_definition: &str) -> MacroRules { | ||
1367 | let source_file = ast::SourceFile::parse(macro_definition).ok().unwrap(); | ||
1368 | let macro_definition = | ||
1369 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1370 | |||
1371 | let (definition_tt, _) = ast_to_token_tree(¯o_definition.token_tree().unwrap()).unwrap(); | ||
1372 | crate::MacroRules::parse(&definition_tt).unwrap() | ||
1373 | } | ||
1374 | |||
1375 | pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { | ||
1376 | let source_file = ast::SourceFile::parse(invocation).ok().unwrap(); | ||
1377 | let macro_invocation = | ||
1378 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
1379 | |||
1380 | let (invocation_tt, _) = ast_to_token_tree(¯o_invocation.token_tree().unwrap()).unwrap(); | ||
1381 | |||
1382 | rules.expand(&invocation_tt).unwrap() | ||
1383 | } | ||
1384 | |||
1385 | pub(crate) enum MacroKind { | ||
1386 | Items, | ||
1387 | Stmts, | ||
1388 | } | ||
1389 | |||
1390 | pub(crate) fn assert_expansion( | ||
1391 | kind: MacroKind, | ||
1392 | rules: &MacroRules, | ||
1393 | invocation: &str, | ||
1394 | expected: &str, | ||
1395 | ) -> tt::Subtree { | ||
1396 | let expanded = expand(rules, invocation); | ||
1397 | assert_eq!(expanded.to_string(), expected); | ||
1398 | |||
1399 | let expected = expected.replace("$crate", "C_C__C"); | ||
1400 | |||
1401 | // wrap the given text to a macro call | ||
1402 | let expected = { | ||
1403 | let wrapped = format!("wrap_macro!( {} )", expected); | ||
1404 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
1405 | let wrapped = wrapped.tree().syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
1406 | let mut wrapped = ast_to_token_tree(&wrapped).unwrap().0; | ||
1407 | wrapped.delimiter = tt::Delimiter::None; | ||
1408 | wrapped | ||
1409 | }; | ||
1410 | let (expanded_tree, expected_tree) = match kind { | ||
1411 | MacroKind::Items => { | ||
1412 | let expanded_tree = token_tree_to_items(&expanded).unwrap().tree(); | ||
1413 | let expected_tree = token_tree_to_items(&expected).unwrap().tree(); | ||
1414 | |||
1415 | ( | ||
1416 | debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), | ||
1417 | debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(), | ||
1418 | ) | ||
1419 | } | ||
1420 | |||
1421 | MacroKind::Stmts => { | ||
1422 | let expanded_tree = token_tree_to_macro_stmts(&expanded).unwrap().tree(); | ||
1423 | let expected_tree = token_tree_to_macro_stmts(&expected).unwrap().tree(); | ||
1424 | |||
1425 | ( | ||
1426 | debug_dump_ignore_spaces(expanded_tree.syntax()).trim().to_string(), | ||
1427 | debug_dump_ignore_spaces(expected_tree.syntax()).trim().to_string(), | ||
1428 | ) | ||
1429 | } | ||
1430 | }; | ||
1431 | |||
1432 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | ||
1433 | assert_eq!( | ||
1434 | expanded_tree, expected_tree, | ||
1435 | "\nleft:\n{}\nright:\n{}", | ||
1436 | expanded_tree, expected_tree, | ||
1437 | ); | ||
1438 | |||
1439 | expanded | ||
1440 | } | ||
1441 | |||
1442 | pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { | ||
1443 | use std::fmt::Write; | ||
1444 | |||
1445 | let mut level = 0; | ||
1446 | let mut buf = String::new(); | ||
1447 | macro_rules! indent { | ||
1448 | () => { | ||
1449 | for _ in 0..level { | ||
1450 | buf.push_str(" "); | ||
1451 | } | ||
1452 | }; | ||
1453 | } | ||
1454 | |||
1455 | for event in node.preorder_with_tokens() { | ||
1456 | match event { | ||
1457 | WalkEvent::Enter(element) => { | ||
1458 | match element { | ||
1459 | NodeOrToken::Node(node) => { | ||
1460 | indent!(); | ||
1461 | writeln!(buf, "{:?}", node.kind()).unwrap(); | ||
1462 | } | ||
1463 | NodeOrToken::Token(token) => match token.kind() { | ||
1464 | ra_syntax::SyntaxKind::WHITESPACE => {} | ||
1465 | _ => { | ||
1466 | indent!(); | ||
1467 | writeln!(buf, "{:?}", token.kind()).unwrap(); | ||
1468 | } | ||
1469 | }, | ||
1470 | } | ||
1471 | level += 1; | ||
1472 | } | ||
1473 | WalkEvent::Leave(_) => level -= 1, | ||
1474 | } | ||
1475 | } | ||
1476 | |||
1477 | buf | ||
1478 | } | ||