aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_expand/src/builtin_macro.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_expand/src/builtin_macro.rs')
-rw-r--r--crates/hir_expand/src/builtin_macro.rs140
1 files changed, 68 insertions, 72 deletions
diff --git a/crates/hir_expand/src/builtin_macro.rs b/crates/hir_expand/src/builtin_macro.rs
index 57bc6fbd7..2a79c892b 100644
--- a/crates/hir_expand/src/builtin_macro.rs
+++ b/crates/hir_expand/src/builtin_macro.rs
@@ -5,8 +5,9 @@ use crate::{
5}; 5};
6 6
7use base_db::{AnchoredPath, FileId}; 7use base_db::{AnchoredPath, FileId};
8use cfg::CfgExpr;
8use either::Either; 9use either::Either;
9use mbe::{parse_to_token_tree, ExpandResult}; 10use mbe::{parse_exprs_with_sep, parse_to_token_tree, ExpandResult};
10use parser::FragmentKind; 11use parser::FragmentKind;
11use syntax::ast::{self, AstToken}; 12use syntax::ast::{self, AstToken};
12 13
@@ -97,6 +98,7 @@ register_builtin! {
97 (format_args_nl, FormatArgsNl) => format_args_expand, 98 (format_args_nl, FormatArgsNl) => format_args_expand,
98 (llvm_asm, LlvmAsm) => asm_expand, 99 (llvm_asm, LlvmAsm) => asm_expand,
99 (asm, Asm) => asm_expand, 100 (asm, Asm) => asm_expand,
101 (cfg, Cfg) => cfg_expand,
100 102
101 EAGER: 103 EAGER:
102 (compile_error, CompileError) => compile_error_expand, 104 (compile_error, CompileError) => compile_error_expand,
@@ -182,25 +184,10 @@ fn assert_expand(
182 // ```, 184 // ```,
183 // which is wrong but useful. 185 // which is wrong but useful.
184 186
185 let mut args = Vec::new(); 187 let args = parse_exprs_with_sep(tt, ',');
186 let mut current = Vec::new();
187 for tt in tt.token_trees.iter().cloned() {
188 match tt {
189 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
190 args.push(current);
191 current = Vec::new();
192 }
193 _ => {
194 current.push(tt);
195 }
196 }
197 }
198 if !current.is_empty() {
199 args.push(current);
200 }
201 188
202 let arg_tts = args.into_iter().flat_map(|arg| { 189 let arg_tts = args.into_iter().flat_map(|arg| {
203 quote! { &(##arg), } 190 quote! { &(#arg), }
204 }.token_trees).collect::<Vec<_>>(); 191 }.token_trees).collect::<Vec<_>>();
205 192
206 let expanded = quote! { 193 let expanded = quote! {
@@ -238,35 +225,21 @@ fn format_args_expand(
238 // ]) 225 // ])
239 // ```, 226 // ```,
240 // which is still not really correct, but close enough for now 227 // which is still not really correct, but close enough for now
241 let mut args = Vec::new(); 228 let mut args = parse_exprs_with_sep(tt, ',');
242 let mut current = Vec::new(); 229
243 for tt in tt.token_trees.iter().cloned() {
244 match tt {
245 tt::TokenTree::Leaf(tt::Leaf::Punct(p)) if p.char == ',' => {
246 args.push(current);
247 current = Vec::new();
248 }
249 _ => {
250 current.push(tt);
251 }
252 }
253 }
254 if !current.is_empty() {
255 args.push(current);
256 }
257 if args.is_empty() { 230 if args.is_empty() {
258 return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule); 231 return ExpandResult::only_err(mbe::ExpandError::NoMatchingRule);
259 } 232 }
260 for arg in &mut args { 233 for arg in &mut args {
261 // Remove `key =`. 234 // Remove `key =`.
262 if matches!(arg.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint) 235 if matches!(arg.token_trees.get(1), Some(tt::TokenTree::Leaf(tt::Leaf::Punct(p))) if p.char == '=' && p.spacing != tt::Spacing::Joint)
263 { 236 {
264 arg.drain(..2); 237 arg.token_trees.drain(..2);
265 } 238 }
266 } 239 }
267 let _format_string = args.remove(0); 240 let _format_string = args.remove(0);
268 let arg_tts = args.into_iter().flat_map(|arg| { 241 let arg_tts = args.into_iter().flat_map(|arg| {
269 quote! { std::fmt::ArgumentV1::new(&(##arg), std::fmt::Display::fmt), } 242 quote! { std::fmt::ArgumentV1::new(&(#arg), std::fmt::Display::fmt), }
270 }.token_trees).collect::<Vec<_>>(); 243 }.token_trees).collect::<Vec<_>>();
271 let expanded = quote! { 244 let expanded = quote! {
272 std::fmt::Arguments::new_v1(&[], &[##arg_tts]) 245 std::fmt::Arguments::new_v1(&[], &[##arg_tts])
@@ -287,6 +260,18 @@ fn asm_expand(
287 ExpandResult::ok(expanded) 260 ExpandResult::ok(expanded)
288} 261}
289 262
263fn cfg_expand(
264 db: &dyn AstDatabase,
265 id: LazyMacroId,
266 tt: &tt::Subtree,
267) -> ExpandResult<tt::Subtree> {
268 let loc = db.lookup_intern_macro(id);
269 let expr = CfgExpr::parse(tt);
270 let enabled = db.crate_graph()[loc.krate].cfg_options.check(&expr) != Some(false);
271 let expanded = if enabled { quote!(true) } else { quote!(false) };
272 ExpandResult::ok(expanded)
273}
274
290fn unquote_str(lit: &tt::Literal) -> Option<String> { 275fn unquote_str(lit: &tt::Literal) -> Option<String> {
291 let lit = ast::make::tokens::literal(&lit.to_string()); 276 let lit = ast::make::tokens::literal(&lit.to_string());
292 let token = ast::String::cast(lit)?; 277 let token = ast::String::cast(lit)?;
@@ -506,6 +491,7 @@ mod tests {
506 MacroCallLoc, 491 MacroCallLoc,
507 }; 492 };
508 use base_db::{fixture::WithFixture, SourceDatabase}; 493 use base_db::{fixture::WithFixture, SourceDatabase};
494 use expect_test::{expect, Expect};
509 use std::sync::Arc; 495 use std::sync::Arc;
510 use syntax::ast::NameOwner; 496 use syntax::ast::NameOwner;
511 497
@@ -589,87 +575,86 @@ mod tests {
589 db.parse_or_expand(file_id).unwrap().to_string() 575 db.parse_or_expand(file_id).unwrap().to_string()
590 } 576 }
591 577
578 fn check_expansion(ra_fixture: &str, expect: Expect) {
579 let expansion = expand_builtin_macro(ra_fixture);
580 expect.assert_eq(&expansion);
581 }
582
592 #[test] 583 #[test]
593 fn test_column_expand() { 584 fn test_column_expand() {
594 let expanded = expand_builtin_macro( 585 check_expansion(
595 r#" 586 r#"
596 #[rustc_builtin_macro] 587 #[rustc_builtin_macro]
597 macro_rules! column {() => {}} 588 macro_rules! column {() => {}}
598 column!() 589 column!()
599 "#, 590 "#,
591 expect![["0"]],
600 ); 592 );
601
602 assert_eq!(expanded, "0");
603 } 593 }
604 594
605 #[test] 595 #[test]
606 fn test_line_expand() { 596 fn test_line_expand() {
607 let expanded = expand_builtin_macro( 597 check_expansion(
608 r#" 598 r#"
609 #[rustc_builtin_macro] 599 #[rustc_builtin_macro]
610 macro_rules! line {() => {}} 600 macro_rules! line {() => {}}
611 line!() 601 line!()
612 "#, 602 "#,
603 expect![["0"]],
613 ); 604 );
614
615 assert_eq!(expanded, "0");
616 } 605 }
617 606
618 #[test] 607 #[test]
619 fn test_stringify_expand() { 608 fn test_stringify_expand() {
620 let expanded = expand_builtin_macro( 609 check_expansion(
621 r#" 610 r#"
622 #[rustc_builtin_macro] 611 #[rustc_builtin_macro]
623 macro_rules! stringify {() => {}} 612 macro_rules! stringify {() => {}}
624 stringify!(a b c) 613 stringify!(a b c)
625 "#, 614 "#,
615 expect![["\"a b c\""]],
626 ); 616 );
627
628 assert_eq!(expanded, "\"a b c\"");
629 } 617 }
630 618
631 #[test] 619 #[test]
632 fn test_env_expand() { 620 fn test_env_expand() {
633 let expanded = expand_builtin_macro( 621 check_expansion(
634 r#" 622 r#"
635 #[rustc_builtin_macro] 623 #[rustc_builtin_macro]
636 macro_rules! env {() => {}} 624 macro_rules! env {() => {}}
637 env!("TEST_ENV_VAR") 625 env!("TEST_ENV_VAR")
638 "#, 626 "#,
627 expect![["\"__RA_UNIMPLEMENTED__\""]],
639 ); 628 );
640
641 assert_eq!(expanded, "\"__RA_UNIMPLEMENTED__\"");
642 } 629 }
643 630
644 #[test] 631 #[test]
645 fn test_option_env_expand() { 632 fn test_option_env_expand() {
646 let expanded = expand_builtin_macro( 633 check_expansion(
647 r#" 634 r#"
648 #[rustc_builtin_macro] 635 #[rustc_builtin_macro]
649 macro_rules! option_env {() => {}} 636 macro_rules! option_env {() => {}}
650 option_env!("TEST_ENV_VAR") 637 option_env!("TEST_ENV_VAR")
651 "#, 638 "#,
639 expect![["std::option::Option::None:: < &str>"]],
652 ); 640 );
653
654 assert_eq!(expanded, "std::option::Option::None:: < &str>");
655 } 641 }
656 642
657 #[test] 643 #[test]
658 fn test_file_expand() { 644 fn test_file_expand() {
659 let expanded = expand_builtin_macro( 645 check_expansion(
660 r#" 646 r#"
661 #[rustc_builtin_macro] 647 #[rustc_builtin_macro]
662 macro_rules! file {() => {}} 648 macro_rules! file {() => {}}
663 file!() 649 file!()
664 "#, 650 "#,
651 expect![[r#""""#]],
665 ); 652 );
666
667 assert_eq!(expanded, "\"\"");
668 } 653 }
669 654
670 #[test] 655 #[test]
671 fn test_assert_expand() { 656 fn test_assert_expand() {
672 let expanded = expand_builtin_macro( 657 check_expansion(
673 r#" 658 r#"
674 #[rustc_builtin_macro] 659 #[rustc_builtin_macro]
675 macro_rules! assert { 660 macro_rules! assert {
@@ -678,14 +663,13 @@ mod tests {
678 } 663 }
679 assert!(true, "{} {:?}", arg1(a, b, c), arg2); 664 assert!(true, "{} {:?}", arg1(a, b, c), arg2);
680 "#, 665 "#,
666 expect![["{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}"]],
681 ); 667 );
682
683 assert_eq!(expanded, "{{(&(true), &(\"{} {:?}\"), &(arg1(a,b,c)), &(arg2),);}}");
684 } 668 }
685 669
686 #[test] 670 #[test]
687 fn test_compile_error_expand() { 671 fn test_compile_error_expand() {
688 let expanded = expand_builtin_macro( 672 check_expansion(
689 r#" 673 r#"
690 #[rustc_builtin_macro] 674 #[rustc_builtin_macro]
691 macro_rules! compile_error { 675 macro_rules! compile_error {
@@ -694,15 +678,14 @@ mod tests {
694 } 678 }
695 compile_error!("error!"); 679 compile_error!("error!");
696 "#, 680 "#,
681 // This expands to nothing (since it's in item position), but emits an error.
682 expect![[""]],
697 ); 683 );
698
699 // This expands to nothing (since it's in item position), but emits an error.
700 assert_eq!(expanded, "");
701 } 684 }
702 685
703 #[test] 686 #[test]
704 fn test_format_args_expand() { 687 fn test_format_args_expand() {
705 let expanded = expand_builtin_macro( 688 check_expansion(
706 r#" 689 r#"
707 #[rustc_builtin_macro] 690 #[rustc_builtin_macro]
708 macro_rules! format_args { 691 macro_rules! format_args {
@@ -711,17 +694,32 @@ mod tests {
711 } 694 }
712 format_args!("{} {:?}", arg1(a, b, c), arg2); 695 format_args!("{} {:?}", arg1(a, b, c), arg2);
713 "#, 696 "#,
697 expect![[
698 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"#
699 ]],
714 ); 700 );
701 }
715 702
716 assert_eq!( 703 #[test]
717 expanded, 704 fn test_format_args_expand_with_comma_exprs() {
718 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(arg1(a,b,c)),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(arg2),std::fmt::Display::fmt),])"# 705 check_expansion(
706 r#"
707 #[rustc_builtin_macro]
708 macro_rules! format_args {
709 ($fmt:expr) => ({ /* compiler built-in */ });
710 ($fmt:expr, $($args:tt)*) => ({ /* compiler built-in */ })
711 }
712 format_args!("{} {:?}", a::<A,B>(), b);
713 "#,
714 expect![[
715 r#"std::fmt::Arguments::new_v1(&[], &[std::fmt::ArgumentV1::new(&(a::<A,B>()),std::fmt::Display::fmt),std::fmt::ArgumentV1::new(&(b),std::fmt::Display::fmt),])"#
716 ]],
719 ); 717 );
720 } 718 }
721 719
722 #[test] 720 #[test]
723 fn test_include_bytes_expand() { 721 fn test_include_bytes_expand() {
724 let expanded = expand_builtin_macro( 722 check_expansion(
725 r#" 723 r#"
726 #[rustc_builtin_macro] 724 #[rustc_builtin_macro]
727 macro_rules! include_bytes { 725 macro_rules! include_bytes {
@@ -730,21 +728,19 @@ mod tests {
730 } 728 }
731 include_bytes("foo"); 729 include_bytes("foo");
732 "#, 730 "#,
731 expect![[r#"b"""#]],
733 ); 732 );
734
735 assert_eq!(expanded, r#"b"""#);
736 } 733 }
737 734
738 #[test] 735 #[test]
739 fn test_concat_expand() { 736 fn test_concat_expand() {
740 let expanded = expand_builtin_macro( 737 check_expansion(
741 r##" 738 r##"
742 #[rustc_builtin_macro] 739 #[rustc_builtin_macro]
743 macro_rules! concat {} 740 macro_rules! concat {}
744 concat!("foo", "r", 0, r#"bar"#, false); 741 concat!("foo", "r", 0, r#"bar"#, false);
745 "##, 742 "##,
743 expect![[r#""foor0barfalse""#]],
746 ); 744 );
747
748 assert_eq!(expanded, r#""foor0barfalse""#);
749 } 745 }
750} 746}