Skip to main content

hir_expand/
prettify_macro_expansion_.rs

1//! Pretty printing of macros output.
2
3use base_db::{Crate, SourceDatabase};
4use rustc_hash::FxHashMap;
5use syntax::SyntaxNode;
6
7use crate::span_map::ExpansionSpanMap;
8
9/// Inserts whitespace and replaces `$crate` in macro expansions.
10#[expect(deprecated)]
11pub fn prettify_macro_expansion(
12    db: &dyn SourceDatabase,
13    syn: SyntaxNode,
14    span_map: &ExpansionSpanMap,
15    target_crate_id: Crate,
16) -> SyntaxNode {
17    // Because `syntax_bridge::prettify_macro_expansion::prettify_macro_expansion()` clones subtree for `syn`,
18    // that means it will be offsetted to the beginning.
19    let span_offset = syn.text_range().start();
20    let target_crate = target_crate_id.data(db);
21    let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
22    syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(
23        syn,
24        &mut |dollar_crate, make| {
25            let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
26            let replacement =
27                syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| {
28                    let macro_call_id = ctx
29                        .outer_expn(db)
30                        .expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
31                    let macro_call = crate::MacroCallId::from(macro_call_id).loc(db);
32                    let macro_def_crate = macro_call.def.krate;
33                    // First, if this is the same crate as the macro, nothing will work but `crate`.
34                    // If not, if the target trait has the macro's crate as a dependency, using the dependency name
35                    // will work in inserted code and match the user's expectation.
36                    // If not, the crate's display name is what the dependency name is likely to be once such dependency
37                    // is inserted, and also understandable to the user.
38                    // Lastly, if nothing else found, resort to leaving `$crate`.
39                    if target_crate_id == macro_def_crate {
40                        make.token(syntax::SyntaxKind::CRATE_KW)
41                    } else if let Some(dep) =
42                        target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
43                    {
44                        make.ident(dep.name.as_str())
45                    } else if let Some(crate_name) = &macro_def_crate.extra_data(db).display_name {
46                        make.ident(crate_name.crate_name().as_str())
47                    } else {
48                        dollar_crate.clone()
49                    }
50                });
51            if replacement.text() == "$crate" {
52                // The parent may have many children, and looking for the token may yield incorrect results.
53                return None;
54            }
55            Some(replacement.clone())
56        },
57        |_| (),
58    )
59}