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