1use hir::db::ExpandDatabase;
2use hir::{ExpandResult, InFile, Semantics};
3use ide_db::{
4 FileId, RootDatabase, base_db::Crate, helpers::pick_best_token,
5 syntax_helpers::prettify_macro_expansion,
6};
7use span::{SpanMap, SyntaxContext, TextRange, TextSize};
8use stdx::format_to;
9use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast, ted};
10
11use crate::FilePosition;
12
13pub struct ExpandedMacro {
14 pub name: String,
15 pub expansion: String,
16}
17
18pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<ExpandedMacro> {
28 let sema = Semantics::new(db);
29 let file_id = sema.attach_first_edition(position.file_id)?;
30 let file = sema.parse(file_id);
31 let krate = sema.file_to_module_def(file_id.file_id(db))?.krate().into();
32
33 let tok = pick_best_token(file.syntax().token_at_offset(position.offset), |kind| match kind {
34 SyntaxKind::IDENT => 1,
35 _ => 0,
36 })?;
37
38 let derive = sema.descend_into_macros_exact(tok.clone()).into_iter().find_map(|descended| {
47 let macro_file = sema.hir_file_for(&descended.parent()?).macro_file()?;
48 if !macro_file.is_derive_attr_pseudo_expansion(db) {
49 return None;
50 }
51
52 let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
53 let InFile { file_id, value: tokens } =
55 hir::InMacroFile::new(macro_file, descended).upmap_once(db);
56 let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
57 let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
58 let expansions = sema.expand_derive_macro(&attr)?;
59 let idx = attr
60 .token_tree()?
61 .token_trees_and_tokens()
62 .filter_map(NodeOrToken::into_token)
63 .take_while(|it| it != &token)
64 .filter(|it| it.kind() == T![,])
65 .count();
66 let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone();
67 let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?;
68 let expansion_span_map = db.expansion_span_map(expansion_file_id);
69 let mut expansion = format(
70 db,
71 SyntaxKind::MACRO_ITEMS,
72 position.file_id,
73 expansion,
74 &expansion_span_map,
75 krate,
76 );
77 if let Some(err) = err {
78 expansion.insert_str(
79 0,
80 &format!("Expansion had errors: {}\n\n", err.render_to_string(sema.db)),
81 );
82 }
83 Some(ExpandedMacro { name, expansion })
84 });
85
86 if derive.is_some() {
87 return derive;
88 }
89
90 let syntax_token = sema.descend_into_macros_exact(tok);
91 'tokens: for syntax_token in syntax_token {
92 let mut anc = syntax_token.parent_ancestors();
93 let mut span_map = SpanMap::empty();
94 let mut error = String::new();
95 let (name, expanded, kind) = loop {
96 let Some(node) = anc.next() else {
97 continue 'tokens;
98 };
99
100 if let Some(item) = ast::Item::cast(node.clone())
101 && let Some(def) = sema.resolve_attr_macro_call(&item)
102 {
103 break (
104 def.name(db).display(db, file_id.edition(db)).to_string(),
105 expand_macro_recur(&sema, &item, &mut error, &mut span_map, TextSize::new(0))?,
106 SyntaxKind::MACRO_ITEMS,
107 );
108 }
109 if let Some(mac) = ast::MacroCall::cast(node) {
110 let mut name = mac.path()?.segment()?.name_ref()?.to_string();
111 name.push('!');
112 let syntax_kind =
113 mac.syntax().parent().map(|it| it.kind()).unwrap_or(SyntaxKind::MACRO_ITEMS);
114 break (
115 name,
116 expand_macro_recur(
117 &sema,
118 &ast::Item::MacroCall(mac),
119 &mut error,
120 &mut span_map,
121 TextSize::new(0),
122 )?,
123 syntax_kind,
124 );
125 }
126 };
127
128 let mut expansion = format(db, kind, position.file_id, expanded, &span_map, krate);
132
133 if !error.is_empty() {
134 expansion.insert_str(0, &format!("Expansion had errors:{error}\n\n"));
135 }
136 return Some(ExpandedMacro { name, expansion });
137 }
138 None
139}
140
141fn expand_macro_recur(
142 sema: &Semantics<'_, RootDatabase>,
143 macro_call: &ast::Item,
144 error: &mut String,
145 result_span_map: &mut SpanMap<SyntaxContext>,
146 offset_in_original_node: TextSize,
147) -> Option<SyntaxNode> {
148 let ExpandResult { value: expanded, err } = match macro_call {
149 item @ ast::Item::MacroCall(macro_call) => sema
150 .expand_attr_macro(item)
151 .map(|it| it.map(|it| it.value))
152 .or_else(|| sema.expand_allowed_builtins(macro_call))?,
153 item => sema.expand_attr_macro(item)?.map(|it| it.value),
154 };
155 let expanded = expanded.clone_for_update();
156 if let Some(err) = err {
157 format_to!(error, "\n{}", err.render_to_string(sema.db));
158 }
159 let file_id =
160 sema.hir_file_for(&expanded).macro_file().expect("expansion must produce a macro file");
161 let expansion_span_map = sema.db.expansion_span_map(file_id);
162 result_span_map.merge(
163 TextRange::at(offset_in_original_node, macro_call.syntax().text_range().len()),
164 expanded.text_range().len(),
165 &expansion_span_map,
166 );
167 Some(expand(sema, expanded, error, result_span_map, u32::from(offset_in_original_node) as i32))
168}
169
170fn expand(
171 sema: &Semantics<'_, RootDatabase>,
172 expanded: SyntaxNode,
173 error: &mut String,
174 result_span_map: &mut SpanMap<SyntaxContext>,
175 mut offset_in_original_node: i32,
176) -> SyntaxNode {
177 let children = expanded.descendants().filter_map(ast::Item::cast);
178 let mut replacements = Vec::new();
179
180 for child in children {
181 if let Some(new_node) = expand_macro_recur(
182 sema,
183 &child,
184 error,
185 result_span_map,
186 TextSize::new(
187 (offset_in_original_node + (u32::from(child.syntax().text_range().start()) as i32))
188 as u32,
189 ),
190 ) {
191 offset_in_original_node = offset_in_original_node
192 + (u32::from(new_node.text_range().len()) as i32)
193 - (u32::from(child.syntax().text_range().len()) as i32);
194 if expanded == *child.syntax() {
196 return new_node;
197 }
198 replacements.push((child, new_node));
199 }
200 }
201
202 replacements.into_iter().rev().for_each(|(old, new)| ted::replace(old.syntax(), new));
203 expanded
204}
205
206fn format(
207 db: &RootDatabase,
208 kind: SyntaxKind,
209 file_id: FileId,
210 expanded: SyntaxNode,
211 span_map: &SpanMap<SyntaxContext>,
212 krate: Crate,
213) -> String {
214 let expansion = prettify_macro_expansion(db, expanded, span_map, krate).to_string();
215
216 _format(db, kind, file_id, &expansion).unwrap_or(expansion)
217}
218
219#[cfg(any(test, target_arch = "wasm32", target_os = "emscripten"))]
220fn _format(
221 _db: &RootDatabase,
222 _kind: SyntaxKind,
223 _file_id: FileId,
224 expansion: &str,
225) -> Option<String> {
226 use itertools::Itertools;
228 Some(expansion.lines().map(|x| x.trim_end()).join("\n"))
229}
230
231#[cfg(not(any(test, target_arch = "wasm32", target_os = "emscripten")))]
232fn _format(
233 db: &RootDatabase,
234 kind: SyntaxKind,
235 file_id: FileId,
236 expansion: &str,
237) -> Option<String> {
238 use ide_db::base_db::RootQueryDb;
239
240 const DOLLAR_CRATE_REPLACE: &str = "__r_a_";
242 const BUILTIN_REPLACE: &str = "builtin__POUND";
243 let expansion =
244 expansion.replace("$crate", DOLLAR_CRATE_REPLACE).replace("builtin #", BUILTIN_REPLACE);
245 let (prefix, suffix) = match kind {
246 SyntaxKind::MACRO_PAT => ("fn __(", ": u32);"),
247 SyntaxKind::MACRO_EXPR | SyntaxKind::MACRO_STMTS => ("fn __() {", "}"),
248 SyntaxKind::MACRO_TYPE => ("type __ =", ";"),
249 _ => ("", ""),
250 };
251 let expansion = format!("{prefix}{expansion}{suffix}");
252
253 let &crate_id = db.relevant_crates(file_id).iter().next()?;
254 let edition = crate_id.data(db).edition;
255
256 #[allow(clippy::disallowed_methods)]
257 let mut cmd = std::process::Command::new(toolchain::Tool::Rustfmt.path());
258 cmd.arg("--edition");
259 cmd.arg(edition.to_string());
260
261 let mut rustfmt = cmd
262 .stdin(std::process::Stdio::piped())
263 .stdout(std::process::Stdio::piped())
264 .stderr(std::process::Stdio::piped())
265 .spawn()
266 .ok()?;
267
268 std::io::Write::write_all(&mut rustfmt.stdin.as_mut()?, expansion.as_bytes()).ok()?;
269
270 let output = rustfmt.wait_with_output().ok()?;
271 let captured_stdout = String::from_utf8(output.stdout).ok()?;
272
273 if output.status.success() && !captured_stdout.trim().is_empty() {
274 let output = captured_stdout
275 .replace(DOLLAR_CRATE_REPLACE, "$crate")
276 .replace(BUILTIN_REPLACE, "builtin #");
277 let output = output.trim().strip_prefix(prefix)?;
278 let output = match kind {
279 SyntaxKind::MACRO_PAT => {
280 output.strip_suffix(suffix).or_else(|| output.strip_suffix(": u32,\n);"))?
281 }
282 _ => output.strip_suffix(suffix)?,
283 };
284 let trim_indent = stdx::trim_indent(output);
285 tracing::debug!("expand_macro: formatting succeeded");
286 Some(trim_indent)
287 } else {
288 None
289 }
290}
291
292#[cfg(test)]
293mod tests {
294 use expect_test::{Expect, expect};
295
296 use crate::fixture;
297
298 #[track_caller]
299 fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
300 let (analysis, pos) = fixture::position(ra_fixture);
301 let expansion = analysis.expand_macro(pos).unwrap().unwrap();
302 let actual = format!("{}\n{}", expansion.name, expansion.expansion);
303 expect.assert_eq(&actual);
304 }
305
306 #[test]
307 fn expand_allowed_builtin_macro() {
308 check(
309 r#"
310//- minicore: concat
311$0concat!("test", 10, 'b', true);"#,
312 expect![[r#"
313 concat!
314 "test10btrue""#]],
315 );
316 }
317
318 #[test]
319 fn do_not_expand_disallowed_macro() {
320 let (analysis, pos) = fixture::position(
321 r#"
322//- minicore: asm
323$0asm!("0x300, x0");"#,
324 );
325 let expansion = analysis.expand_macro(pos).unwrap();
326 assert!(expansion.is_none());
327 }
328
329 #[test]
330 fn macro_expand_as_keyword() {
331 check(
332 r#"
333macro_rules! bar {
334 ($i:tt) => { $i as _ }
335}
336fn main() {
337 let x: u64 = ba$0r!(5i64);
338}
339"#,
340 expect![[r#"
341 bar!
342 5i64 as _"#]],
343 );
344 }
345
346 #[test]
347 fn macro_expand_underscore() {
348 check(
349 r#"
350macro_rules! bar {
351 ($i:tt) => { for _ in 0..$i {} }
352}
353fn main() {
354 ba$0r!(42);
355}
356"#,
357 expect![[r#"
358 bar!
359 for _ in 0..42{}"#]],
360 );
361 }
362
363 #[test]
364 fn macro_expand_recursive_expansion() {
365 check(
366 r#"
367macro_rules! bar {
368 () => { fn b() {} }
369}
370macro_rules! foo {
371 () => { bar!(); }
372}
373macro_rules! baz {
374 () => { foo!(); }
375}
376f$0oo!();
377"#,
378 expect![[r#"
379 foo!
380 fn b(){}"#]],
381 );
382 }
383
384 #[test]
385 fn macro_expand_multiple_lines() {
386 check(
387 r#"
388macro_rules! foo {
389 () => {
390 fn some_thing() -> u32 {
391 let a = 0;
392 a + 10
393 }
394 }
395}
396f$0oo!();
397 "#,
398 expect![[r#"
399 foo!
400 fn some_thing() -> u32 {
401 let a = 0;
402 a+10
403 }"#]],
404 );
405 }
406
407 #[test]
408 fn macro_expand_match_ast() {
409 check(
410 r#"
411macro_rules! match_ast {
412 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
413 (match ($node:expr) {
414 $( ast::$ast:ident($it:ident) => $res:block, )*
415 _ => $catch_all:expr $(,)?
416 }) => {{
417 $( if let Some($it) = ast::$ast::cast($node.clone()) $res else )*
418 { $catch_all }
419 }};
420}
421
422fn main() {
423 mat$0ch_ast! {
424 match container {
425 ast::TraitDef(it) => {},
426 ast::ImplDef(it) => {},
427 _ => { continue },
428 }
429 }
430}
431"#,
432 expect![[r#"
433 match_ast!
434 {
435 if let Some(it) = ast::TraitDef::cast(container.clone()){}
436 else if let Some(it) = ast::ImplDef::cast(container.clone()){}
437 else {
438 {
439 continue
440 }
441 }
442 }"#]],
443 );
444 }
445
446 #[test]
447 fn macro_expand_match_ast_inside_let_statement() {
448 check(
449 r#"
450macro_rules! match_ast {
451 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
452 (match ($node:expr) {}) => {{}};
453}
454
455fn main() {
456 let p = f(|it| {
457 let res = mat$0ch_ast! { match c {}};
458 Some(res)
459 })?;
460}
461"#,
462 expect![[r#"
463 match_ast!
464 {}"#]],
465 );
466 }
467
468 #[test]
469 fn macro_expand_inner_macro_rules() {
470 check(
471 r#"
472macro_rules! foo {
473 ($t:tt) => {{
474 macro_rules! bar {
475 () => {
476 $t
477 }
478 }
479 bar!()
480 }};
481}
482
483fn main() {
484 foo$0!(42);
485}
486 "#,
487 expect![[r#"
488 foo!
489 {
490 macro_rules! bar {
491 () => {
492 42
493 }
494 }
495 42
496 }"#]],
497 );
498 }
499
500 #[test]
501 fn macro_expand_inner_macro_fail_to_expand() {
502 check(
503 r#"
504macro_rules! bar {
505 (BAD) => {};
506}
507macro_rules! foo {
508 () => {bar!()};
509}
510
511fn main() {
512 let res = fo$0o!();
513}
514"#,
515 expect![[r#"
516 foo!
517 Expansion had errors:
518 expected ident: `BAD`
519
520 "#]],
521 );
522 }
523
524 #[test]
525 fn macro_expand_with_dollar_crate() {
526 check(
527 r#"
528#[macro_export]
529macro_rules! bar {
530 () => {0};
531}
532macro_rules! foo {
533 () => {$crate::bar!()};
534}
535
536fn main() {
537 let res = fo$0o!();
538}
539"#,
540 expect![[r#"
541 foo!
542 0"#]],
543 );
544 }
545
546 #[test]
547 fn macro_expand_with_dyn_absolute_path() {
548 check(
549 r#"
550macro_rules! foo {
551 () => {fn f<T>(_: &dyn ::std::marker::Copy) {}};
552}
553
554fn main() {
555 fo$0o!()
556}
557"#,
558 expect![[r#"
559 foo!
560 fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
561 );
562 }
563
564 #[test]
565 fn macro_expand_item_expansion_in_expression_call() {
566 check(
567 r#"
568macro_rules! foo {
569 () => {fn f<T>() {}};
570}
571
572fn main() {
573 let res = fo$0o!();
574}
575"#,
576 expect![[r#"
577 foo!
578 fn f<T>(){}"#]],
579 );
580 }
581
582 #[test]
583 fn macro_expand_derive() {
584 check(
585 r#"
586//- proc_macros: identity
587//- minicore: clone, derive
588
589#[proc_macros::identity]
590#[derive(C$0lone)]
591struct Foo {}
592"#,
593 expect![[r#"
594 Clone
595 impl <>core::clone::Clone for Foo< >where {
596 fn clone(&self) -> Self {
597 match self {
598 Foo{}
599 => Foo{}
600 ,
601
602 }
603 }
604
605 }"#]],
606 );
607 }
608
609 #[test]
610 fn macro_expand_derive2() {
611 check(
612 r#"
613//- minicore: copy, clone, derive
614
615#[derive(Cop$0y)]
616#[derive(Clone)]
617struct Foo {}
618"#,
619 expect![[r#"
620 Copy
621 impl <>core::marker::Copy for Foo< >where{}"#]],
622 );
623 }
624
625 #[test]
626 fn macro_expand_derive_multi() {
627 check(
628 r#"
629//- minicore: copy, clone, derive
630
631#[derive(Cop$0y, Clone)]
632struct Foo {}
633"#,
634 expect![[r#"
635 Copy
636 impl <>core::marker::Copy for Foo< >where{}"#]],
637 );
638 check(
639 r#"
640//- minicore: copy, clone, derive
641
642#[derive(Copy, Cl$0one)]
643struct Foo {}
644"#,
645 expect![[r#"
646 Clone
647 impl <>core::clone::Clone for Foo< >where {
648 fn clone(&self) -> Self {
649 match self {
650 Foo{}
651 => Foo{}
652 ,
653
654 }
655 }
656
657 }"#]],
658 );
659 }
660
661 #[test]
662 fn dollar_crate() {
663 check(
664 r#"
665//- /a.rs crate:a
666pub struct Foo;
667#[macro_export]
668macro_rules! m {
669 ( $i:ident ) => { $crate::Foo; $crate::Foo; $i::Foo; };
670}
671//- /b.rs crate:b deps:a
672pub struct Foo;
673#[macro_export]
674macro_rules! m {
675 () => { a::m!($crate); $crate::Foo; $crate::Foo; };
676}
677//- /c.rs crate:c deps:b,a
678pub struct Foo;
679#[macro_export]
680macro_rules! m {
681 () => { b::m!(); $crate::Foo; $crate::Foo; };
682}
683fn bar() {
684 m$0!();
685}
686"#,
687 expect![[r#"
688m!
689a::Foo;
690a::Foo;
691b::Foo;
692;
693b::Foo;
694b::Foo;
695;
696crate::Foo;
697crate::Foo;"#]],
698 );
699 }
700
701 #[test]
702 fn semi_glueing() {
703 check(
704 r#"
705macro_rules! __log_value {
706 ($key:ident :$capture:tt =) => {};
707}
708
709macro_rules! __log {
710 ($key:tt $(:$capture:tt)? $(= $value:expr)?; $($arg:tt)+) => {
711 __log_value!($key $(:$capture)* = $($value)*);
712 };
713}
714
715__log!(written:%; "Test"$0);
716 "#,
717 expect![[r#"
718 __log!
719 "#]],
720 );
721 }
722
723 #[test]
724 fn assoc_call() {
725 check(
726 r#"
727macro_rules! mac {
728 () => { fn assoc() {} }
729}
730impl () {
731 mac$0!();
732}
733 "#,
734 expect![[r#"
735 mac!
736 fn assoc(){}"#]],
737 );
738 }
739
740 #[test]
741 fn eager() {
742 check(
743 r#"
744//- minicore: concat
745macro_rules! my_concat {
746 ($head:expr, $($tail:tt)*) => { concat!($head, $($tail)*) };
747}
748
749
750fn test() {
751 _ = my_concat!(
752 conc$0at!("<", ">"),
753 "hi",
754 );
755}
756 "#,
757 expect![[r#"
758 concat!
759 "<>""#]],
760 );
761 }
762
763 #[test]
764 fn in_included() {
765 check(
766 r#"
767//- minicore: include
768//- /main.rs crate:main
769include!("./included.rs");
770//- /included.rs
771macro_rules! foo {
772 () => { fn item() {} };
773}
774foo$0!();
775"#,
776 expect![[r#"
777 foo!
778 fn item(){}"#]],
779 );
780 }
781
782 #[test]
783 fn include() {
784 check(
785 r#"
786//- minicore: include
787//- /main.rs crate:main
788include$0!("./included.rs");
789//- /included.rs
790macro_rules! foo {
791 () => { fn item() {} };
792}
793foo();
794"#,
795 expect![[r#"
796 include!
797 macro_rules! foo {
798 () => {
799 fn item(){}
800
801 };
802 }
803 foo();"#]],
804 );
805 }
806
807 #[test]
808 fn works_in_sig() {
809 check(
810 r#"
811macro_rules! foo {
812 () => { u32 };
813}
814fn foo() -> foo$0!() {
815 42
816}
817"#,
818 expect![[r#"
819 foo!
820 u32"#]],
821 );
822 check(
823 r#"
824macro_rules! foo {
825 () => { u32 };
826}
827fn foo(_: foo$0!() ) {}
828"#,
829 expect![[r#"
830 foo!
831 u32"#]],
832 );
833 }
834
835 #[test]
836 fn works_in_generics() {
837 check(
838 r#"
839trait Trait {}
840macro_rules! foo {
841 () => { Trait };
842}
843impl<const C: foo$0!()> Trait for () {}
844"#,
845 expect![[r#"
846 foo!
847 Trait"#]],
848 );
849 }
850
851 #[test]
852 fn works_in_fields() {
853 check(
854 r#"
855macro_rules! foo {
856 () => { u32 };
857}
858struct S {
859 field: foo$0!(),
860}
861"#,
862 expect![[r#"
863 foo!
864 u32"#]],
865 );
866 }
867}