diff options
-rw-r--r-- | crates/ra_mbe/src/lib.rs | 1138 | ||||
-rw-r--r-- | crates/ra_mbe/src/mbe_expander.rs | 50 | ||||
-rw-r--r-- | crates/ra_mbe/src/tests.rs | 1363 |
3 files changed, 1409 insertions, 1142 deletions
diff --git a/crates/ra_mbe/src/lib.rs b/crates/ra_mbe/src/lib.rs index ea2104b1c..c146252a4 100644 --- a/crates/ra_mbe/src/lib.rs +++ b/crates/ra_mbe/src/lib.rs | |||
@@ -15,7 +15,6 @@ macro_rules! impl_froms { | |||
15 | } | 15 | } |
16 | } | 16 | } |
17 | 17 | ||
18 | // mod tt_cursor; | ||
19 | mod mbe_parser; | 18 | mod mbe_parser; |
20 | mod mbe_expander; | 19 | mod mbe_expander; |
21 | mod syntax_bridge; | 20 | mod syntax_bridge; |
@@ -155,1139 +154,4 @@ pub(crate) struct Var { | |||
155 | } | 154 | } |
156 | 155 | ||
157 | #[cfg(test)] | 156 | #[cfg(test)] |
158 | mod tests { | 157 | mod tests; |
159 | use ra_syntax::{ast, AstNode}; | ||
160 | |||
161 | use super::*; | ||
162 | |||
163 | // Good first issue (although a slightly challenging one): | ||
164 | // | ||
165 | // * Pick a random test from here | ||
166 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
167 | // * Port the test to rust and add it to this module | ||
168 | // * Make it pass :-) | ||
169 | |||
170 | #[test] | ||
171 | fn test_convert_tt() { | ||
172 | let macro_definition = r#" | ||
173 | macro_rules! impl_froms { | ||
174 | ($e:ident: $($v:ident),*) => { | ||
175 | $( | ||
176 | impl From<$v> for $e { | ||
177 | fn from(it: $v) -> $e { | ||
178 | $e::$v(it) | ||
179 | } | ||
180 | } | ||
181 | )* | ||
182 | } | ||
183 | } | ||
184 | "#; | ||
185 | |||
186 | let macro_invocation = r#" | ||
187 | impl_froms!(TokenTree: Leaf, Subtree); | ||
188 | "#; | ||
189 | |||
190 | let source_file = ast::SourceFile::parse(macro_definition); | ||
191 | let macro_definition = | ||
192 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
193 | |||
194 | let source_file = ast::SourceFile::parse(macro_invocation); | ||
195 | let macro_invocation = | ||
196 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
197 | |||
198 | let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap(); | ||
199 | let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap(); | ||
200 | let rules = crate::MacroRules::parse(&definition_tt).unwrap(); | ||
201 | let expansion = rules.expand(&invocation_tt).unwrap(); | ||
202 | assert_eq!( | ||
203 | expansion.to_string(), | ||
204 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | ||
205 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" | ||
206 | ) | ||
207 | } | ||
208 | |||
209 | pub(crate) fn create_rules(macro_definition: &str) -> MacroRules { | ||
210 | let source_file = ast::SourceFile::parse(macro_definition); | ||
211 | let macro_definition = | ||
212 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
213 | |||
214 | let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap(); | ||
215 | crate::MacroRules::parse(&definition_tt).unwrap() | ||
216 | } | ||
217 | |||
218 | pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { | ||
219 | let source_file = ast::SourceFile::parse(invocation); | ||
220 | let macro_invocation = | ||
221 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
222 | |||
223 | let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap(); | ||
224 | |||
225 | rules.expand(&invocation_tt).unwrap() | ||
226 | } | ||
227 | |||
228 | pub(crate) fn expand_to_items( | ||
229 | rules: &MacroRules, | ||
230 | invocation: &str, | ||
231 | ) -> ra_syntax::TreeArc<ast::MacroItems> { | ||
232 | let expanded = expand(rules, invocation); | ||
233 | token_tree_to_macro_items(&expanded).unwrap() | ||
234 | } | ||
235 | |||
236 | #[allow(unused)] | ||
237 | pub(crate) fn expand_to_stmts( | ||
238 | rules: &MacroRules, | ||
239 | invocation: &str, | ||
240 | ) -> ra_syntax::TreeArc<ast::MacroStmts> { | ||
241 | let expanded = expand(rules, invocation); | ||
242 | token_tree_to_macro_stmts(&expanded).unwrap() | ||
243 | } | ||
244 | |||
245 | pub(crate) fn expand_to_expr( | ||
246 | rules: &MacroRules, | ||
247 | invocation: &str, | ||
248 | ) -> ra_syntax::TreeArc<ast::Expr> { | ||
249 | let expanded = expand(rules, invocation); | ||
250 | token_tree_to_expr(&expanded).unwrap() | ||
251 | } | ||
252 | |||
253 | pub(crate) fn assert_expansion( | ||
254 | rules: &MacroRules, | ||
255 | invocation: &str, | ||
256 | expansion: &str, | ||
257 | ) -> tt::Subtree { | ||
258 | let expanded = expand(rules, invocation); | ||
259 | assert_eq!(expanded.to_string(), expansion); | ||
260 | |||
261 | // FIXME: Temp comment below code | ||
262 | // It is because after the lexer change, | ||
263 | // The SyntaxNode structure cannot be matched easily | ||
264 | |||
265 | // let tree = token_tree_to_macro_items(&expanded); | ||
266 | |||
267 | // // Eat all white space by parse it back and forth | ||
268 | // // Because $crate will seperate in two token , will do some special treatment here | ||
269 | // let expansion = expansion.replace("$crate", "C_C__C"); | ||
270 | // let expansion = ast::SourceFile::parse(&expansion); | ||
271 | // let expansion = syntax_node_to_token_tree(expansion.syntax()).unwrap().0; | ||
272 | // let file = token_tree_to_macro_items(&expansion); | ||
273 | // let file = file.unwrap().syntax().debug_dump().trim().to_string(); | ||
274 | // let tree = tree.unwrap().syntax().debug_dump().trim().to_string(); | ||
275 | |||
276 | // let file = file.replace("C_C__C", "$crate"); | ||
277 | // assert_eq!(tree, file,); | ||
278 | |||
279 | expanded | ||
280 | } | ||
281 | |||
282 | #[test] | ||
283 | fn test_fail_match_pattern_by_first_token() { | ||
284 | let rules = create_rules( | ||
285 | r#" | ||
286 | macro_rules! foo { | ||
287 | ($ i:ident) => ( | ||
288 | mod $ i {} | ||
289 | ); | ||
290 | (= $ i:ident) => ( | ||
291 | fn $ i() {} | ||
292 | ); | ||
293 | (+ $ i:ident) => ( | ||
294 | struct $ i; | ||
295 | ) | ||
296 | } | ||
297 | "#, | ||
298 | ); | ||
299 | |||
300 | assert_expansion(&rules, "foo! { foo }", "mod foo {}"); | ||
301 | assert_expansion(&rules, "foo! { = bar }", "fn bar () {}"); | ||
302 | assert_expansion(&rules, "foo! { + Baz }", "struct Baz ;"); | ||
303 | } | ||
304 | |||
305 | #[test] | ||
306 | fn test_fail_match_pattern_by_last_token() { | ||
307 | let rules = create_rules( | ||
308 | r#" | ||
309 | macro_rules! foo { | ||
310 | ($ i:ident) => ( | ||
311 | mod $ i {} | ||
312 | ); | ||
313 | ($ i:ident =) => ( | ||
314 | fn $ i() {} | ||
315 | ); | ||
316 | ($ i:ident +) => ( | ||
317 | struct $ i; | ||
318 | ) | ||
319 | } | ||
320 | "#, | ||
321 | ); | ||
322 | |||
323 | assert_expansion(&rules, "foo! { foo }", "mod foo {}"); | ||
324 | assert_expansion(&rules, "foo! { bar = }", "fn bar () {}"); | ||
325 | assert_expansion(&rules, "foo! { Baz + }", "struct Baz ;"); | ||
326 | } | ||
327 | |||
328 | #[test] | ||
329 | fn test_fail_match_pattern_by_word_token() { | ||
330 | let rules = create_rules( | ||
331 | r#" | ||
332 | macro_rules! foo { | ||
333 | ($ i:ident) => ( | ||
334 | mod $ i {} | ||
335 | ); | ||
336 | (spam $ i:ident) => ( | ||
337 | fn $ i() {} | ||
338 | ); | ||
339 | (eggs $ i:ident) => ( | ||
340 | struct $ i; | ||
341 | ) | ||
342 | } | ||
343 | "#, | ||
344 | ); | ||
345 | |||
346 | assert_expansion(&rules, "foo! { foo }", "mod foo {}"); | ||
347 | assert_expansion(&rules, "foo! { spam bar }", "fn bar () {}"); | ||
348 | assert_expansion(&rules, "foo! { eggs Baz }", "struct Baz ;"); | ||
349 | } | ||
350 | |||
351 | #[test] | ||
352 | fn test_match_group_pattern_by_separator_token() { | ||
353 | let rules = create_rules( | ||
354 | r#" | ||
355 | macro_rules! foo { | ||
356 | ($ ($ i:ident),*) => ($ ( | ||
357 | mod $ i {} | ||
358 | )*); | ||
359 | ($ ($ i:ident)#*) => ($ ( | ||
360 | fn $ i() {} | ||
361 | )*); | ||
362 | ($ i:ident ,# $ j:ident) => ( | ||
363 | struct $ i; | ||
364 | struct $ j; | ||
365 | ) | ||
366 | } | ||
367 | "#, | ||
368 | ); | ||
369 | |||
370 | assert_expansion(&rules, "foo! { foo, bar }", "mod foo {} mod bar {}"); | ||
371 | assert_expansion(&rules, "foo! { foo# bar }", "fn foo () {} fn bar () {}"); | ||
372 | assert_expansion(&rules, "foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); | ||
373 | } | ||
374 | |||
375 | #[test] | ||
376 | fn test_match_group_pattern_with_multiple_defs() { | ||
377 | let rules = create_rules( | ||
378 | r#" | ||
379 | macro_rules! foo { | ||
380 | ($ ($ i:ident),*) => ( struct Bar { $ ( | ||
381 | fn $ i {} | ||
382 | )*} ); | ||
383 | } | ||
384 | "#, | ||
385 | ); | ||
386 | |||
387 | assert_expansion(&rules, "foo! { foo, bar }", "struct Bar {fn foo {} fn bar {}}"); | ||
388 | } | ||
389 | |||
390 | #[test] | ||
391 | fn test_match_group_pattern_with_multiple_statement() { | ||
392 | let rules = create_rules( | ||
393 | r#" | ||
394 | macro_rules! foo { | ||
395 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
396 | $ i (); | ||
397 | )*} ); | ||
398 | } | ||
399 | "#, | ||
400 | ); | ||
401 | |||
402 | assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | ||
403 | } | ||
404 | |||
405 | #[test] | ||
406 | fn test_match_group_pattern_with_multiple_statement_without_semi() { | ||
407 | let rules = create_rules( | ||
408 | r#" | ||
409 | macro_rules! foo { | ||
410 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
411 | $i() | ||
412 | );*} ); | ||
413 | } | ||
414 | "#, | ||
415 | ); | ||
416 | |||
417 | assert_expansion(&rules, "foo! { foo, bar }", "fn baz {foo () ;bar ()}"); | ||
418 | } | ||
419 | |||
420 | #[test] | ||
421 | fn test_match_group_empty_fixed_token() { | ||
422 | let rules = create_rules( | ||
423 | r#" | ||
424 | macro_rules! foo { | ||
425 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
426 | $ i (); | ||
427 | )*} ); | ||
428 | } | ||
429 | "#, | ||
430 | ); | ||
431 | |||
432 | assert_expansion(&rules, "foo! {#abc}", "fn baz {}"); | ||
433 | } | ||
434 | |||
435 | #[test] | ||
436 | fn test_match_group_in_subtree() { | ||
437 | let rules = create_rules( | ||
438 | r#" | ||
439 | macro_rules! foo { | ||
440 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
441 | $ i (); | ||
442 | )*} ); | ||
443 | }"#, | ||
444 | ); | ||
445 | |||
446 | assert_expansion(&rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
447 | } | ||
448 | |||
449 | #[test] | ||
450 | fn test_match_group_with_multichar_sep() { | ||
451 | let rules = create_rules( | ||
452 | r#" | ||
453 | macro_rules! foo { | ||
454 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
455 | }"#, | ||
456 | ); | ||
457 | |||
458 | assert_expansion(&rules, "foo! (fn baz {true true} )", "fn baz () -> bool {true &&true}"); | ||
459 | } | ||
460 | |||
461 | #[test] | ||
462 | fn test_match_group_zero_match() { | ||
463 | let rules = create_rules( | ||
464 | r#" | ||
465 | macro_rules! foo { | ||
466 | ( $($i:ident)* ) => (); | ||
467 | }"#, | ||
468 | ); | ||
469 | |||
470 | assert_expansion(&rules, "foo! ()", ""); | ||
471 | } | ||
472 | |||
473 | #[test] | ||
474 | fn test_match_group_in_group() { | ||
475 | let rules = create_rules( | ||
476 | r#" | ||
477 | macro_rules! foo { | ||
478 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
479 | }"#, | ||
480 | ); | ||
481 | |||
482 | assert_expansion(&rules, "foo! ( (a b) )", "(a b)"); | ||
483 | } | ||
484 | |||
485 | #[test] | ||
486 | fn test_expand_to_item_list() { | ||
487 | let rules = create_rules( | ||
488 | " | ||
489 | macro_rules! structs { | ||
490 | ($($i:ident),*) => { | ||
491 | $(struct $i { field: u32 } )* | ||
492 | } | ||
493 | } | ||
494 | ", | ||
495 | ); | ||
496 | let expansion = expand(&rules, "structs!(Foo, Bar)"); | ||
497 | let tree = token_tree_to_macro_items(&expansion); | ||
498 | assert_eq!( | ||
499 | tree.unwrap().syntax().debug_dump().trim(), | ||
500 | r#" | ||
501 | MACRO_ITEMS@[0; 40) | ||
502 | STRUCT_DEF@[0; 20) | ||
503 | STRUCT_KW@[0; 6) "struct" | ||
504 | NAME@[6; 9) | ||
505 | IDENT@[6; 9) "Foo" | ||
506 | NAMED_FIELD_DEF_LIST@[9; 20) | ||
507 | L_CURLY@[9; 10) "{" | ||
508 | NAMED_FIELD_DEF@[10; 19) | ||
509 | NAME@[10; 15) | ||
510 | IDENT@[10; 15) "field" | ||
511 | COLON@[15; 16) ":" | ||
512 | PATH_TYPE@[16; 19) | ||
513 | PATH@[16; 19) | ||
514 | PATH_SEGMENT@[16; 19) | ||
515 | NAME_REF@[16; 19) | ||
516 | IDENT@[16; 19) "u32" | ||
517 | R_CURLY@[19; 20) "}" | ||
518 | STRUCT_DEF@[20; 40) | ||
519 | STRUCT_KW@[20; 26) "struct" | ||
520 | NAME@[26; 29) | ||
521 | IDENT@[26; 29) "Bar" | ||
522 | NAMED_FIELD_DEF_LIST@[29; 40) | ||
523 | L_CURLY@[29; 30) "{" | ||
524 | NAMED_FIELD_DEF@[30; 39) | ||
525 | NAME@[30; 35) | ||
526 | IDENT@[30; 35) "field" | ||
527 | COLON@[35; 36) ":" | ||
528 | PATH_TYPE@[36; 39) | ||
529 | PATH@[36; 39) | ||
530 | PATH_SEGMENT@[36; 39) | ||
531 | NAME_REF@[36; 39) | ||
532 | IDENT@[36; 39) "u32" | ||
533 | R_CURLY@[39; 40) "}""# | ||
534 | .trim() | ||
535 | ); | ||
536 | } | ||
537 | |||
538 | #[test] | ||
539 | fn test_expand_literals_to_token_tree() { | ||
540 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | ||
541 | if let tt::TokenTree::Subtree(subtree) = tt { | ||
542 | return &subtree; | ||
543 | } | ||
544 | unreachable!("It is not a subtree"); | ||
545 | } | ||
546 | |||
547 | fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { | ||
548 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { | ||
549 | return lit; | ||
550 | } | ||
551 | unreachable!("It is not a literal"); | ||
552 | } | ||
553 | |||
554 | let rules = create_rules( | ||
555 | r#" | ||
556 | macro_rules! literals { | ||
557 | ($i:ident) => { | ||
558 | { | ||
559 | let a = 'c'; | ||
560 | let c = 1000; | ||
561 | let f = 12E+99_f64; | ||
562 | let s = "rust1"; | ||
563 | } | ||
564 | } | ||
565 | } | ||
566 | "#, | ||
567 | ); | ||
568 | let expansion = expand(&rules, "literals!(foo)"); | ||
569 | let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; | ||
570 | |||
571 | // [let] [a] [=] ['c'] [;] | ||
572 | assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); | ||
573 | // [let] [c] [=] [1000] [;] | ||
574 | assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); | ||
575 | // [let] [f] [=] [12E+99_f64] [;] | ||
576 | assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); | ||
577 | // [let] [s] [=] ["rust1"] [;] | ||
578 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | ||
579 | } | ||
580 | |||
581 | #[test] | ||
582 | fn test_two_idents() { | ||
583 | let rules = create_rules( | ||
584 | r#" | ||
585 | macro_rules! foo { | ||
586 | ($ i:ident, $ j:ident) => { | ||
587 | fn foo() { let a = $ i; let b = $j; } | ||
588 | } | ||
589 | } | ||
590 | "#, | ||
591 | ); | ||
592 | assert_expansion(&rules, "foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
593 | } | ||
594 | |||
595 | #[test] | ||
596 | fn test_tt_to_stmts() { | ||
597 | let rules = create_rules( | ||
598 | r#" | ||
599 | macro_rules! foo { | ||
600 | () => { | ||
601 | let a = 0; | ||
602 | a = 10 + 1; | ||
603 | a | ||
604 | } | ||
605 | } | ||
606 | "#, | ||
607 | ); | ||
608 | |||
609 | let expanded = expand(&rules, "foo!{}"); | ||
610 | let stmts = token_tree_to_macro_stmts(&expanded); | ||
611 | |||
612 | assert_eq!( | ||
613 | stmts.unwrap().syntax().debug_dump().trim(), | ||
614 | r#"MACRO_STMTS@[0; 15) | ||
615 | LET_STMT@[0; 7) | ||
616 | LET_KW@[0; 3) "let" | ||
617 | BIND_PAT@[3; 4) | ||
618 | NAME@[3; 4) | ||
619 | IDENT@[3; 4) "a" | ||
620 | EQ@[4; 5) "=" | ||
621 | LITERAL@[5; 6) | ||
622 | INT_NUMBER@[5; 6) "0" | ||
623 | SEMI@[6; 7) ";" | ||
624 | EXPR_STMT@[7; 14) | ||
625 | BIN_EXPR@[7; 13) | ||
626 | PATH_EXPR@[7; 8) | ||
627 | PATH@[7; 8) | ||
628 | PATH_SEGMENT@[7; 8) | ||
629 | NAME_REF@[7; 8) | ||
630 | IDENT@[7; 8) "a" | ||
631 | EQ@[8; 9) "=" | ||
632 | BIN_EXPR@[9; 13) | ||
633 | LITERAL@[9; 11) | ||
634 | INT_NUMBER@[9; 11) "10" | ||
635 | PLUS@[11; 12) "+" | ||
636 | LITERAL@[12; 13) | ||
637 | INT_NUMBER@[12; 13) "1" | ||
638 | SEMI@[13; 14) ";" | ||
639 | EXPR_STMT@[14; 15) | ||
640 | PATH_EXPR@[14; 15) | ||
641 | PATH@[14; 15) | ||
642 | PATH_SEGMENT@[14; 15) | ||
643 | NAME_REF@[14; 15) | ||
644 | IDENT@[14; 15) "a""#, | ||
645 | ); | ||
646 | } | ||
647 | |||
648 | // The following tests are port from intellij-rust directly | ||
649 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
650 | |||
651 | #[test] | ||
652 | fn test_path() { | ||
653 | let rules = create_rules( | ||
654 | r#" | ||
655 | macro_rules! foo { | ||
656 | ($ i:path) => { | ||
657 | fn foo() { let a = $ i; } | ||
658 | } | ||
659 | } | ||
660 | "#, | ||
661 | ); | ||
662 | assert_expansion(&rules, "foo! { foo }", "fn foo () {let a = foo ;}"); | ||
663 | assert_expansion( | ||
664 | &rules, | ||
665 | "foo! { bar::<u8>::baz::<u8> }", | ||
666 | "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", | ||
667 | ); | ||
668 | } | ||
669 | |||
670 | #[test] | ||
671 | fn test_two_paths() { | ||
672 | let rules = create_rules( | ||
673 | r#" | ||
674 | macro_rules! foo { | ||
675 | ($ i:path, $ j:path) => { | ||
676 | fn foo() { let a = $ i; let b = $j; } | ||
677 | } | ||
678 | } | ||
679 | "#, | ||
680 | ); | ||
681 | assert_expansion(&rules, "foo! { foo, bar }", "fn foo () {let a = foo ; let b = bar ;}"); | ||
682 | } | ||
683 | |||
684 | #[test] | ||
685 | fn test_path_with_path() { | ||
686 | let rules = create_rules( | ||
687 | r#" | ||
688 | macro_rules! foo { | ||
689 | ($ i:path) => { | ||
690 | fn foo() { let a = $ i :: bar; } | ||
691 | } | ||
692 | } | ||
693 | "#, | ||
694 | ); | ||
695 | assert_expansion(&rules, "foo! { foo }", "fn foo () {let a = foo :: bar ;}"); | ||
696 | } | ||
697 | |||
698 | #[test] | ||
699 | fn test_expr() { | ||
700 | let rules = create_rules( | ||
701 | r#" | ||
702 | macro_rules! foo { | ||
703 | ($ i:expr) => { | ||
704 | fn bar() { $ i; } | ||
705 | } | ||
706 | } | ||
707 | "#, | ||
708 | ); | ||
709 | |||
710 | assert_expansion( | ||
711 | &rules, | ||
712 | "foo! { 2 + 2 * baz(3).quux() }", | ||
713 | "fn bar () {2 + 2 * baz (3) . quux () ;}", | ||
714 | ); | ||
715 | } | ||
716 | |||
717 | #[test] | ||
718 | fn test_expr_order() { | ||
719 | let rules = create_rules( | ||
720 | r#" | ||
721 | macro_rules! foo { | ||
722 | ($ i:expr) => { | ||
723 | fn bar() { $ i * 2; } | ||
724 | } | ||
725 | } | ||
726 | "#, | ||
727 | ); | ||
728 | |||
729 | assert_eq!( | ||
730 | expand_to_items(&rules, "foo! { 1 + 1 }").syntax().debug_dump().trim(), | ||
731 | r#"MACRO_ITEMS@[0; 15) | ||
732 | FN_DEF@[0; 15) | ||
733 | FN_KW@[0; 2) "fn" | ||
734 | NAME@[2; 5) | ||
735 | IDENT@[2; 5) "bar" | ||
736 | PARAM_LIST@[5; 7) | ||
737 | L_PAREN@[5; 6) "(" | ||
738 | R_PAREN@[6; 7) ")" | ||
739 | BLOCK@[7; 15) | ||
740 | L_CURLY@[7; 8) "{" | ||
741 | EXPR_STMT@[8; 14) | ||
742 | BIN_EXPR@[8; 13) | ||
743 | BIN_EXPR@[8; 11) | ||
744 | LITERAL@[8; 9) | ||
745 | INT_NUMBER@[8; 9) "1" | ||
746 | PLUS@[9; 10) "+" | ||
747 | LITERAL@[10; 11) | ||
748 | INT_NUMBER@[10; 11) "1" | ||
749 | STAR@[11; 12) "*" | ||
750 | LITERAL@[12; 13) | ||
751 | INT_NUMBER@[12; 13) "2" | ||
752 | SEMI@[13; 14) ";" | ||
753 | R_CURLY@[14; 15) "}""#, | ||
754 | ); | ||
755 | } | ||
756 | |||
757 | #[test] | ||
758 | fn test_last_expr() { | ||
759 | let rules = create_rules( | ||
760 | r#" | ||
761 | macro_rules! vec { | ||
762 | ($($item:expr),*) => { | ||
763 | { | ||
764 | let mut v = Vec::new(); | ||
765 | $( | ||
766 | v.push($item); | ||
767 | )* | ||
768 | v | ||
769 | } | ||
770 | }; | ||
771 | } | ||
772 | "#, | ||
773 | ); | ||
774 | assert_expansion( | ||
775 | &rules, | ||
776 | "vec!(1,2,3)", | ||
777 | "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", | ||
778 | ); | ||
779 | } | ||
780 | |||
781 | #[test] | ||
782 | fn test_ty() { | ||
783 | let rules = create_rules( | ||
784 | r#" | ||
785 | macro_rules! foo { | ||
786 | ($ i:ty) => ( | ||
787 | fn bar() -> $ i { unimplemented!() } | ||
788 | ) | ||
789 | } | ||
790 | "#, | ||
791 | ); | ||
792 | assert_expansion( | ||
793 | &rules, | ||
794 | "foo! { Baz<u8> }", | ||
795 | "fn bar () -> Baz < u8 > {unimplemented ! ()}", | ||
796 | ); | ||
797 | } | ||
798 | |||
799 | #[test] | ||
800 | fn test_ty_with_complex_type() { | ||
801 | let rules = create_rules( | ||
802 | r#" | ||
803 | macro_rules! foo { | ||
804 | ($ i:ty) => ( | ||
805 | fn bar() -> $ i { unimplemented!() } | ||
806 | ) | ||
807 | } | ||
808 | "#, | ||
809 | ); | ||
810 | |||
811 | // Reference lifetime struct with generic type | ||
812 | assert_expansion( | ||
813 | &rules, | ||
814 | "foo! { &'a Baz<u8> }", | ||
815 | "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", | ||
816 | ); | ||
817 | |||
818 | // extern "Rust" func type | ||
819 | assert_expansion( | ||
820 | &rules, | ||
821 | r#"foo! { extern "Rust" fn() -> Ret }"#, | ||
822 | r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, | ||
823 | ); | ||
824 | } | ||
825 | |||
826 | #[test] | ||
827 | fn test_pat_() { | ||
828 | let rules = create_rules( | ||
829 | r#" | ||
830 | macro_rules! foo { | ||
831 | ($ i:pat) => { fn foo() { let $ i; } } | ||
832 | } | ||
833 | "#, | ||
834 | ); | ||
835 | assert_expansion(&rules, "foo! { (a, b) }", "fn foo () {let (a , b) ;}"); | ||
836 | } | ||
837 | |||
838 | #[test] | ||
839 | fn test_stmt() { | ||
840 | let rules = create_rules( | ||
841 | r#" | ||
842 | macro_rules! foo { | ||
843 | ($ i:stmt) => ( | ||
844 | fn bar() { $ i; } | ||
845 | ) | ||
846 | } | ||
847 | "#, | ||
848 | ); | ||
849 | assert_expansion(&rules, "foo! { 2 }", "fn bar () {2 ;}"); | ||
850 | assert_expansion(&rules, "foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); | ||
851 | } | ||
852 | |||
853 | #[test] | ||
854 | fn test_single_item() { | ||
855 | let rules = create_rules( | ||
856 | r#" | ||
857 | macro_rules! foo { | ||
858 | ($ i:item) => ( | ||
859 | $ i | ||
860 | ) | ||
861 | } | ||
862 | "#, | ||
863 | ); | ||
864 | assert_expansion(&rules, "foo! {mod c {}}", "mod c {}"); | ||
865 | } | ||
866 | |||
867 | #[test] | ||
868 | fn test_all_items() { | ||
869 | let rules = create_rules( | ||
870 | r#" | ||
871 | macro_rules! foo { | ||
872 | ($ ($ i:item)*) => ($ ( | ||
873 | $ i | ||
874 | )*) | ||
875 | } | ||
876 | "#, | ||
877 | ); | ||
878 | assert_expansion(&rules, r#" | ||
879 | foo! { | ||
880 | extern crate a; | ||
881 | mod b; | ||
882 | mod c {} | ||
883 | use d; | ||
884 | const E: i32 = 0; | ||
885 | static F: i32 = 0; | ||
886 | impl G {} | ||
887 | struct H; | ||
888 | enum I { Foo } | ||
889 | trait J {} | ||
890 | fn h() {} | ||
891 | extern {} | ||
892 | type T = u8; | ||
893 | } | ||
894 | "#, 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 ;"#); | ||
895 | } | ||
896 | |||
897 | #[test] | ||
898 | fn test_block() { | ||
899 | let rules = create_rules( | ||
900 | r#" | ||
901 | macro_rules! foo { | ||
902 | ($ i:block) => { fn foo() $ i } | ||
903 | } | ||
904 | "#, | ||
905 | ); | ||
906 | assert_expansion(&rules, "foo! { { 1; } }", "fn foo () {1 ;}"); | ||
907 | } | ||
908 | |||
909 | #[test] | ||
910 | fn test_meta() { | ||
911 | let rules = create_rules( | ||
912 | r#" | ||
913 | macro_rules! foo { | ||
914 | ($ i:meta) => ( | ||
915 | #[$ i] | ||
916 | fn bar() {} | ||
917 | ) | ||
918 | } | ||
919 | "#, | ||
920 | ); | ||
921 | assert_expansion( | ||
922 | &rules, | ||
923 | r#"foo! { cfg(target_os = "windows") }"#, | ||
924 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, | ||
925 | ); | ||
926 | } | ||
927 | |||
928 | #[test] | ||
929 | // fn test_tt_block() { | ||
930 | // let rules = create_rules( | ||
931 | // r#" | ||
932 | // macro_rules! foo { | ||
933 | // ($ i:tt) => { fn foo() $ i } | ||
934 | // } | ||
935 | // "#, | ||
936 | // ); | ||
937 | // assert_expansion(&rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); | ||
938 | // } | ||
939 | |||
940 | // #[test] | ||
941 | // fn test_tt_group() { | ||
942 | // let rules = create_rules( | ||
943 | // r#" | ||
944 | // macro_rules! foo { | ||
945 | // ($($ i:tt)*) => { $($ i)* } | ||
946 | // } | ||
947 | // "#, | ||
948 | // ); | ||
949 | // assert_expansion(&rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); | ||
950 | // } | ||
951 | #[test] | ||
952 | fn test_lifetime() { | ||
953 | let rules = create_rules( | ||
954 | r#" | ||
955 | macro_rules! foo { | ||
956 | ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } | ||
957 | } | ||
958 | "#, | ||
959 | ); | ||
960 | assert_expansion(&rules, r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); | ||
961 | } | ||
962 | |||
963 | #[test] | ||
964 | fn test_literal() { | ||
965 | let rules = create_rules( | ||
966 | r#" | ||
967 | macro_rules! foo { | ||
968 | ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
969 | } | ||
970 | "#, | ||
971 | ); | ||
972 | assert_expansion(&rules, r#"foo!(u8 0)"#, r#"const VALUE : u8 = 0 ;"#); | ||
973 | } | ||
974 | |||
975 | #[test] | ||
976 | fn test_vis() { | ||
977 | let rules = create_rules( | ||
978 | r#" | ||
979 | macro_rules! foo { | ||
980 | ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; | ||
981 | } | ||
982 | "#, | ||
983 | ); | ||
984 | assert_expansion(&rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); | ||
985 | } | ||
986 | |||
987 | // The following tests are based on real world situations | ||
988 | #[test] | ||
989 | fn test_vec() { | ||
990 | let rules = create_rules( | ||
991 | r#" | ||
992 | macro_rules! vec { | ||
993 | ($($item:expr),*) => { | ||
994 | { | ||
995 | let mut v = Vec::new(); | ||
996 | $( | ||
997 | v.push($item); | ||
998 | )* | ||
999 | v | ||
1000 | } | ||
1001 | }; | ||
1002 | } | ||
1003 | "#, | ||
1004 | ); | ||
1005 | assert_expansion(&rules, r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#); | ||
1006 | assert_expansion( | ||
1007 | &rules, | ||
1008 | r#"vec![1u32,2]"#, | ||
1009 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, | ||
1010 | ); | ||
1011 | |||
1012 | assert_eq!( | ||
1013 | expand_to_expr(&rules, r#"vec![1u32,2]"#).syntax().debug_dump().trim(), | ||
1014 | r#"BLOCK_EXPR@[0; 45) | ||
1015 | BLOCK@[0; 45) | ||
1016 | L_CURLY@[0; 1) "{" | ||
1017 | LET_STMT@[1; 20) | ||
1018 | LET_KW@[1; 4) "let" | ||
1019 | BIND_PAT@[4; 8) | ||
1020 | MUT_KW@[4; 7) "mut" | ||
1021 | NAME@[7; 8) | ||
1022 | IDENT@[7; 8) "v" | ||
1023 | EQ@[8; 9) "=" | ||
1024 | CALL_EXPR@[9; 19) | ||
1025 | PATH_EXPR@[9; 17) | ||
1026 | PATH@[9; 17) | ||
1027 | PATH@[9; 12) | ||
1028 | PATH_SEGMENT@[9; 12) | ||
1029 | NAME_REF@[9; 12) | ||
1030 | IDENT@[9; 12) "Vec" | ||
1031 | COLONCOLON@[12; 14) "::" | ||
1032 | PATH_SEGMENT@[14; 17) | ||
1033 | NAME_REF@[14; 17) | ||
1034 | IDENT@[14; 17) "new" | ||
1035 | ARG_LIST@[17; 19) | ||
1036 | L_PAREN@[17; 18) "(" | ||
1037 | R_PAREN@[18; 19) ")" | ||
1038 | SEMI@[19; 20) ";" | ||
1039 | EXPR_STMT@[20; 33) | ||
1040 | METHOD_CALL_EXPR@[20; 32) | ||
1041 | PATH_EXPR@[20; 21) | ||
1042 | PATH@[20; 21) | ||
1043 | PATH_SEGMENT@[20; 21) | ||
1044 | NAME_REF@[20; 21) | ||
1045 | IDENT@[20; 21) "v" | ||
1046 | DOT@[21; 22) "." | ||
1047 | NAME_REF@[22; 26) | ||
1048 | IDENT@[22; 26) "push" | ||
1049 | ARG_LIST@[26; 32) | ||
1050 | L_PAREN@[26; 27) "(" | ||
1051 | LITERAL@[27; 31) | ||
1052 | INT_NUMBER@[27; 31) "1u32" | ||
1053 | R_PAREN@[31; 32) ")" | ||
1054 | SEMI@[32; 33) ";" | ||
1055 | EXPR_STMT@[33; 43) | ||
1056 | METHOD_CALL_EXPR@[33; 42) | ||
1057 | PATH_EXPR@[33; 34) | ||
1058 | PATH@[33; 34) | ||
1059 | PATH_SEGMENT@[33; 34) | ||
1060 | NAME_REF@[33; 34) | ||
1061 | IDENT@[33; 34) "v" | ||
1062 | DOT@[34; 35) "." | ||
1063 | NAME_REF@[35; 39) | ||
1064 | IDENT@[35; 39) "push" | ||
1065 | ARG_LIST@[39; 42) | ||
1066 | L_PAREN@[39; 40) "(" | ||
1067 | LITERAL@[40; 41) | ||
1068 | INT_NUMBER@[40; 41) "2" | ||
1069 | R_PAREN@[41; 42) ")" | ||
1070 | SEMI@[42; 43) ";" | ||
1071 | PATH_EXPR@[43; 44) | ||
1072 | PATH@[43; 44) | ||
1073 | PATH_SEGMENT@[43; 44) | ||
1074 | NAME_REF@[43; 44) | ||
1075 | IDENT@[43; 44) "v" | ||
1076 | R_CURLY@[44; 45) "}""# | ||
1077 | ); | ||
1078 | } | ||
1079 | |||
1080 | #[test] | ||
1081 | fn test_winapi_struct() { | ||
1082 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 | ||
1083 | |||
1084 | let rules = create_rules( | ||
1085 | r#" | ||
1086 | macro_rules! STRUCT { | ||
1087 | ($(#[$attrs:meta])* struct $name:ident { | ||
1088 | $($field:ident: $ftype:ty,)+ | ||
1089 | }) => ( | ||
1090 | #[repr(C)] #[derive(Copy)] $(#[$attrs])* | ||
1091 | pub struct $name { | ||
1092 | $(pub $field: $ftype,)+ | ||
1093 | } | ||
1094 | impl Clone for $name { | ||
1095 | #[inline] | ||
1096 | fn clone(&self) -> $name { *self } | ||
1097 | } | ||
1098 | #[cfg(feature = "impl-default")] | ||
1099 | impl Default for $name { | ||
1100 | #[inline] | ||
1101 | fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } | ||
1102 | } | ||
1103 | ); | ||
1104 | } | ||
1105 | "#, | ||
1106 | ); | ||
1107 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs | ||
1108 | assert_expansion(&rules, r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, | ||
1109 | "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
1110 | assert_expansion(&rules, r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, | ||
1111 | "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
1112 | } | ||
1113 | |||
1114 | #[test] | ||
1115 | fn test_int_base() { | ||
1116 | let rules = create_rules( | ||
1117 | r#" | ||
1118 | macro_rules! int_base { | ||
1119 | ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { | ||
1120 | #[stable(feature = "rust1", since = "1.0.0")] | ||
1121 | impl fmt::$Trait for $T { | ||
1122 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
1123 | $Radix.fmt_int(*self as $U, f) | ||
1124 | } | ||
1125 | } | ||
1126 | } | ||
1127 | } | ||
1128 | "#, | ||
1129 | ); | ||
1130 | |||
1131 | assert_expansion(&rules, r#" int_base!{Binary for isize as usize -> Binary}"#, | ||
1132 | "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" | ||
1133 | ); | ||
1134 | } | ||
1135 | |||
1136 | #[test] | ||
1137 | fn test_generate_pattern_iterators() { | ||
1138 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs | ||
1139 | let rules = create_rules( | ||
1140 | r#" | ||
1141 | macro_rules! generate_pattern_iterators { | ||
1142 | { double ended; with $(#[$common_stability_attribute:meta])*, | ||
1143 | $forward_iterator:ident, | ||
1144 | $reverse_iterator:ident, $iterty:ty | ||
1145 | } => { | ||
1146 | fn foo(){} | ||
1147 | } | ||
1148 | } | ||
1149 | "#, | ||
1150 | ); | ||
1151 | |||
1152 | assert_expansion(&rules, r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str )"#, | ||
1153 | "fn foo () {}"); | ||
1154 | } | ||
1155 | |||
1156 | #[test] | ||
1157 | fn test_impl_fn_for_zst() { | ||
1158 | // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs | ||
1159 | let rules = create_rules( | ||
1160 | r#" | ||
1161 | macro_rules! impl_fn_for_zst { | ||
1162 | { $( $( #[$attr: meta] )* | ||
1163 | struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = | ||
1164 | |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty | ||
1165 | $body: block; )+ | ||
1166 | } => { | ||
1167 | $( | ||
1168 | $( #[$attr] )* | ||
1169 | struct $Name; | ||
1170 | |||
1171 | impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { | ||
1172 | #[inline] | ||
1173 | extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1174 | $body | ||
1175 | } | ||
1176 | } | ||
1177 | |||
1178 | impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { | ||
1179 | #[inline] | ||
1180 | extern "rust-call" fn call_mut( | ||
1181 | &mut self, | ||
1182 | ($( $arg, )*): ($( $ArgTy, )*) | ||
1183 | ) -> $ReturnTy { | ||
1184 | Fn::call(&*self, ($( $arg, )*)) | ||
1185 | } | ||
1186 | } | ||
1187 | |||
1188 | impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { | ||
1189 | type Output = $ReturnTy; | ||
1190 | |||
1191 | #[inline] | ||
1192 | extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1193 | Fn::call(&self, ($( $arg, )*)) | ||
1194 | } | ||
1195 | } | ||
1196 | )+ | ||
1197 | } | ||
1198 | } | ||
1199 | } | ||
1200 | "#, | ||
1201 | ); | ||
1202 | |||
1203 | assert_expansion(&rules, r#" | ||
1204 | impl_fn_for_zst ! { | ||
1205 | # [ derive ( Clone ) ] | ||
1206 | struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { | ||
1207 | c . escape_debug_ext ( false ) | ||
1208 | } ; | ||
1209 | |||
1210 | # [ derive ( Clone ) ] | ||
1211 | struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { | ||
1212 | c . escape_unicode ( ) | ||
1213 | } ; | ||
1214 | # [ derive ( Clone ) ] | ||
1215 | struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { | ||
1216 | c . escape_default ( ) | ||
1217 | } ; | ||
1218 | } | ||
1219 | "#, | ||
1220 | "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}"); | ||
1221 | } | ||
1222 | |||
1223 | #[test] | ||
1224 | fn test_impl_nonzero_fmt() { | ||
1225 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 | ||
1226 | let rules = create_rules( | ||
1227 | r#" | ||
1228 | macro_rules! impl_nonzero_fmt { | ||
1229 | ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { | ||
1230 | fn foo() {} | ||
1231 | } | ||
1232 | } | ||
1233 | "#, | ||
1234 | ); | ||
1235 | |||
1236 | assert_expansion(&rules, r#"impl_nonzero_fmt ! { # [ stable ( feature = "nonzero" , since = "1.28.0" ) ] ( Debug , Display , Binary , Octal , LowerHex , UpperHex ) for NonZeroU8 }"#, | ||
1237 | "fn foo () {}"); | ||
1238 | } | ||
1239 | |||
1240 | #[test] | ||
1241 | fn test_cfg_if_items() { | ||
1242 | // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 | ||
1243 | let rules = create_rules( | ||
1244 | r#" | ||
1245 | macro_rules! __cfg_if_items { | ||
1246 | (($($not:meta,)*) ; ) => {}; | ||
1247 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
1248 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
1249 | } | ||
1250 | } | ||
1251 | "#, | ||
1252 | ); | ||
1253 | |||
1254 | assert_expansion(&rules, r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, | ||
1255 | "__cfg_if_items ! {(rustdoc , ) ; }"); | ||
1256 | } | ||
1257 | |||
1258 | #[test] | ||
1259 | fn test_cfg_if_main() { | ||
1260 | // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 | ||
1261 | let rules = create_rules( | ||
1262 | r#" | ||
1263 | macro_rules! cfg_if { | ||
1264 | ($( | ||
1265 | if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
1266 | ) else * else { | ||
1267 | $($it2:item)* | ||
1268 | }) => { | ||
1269 | __cfg_if_items! { | ||
1270 | () ; | ||
1271 | $( ( ($($meta),*) ($($it)*) ), )* | ||
1272 | ( () ($($it2)*) ), | ||
1273 | } | ||
1274 | } | ||
1275 | } | ||
1276 | "#, | ||
1277 | ); | ||
1278 | |||
1279 | assert_expansion(&rules, r#" | ||
1280 | cfg_if ! { | ||
1281 | if # [ cfg ( target_env = "msvc" ) ] { | ||
1282 | // no extra unwinder support needed | ||
1283 | } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { | ||
1284 | // no unwinder on the system! | ||
1285 | } else { | ||
1286 | mod libunwind ; | ||
1287 | pub use libunwind :: * ; | ||
1288 | } | ||
1289 | } | ||
1290 | "#, | ||
1291 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); | ||
1292 | } | ||
1293 | } | ||
diff --git a/crates/ra_mbe/src/mbe_expander.rs b/crates/ra_mbe/src/mbe_expander.rs index 361b1e404..8f8a79855 100644 --- a/crates/ra_mbe/src/mbe_expander.rs +++ b/crates/ra_mbe/src/mbe_expander.rs | |||
@@ -84,6 +84,10 @@ enum Binding { | |||
84 | } | 84 | } |
85 | 85 | ||
86 | impl Bindings { | 86 | impl Bindings { |
87 | fn contains(&self, name: &SmolStr) -> bool { | ||
88 | self.inner.contains_key(name) | ||
89 | } | ||
90 | |||
87 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { | 91 | fn get(&self, name: &SmolStr, nesting: &[usize]) -> Result<&tt::TokenTree, ExpandError> { |
88 | let mut b = self | 92 | let mut b = self |
89 | .inner | 93 | .inner |
@@ -329,6 +333,14 @@ fn expand_subtree( | |||
329 | .token_trees | 333 | .token_trees |
330 | .iter() | 334 | .iter() |
331 | .map(|it| expand_tt(it, ctx)) | 335 | .map(|it| expand_tt(it, ctx)) |
336 | .filter(|it| { | ||
337 | // Filter empty subtree | ||
338 | if let Ok(tt::TokenTree::Subtree(subtree)) = it { | ||
339 | subtree.delimiter != tt::Delimiter::None || !subtree.token_trees.is_empty() | ||
340 | } else { | ||
341 | true | ||
342 | } | ||
343 | }) | ||
332 | .collect::<Result<Vec<_>, ExpandError>>()?; | 344 | .collect::<Result<Vec<_>, ExpandError>>()?; |
333 | 345 | ||
334 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) | 346 | Ok(tt::Subtree { token_trees, delimiter: template.delimiter }) |
@@ -450,6 +462,33 @@ fn expand_tt( | |||
450 | // FIXME: Properly handle $crate token | 462 | // FIXME: Properly handle $crate token |
451 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) | 463 | tt::Leaf::from(tt::Ident { text: "$crate".into(), id: TokenId::unspecified() }) |
452 | .into() | 464 | .into() |
465 | } else if !ctx.bindings.contains(&v.text) { | ||
466 | // Note that it is possible to have a `$var` inside a macro which is not bound. | ||
467 | // For example: | ||
468 | // ``` | ||
469 | // macro_rules! foo { | ||
470 | // ($a:ident, $b:ident, $c:tt) => { | ||
471 | // macro_rules! bar { | ||
472 | // ($bi:ident) => { | ||
473 | // fn $bi() -> u8 {$c} | ||
474 | // } | ||
475 | // } | ||
476 | // } | ||
477 | // ``` | ||
478 | // We just treat it a normal tokens | ||
479 | tt::Subtree { | ||
480 | delimiter: tt::Delimiter::None, | ||
481 | token_trees: vec![ | ||
482 | tt::Leaf::from(tt::Punct { char: '$', spacing: tt::Spacing::Alone }) | ||
483 | .into(), | ||
484 | tt::Leaf::from(tt::Ident { | ||
485 | text: v.text.clone(), | ||
486 | id: TokenId::unspecified(), | ||
487 | }) | ||
488 | .into(), | ||
489 | ], | ||
490 | } | ||
491 | .into() | ||
453 | } else { | 492 | } else { |
454 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); | 493 | let tkn = ctx.bindings.get(&v.text, &ctx.nesting)?.clone(); |
455 | ctx.var_expanded = true; | 494 | ctx.var_expanded = true; |
@@ -476,11 +515,12 @@ mod tests { | |||
476 | 515 | ||
477 | #[test] | 516 | #[test] |
478 | fn test_expand_rule() { | 517 | fn test_expand_rule() { |
479 | assert_err( | 518 | // FIXME: The missing $var check should be in parsing phase |
480 | "($i:ident) => ($j)", | 519 | // assert_err( |
481 | "foo!{a}", | 520 | // "($i:ident) => ($j)", |
482 | ExpandError::BindingError(String::from("could not find binding `j`")), | 521 | // "foo!{a}", |
483 | ); | 522 | // ExpandError::BindingError(String::from("could not find binding `j`")), |
523 | // ); | ||
484 | 524 | ||
485 | assert_err( | 525 | assert_err( |
486 | "($($i:ident);*) => ($i)", | 526 | "($($i:ident);*) => ($i)", |
diff --git a/crates/ra_mbe/src/tests.rs b/crates/ra_mbe/src/tests.rs new file mode 100644 index 000000000..cdbd4dd1c --- /dev/null +++ b/crates/ra_mbe/src/tests.rs | |||
@@ -0,0 +1,1363 @@ | |||
1 | use ra_syntax::{ast, AstNode}; | ||
2 | |||
3 | use super::*; | ||
4 | |||
5 | // Good first issue (although a slightly challenging one): | ||
6 | // | ||
7 | // * Pick a random test from here | ||
8 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
9 | // * Port the test to rust and add it to this module | ||
10 | // * Make it pass :-) | ||
11 | |||
12 | #[test] | ||
13 | fn test_convert_tt() { | ||
14 | let macro_definition = r#" | ||
15 | macro_rules! impl_froms { | ||
16 | ($e:ident: $($v:ident),*) => { | ||
17 | $( | ||
18 | impl From<$v> for $e { | ||
19 | fn from(it: $v) -> $e { | ||
20 | $e::$v(it) | ||
21 | } | ||
22 | } | ||
23 | )* | ||
24 | } | ||
25 | } | ||
26 | "#; | ||
27 | |||
28 | let macro_invocation = r#" | ||
29 | impl_froms!(TokenTree: Leaf, Subtree); | ||
30 | "#; | ||
31 | |||
32 | let source_file = ast::SourceFile::parse(macro_definition); | ||
33 | let macro_definition = | ||
34 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
35 | |||
36 | let source_file = ast::SourceFile::parse(macro_invocation); | ||
37 | let macro_invocation = | ||
38 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
39 | |||
40 | let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap(); | ||
41 | let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap(); | ||
42 | let rules = crate::MacroRules::parse(&definition_tt).unwrap(); | ||
43 | let expansion = rules.expand(&invocation_tt).unwrap(); | ||
44 | assert_eq!( | ||
45 | expansion.to_string(), | ||
46 | "impl From <Leaf > for TokenTree {fn from (it : Leaf) -> TokenTree {TokenTree ::Leaf (it)}} \ | ||
47 | impl From <Subtree > for TokenTree {fn from (it : Subtree) -> TokenTree {TokenTree ::Subtree (it)}}" | ||
48 | ) | ||
49 | } | ||
50 | |||
51 | pub(crate) fn create_rules(macro_definition: &str) -> MacroRules { | ||
52 | let source_file = ast::SourceFile::parse(macro_definition); | ||
53 | let macro_definition = | ||
54 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
55 | |||
56 | let (definition_tt, _) = ast_to_token_tree(macro_definition.token_tree().unwrap()).unwrap(); | ||
57 | crate::MacroRules::parse(&definition_tt).unwrap() | ||
58 | } | ||
59 | |||
60 | pub(crate) fn expand(rules: &MacroRules, invocation: &str) -> tt::Subtree { | ||
61 | let source_file = ast::SourceFile::parse(invocation); | ||
62 | let macro_invocation = | ||
63 | source_file.syntax().descendants().find_map(ast::MacroCall::cast).unwrap(); | ||
64 | |||
65 | let (invocation_tt, _) = ast_to_token_tree(macro_invocation.token_tree().unwrap()).unwrap(); | ||
66 | |||
67 | rules.expand(&invocation_tt).unwrap() | ||
68 | } | ||
69 | |||
70 | pub(crate) fn expand_to_items( | ||
71 | rules: &MacroRules, | ||
72 | invocation: &str, | ||
73 | ) -> ra_syntax::TreeArc<ast::MacroItems> { | ||
74 | let expanded = expand(rules, invocation); | ||
75 | token_tree_to_macro_items(&expanded).unwrap() | ||
76 | } | ||
77 | |||
78 | #[allow(unused)] | ||
79 | pub(crate) fn expand_to_stmts( | ||
80 | rules: &MacroRules, | ||
81 | invocation: &str, | ||
82 | ) -> ra_syntax::TreeArc<ast::MacroStmts> { | ||
83 | let expanded = expand(rules, invocation); | ||
84 | token_tree_to_macro_stmts(&expanded).unwrap() | ||
85 | } | ||
86 | |||
87 | pub(crate) fn expand_to_expr( | ||
88 | rules: &MacroRules, | ||
89 | invocation: &str, | ||
90 | ) -> ra_syntax::TreeArc<ast::Expr> { | ||
91 | let expanded = expand(rules, invocation); | ||
92 | token_tree_to_expr(&expanded).unwrap() | ||
93 | } | ||
94 | |||
95 | pub(crate) fn text_to_tokentree(text: &str) -> tt::Subtree { | ||
96 | // wrap the given text to a macro call | ||
97 | let wrapped = format!("wrap_macro!( {} )", text); | ||
98 | let wrapped = ast::SourceFile::parse(&wrapped); | ||
99 | let wrapped = wrapped.syntax().descendants().find_map(ast::TokenTree::cast).unwrap(); | ||
100 | let mut wrapped = ast_to_token_tree(wrapped).unwrap().0; | ||
101 | wrapped.delimiter = tt::Delimiter::None; | ||
102 | |||
103 | wrapped | ||
104 | } | ||
105 | |||
106 | pub(crate) enum MacroKind { | ||
107 | Items, | ||
108 | Stmts, | ||
109 | } | ||
110 | |||
111 | use ra_syntax::WalkEvent; | ||
112 | |||
113 | pub fn debug_dump_ignore_spaces(node: &ra_syntax::SyntaxNode) -> String { | ||
114 | use std::fmt::Write; | ||
115 | |||
116 | let mut level = 0; | ||
117 | let mut buf = String::new(); | ||
118 | macro_rules! indent { | ||
119 | () => { | ||
120 | for _ in 0..level { | ||
121 | buf.push_str(" "); | ||
122 | } | ||
123 | }; | ||
124 | } | ||
125 | |||
126 | for event in node.preorder_with_tokens() { | ||
127 | match event { | ||
128 | WalkEvent::Enter(element) => { | ||
129 | match element { | ||
130 | ra_syntax::SyntaxElement::Node(node) => { | ||
131 | indent!(); | ||
132 | writeln!(buf, "{:?}", node.kind()).unwrap(); | ||
133 | } | ||
134 | ra_syntax::SyntaxElement::Token(token) => match token.kind() { | ||
135 | ra_syntax::SyntaxKind::WHITESPACE => {} | ||
136 | _ => { | ||
137 | indent!(); | ||
138 | writeln!(buf, "{:?}", token.kind()).unwrap(); | ||
139 | } | ||
140 | }, | ||
141 | } | ||
142 | level += 1; | ||
143 | } | ||
144 | WalkEvent::Leave(_) => level -= 1, | ||
145 | } | ||
146 | } | ||
147 | |||
148 | buf | ||
149 | } | ||
150 | |||
151 | pub(crate) fn assert_expansion( | ||
152 | kind: MacroKind, | ||
153 | rules: &MacroRules, | ||
154 | invocation: &str, | ||
155 | expected: &str, | ||
156 | ) -> tt::Subtree { | ||
157 | let expanded = expand(rules, invocation); | ||
158 | assert_eq!(expanded.to_string(), expected); | ||
159 | |||
160 | let expected = expected.replace("$crate", "C_C__C"); | ||
161 | |||
162 | // wrap the given text to a macro call | ||
163 | let expected = text_to_tokentree(&expected); | ||
164 | |||
165 | let (expanded_tree, expected_tree) = match kind { | ||
166 | MacroKind::Items => { | ||
167 | let expanded_tree = token_tree_to_macro_items(&expanded); | ||
168 | let expected_tree = token_tree_to_macro_items(&expected); | ||
169 | |||
170 | ( | ||
171 | debug_dump_ignore_spaces(expanded_tree.unwrap().syntax()).trim().to_string(), | ||
172 | debug_dump_ignore_spaces(expected_tree.unwrap().syntax()).trim().to_string(), | ||
173 | ) | ||
174 | } | ||
175 | |||
176 | MacroKind::Stmts => { | ||
177 | let expanded_tree = token_tree_to_macro_stmts(&expanded); | ||
178 | let expected_tree = token_tree_to_macro_stmts(&expected); | ||
179 | |||
180 | ( | ||
181 | debug_dump_ignore_spaces(expanded_tree.unwrap().syntax()).trim().to_string(), | ||
182 | debug_dump_ignore_spaces(expected_tree.unwrap().syntax()).trim().to_string(), | ||
183 | ) | ||
184 | } | ||
185 | }; | ||
186 | |||
187 | let expected_tree = expected_tree.replace("C_C__C", "$crate"); | ||
188 | assert_eq!( | ||
189 | expanded_tree, expected_tree, | ||
190 | "left => {}\nright => {}", | ||
191 | expanded_tree, expected_tree, | ||
192 | ); | ||
193 | |||
194 | expanded | ||
195 | } | ||
196 | |||
197 | #[test] | ||
198 | fn test_fail_match_pattern_by_first_token() { | ||
199 | let rules = create_rules( | ||
200 | r#" | ||
201 | macro_rules! foo { | ||
202 | ($ i:ident) => ( | ||
203 | mod $ i {} | ||
204 | ); | ||
205 | (= $ i:ident) => ( | ||
206 | fn $ i() {} | ||
207 | ); | ||
208 | (+ $ i:ident) => ( | ||
209 | struct $ i; | ||
210 | ) | ||
211 | } | ||
212 | "#, | ||
213 | ); | ||
214 | |||
215 | assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); | ||
216 | assert_expansion(MacroKind::Items, &rules, "foo! { = bar }", "fn bar () {}"); | ||
217 | assert_expansion(MacroKind::Items, &rules, "foo! { + Baz }", "struct Baz ;"); | ||
218 | } | ||
219 | |||
220 | #[test] | ||
221 | fn test_fail_match_pattern_by_last_token() { | ||
222 | let rules = create_rules( | ||
223 | r#" | ||
224 | macro_rules! foo { | ||
225 | ($ i:ident) => ( | ||
226 | mod $ i {} | ||
227 | ); | ||
228 | ($ i:ident =) => ( | ||
229 | fn $ i() {} | ||
230 | ); | ||
231 | ($ i:ident +) => ( | ||
232 | struct $ i; | ||
233 | ) | ||
234 | } | ||
235 | "#, | ||
236 | ); | ||
237 | |||
238 | assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); | ||
239 | assert_expansion(MacroKind::Items, &rules, "foo! { bar = }", "fn bar () {}"); | ||
240 | assert_expansion(MacroKind::Items, &rules, "foo! { Baz + }", "struct Baz ;"); | ||
241 | } | ||
242 | |||
243 | #[test] | ||
244 | fn test_fail_match_pattern_by_word_token() { | ||
245 | let rules = create_rules( | ||
246 | r#" | ||
247 | macro_rules! foo { | ||
248 | ($ i:ident) => ( | ||
249 | mod $ i {} | ||
250 | ); | ||
251 | (spam $ i:ident) => ( | ||
252 | fn $ i() {} | ||
253 | ); | ||
254 | (eggs $ i:ident) => ( | ||
255 | struct $ i; | ||
256 | ) | ||
257 | } | ||
258 | "#, | ||
259 | ); | ||
260 | |||
261 | assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "mod foo {}"); | ||
262 | assert_expansion(MacroKind::Items, &rules, "foo! { spam bar }", "fn bar () {}"); | ||
263 | assert_expansion(MacroKind::Items, &rules, "foo! { eggs Baz }", "struct Baz ;"); | ||
264 | } | ||
265 | |||
266 | #[test] | ||
267 | fn test_match_group_pattern_by_separator_token() { | ||
268 | let rules = create_rules( | ||
269 | r#" | ||
270 | macro_rules! foo { | ||
271 | ($ ($ i:ident),*) => ($ ( | ||
272 | mod $ i {} | ||
273 | )*); | ||
274 | ($ ($ i:ident)#*) => ($ ( | ||
275 | fn $ i() {} | ||
276 | )*); | ||
277 | ($ i:ident ,# $ j:ident) => ( | ||
278 | struct $ i; | ||
279 | struct $ j; | ||
280 | ) | ||
281 | } | ||
282 | "#, | ||
283 | ); | ||
284 | |||
285 | assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "mod foo {} mod bar {}"); | ||
286 | assert_expansion(MacroKind::Items, &rules, "foo! { foo# bar }", "fn foo () {} fn bar () {}"); | ||
287 | assert_expansion(MacroKind::Items, &rules, "foo! { Foo,# Bar }", "struct Foo ; struct Bar ;"); | ||
288 | } | ||
289 | |||
290 | #[test] | ||
291 | fn test_match_group_pattern_with_multiple_defs() { | ||
292 | let rules = create_rules( | ||
293 | r#" | ||
294 | macro_rules! foo { | ||
295 | ($ ($ i:ident),*) => ( struct Bar { $ ( | ||
296 | fn $ i {} | ||
297 | )*} ); | ||
298 | } | ||
299 | "#, | ||
300 | ); | ||
301 | |||
302 | assert_expansion( | ||
303 | MacroKind::Items, | ||
304 | &rules, | ||
305 | "foo! { foo, bar }", | ||
306 | "struct Bar {fn foo {} fn bar {}}", | ||
307 | ); | ||
308 | } | ||
309 | |||
310 | #[test] | ||
311 | fn test_match_group_pattern_with_multiple_statement() { | ||
312 | let rules = create_rules( | ||
313 | r#" | ||
314 | macro_rules! foo { | ||
315 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
316 | $ i (); | ||
317 | )*} ); | ||
318 | } | ||
319 | "#, | ||
320 | ); | ||
321 | |||
322 | assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "fn baz {foo () ; bar () ;}"); | ||
323 | } | ||
324 | |||
325 | #[test] | ||
326 | fn test_match_group_pattern_with_multiple_statement_without_semi() { | ||
327 | let rules = create_rules( | ||
328 | r#" | ||
329 | macro_rules! foo { | ||
330 | ($ ($ i:ident),*) => ( fn baz { $ ( | ||
331 | $i() | ||
332 | );*} ); | ||
333 | } | ||
334 | "#, | ||
335 | ); | ||
336 | |||
337 | assert_expansion(MacroKind::Items, &rules, "foo! { foo, bar }", "fn baz {foo () ;bar ()}"); | ||
338 | } | ||
339 | |||
340 | #[test] | ||
341 | fn test_match_group_empty_fixed_token() { | ||
342 | let rules = create_rules( | ||
343 | r#" | ||
344 | macro_rules! foo { | ||
345 | ($ ($ i:ident)* #abc) => ( fn baz { $ ( | ||
346 | $ i (); | ||
347 | )*} ); | ||
348 | } | ||
349 | "#, | ||
350 | ); | ||
351 | |||
352 | assert_expansion(MacroKind::Items, &rules, "foo! {#abc}", "fn baz {}"); | ||
353 | } | ||
354 | |||
355 | #[test] | ||
356 | fn test_match_group_in_subtree() { | ||
357 | let rules = create_rules( | ||
358 | r#" | ||
359 | macro_rules! foo { | ||
360 | (fn $name:ident {$($i:ident)*} ) => ( fn $name() { $ ( | ||
361 | $ i (); | ||
362 | )*} ); | ||
363 | }"#, | ||
364 | ); | ||
365 | |||
366 | assert_expansion(MacroKind::Items, &rules, "foo! {fn baz {a b} }", "fn baz () {a () ; b () ;}"); | ||
367 | } | ||
368 | |||
369 | #[test] | ||
370 | fn test_match_group_with_multichar_sep() { | ||
371 | let rules = create_rules( | ||
372 | r#" | ||
373 | macro_rules! foo { | ||
374 | (fn $name:ident {$($i:literal)*} ) => ( fn $name() -> bool { $($i)&&*} ); | ||
375 | }"#, | ||
376 | ); | ||
377 | |||
378 | assert_expansion( | ||
379 | MacroKind::Items, | ||
380 | &rules, | ||
381 | "foo! (fn baz {true true} )", | ||
382 | "fn baz () -> bool {true &&true}", | ||
383 | ); | ||
384 | } | ||
385 | |||
386 | #[test] | ||
387 | fn test_match_group_zero_match() { | ||
388 | let rules = create_rules( | ||
389 | r#" | ||
390 | macro_rules! foo { | ||
391 | ( $($i:ident)* ) => (); | ||
392 | }"#, | ||
393 | ); | ||
394 | |||
395 | assert_expansion(MacroKind::Items, &rules, "foo! ()", ""); | ||
396 | } | ||
397 | |||
398 | #[test] | ||
399 | fn test_match_group_in_group() { | ||
400 | let rules = create_rules( | ||
401 | r#" | ||
402 | macro_rules! foo { | ||
403 | { $( ( $($i:ident)* ) )* } => ( $( ( $($i)* ) )* ); | ||
404 | }"#, | ||
405 | ); | ||
406 | |||
407 | assert_expansion(MacroKind::Items, &rules, "foo! ( (a b) )", "(a b)"); | ||
408 | } | ||
409 | |||
410 | #[test] | ||
411 | fn test_expand_to_item_list() { | ||
412 | let rules = create_rules( | ||
413 | " | ||
414 | macro_rules! structs { | ||
415 | ($($i:ident),*) => { | ||
416 | $(struct $i { field: u32 } )* | ||
417 | } | ||
418 | } | ||
419 | ", | ||
420 | ); | ||
421 | let expansion = expand(&rules, "structs!(Foo, Bar)"); | ||
422 | let tree = token_tree_to_macro_items(&expansion); | ||
423 | assert_eq!( | ||
424 | tree.unwrap().syntax().debug_dump().trim(), | ||
425 | r#" | ||
426 | MACRO_ITEMS@[0; 40) | ||
427 | STRUCT_DEF@[0; 20) | ||
428 | STRUCT_KW@[0; 6) "struct" | ||
429 | NAME@[6; 9) | ||
430 | IDENT@[6; 9) "Foo" | ||
431 | NAMED_FIELD_DEF_LIST@[9; 20) | ||
432 | L_CURLY@[9; 10) "{" | ||
433 | NAMED_FIELD_DEF@[10; 19) | ||
434 | NAME@[10; 15) | ||
435 | IDENT@[10; 15) "field" | ||
436 | COLON@[15; 16) ":" | ||
437 | PATH_TYPE@[16; 19) | ||
438 | PATH@[16; 19) | ||
439 | PATH_SEGMENT@[16; 19) | ||
440 | NAME_REF@[16; 19) | ||
441 | IDENT@[16; 19) "u32" | ||
442 | R_CURLY@[19; 20) "}" | ||
443 | STRUCT_DEF@[20; 40) | ||
444 | STRUCT_KW@[20; 26) "struct" | ||
445 | NAME@[26; 29) | ||
446 | IDENT@[26; 29) "Bar" | ||
447 | NAMED_FIELD_DEF_LIST@[29; 40) | ||
448 | L_CURLY@[29; 30) "{" | ||
449 | NAMED_FIELD_DEF@[30; 39) | ||
450 | NAME@[30; 35) | ||
451 | IDENT@[30; 35) "field" | ||
452 | COLON@[35; 36) ":" | ||
453 | PATH_TYPE@[36; 39) | ||
454 | PATH@[36; 39) | ||
455 | PATH_SEGMENT@[36; 39) | ||
456 | NAME_REF@[36; 39) | ||
457 | IDENT@[36; 39) "u32" | ||
458 | R_CURLY@[39; 40) "}""# | ||
459 | .trim() | ||
460 | ); | ||
461 | } | ||
462 | |||
463 | #[test] | ||
464 | fn test_expand_literals_to_token_tree() { | ||
465 | fn to_subtree(tt: &tt::TokenTree) -> &tt::Subtree { | ||
466 | if let tt::TokenTree::Subtree(subtree) = tt { | ||
467 | return &subtree; | ||
468 | } | ||
469 | unreachable!("It is not a subtree"); | ||
470 | } | ||
471 | |||
472 | fn to_literal(tt: &tt::TokenTree) -> &tt::Literal { | ||
473 | if let tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) = tt { | ||
474 | return lit; | ||
475 | } | ||
476 | unreachable!("It is not a literal"); | ||
477 | } | ||
478 | |||
479 | let rules = create_rules( | ||
480 | r#" | ||
481 | macro_rules! literals { | ||
482 | ($i:ident) => { | ||
483 | { | ||
484 | let a = 'c'; | ||
485 | let c = 1000; | ||
486 | let f = 12E+99_f64; | ||
487 | let s = "rust1"; | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | "#, | ||
492 | ); | ||
493 | let expansion = expand(&rules, "literals!(foo)"); | ||
494 | let stm_tokens = &to_subtree(&expansion.token_trees[0]).token_trees; | ||
495 | |||
496 | // [let] [a] [=] ['c'] [;] | ||
497 | assert_eq!(to_literal(&stm_tokens[3]).text, "'c'"); | ||
498 | // [let] [c] [=] [1000] [;] | ||
499 | assert_eq!(to_literal(&stm_tokens[5 + 3]).text, "1000"); | ||
500 | // [let] [f] [=] [12E+99_f64] [;] | ||
501 | assert_eq!(to_literal(&stm_tokens[10 + 3]).text, "12E+99_f64"); | ||
502 | // [let] [s] [=] ["rust1"] [;] | ||
503 | assert_eq!(to_literal(&stm_tokens[15 + 3]).text, "\"rust1\""); | ||
504 | } | ||
505 | |||
506 | #[test] | ||
507 | fn test_two_idents() { | ||
508 | let rules = create_rules( | ||
509 | r#" | ||
510 | macro_rules! foo { | ||
511 | ($ i:ident, $ j:ident) => { | ||
512 | fn foo() { let a = $ i; let b = $j; } | ||
513 | } | ||
514 | } | ||
515 | "#, | ||
516 | ); | ||
517 | assert_expansion( | ||
518 | MacroKind::Items, | ||
519 | &rules, | ||
520 | "foo! { foo, bar }", | ||
521 | "fn foo () {let a = foo ; let b = bar ;}", | ||
522 | ); | ||
523 | } | ||
524 | |||
525 | #[test] | ||
526 | fn test_tt_to_stmts() { | ||
527 | let rules = create_rules( | ||
528 | r#" | ||
529 | macro_rules! foo { | ||
530 | () => { | ||
531 | let a = 0; | ||
532 | a = 10 + 1; | ||
533 | a | ||
534 | } | ||
535 | } | ||
536 | "#, | ||
537 | ); | ||
538 | |||
539 | let expanded = expand(&rules, "foo!{}"); | ||
540 | let stmts = token_tree_to_macro_stmts(&expanded); | ||
541 | |||
542 | assert_eq!( | ||
543 | stmts.unwrap().syntax().debug_dump().trim(), | ||
544 | r#"MACRO_STMTS@[0; 15) | ||
545 | LET_STMT@[0; 7) | ||
546 | LET_KW@[0; 3) "let" | ||
547 | BIND_PAT@[3; 4) | ||
548 | NAME@[3; 4) | ||
549 | IDENT@[3; 4) "a" | ||
550 | EQ@[4; 5) "=" | ||
551 | LITERAL@[5; 6) | ||
552 | INT_NUMBER@[5; 6) "0" | ||
553 | SEMI@[6; 7) ";" | ||
554 | EXPR_STMT@[7; 14) | ||
555 | BIN_EXPR@[7; 13) | ||
556 | PATH_EXPR@[7; 8) | ||
557 | PATH@[7; 8) | ||
558 | PATH_SEGMENT@[7; 8) | ||
559 | NAME_REF@[7; 8) | ||
560 | IDENT@[7; 8) "a" | ||
561 | EQ@[8; 9) "=" | ||
562 | BIN_EXPR@[9; 13) | ||
563 | LITERAL@[9; 11) | ||
564 | INT_NUMBER@[9; 11) "10" | ||
565 | PLUS@[11; 12) "+" | ||
566 | LITERAL@[12; 13) | ||
567 | INT_NUMBER@[12; 13) "1" | ||
568 | SEMI@[13; 14) ";" | ||
569 | EXPR_STMT@[14; 15) | ||
570 | PATH_EXPR@[14; 15) | ||
571 | PATH@[14; 15) | ||
572 | PATH_SEGMENT@[14; 15) | ||
573 | NAME_REF@[14; 15) | ||
574 | IDENT@[14; 15) "a""#, | ||
575 | ); | ||
576 | } | ||
577 | |||
578 | // The following tests are port from intellij-rust directly | ||
579 | // https://github.com/intellij-rust/intellij-rust/blob/c4e9feee4ad46e7953b1948c112533360b6087bb/src/test/kotlin/org/rust/lang/core/macros/RsMacroExpansionTest.kt | ||
580 | |||
581 | #[test] | ||
582 | fn test_path() { | ||
583 | let rules = create_rules( | ||
584 | r#" | ||
585 | macro_rules! foo { | ||
586 | ($ i:path) => { | ||
587 | fn foo() { let a = $ i; } | ||
588 | } | ||
589 | } | ||
590 | "#, | ||
591 | ); | ||
592 | assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "fn foo () {let a = foo ;}"); | ||
593 | assert_expansion( | ||
594 | MacroKind::Items, | ||
595 | &rules, | ||
596 | "foo! { bar::<u8>::baz::<u8> }", | ||
597 | "fn foo () {let a = bar ::< u8 >:: baz ::< u8 > ;}", | ||
598 | ); | ||
599 | } | ||
600 | |||
601 | #[test] | ||
602 | fn test_two_paths() { | ||
603 | let rules = create_rules( | ||
604 | r#" | ||
605 | macro_rules! foo { | ||
606 | ($ i:path, $ j:path) => { | ||
607 | fn foo() { let a = $ i; let b = $j; } | ||
608 | } | ||
609 | } | ||
610 | "#, | ||
611 | ); | ||
612 | assert_expansion( | ||
613 | MacroKind::Items, | ||
614 | &rules, | ||
615 | "foo! { foo, bar }", | ||
616 | "fn foo () {let a = foo ; let b = bar ;}", | ||
617 | ); | ||
618 | } | ||
619 | |||
620 | #[test] | ||
621 | fn test_path_with_path() { | ||
622 | let rules = create_rules( | ||
623 | r#" | ||
624 | macro_rules! foo { | ||
625 | ($ i:path) => { | ||
626 | fn foo() { let a = $ i :: bar; } | ||
627 | } | ||
628 | } | ||
629 | "#, | ||
630 | ); | ||
631 | assert_expansion(MacroKind::Items, &rules, "foo! { foo }", "fn foo () {let a = foo :: bar ;}"); | ||
632 | } | ||
633 | |||
634 | #[test] | ||
635 | fn test_expr() { | ||
636 | let rules = create_rules( | ||
637 | r#" | ||
638 | macro_rules! foo { | ||
639 | ($ i:expr) => { | ||
640 | fn bar() { $ i; } | ||
641 | } | ||
642 | } | ||
643 | "#, | ||
644 | ); | ||
645 | |||
646 | assert_expansion( | ||
647 | MacroKind::Items, | ||
648 | &rules, | ||
649 | "foo! { 2 + 2 * baz(3).quux() }", | ||
650 | "fn bar () {2 + 2 * baz (3) . quux () ;}", | ||
651 | ); | ||
652 | } | ||
653 | |||
654 | #[test] | ||
655 | fn test_expr_order() { | ||
656 | let rules = create_rules( | ||
657 | r#" | ||
658 | macro_rules! foo { | ||
659 | ($ i:expr) => { | ||
660 | fn bar() { $ i * 2; } | ||
661 | } | ||
662 | } | ||
663 | "#, | ||
664 | ); | ||
665 | |||
666 | assert_eq!( | ||
667 | expand_to_items(&rules, "foo! { 1 + 1 }").syntax().debug_dump().trim(), | ||
668 | r#"MACRO_ITEMS@[0; 15) | ||
669 | FN_DEF@[0; 15) | ||
670 | FN_KW@[0; 2) "fn" | ||
671 | NAME@[2; 5) | ||
672 | IDENT@[2; 5) "bar" | ||
673 | PARAM_LIST@[5; 7) | ||
674 | L_PAREN@[5; 6) "(" | ||
675 | R_PAREN@[6; 7) ")" | ||
676 | BLOCK@[7; 15) | ||
677 | L_CURLY@[7; 8) "{" | ||
678 | EXPR_STMT@[8; 14) | ||
679 | BIN_EXPR@[8; 13) | ||
680 | BIN_EXPR@[8; 11) | ||
681 | LITERAL@[8; 9) | ||
682 | INT_NUMBER@[8; 9) "1" | ||
683 | PLUS@[9; 10) "+" | ||
684 | LITERAL@[10; 11) | ||
685 | INT_NUMBER@[10; 11) "1" | ||
686 | STAR@[11; 12) "*" | ||
687 | LITERAL@[12; 13) | ||
688 | INT_NUMBER@[12; 13) "2" | ||
689 | SEMI@[13; 14) ";" | ||
690 | R_CURLY@[14; 15) "}""#, | ||
691 | ); | ||
692 | } | ||
693 | |||
694 | #[test] | ||
695 | fn test_last_expr() { | ||
696 | let rules = create_rules( | ||
697 | r#" | ||
698 | macro_rules! vec { | ||
699 | ($($item:expr),*) => { | ||
700 | { | ||
701 | let mut v = Vec::new(); | ||
702 | $( | ||
703 | v.push($item); | ||
704 | )* | ||
705 | v | ||
706 | } | ||
707 | }; | ||
708 | } | ||
709 | "#, | ||
710 | ); | ||
711 | assert_expansion( | ||
712 | MacroKind::Items, | ||
713 | &rules, | ||
714 | "vec!(1,2,3)", | ||
715 | "{let mut v = Vec :: new () ; v . push (1) ; v . push (2) ; v . push (3) ; v}", | ||
716 | ); | ||
717 | } | ||
718 | |||
719 | #[test] | ||
720 | fn test_ty() { | ||
721 | let rules = create_rules( | ||
722 | r#" | ||
723 | macro_rules! foo { | ||
724 | ($ i:ty) => ( | ||
725 | fn bar() -> $ i { unimplemented!() } | ||
726 | ) | ||
727 | } | ||
728 | "#, | ||
729 | ); | ||
730 | assert_expansion( | ||
731 | MacroKind::Items, | ||
732 | &rules, | ||
733 | "foo! { Baz<u8> }", | ||
734 | "fn bar () -> Baz < u8 > {unimplemented ! ()}", | ||
735 | ); | ||
736 | } | ||
737 | |||
738 | #[test] | ||
739 | fn test_ty_with_complex_type() { | ||
740 | let rules = create_rules( | ||
741 | r#" | ||
742 | macro_rules! foo { | ||
743 | ($ i:ty) => ( | ||
744 | fn bar() -> $ i { unimplemented!() } | ||
745 | ) | ||
746 | } | ||
747 | "#, | ||
748 | ); | ||
749 | |||
750 | // Reference lifetime struct with generic type | ||
751 | assert_expansion( | ||
752 | MacroKind::Items, | ||
753 | &rules, | ||
754 | "foo! { &'a Baz<u8> }", | ||
755 | "fn bar () -> & 'a Baz < u8 > {unimplemented ! ()}", | ||
756 | ); | ||
757 | |||
758 | // extern "Rust" func type | ||
759 | assert_expansion( | ||
760 | MacroKind::Items, | ||
761 | &rules, | ||
762 | r#"foo! { extern "Rust" fn() -> Ret }"#, | ||
763 | r#"fn bar () -> extern "Rust" fn () -> Ret {unimplemented ! ()}"#, | ||
764 | ); | ||
765 | } | ||
766 | |||
767 | #[test] | ||
768 | fn test_pat_() { | ||
769 | let rules = create_rules( | ||
770 | r#" | ||
771 | macro_rules! foo { | ||
772 | ($ i:pat) => { fn foo() { let $ i; } } | ||
773 | } | ||
774 | "#, | ||
775 | ); | ||
776 | assert_expansion(MacroKind::Items, &rules, "foo! { (a, b) }", "fn foo () {let (a , b) ;}"); | ||
777 | } | ||
778 | |||
779 | #[test] | ||
780 | fn test_stmt() { | ||
781 | let rules = create_rules( | ||
782 | r#" | ||
783 | macro_rules! foo { | ||
784 | ($ i:stmt) => ( | ||
785 | fn bar() { $ i; } | ||
786 | ) | ||
787 | } | ||
788 | "#, | ||
789 | ); | ||
790 | assert_expansion(MacroKind::Items, &rules, "foo! { 2 }", "fn bar () {2 ;}"); | ||
791 | assert_expansion(MacroKind::Items, &rules, "foo! { let a = 0 }", "fn bar () {let a = 0 ;}"); | ||
792 | } | ||
793 | |||
794 | #[test] | ||
795 | fn test_single_item() { | ||
796 | let rules = create_rules( | ||
797 | r#" | ||
798 | macro_rules! foo { | ||
799 | ($ i:item) => ( | ||
800 | $ i | ||
801 | ) | ||
802 | } | ||
803 | "#, | ||
804 | ); | ||
805 | assert_expansion(MacroKind::Items, &rules, "foo! {mod c {}}", "mod c {}"); | ||
806 | } | ||
807 | |||
808 | #[test] | ||
809 | fn test_all_items() { | ||
810 | let rules = create_rules( | ||
811 | r#" | ||
812 | macro_rules! foo { | ||
813 | ($ ($ i:item)*) => ($ ( | ||
814 | $ i | ||
815 | )*) | ||
816 | } | ||
817 | "#, | ||
818 | ); | ||
819 | assert_expansion(MacroKind::Items, &rules, r#" | ||
820 | foo! { | ||
821 | extern crate a; | ||
822 | mod b; | ||
823 | mod c {} | ||
824 | use d; | ||
825 | const E: i32 = 0; | ||
826 | static F: i32 = 0; | ||
827 | impl G {} | ||
828 | struct H; | ||
829 | enum I { Foo } | ||
830 | trait J {} | ||
831 | fn h() {} | ||
832 | extern {} | ||
833 | type T = u8; | ||
834 | } | ||
835 | "#, 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 ;"#); | ||
836 | } | ||
837 | |||
838 | #[test] | ||
839 | fn test_block() { | ||
840 | let rules = create_rules( | ||
841 | r#" | ||
842 | macro_rules! foo { | ||
843 | ($ i:block) => { fn foo() $ i } | ||
844 | } | ||
845 | "#, | ||
846 | ); | ||
847 | assert_expansion(MacroKind::Stmts, &rules, "foo! { { 1; } }", "fn foo () {1 ;}"); | ||
848 | } | ||
849 | |||
850 | #[test] | ||
851 | fn test_meta() { | ||
852 | let rules = create_rules( | ||
853 | r#" | ||
854 | macro_rules! foo { | ||
855 | ($ i:meta) => ( | ||
856 | #[$ i] | ||
857 | fn bar() {} | ||
858 | ) | ||
859 | } | ||
860 | "#, | ||
861 | ); | ||
862 | assert_expansion( | ||
863 | MacroKind::Items, | ||
864 | &rules, | ||
865 | r#"foo! { cfg(target_os = "windows") }"#, | ||
866 | r#"# [cfg (target_os = "windows")] fn bar () {}"#, | ||
867 | ); | ||
868 | } | ||
869 | |||
870 | #[test] | ||
871 | fn test_tt_block() { | ||
872 | let rules = create_rules( | ||
873 | r#" | ||
874 | macro_rules! foo { | ||
875 | ($ i:tt) => { fn foo() $ i } | ||
876 | } | ||
877 | "#, | ||
878 | ); | ||
879 | assert_expansion(MacroKind::Items, &rules, r#"foo! { { 1; } }"#, r#"fn foo () {1 ;}"#); | ||
880 | } | ||
881 | |||
882 | #[test] | ||
883 | fn test_tt_group() { | ||
884 | let rules = create_rules( | ||
885 | r#" | ||
886 | macro_rules! foo { | ||
887 | ($($ i:tt)*) => { $($ i)* } | ||
888 | } | ||
889 | "#, | ||
890 | ); | ||
891 | assert_expansion(MacroKind::Items, &rules, r#"foo! { fn foo() {} }"#, r#"fn foo () {}"#); | ||
892 | } | ||
893 | #[test] | ||
894 | fn test_lifetime() { | ||
895 | let rules = create_rules( | ||
896 | r#" | ||
897 | macro_rules! foo { | ||
898 | ($ lt:lifetime) => { struct Ref<$ lt>{ s: &$ lt str } } | ||
899 | } | ||
900 | "#, | ||
901 | ); | ||
902 | assert_expansion(MacroKind::Items, &rules, r#"foo!{'a}"#, r#"struct Ref <'a > {s : &'a str}"#); | ||
903 | } | ||
904 | |||
905 | #[test] | ||
906 | fn test_literal() { | ||
907 | let rules = create_rules( | ||
908 | r#" | ||
909 | macro_rules! foo { | ||
910 | ($ type:ty $ lit:literal) => { const VALUE: $ type = $ lit;}; | ||
911 | } | ||
912 | "#, | ||
913 | ); | ||
914 | assert_expansion(MacroKind::Items, &rules, r#"foo!(u8 0)"#, r#"const VALUE : u8 = 0 ;"#); | ||
915 | } | ||
916 | |||
917 | #[test] | ||
918 | fn test_vis() { | ||
919 | let rules = create_rules( | ||
920 | r#" | ||
921 | macro_rules! foo { | ||
922 | ($ vis:vis $ name:ident) => { $ vis fn $ name() {}}; | ||
923 | } | ||
924 | "#, | ||
925 | ); | ||
926 | assert_expansion(MacroKind::Items, &rules, r#"foo!(pub foo);"#, r#"pub fn foo () {}"#); | ||
927 | |||
928 | // test optional casse | ||
929 | assert_expansion(MacroKind::Items, &rules, r#"foo!(foo);"#, r#"fn foo () {}"#); | ||
930 | } | ||
931 | |||
932 | #[test] | ||
933 | fn test_inner_macro_rules() { | ||
934 | let rules = create_rules( | ||
935 | r#" | ||
936 | macro_rules! foo { | ||
937 | ($a:ident, $b:ident, $c:tt) => { | ||
938 | |||
939 | macro_rules! bar { | ||
940 | ($bi:ident) => { | ||
941 | fn $bi() -> u8 {$c} | ||
942 | } | ||
943 | } | ||
944 | |||
945 | bar!($a); | ||
946 | fn $b() -> u8 {$c} | ||
947 | } | ||
948 | } | ||
949 | "#, | ||
950 | ); | ||
951 | assert_expansion( | ||
952 | MacroKind::Items, | ||
953 | &rules, | ||
954 | r#"foo!(x,y, 1);"#, | ||
955 | r#"macro_rules ! bar {($ bi : ident) => {fn $ bi () -> u8 {1}}} bar ! (x) ; fn y () -> u8 {1}"#, | ||
956 | ); | ||
957 | } | ||
958 | |||
959 | // The following tests are based on real world situations | ||
960 | #[test] | ||
961 | fn test_vec() { | ||
962 | let rules = create_rules( | ||
963 | r#" | ||
964 | macro_rules! vec { | ||
965 | ($($item:expr),*) => { | ||
966 | { | ||
967 | let mut v = Vec::new(); | ||
968 | $( | ||
969 | v.push($item); | ||
970 | )* | ||
971 | v | ||
972 | } | ||
973 | }; | ||
974 | } | ||
975 | "#, | ||
976 | ); | ||
977 | assert_expansion(MacroKind::Items, &rules, r#"vec!();"#, r#"{let mut v = Vec :: new () ; v}"#); | ||
978 | assert_expansion( | ||
979 | MacroKind::Items, | ||
980 | &rules, | ||
981 | r#"vec![1u32,2]"#, | ||
982 | r#"{let mut v = Vec :: new () ; v . push (1u32) ; v . push (2) ; v}"#, | ||
983 | ); | ||
984 | |||
985 | assert_eq!( | ||
986 | expand_to_expr(&rules, r#"vec![1u32,2]"#).syntax().debug_dump().trim(), | ||
987 | r#"BLOCK_EXPR@[0; 45) | ||
988 | BLOCK@[0; 45) | ||
989 | L_CURLY@[0; 1) "{" | ||
990 | LET_STMT@[1; 20) | ||
991 | LET_KW@[1; 4) "let" | ||
992 | BIND_PAT@[4; 8) | ||
993 | MUT_KW@[4; 7) "mut" | ||
994 | NAME@[7; 8) | ||
995 | IDENT@[7; 8) "v" | ||
996 | EQ@[8; 9) "=" | ||
997 | CALL_EXPR@[9; 19) | ||
998 | PATH_EXPR@[9; 17) | ||
999 | PATH@[9; 17) | ||
1000 | PATH@[9; 12) | ||
1001 | PATH_SEGMENT@[9; 12) | ||
1002 | NAME_REF@[9; 12) | ||
1003 | IDENT@[9; 12) "Vec" | ||
1004 | COLONCOLON@[12; 14) "::" | ||
1005 | PATH_SEGMENT@[14; 17) | ||
1006 | NAME_REF@[14; 17) | ||
1007 | IDENT@[14; 17) "new" | ||
1008 | ARG_LIST@[17; 19) | ||
1009 | L_PAREN@[17; 18) "(" | ||
1010 | R_PAREN@[18; 19) ")" | ||
1011 | SEMI@[19; 20) ";" | ||
1012 | EXPR_STMT@[20; 33) | ||
1013 | METHOD_CALL_EXPR@[20; 32) | ||
1014 | PATH_EXPR@[20; 21) | ||
1015 | PATH@[20; 21) | ||
1016 | PATH_SEGMENT@[20; 21) | ||
1017 | NAME_REF@[20; 21) | ||
1018 | IDENT@[20; 21) "v" | ||
1019 | DOT@[21; 22) "." | ||
1020 | NAME_REF@[22; 26) | ||
1021 | IDENT@[22; 26) "push" | ||
1022 | ARG_LIST@[26; 32) | ||
1023 | L_PAREN@[26; 27) "(" | ||
1024 | LITERAL@[27; 31) | ||
1025 | INT_NUMBER@[27; 31) "1u32" | ||
1026 | R_PAREN@[31; 32) ")" | ||
1027 | SEMI@[32; 33) ";" | ||
1028 | EXPR_STMT@[33; 43) | ||
1029 | METHOD_CALL_EXPR@[33; 42) | ||
1030 | PATH_EXPR@[33; 34) | ||
1031 | PATH@[33; 34) | ||
1032 | PATH_SEGMENT@[33; 34) | ||
1033 | NAME_REF@[33; 34) | ||
1034 | IDENT@[33; 34) "v" | ||
1035 | DOT@[34; 35) "." | ||
1036 | NAME_REF@[35; 39) | ||
1037 | IDENT@[35; 39) "push" | ||
1038 | ARG_LIST@[39; 42) | ||
1039 | L_PAREN@[39; 40) "(" | ||
1040 | LITERAL@[40; 41) | ||
1041 | INT_NUMBER@[40; 41) "2" | ||
1042 | R_PAREN@[41; 42) ")" | ||
1043 | SEMI@[42; 43) ";" | ||
1044 | PATH_EXPR@[43; 44) | ||
1045 | PATH@[43; 44) | ||
1046 | PATH_SEGMENT@[43; 44) | ||
1047 | NAME_REF@[43; 44) | ||
1048 | IDENT@[43; 44) "v" | ||
1049 | R_CURLY@[44; 45) "}""# | ||
1050 | ); | ||
1051 | } | ||
1052 | |||
1053 | #[test] | ||
1054 | fn test_winapi_struct() { | ||
1055 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/macros.rs#L366 | ||
1056 | |||
1057 | let rules = create_rules( | ||
1058 | r#" | ||
1059 | macro_rules! STRUCT { | ||
1060 | ($(#[$attrs:meta])* struct $name:ident { | ||
1061 | $($field:ident: $ftype:ty,)+ | ||
1062 | }) => ( | ||
1063 | #[repr(C)] #[derive(Copy)] $(#[$attrs])* | ||
1064 | pub struct $name { | ||
1065 | $(pub $field: $ftype,)+ | ||
1066 | } | ||
1067 | impl Clone for $name { | ||
1068 | #[inline] | ||
1069 | fn clone(&self) -> $name { *self } | ||
1070 | } | ||
1071 | #[cfg(feature = "impl-default")] | ||
1072 | impl Default for $name { | ||
1073 | #[inline] | ||
1074 | fn default() -> $name { unsafe { $crate::_core::mem::zeroed() } } | ||
1075 | } | ||
1076 | ); | ||
1077 | } | ||
1078 | "#, | ||
1079 | ); | ||
1080 | // from https://github.com/retep998/winapi-rs/blob/a7ef2bca086aae76cf6c4ce4c2552988ed9798ad/src/shared/d3d9caps.rs | ||
1081 | assert_expansion(MacroKind::Items, &rules, r#"STRUCT!{struct D3DVSHADERCAPS2_0 {Caps: u8,}}"#, | ||
1082 | "# [repr (C)] # [derive (Copy)] pub struct D3DVSHADERCAPS2_0 {pub Caps : u8 ,} impl Clone for D3DVSHADERCAPS2_0 {# [inline] fn clone (& self) -> D3DVSHADERCAPS2_0 {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DVSHADERCAPS2_0 {# [inline] fn default () -> D3DVSHADERCAPS2_0 {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
1083 | assert_expansion(MacroKind::Items, &rules, r#"STRUCT!{#[cfg_attr(target_arch = "x86", repr(packed))] struct D3DCONTENTPROTECTIONCAPS {Caps : u8 ,}}"#, | ||
1084 | "# [repr (C)] # [derive (Copy)] # [cfg_attr (target_arch = \"x86\" , repr (packed))] pub struct D3DCONTENTPROTECTIONCAPS {pub Caps : u8 ,} impl Clone for D3DCONTENTPROTECTIONCAPS {# [inline] fn clone (& self) -> D3DCONTENTPROTECTIONCAPS {* self}} # [cfg (feature = \"impl-default\")] impl Default for D3DCONTENTPROTECTIONCAPS {# [inline] fn default () -> D3DCONTENTPROTECTIONCAPS {unsafe {$crate :: _core :: mem :: zeroed ()}}}"); | ||
1085 | } | ||
1086 | |||
1087 | #[test] | ||
1088 | fn test_int_base() { | ||
1089 | let rules = create_rules( | ||
1090 | r#" | ||
1091 | macro_rules! int_base { | ||
1092 | ($Trait:ident for $T:ident as $U:ident -> $Radix:ident) => { | ||
1093 | #[stable(feature = "rust1", since = "1.0.0")] | ||
1094 | impl fmt::$Trait for $T { | ||
1095 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
1096 | $Radix.fmt_int(*self as $U, f) | ||
1097 | } | ||
1098 | } | ||
1099 | } | ||
1100 | } | ||
1101 | "#, | ||
1102 | ); | ||
1103 | |||
1104 | assert_expansion(MacroKind::Items, &rules, r#" int_base!{Binary for isize as usize -> Binary}"#, | ||
1105 | "# [stable (feature = \"rust1\" , since = \"1.0.0\")] impl fmt ::Binary for isize {fn fmt (& self , f : & mut fmt :: Formatter < \'_ >) -> fmt :: Result {Binary . fmt_int (* self as usize , f)}}" | ||
1106 | ); | ||
1107 | } | ||
1108 | |||
1109 | #[test] | ||
1110 | fn test_generate_pattern_iterators() { | ||
1111 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/str/mod.rs | ||
1112 | let rules = create_rules( | ||
1113 | r#" | ||
1114 | macro_rules! generate_pattern_iterators { | ||
1115 | { double ended; with $(#[$common_stability_attribute:meta])*, | ||
1116 | $forward_iterator:ident, | ||
1117 | $reverse_iterator:ident, $iterty:ty | ||
1118 | } => { | ||
1119 | fn foo(){} | ||
1120 | } | ||
1121 | } | ||
1122 | "#, | ||
1123 | ); | ||
1124 | |||
1125 | assert_expansion(MacroKind::Items, &rules, r#"generate_pattern_iterators ! ( double ended ; with # [ stable ( feature = "rust1" , since = "1.0.0" ) ] , Split , RSplit , & 'a str )"#, | ||
1126 | "fn foo () {}"); | ||
1127 | } | ||
1128 | |||
1129 | #[test] | ||
1130 | fn test_impl_fn_for_zst() { | ||
1131 | // from https://github.com/rust-lang/rust/blob/5d20ff4d2718c820632b38c1e49d4de648a9810b/src/libcore/internal_macros.rs | ||
1132 | let rules = create_rules( | ||
1133 | r#" | ||
1134 | macro_rules! impl_fn_for_zst { | ||
1135 | { $( $( #[$attr: meta] )* | ||
1136 | struct $Name: ident impl$( <$( $lifetime : lifetime ),+> )? Fn = | ||
1137 | |$( $arg: ident: $ArgTy: ty ),*| -> $ReturnTy: ty | ||
1138 | $body: block; )+ | ||
1139 | } => { | ||
1140 | $( | ||
1141 | $( #[$attr] )* | ||
1142 | struct $Name; | ||
1143 | |||
1144 | impl $( <$( $lifetime ),+> )? Fn<($( $ArgTy, )*)> for $Name { | ||
1145 | #[inline] | ||
1146 | extern "rust-call" fn call(&self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1147 | $body | ||
1148 | } | ||
1149 | } | ||
1150 | |||
1151 | impl $( <$( $lifetime ),+> )? FnMut<($( $ArgTy, )*)> for $Name { | ||
1152 | #[inline] | ||
1153 | extern "rust-call" fn call_mut( | ||
1154 | &mut self, | ||
1155 | ($( $arg, )*): ($( $ArgTy, )*) | ||
1156 | ) -> $ReturnTy { | ||
1157 | Fn::call(&*self, ($( $arg, )*)) | ||
1158 | } | ||
1159 | } | ||
1160 | |||
1161 | impl $( <$( $lifetime ),+> )? FnOnce<($( $ArgTy, )*)> for $Name { | ||
1162 | type Output = $ReturnTy; | ||
1163 | |||
1164 | #[inline] | ||
1165 | extern "rust-call" fn call_once(self, ($( $arg, )*): ($( $ArgTy, )*)) -> $ReturnTy { | ||
1166 | Fn::call(&self, ($( $arg, )*)) | ||
1167 | } | ||
1168 | } | ||
1169 | )+ | ||
1170 | } | ||
1171 | } | ||
1172 | } | ||
1173 | "#, | ||
1174 | ); | ||
1175 | |||
1176 | assert_expansion(MacroKind::Items, &rules, r#" | ||
1177 | impl_fn_for_zst ! { | ||
1178 | # [ derive ( Clone ) ] | ||
1179 | struct CharEscapeDebugContinue impl Fn = | c : char | -> char :: EscapeDebug { | ||
1180 | c . escape_debug_ext ( false ) | ||
1181 | } ; | ||
1182 | |||
1183 | # [ derive ( Clone ) ] | ||
1184 | struct CharEscapeUnicode impl Fn = | c : char | -> char :: EscapeUnicode { | ||
1185 | c . escape_unicode ( ) | ||
1186 | } ; | ||
1187 | # [ derive ( Clone ) ] | ||
1188 | struct CharEscapeDefault impl Fn = | c : char | -> char :: EscapeDefault { | ||
1189 | c . escape_default ( ) | ||
1190 | } ; | ||
1191 | } | ||
1192 | "#, | ||
1193 | "# [derive (Clone)] struct CharEscapeDebugContinue ; impl Fn < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDebug {{c . escape_debug_ext (false)}}} impl FnMut < (char ,) > for CharEscapeDebugContinue {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDebugContinue {type Output = char :: EscapeDebug ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDebug {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeUnicode ; impl Fn < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeUnicode {{c . escape_unicode ()}}} impl FnMut < (char ,) > for CharEscapeUnicode {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeUnicode {type Output = char :: EscapeUnicode ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeUnicode {Fn :: call (& self , (c ,))}} # [derive (Clone)] struct CharEscapeDefault ; impl Fn < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call (& self , (c ,) : (char ,)) -> char :: EscapeDefault {{c . escape_default ()}}} impl FnMut < (char ,) > for CharEscapeDefault {# [inline] extern \"rust-call\" fn call_mut (& mut self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (&* self , (c ,))}} impl FnOnce < (char ,) > for CharEscapeDefault {type Output = char :: EscapeDefault ; # [inline] extern \"rust-call\" fn call_once (self , (c ,) : (char ,)) -> char :: EscapeDefault {Fn :: call (& self , (c ,))}}"); | ||
1194 | } | ||
1195 | |||
1196 | #[test] | ||
1197 | fn test_impl_nonzero_fmt() { | ||
1198 | // from https://github.com/rust-lang/rust/blob/316a391dcb7d66dc25f1f9a4ec9d368ef7615005/src/libcore/num/mod.rs#L12 | ||
1199 | let rules = create_rules( | ||
1200 | r#" | ||
1201 | macro_rules! impl_nonzero_fmt { | ||
1202 | ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { | ||
1203 | fn foo () {} | ||
1204 | } | ||
1205 | } | ||
1206 | "#, | ||
1207 | ); | ||
1208 | |||
1209 | assert_expansion(MacroKind::Items, &rules, r#"impl_nonzero_fmt! { # [stable(feature= "nonzero",since="1.28.0")] (Debug,Display,Binary,Octal,LowerHex,UpperHex) for NonZeroU8}"#, | ||
1210 | "fn foo () {}"); | ||
1211 | } | ||
1212 | |||
1213 | #[test] | ||
1214 | fn test_cfg_if_items() { | ||
1215 | // from https://github.com/rust-lang/rust/blob/33fe1131cadba69d317156847be9a402b89f11bb/src/libstd/macros.rs#L986 | ||
1216 | let rules = create_rules( | ||
1217 | r#" | ||
1218 | macro_rules! __cfg_if_items { | ||
1219 | (($($not:meta,)*) ; ) => {}; | ||
1220 | (($($not:meta,)*) ; ( ($($m:meta),*) ($($it:item)*) ), $($rest:tt)*) => { | ||
1221 | __cfg_if_items! { ($($not,)* $($m,)*) ; $($rest)* } | ||
1222 | } | ||
1223 | } | ||
1224 | "#, | ||
1225 | ); | ||
1226 | |||
1227 | assert_expansion(MacroKind::Items, &rules, r#"__cfg_if_items ! { ( rustdoc , ) ; ( ( ) ( # [ cfg ( any ( target_os = "redox" , unix ) ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as unix ; # [ cfg ( windows ) ] # [ stable ( feature = "rust1" , since = "1.0.0" ) ] pub use sys :: ext as windows ; # [ cfg ( any ( target_os = "linux" , target_os = "l4re" ) ) ] pub mod linux ; ) ) , }"#, | ||
1228 | "__cfg_if_items ! {(rustdoc ,) ;}"); | ||
1229 | } | ||
1230 | |||
1231 | #[test] | ||
1232 | fn test_cfg_if_main() { | ||
1233 | // from https://github.com/rust-lang/rust/blob/3d211248393686e0f73851fc7548f6605220fbe1/src/libpanic_unwind/macros.rs#L9 | ||
1234 | let rules = create_rules( | ||
1235 | r#" | ||
1236 | macro_rules! cfg_if { | ||
1237 | ($( | ||
1238 | if #[cfg($($meta:meta),*)] { $($it:item)* } | ||
1239 | ) else * else { | ||
1240 | $($it2:item)* | ||
1241 | }) => { | ||
1242 | __cfg_if_items! { | ||
1243 | () ; | ||
1244 | $( ( ($($meta),*) ($($it)*) ), )* | ||
1245 | ( () ($($it2)*) ), | ||
1246 | } | ||
1247 | } | ||
1248 | } | ||
1249 | "#, | ||
1250 | ); | ||
1251 | |||
1252 | assert_expansion(MacroKind::Items, &rules, r#" | ||
1253 | cfg_if ! { | ||
1254 | if # [ cfg ( target_env = "msvc" ) ] { | ||
1255 | // no extra unwinder support needed | ||
1256 | } else if # [ cfg ( all ( target_arch = "wasm32" , not ( target_os = "emscripten" ) ) ) ] { | ||
1257 | // no unwinder on the system! | ||
1258 | } else { | ||
1259 | mod libunwind ; | ||
1260 | pub use libunwind :: * ; | ||
1261 | } | ||
1262 | } | ||
1263 | "#, | ||
1264 | "__cfg_if_items ! {() ; ((target_env = \"msvc\") ()) , ((all (target_arch = \"wasm32\" , not (target_os = \"emscripten\"))) ()) , (() (mod libunwind ; pub use libunwind :: * ;)) ,}"); | ||
1265 | } | ||
1266 | |||
1267 | #[test] | ||
1268 | fn test_proptest_arbitrary() { | ||
1269 | // from https://github.com/AltSysrq/proptest/blob/d1c4b049337d2f75dd6f49a095115f7c532e5129/proptest/src/arbitrary/macros.rs#L16 | ||
1270 | let rules = create_rules( | ||
1271 | r#" | ||
1272 | macro_rules! arbitrary { | ||
1273 | ([$($bounds : tt)*] $typ: ty, $strat: ty, $params: ty; | ||
1274 | $args: ident => $logic: expr) => { | ||
1275 | impl<$($bounds)*> $crate::arbitrary::Arbitrary for $typ { | ||
1276 | type Parameters = $params; | ||
1277 | type Strategy = $strat; | ||
1278 | fn arbitrary_with($args: Self::Parameters) -> Self::Strategy { | ||
1279 | $logic | ||
1280 | } | ||
1281 | } | ||
1282 | }; | ||
1283 | |||
1284 | }"#, | ||
1285 | ); | ||
1286 | |||
1287 | assert_expansion(MacroKind::Items, &rules, r#"arbitrary ! ( [ A : Arbitrary ] | ||
1288 | Vec < A > , | ||
1289 | VecStrategy < A :: Strategy > , | ||
1290 | RangedParams1 < A :: Parameters > ; | ||
1291 | args => { let product_unpack ! [ range , a ] = args ; vec ( any_with :: < A > ( a ) , range ) } | ||
1292 | ) ;"#, | ||
1293 | "impl <A : Arbitrary > $crate :: arbitrary :: Arbitrary for Vec < A > {type Parameters = RangedParams1 < A :: Parameters > ; type Strategy = VecStrategy < A :: Strategy > ; fn arbitrary_with (args : Self :: Parameters) -> Self :: Strategy {{let product_unpack ! [range , a] = args ; vec (any_with :: < A > (a) , range)}}}"); | ||
1294 | } | ||
1295 | |||
1296 | #[test] | ||
1297 | fn test_old_ridl() { | ||
1298 | // This is from winapi 2.8, which do not have a link from github | ||
1299 | // | ||
1300 | let rules = create_rules( | ||
1301 | r#" | ||
1302 | #[macro_export] | ||
1303 | macro_rules! RIDL { | ||
1304 | (interface $interface:ident ($vtbl:ident) : $pinterface:ident ($pvtbl:ident) | ||
1305 | {$( | ||
1306 | fn $method:ident(&mut self $(,$p:ident : $t:ty)*) -> $rtr:ty | ||
1307 | ),+} | ||
1308 | ) => { | ||
1309 | impl $interface { | ||
1310 | $(pub unsafe fn $method(&mut self) -> $rtr { | ||
1311 | ((*self.lpVtbl).$method)(self $(,$p)*) | ||
1312 | })+ | ||
1313 | } | ||
1314 | }; | ||
1315 | }"#, | ||
1316 | ); | ||
1317 | |||
1318 | let expanded = expand(&rules, r#" | ||
1319 | RIDL!{interface ID3D11Asynchronous(ID3D11AsynchronousVtbl): ID3D11DeviceChild(ID3D11DeviceChildVtbl) { | ||
1320 | fn GetDataSize(&mut self) -> UINT | ||
1321 | }}"#); | ||
1322 | assert_eq!(expanded.to_string(), "impl ID3D11Asynchronous {pub unsafe fn GetDataSize (& mut self) -> UINT {((* self . lpVtbl) .GetDataSize) (self)}}"); | ||
1323 | } | ||
1324 | |||
1325 | #[test] | ||
1326 | fn test_quick_error() { | ||
1327 | let rules = create_rules( | ||
1328 | r#" | ||
1329 | macro_rules! quick_error { | ||
1330 | |||
1331 | (SORT [enum $name:ident $( #[$meta:meta] )*] | ||
1332 | items [$($( #[$imeta:meta] )* | ||
1333 | => $iitem:ident: $imode:tt [$( $ivar:ident: $ityp:ty ),*] | ||
1334 | {$( $ifuncs:tt )*} )* ] | ||
1335 | buf [ ] | ||
1336 | queue [ ] | ||
1337 | ) => { | ||
1338 | quick_error!(ENUM_DEFINITION [enum $name $( #[$meta] )*] | ||
1339 | body [] | ||
1340 | queue [$( | ||
1341 | $( #[$imeta] )* | ||
1342 | => | ||
1343 | $iitem: $imode [$( $ivar: $ityp ),*] | ||
1344 | )*] | ||
1345 | ); | ||
1346 | }; | ||
1347 | |||
1348 | } | ||
1349 | "#, | ||
1350 | ); | ||
1351 | |||
1352 | let expanded = expand( | ||
1353 | &rules, | ||
1354 | r#" | ||
1355 | quick_error ! (SORT [enum Wrapped # [derive (Debug)]] items [ | ||
1356 | => One : UNIT [] {} | ||
1357 | => Two : TUPLE [s :String] {display ("two: {}" , s) from ()} | ||
1358 | ] buf [] queue []) ; | ||
1359 | "#, | ||
1360 | ); | ||
1361 | |||
1362 | assert_eq!(expanded.to_string(), "quick_error ! (ENUM_DEFINITION [enum Wrapped # [derive (Debug)]] body [] queue [=> One : UNIT [] => Two : TUPLE [s : String]]) ;"); | ||
1363 | } | ||