Skip to content

Commit d878b53

Browse files
Get rid of $crate in expansions shown to the user
Be it "Expand Macro Recursively", "Inline macro" or few other things. We replace it with the crate name, as should've always been.
1 parent b9be0f2 commit d878b53

File tree

15 files changed

+396
-64
lines changed

15 files changed

+396
-64
lines changed

src/tools/rust-analyzer/crates/hir-expand/src/lib.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub mod span_map;
2121

2222
mod cfg_process;
2323
mod fixup;
24+
mod prettify_macro_expansion_;
2425

2526
use attrs::collect_attrs;
2627
use rustc_hash::FxHashMap;
@@ -51,11 +52,13 @@ use crate::{
5152
span_map::{ExpansionSpanMap, SpanMap},
5253
};
5354

54-
pub use crate::files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile};
55+
pub use crate::{
56+
files::{AstId, ErasedAstId, FileRange, InFile, InMacroFile, InRealFile},
57+
prettify_macro_expansion_::prettify_macro_expansion,
58+
};
5559

5660
pub use mbe::{DeclarativeMacro, ValueResult};
5761
pub use span::{HirFileId, MacroCallId, MacroFileId};
58-
pub use syntax_bridge::insert_whitespace_into_node;
5962

6063
pub mod tt {
6164
pub use span::Span;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//! Pretty printing of macros output.
2+
3+
use base_db::CrateId;
4+
use rustc_hash::FxHashMap;
5+
use syntax::NodeOrToken;
6+
use syntax::{ast::make, SyntaxNode};
7+
8+
use crate::{db::ExpandDatabase, span_map::ExpansionSpanMap};
9+
10+
/// Inserts whitespace and replaces `$crate` in macro expansions.
11+
#[expect(deprecated)]
12+
pub fn prettify_macro_expansion(
13+
db: &dyn ExpandDatabase,
14+
syn: SyntaxNode,
15+
span_map: &ExpansionSpanMap,
16+
target_crate_id: CrateId,
17+
) -> SyntaxNode {
18+
let crate_graph = db.crate_graph();
19+
let target_crate = &crate_graph[target_crate_id];
20+
let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
21+
syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
22+
let ctx = span_map.span_at(dollar_crate.text_range().start()).ctx;
23+
let replacement =
24+
syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| {
25+
let ctx_data = db.lookup_intern_syntax_context(ctx);
26+
let macro_call_id =
27+
ctx_data.outer_expn.expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
28+
let macro_call = db.lookup_intern_macro_call(macro_call_id);
29+
let macro_def_crate = macro_call.def.krate;
30+
// First, if this is the same crate as the macro, nothing will work but `crate`.
31+
// If not, if the target trait has the macro's crate as a dependency, using the dependency name
32+
// will work in inserted code and match the user's expectation.
33+
// If not, the crate's display name is what the dependency name is likely to be once such dependency
34+
// is inserted, and also understandable to the user.
35+
// Lastly, if nothing else found, resort to leaving `$crate`.
36+
if target_crate_id == macro_def_crate {
37+
make::tokens::crate_kw()
38+
} else if let Some(dep) =
39+
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
40+
{
41+
make::tokens::ident(&dep.name)
42+
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
43+
make::tokens::ident(crate_name.crate_name())
44+
} else {
45+
return dollar_crate.clone();
46+
}
47+
});
48+
if replacement.text() == "$crate" {
49+
// The parent may have many children, and looking for the token may yield incorrect results.
50+
return dollar_crate.clone();
51+
}
52+
// We need to `clone_subtree()` but rowan doesn't provide such operation for tokens.
53+
let parent = replacement.parent().unwrap().clone_subtree().clone_for_update();
54+
parent
55+
.children_with_tokens()
56+
.filter_map(NodeOrToken::into_token)
57+
.find(|it| it.kind() == replacement.kind())
58+
.unwrap()
59+
})
60+
}

src/tools/rust-analyzer/crates/hir/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ pub use {
136136
},
137137
hygiene::{marks_rev, SyntaxContextExt},
138138
inert_attr_macro::AttributeTemplate,
139-
insert_whitespace_into_node,
140139
name::Name,
140+
prettify_macro_expansion,
141141
proc_macro::{ProcMacros, ProcMacrosBuilder},
142142
tt, ExpandResult, HirFileId, HirFileIdExt, MacroFileId, MacroFileIdExt,
143143
},

src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_call.rs

Lines changed: 33 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,18 @@ use std::collections::BTreeSet;
22

