Skip to content

Commit 4ed7f4b

Browse files
committed
Auto merge of rust-lang#18131 - ChayimFriedman2:macro-expand-dollar-crate, r=Veykril
fix: 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. Probably fixes some issues, but I don't know what they are.
2 parents 627ccda + d878b53 commit 4ed7f4b

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

+5-2
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;
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

+1-1
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

+33-10
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

+80-2
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

+14-6
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)