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