33
use ast::make;
44
use either::Either;
5-
use hir::{db::HirDatabase, sym, FileRange, PathResolution, Semantics, TypeInfo};
5+
use hir::{
6+
db::{ExpandDatabase, HirDatabase},
7+
sym, FileRange, PathResolution, Semantics, TypeInfo,
8+
};
69
use ide_db::{
10+
base_db::CrateId,
711
defs::Definition,
812
imports::insert_use::remove_path_if_in_use_stmt,
913
path_transform::PathTransform,
1014
search::{FileReference, FileReferenceNode, SearchScope},
1115
source_change::SourceChangeBuilder,
12-
syntax_helpers::{insert_whitespace_into_node::insert_ws_into, node_ext::expr_as_name_ref},
16+
syntax_helpers::{node_ext::expr_as_name_ref, prettify_macro_expansion},
1317
EditionedFileId, RootDatabase,
1418
};
1519
use itertools::{izip, Itertools};
@@ -102,12 +106,13 @@ pub(crate) fn inline_into_callers(acc: &mut Assists, ctx: &AssistContext<'_>) ->
102106
let mut remove_def = true;
103107
let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
104108
builder.edit_file(file_id);
109+
let call_krate = ctx.sema.file_to_module_def(file_id).map(|it| it.krate());
105110
let count = refs.len();
106111
// The collects are required as we are otherwise iterating while mutating 🙅‍♀️🙅‍♂️
107112
let (name_refs, name_refs_use) = split_refs_and_uses(builder, refs, Some);
108113
let call_infos: Vec<_> = name_refs
109114
.into_iter()
110-
.filter_map(CallInfo::from_name_ref)
115+
.filter_map(|it| CallInfo::from_name_ref(it, call_krate?.into()))
111116
// FIXME: do not handle callsites in macros' parameters, because
112117
// directly inlining into macros may cause errors.
113118
.filter(|call_info| !ctx.sema.hir_file_for(call_info.node.syntax()).is_macro())
@@ -185,7 +190,10 @@ pub(super) fn split_refs_and_uses<T: ast::AstNode>(
185190
// ```
186191
pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
187192
let name_ref: ast::NameRef = ctx.find_node_at_offset()?;
188-
let call_info = CallInfo::from_name_ref(name_ref.clone())?;
193+
let call_info = CallInfo::from_name_ref(
194+
name_ref.clone(),
195+
ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(),
196+
)?;
189197
let (function, label) = match &call_info.node {
190198
ast::CallableExpr::Call(call) => {
191199
let path = match call.expr()? {
@@ -243,10 +251,11 @@ struct CallInfo {
243251
node: ast::CallableExpr,
244252
arguments: Vec<ast::Expr>,
245253
generic_arg_list: Option<ast::GenericArgList>,
254+
krate: CrateId,
246255
}
247256

248257
impl CallInfo {
249-
fn from_name_ref(name_ref: ast::NameRef) -> Option<CallInfo> {
258+
fn from_name_ref(name_ref: ast::NameRef, krate: CrateId) -> Option<CallInfo> {
250259
let parent = name_ref.syntax().parent()?;
251260
if let Some(call) = ast::MethodCallExpr::cast(parent.clone()) {
252261
let receiver = call.receiver()?;
@@ -256,6 +265,7 @@ impl CallInfo {
256265
generic_arg_list: call.generic_arg_list(),
257266
node: ast::CallableExpr::MethodCall(call),
258267
arguments,
268+
krate,
259269
})
260270
} else if let Some(segment) = ast::PathSegment::cast(parent) {
261271
let path = segment.syntax().parent().and_then(ast::Path::cast)?;
@@ -266,6 +276,7 @@ impl CallInfo {
266276
arguments: call.arg_list()?.args().collect(),
267277
node: ast::CallableExpr::Call(call),
268278
generic_arg_list: segment.generic_arg_list(),
279+
krate,
269280
})
270281
} else {
271282
None
@@ -307,11 +318,15 @@ fn inline(
307318
function: hir::Function,
308319
fn_body: &ast::BlockExpr,
309320
params: &[(ast::Pat, Option<ast::Type>, hir::Param)],
310-
CallInfo { node, arguments, generic_arg_list }: &CallInfo,
321+
CallInfo { node, arguments, generic_arg_list, krate }: &CallInfo,
311322
) -> ast::Expr {
312-
let mut body = if sema.hir_file_for(fn_body.syntax()).is_macro() {
323+
let file_id = sema.hir_file_for(fn_body.syntax());
324+
let mut body = if let Some(macro_file) = file_id.macro_file() {
313325
cov_mark::hit!(inline_call_defined_in_macro);
314-
if let Some(body) = ast::BlockExpr::cast(insert_ws_into(fn_body.syntax().clone())) {
326+
let span_map = sema.db.expansion_span_map(macro_file);
327+
let body_prettified =
328+
prettify_macro_expansion(sema.db, fn_body.syntax().clone(), &span_map, *krate);
329+
if let Some(body) = ast::BlockExpr::cast(body_prettified) {
315330
body
316331
} else {
317332
fn_body.clone_for_update()
@@ -420,8 +435,16 @@ fn inline(
420435

421436
let mut insert_let_stmt = || {
422437
let param_ty = param_ty.clone().map(|param_ty| {
423-
if sema.hir_file_for(param_ty.syntax()).is_macro() {
424-
ast::Type::cast(insert_ws_into(param_ty.syntax().clone())).unwrap_or(param_ty)
438+
let file_id = sema.hir_file_for(param_ty.syntax());
439+
if let Some(macro_file) = file_id.macro_file() {
440+
let span_map = sema.db.expansion_span_map(macro_file);
441+
let param_ty_prettified = prettify_macro_expansion(
442+
sema.db,
443+
param_ty.syntax().clone(),
444+
&span_map,
445+
*krate,
446+
);
447+
ast::Type::cast(param_ty_prettified).unwrap_or(param_ty)
425448
} else {
426449
param_ty
427450
}

src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_macro.rs

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
use ide_db::syntax_helpers::insert_whitespace_into_node::insert_ws_into;
1+
use hir::db::ExpandDatabase;
2+
use ide_db::syntax_helpers::prettify_macro_expansion;
23
use syntax::ast::{self, AstNode};
34

45
use crate::{AssistContext, AssistId, AssistKind, Assists};
@@ -36,7 +37,15 @@ use crate::{AssistContext, AssistId, AssistKind, Assists};
3637
// ```
3738
pub(crate) fn inline_macro(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
3839
let unexpanded = ctx.find_node_at_offset::<ast::MacroCall>()?;
39-
let expanded = insert_ws_into(ctx.sema.expand(&unexpanded)?.clone_for_update());
40+
let macro_call = ctx.sema.to_def(&unexpanded)?;
41+
let expanded = ctx.sema.parse_or_expand(macro_call.as_file());
42+
let span_map = ctx.sema.db.expansion_span_map(macro_call.as_macro_file());
43+
let expanded = prettify_macro_expansion(
44+
ctx.db(),
45+
expanded,
46+
&span_map,
47+
ctx.sema.file_to_module_def(ctx.file_id())?.krate().into(),
48+
);
4049
let text_range = unexpanded.syntax().text_range();
4150

4251
acc.add(
@@ -295,6 +304,75 @@ fn main() {
295304
}
296305
};
297306
}
307+
"#,
308+
);
309+
}
310+
311+
#[test]
312+
fn dollar_crate() {
313+
check_assist(
314+
inline_macro,
315+
r#"
316+
pub struct Foo;
317+
#[macro_export]
318+
macro_rules! m {
319+
() => { $crate::Foo };
320+
}
321+
fn bar() {
322+
m$0!();
323+
}
324+
"#,
325+
r#"
326+
pub struct Foo;
327+
#[macro_export]
328+
macro_rules! m {
329+
() => { $crate::Foo };
330+
}
331+
fn bar() {
332+
crate::Foo;
333+
}
334+
"#,
335+
);
336+
check_assist(
337+
inline_macro,
338+
r#"
339+
//- /a.rs crate:a
340+
pub struct Foo;
341+
#[macro_export]
342+
macro_rules! m {
343+
() => { $crate::Foo };
344+
}
345+
//- /b.rs crate:b deps:a
346+
fn bar() {
347+
a::m$0!();
348+
}
349+
"#,
350+
r#"
351+
fn bar() {
352+
a::Foo;
353+
}
354+
"#,
355+
);
356+
check_assist(
357+
inline_macro,
358+
r#"
359+
//- /a.rs crate:a
360+
pub struct Foo;
361+
#[macro_export]
362+
macro_rules! m {
363+
() => { $crate::Foo };
364+
}
365+
//- /b.rs crate:b deps:a
366+
pub use a::m;
367+
//- /c.rs crate:c deps:b
368+
fn bar() {
369+
b::m$0!();
370+
}
371+
"#,
372+
r#"
373+
fn bar() {
374+
a::Foo;
375+
}
298376
"#,
299377
);
300378
}

src/tools/rust-analyzer/crates/ide-assists/src/utils.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
//! Assorted functions shared by several assists.
22
33
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
4-
use hir::{db::HirDatabase, HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics};
4+
use hir::{
5+
db::{ExpandDatabase, HirDatabase},
6+
HasAttrs as HirHasAttrs, HirDisplay, InFile, Semantics,
7+
};
58
use ide_db::{
69
famous_defs::FamousDefs, path_transform::PathTransform,
7-
syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase,
10+
syntax_helpers::prettify_macro_expansion, RootDatabase,
811
};
912
use stdx::format_to;
1013
use syntax::{
@@ -178,10 +181,15 @@ pub fn add_trait_assoc_items_to_impl(
178181
let new_indent_level = IndentLevel::from_node(impl_.syntax()) + 1;
179182
let items = original_items.iter().map(|InFile { file_id, value: original_item }| {
180183
let cloned_item = {
181-
if file_id.is_macro() {
182-
if let Some(formatted) =
183-
ast::AssocItem::cast(insert_ws_into(original_item.syntax().clone()))
184-
{
184+
if let Some(macro_file) = file_id.macro_file() {
185+
let span_map = sema.db.expansion_span_map(macro_file);
186+
let item_prettified = prettify_macro_expansion(
187+
sema.db,
188+
original_item.syntax().clone(),
189+
&span_map,
190+
target_scope.krate().into(),
191+
);
192+
if let Some(formatted) = ast::AssocItem::cast(item_prettified) {
185193
return formatted;
186194
} else {
187195
stdx::never!("formatted `AssocItem` could not be cast back to `AssocItem`");

0 commit comments

Comments
 (0)