1use hir::{ExpandResult, InFile, Semantics};
2use ide_db::{
3 FileId, RootDatabase, base_db::Crate, helpers::pick_best_token,
4 syntax_helpers::prettify_macro_expansion,
5};
6use span::{SpanMap, TextRange, TextSize};
7use stdx::format_to;
8use syntax::syntax_editor::SyntaxEditor;
9use syntax::{AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T, ast};
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 = expansion_file_id.expansion_span_map(db);
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 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 = file_id.expansion_span_map(sema.db);
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,
175 mut offset_in_original_node: i32,
176) -> SyntaxNode {
177 let (editor, expanded) = SyntaxEditor::new(expanded);
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)| editor.replace(old.syntax(), new));
204 editor.finish().new_root().clone()
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#"
451//- minicore: try
452macro_rules! match_ast {
453 (match $node:ident { $($tt:tt)* }) => { match_ast!(match ($node) { $($tt)* }) };
454 (match ($node:expr) {}) => {{}};
455}
456
457fn main() {
458 let p = f(|it| {
459 let res = mat$0ch_ast! { match c {}};
460 Some(res)
461 })?;
462}
463"#,
464 expect![[r#"
465 match_ast!
466 {}"#]],
467 );
468 }
469
470 #[test]
471 fn macro_expand_inner_macro_rules() {
472 check(
473 r#"
474macro_rules! foo {
475 ($t:tt) => {{
476 macro_rules! bar {
477 () => {
478 $t
479 }
480 }
481 bar!()
482 }};
483}
484
485fn main() {
486 foo$0!(42);
487}
488 "#,
489 expect![[r#"
490 foo!
491 {
492 macro_rules! bar {
493 () => {
494 42
495 }
496 }
497 42
498 }"#]],
499 );
500 }
501
502 #[test]
503 fn macro_expand_inner_macro_fail_to_expand() {
504 check(
505 r#"
506macro_rules! bar {
507 (BAD) => {};
508}
509macro_rules! foo {
510 () => {bar!()};
511}
512
513fn main() {
514 let res = fo$0o!();
515}
516"#,
517 expect![[r#"
518 foo!
519 Expansion had errors:
520 expected ident: `BAD`
521
522 "#]],
523 );
524 }
525
526 #[test]
527 fn macro_expand_with_dollar_crate() {
528 check(
529 r#"
530#[macro_export]
531macro_rules! bar {
532 () => {0};
533}
534macro_rules! foo {
535 () => {$crate::bar!()};
536}
537
538fn main() {
539 let res = fo$0o!();
540}
541"#,
542 expect![[r#"
543 foo!
544 0"#]],
545 );
546 }
547
548 #[test]
549 fn macro_expand_with_dyn_absolute_path() {
550 check(
551 r#"
552macro_rules! foo {
553 () => {fn f<T>(_: &dyn ::std::marker::Copy) {}};
554}
555
556fn main() {
557 fo$0o!()
558}
559"#,
560 expect![[r#"
561 foo!
562 fn f<T>(_: &dyn ::std::marker::Copy){}"#]],
563 );
564 }
565
566 #[test]
567 fn macro_expand_item_expansion_in_expression_call() {
568 check(
569 r#"
570macro_rules! foo {
571 () => {fn f<T>() {}};
572}
573
574fn main() {
575 let res = fo$0o!();
576}
577"#,
578 expect![[r#"
579 foo!
580 fn f<T>(){}"#]],
581 );
582 }
583
584 #[test]
585 fn macro_expand_derive() {
586 check(
587 r#"
588//- proc_macros: identity, derive_identity
589//- minicore: derive
590
591#[proc_macros::identity]
592#[derive(proc_macros::DeriveIde$0ntity)]
593struct Foo {}
594"#,
595 expect![[r#"
596 proc_macros::DeriveIdentity
597 struct Foo {}"#]],
598 );
599 }
600
601 #[test]
602 fn macro_expand_derive2() {
603 check(
604 r#"
605//- proc_macros: derive_identity
606//- minicore: derive
607
608#[derive(proc_macros::$0DeriveIdentity)]
609#[derive(proc_macros::DeriveIdentity)]
610struct Foo {}
611"#,
612 expect![[r#"
613 proc_macros::DeriveIdentity
614 #[derive(proc_macros::DeriveIdentity)]
615 struct Foo {}"#]],
616 );
617 }
618
619 #[test]
620 fn macro_expand_derive_multi() {
621 check(
622 r#"
623//- proc_macros: derive_identity
624//- minicore: derive
625
626#[derive(proc_macros::DeriveIdent$0ity, proc_macros::DeriveIdentity)]
627struct Foo {}
628"#,
629 expect![[r#"
630 proc_macros::DeriveIdentity
631 struct Foo {}"#]],
632 );
633 check(
634 r#"
635//- proc_macros: derive_identity
636//- minicore: derive
637
638#[derive(proc_macros::DeriveIdentity, proc_macros::De$0riveIdentity)]
639struct Foo {}
640"#,
641 expect![[r#"
642 proc_macros::DeriveIdentity
643 struct Foo {}"#]],
644 );
645 }
646
647 #[test]
648 fn dollar_crate() {
649 check(
650 r#"
651//- /a.rs crate:a
652pub struct Foo;
653#[macro_export]
654macro_rules! m {
655 ( $i:ident ) => { $crate::Foo; $crate::Foo; $i::Foo; };
656}
657//- /b.rs crate:b deps:a
658pub struct Foo;
659#[macro_export]
660macro_rules! m {
661 () => { a::m!($crate); $crate::Foo; $crate::Foo; };
662}
663//- /c.rs crate:c deps:b,a
664pub struct Foo;
665#[macro_export]
666macro_rules! m {
667 () => { b::m!(); $crate::Foo; $crate::Foo; };
668}
669fn bar() {
670 m$0!();
671}
672"#,
673 expect![[r#"
674m!
675a::Foo;
676a::Foo;
677b::Foo;
678;
679b::Foo;
680b::Foo;
681;
682crate::Foo;
683crate::Foo;"#]],
684 );
685 }
686
687 #[test]
688 fn semi_glueing() {
689 check(
690 r#"
691macro_rules! __log_value {
692 ($key:ident :$capture:tt =) => {};
693}
694
695macro_rules! __log {
696 ($key:tt $(:$capture:tt)? $(= $value:expr)?; $($arg:tt)+) => {
697 __log_value!($key $(:$capture)* = $($value)*);
698 };
699}
700
701__log!(written:%; "Test"$0);
702 "#,
703 expect![[r#"
704 __log!
705 "#]],
706 );
707 }
708
709 #[test]
710 fn assoc_call() {
711 check(
712 r#"
713macro_rules! mac {
714 () => { fn assoc() {} }
715}
716impl () {
717 mac$0!();
718}
719 "#,
720 expect![[r#"
721 mac!
722 fn assoc(){}"#]],
723 );
724 }
725
726 #[test]
727 fn eager() {
728 check(
729 r#"
730//- minicore: concat
731macro_rules! my_concat {
732 ($head:expr, $($tail:tt)*) => { concat!($head, $($tail)*) };
733}
734
735
736fn test() {
737 _ = my_concat!(
738 conc$0at!("<", ">"),
739 "hi",
740 );
741}
742 "#,
743 expect![[r#"
744 concat!
745 "<>""#]],
746 );
747 }
748
749 #[test]
750 fn in_included() {
751 check(
752 r#"
753//- minicore: include
754//- /main.rs crate:main
755include!("./included.rs");
756//- /included.rs
757macro_rules! foo {
758 () => { fn item() {} };
759}
760foo$0!();
761"#,
762 expect![[r#"
763 foo!
764 fn item(){}"#]],
765 );
766 }
767
768 #[test]
769 fn include() {
770 check(
771 r#"
772//- minicore: include
773//- /main.rs crate:main
774include$0!("./included.rs");
775//- /included.rs
776macro_rules! foo {
777 () => { fn item() {} };
778}
779foo();
780"#,
781 expect![[r#"
782 include!
783 macro_rules! foo {
784 () => {
785 fn item(){}
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}