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