Skip to content

Commit 92e80ec

Browse files
committed
feat: Virtual macro files
1 parent e865b24 commit 92e80ec

File tree

102 files changed

+3132
-2338
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

102 files changed

+3132
-2338
lines changed

Diff for: Cargo.lock

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: crates/hir-def/src/nameres.rs

+18-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ use intern::Symbol;
6767
use itertools::Itertools;
6868
use la_arena::Arena;
6969
use rustc_hash::{FxHashMap, FxHashSet};
70-
use span::{Edition, EditionedFileId, FileAstId, FileId, ROOT_ERASED_FILE_AST_ID};
70+
use span::{Edition, EditionedFileId, FileAstId, FileId, MacroFileId, ROOT_ERASED_FILE_AST_ID};
7171
use stdx::format_to;
7272
use syntax::{ast, AstNode, SmolStr, SyntaxNode};
7373
use triomphe::Arc;
@@ -441,6 +441,23 @@ impl DefMap {
441441
.map(|(id, _data)| id)
442442
}
443443

444+
pub fn inline_modules_for_macro_file(
445+
&self,
446+
file_id: MacroFileId,
447+
) -> impl Iterator<Item = LocalModuleId> + '_ {
448+
self.modules
449+
.iter()
450+
.filter(move |(_id, data)| {
451+
(match data.origin {
452+
ModuleOrigin::Inline { definition_tree_id, .. } => {
453+
definition_tree_id.file_id().macro_file()
454+
}
455+
_ => None,
456+
}) == Some(file_id)
457+
})
458+
.map(|(id, _data)| id)
459+
}
460+
444461
pub fn modules(&self) -> impl Iterator<Item = (LocalModuleId, &ModuleData)> + '_ {
445462
self.modules.iter()
446463
}

Diff for: crates/hir-expand/src/files.rs

+48-5
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,51 @@ pub type HirFilePosition = FilePositionWrapper<HirFileId>;
3838
pub type MacroFilePosition = FilePositionWrapper<MacroFileId>;
3939
pub type FilePosition = FilePositionWrapper<EditionedFileId>;
4040

41-
impl From<FilePositionWrapper<EditionedFileId>> for FilePositionWrapper<span::FileId> {
42-
fn from(value: FilePositionWrapper<EditionedFileId>) -> Self {
41+
impl From<FilePosition> for FilePositionWrapper<span::FileId> {
42+
fn from(value: FilePosition) -> Self {
4343
FilePositionWrapper { file_id: value.file_id.into(), offset: value.offset }
4444
}
4545
}
46+
47+
impl From<FileRange> for HirFileRange {
48+
fn from(value: FileRange) -> Self {
49+
HirFileRange { file_id: value.file_id.into(), range: value.range }
50+
}
51+
}
52+
53+
impl From<FilePosition> for HirFilePosition {
54+
fn from(value: FilePosition) -> Self {
55+
HirFilePosition { file_id: value.file_id.into(), offset: value.offset }
56+
}
57+
}
58+
59+
impl FilePositionWrapper<span::FileId> {
60+
pub fn with_edition(self, edition: span::Edition) -> FilePosition {
61+
FilePositionWrapper {
62+
file_id: EditionedFileId::new(self.file_id, edition),
63+
offset: self.offset,
64+
}
65+
}
66+
}
67+
68+
impl FileRangeWrapper<span::FileId> {
69+
pub fn with_edition(self, edition: span::Edition) -> FileRange {
70+
FileRangeWrapper { file_id: EditionedFileId::new(self.file_id, edition), range: self.range }
71+
}
72+
}
73+
74+
impl<T> InFileWrapper<span::FileId, T> {
75+
pub fn with_edition(self, edition: span::Edition) -> InRealFile<T> {
76+
InRealFile { file_id: EditionedFileId::new(self.file_id, edition), value: self.value }
77+
}
78+
}
79+
80+
impl HirFileRange {
81+
pub fn file_range(self) -> Option<FileRange> {
82+
Some(FileRange { file_id: self.file_id.file_id()?, range: self.range })
83+
}
84+
}
85+
4686
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
4787
pub struct FileRangeWrapper<FileKind> {
4888
pub file_id: FileKind,
@@ -191,6 +231,9 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
191231
pub fn syntax(&self) -> InFileWrapper<FileId, &SyntaxNode> {
192232
self.with_value(self.value.syntax())
193233
}
234+
pub fn node_file_range(&self) -> FileRangeWrapper<FileId> {
235+
FileRangeWrapper { file_id: self.file_id, range: self.value.syntax().text_range() }
236+
}
194237
}
195238

196239
impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
@@ -201,9 +244,9 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, &N> {
201244
}
202245

203246
// region:specific impls
204-
impl<SN: Borrow<SyntaxNode>> InRealFile<SN> {
205-
pub fn file_range(&self) -> FileRange {
206-
FileRange { file_id: self.file_id, range: self.value.borrow().text_range() }
247+
impl<FileId: Copy, SN: Borrow<SyntaxNode>> InFileWrapper<FileId, SN> {
248+
pub fn file_range(&self) -> FileRangeWrapper<FileId> {
249+
FileRangeWrapper { file_id: self.file_id, range: self.value.borrow().text_range() }
207250
}
208251
}
209252

Diff for: crates/hir-expand/src/lib.rs

+13-6
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,7 @@ pub trait HirFileIdExt {
347347

348348
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
349349
fn original_call_node(self, db: &dyn ExpandDatabase) -> Option<InRealFile<SyntaxNode>>;
350+
fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>>;
350351

351352
fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>>;
352353
}
@@ -405,6 +406,10 @@ impl HirFileIdExt for HirFileId {
405406
}
406407
}
407408

409+
fn call_node(self, db: &dyn ExpandDatabase) -> Option<InFile<SyntaxNode>> {
410+
Some(db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db))
411+
}
412+
408413
fn as_builtin_derive_attr_node(&self, db: &dyn ExpandDatabase) -> Option<InFile<ast::Attr>> {
409414
let macro_file = self.macro_file()?;
410415
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
@@ -873,7 +878,10 @@ impl ExpansionInfo {
873878
map_node_range_up(db, &self.exp_map, range)
874879
}
875880

876-
/// Maps up the text range out of the expansion into is macro call.
881+
/// Maps up the text range out of the expansion into its macro call.
882+
///
883+
/// Note that this may return multiple ranges as we lose the precise association between input to output
884+
/// and as such we may consider inputs that are unrelated.
877885
pub fn map_range_up_once(
878886
&self,
879887
db: &dyn ExpandDatabase,
@@ -889,11 +897,10 @@ impl ExpansionInfo {
889897
InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
890898
}
891899
SpanMap::ExpansionSpanMap(arg_map) => {
892-
let arg_range = self
893-
.arg
894-
.value
895-
.as_ref()
896-
.map_or_else(|| TextRange::empty(TextSize::from(0)), |it| it.text_range());
900+
let Some(arg_node) = &self.arg.value else {
901+
return InFile::new(self.arg.file_id, smallvec::smallvec![]);
902+
};
903+
let arg_range = arg_node.text_range();
897904
InFile::new(
898905
self.arg.file_id,
899906
arg_map

Diff for: crates/hir-expand/src/prettify_macro_expansion_.rs

+43-39
Original file line numberDiff line numberDiff line change
@@ -21,43 +21,47 @@ pub fn prettify_macro_expansion(
2121
let crate_graph = db.crate_graph();
2222
let target_crate = &crate_graph[target_crate_id];
2323
let mut syntax_ctx_id_to_dollar_crate_replacement = FxHashMap::default();
24-
syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(syn, &mut |dollar_crate| {
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 ctx_data = db.lookup_intern_syntax_context(ctx);
29-
let macro_call_id =
30-
ctx_data.outer_expn.expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
31-
let macro_call = db.lookup_intern_macro_call(macro_call_id);
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::tokens::crate_kw()
41-
} else if let Some(dep) =
42-
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
43-
{
44-
make::tokens::ident(dep.name.as_str())
45-
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
46-
make::tokens::ident(crate_name.crate_name().as_str())
47-
} else {
48-
return 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 dollar_crate.clone();
54-
}
55-
// We need to `clone_subtree()` but rowan doesn't provide such operation for tokens.
56-
let parent = replacement.parent().unwrap().clone_subtree().clone_for_update();
57-
parent
58-
.children_with_tokens()
59-
.filter_map(NodeOrToken::into_token)
60-
.find(|it| it.kind() == replacement.kind())
61-
.unwrap()
62-
})
24+
syntax_bridge::prettify_macro_expansion::prettify_macro_expansion(
25+
syn,
26+
&mut |dollar_crate| {
27+
let ctx = span_map.span_at(dollar_crate.text_range().start() + span_offset).ctx;
28+
let replacement =
29+
syntax_ctx_id_to_dollar_crate_replacement.entry(ctx).or_insert_with(|| {
30+
let ctx_data = db.lookup_intern_syntax_context(ctx);
31+
let macro_call_id = ctx_data
32+
.outer_expn
33+
.expect("`$crate` cannot come from `SyntaxContextId::ROOT`");
34+
let macro_call = db.lookup_intern_macro_call(macro_call_id);
35+
let macro_def_crate = macro_call.def.krate;
36+
// First, if this is the same crate as the macro, nothing will work but `crate`.
37+
// If not, if the target trait has the macro's crate as a dependency, using the dependency name
38+
// will work in inserted code and match the user's expectation.
39+
// If not, the crate's display name is what the dependency name is likely to be once such dependency
40+
// is inserted, and also understandable to the user.
41+
// Lastly, if nothing else found, resort to leaving `$crate`.
42+
if target_crate_id == macro_def_crate {
43+
make::tokens::crate_kw()
44+
} else if let Some(dep) =
45+
target_crate.dependencies.iter().find(|dep| dep.crate_id == macro_def_crate)
46+
{
47+
make::tokens::ident(dep.name.as_str())
48+
} else if let Some(crate_name) = &crate_graph[macro_def_crate].display_name {
49+
make::tokens::ident(crate_name.crate_name().as_str())
50+
} else {
51+
return dollar_crate.clone();
52+
}
53+
});
54+
if replacement.text() == "$crate" {
55+
// The parent may have many children, and looking for the token may yield incorrect results.
56+
return None;
57+
}
58+
// We need to `clone_subtree()` but rowan doesn't provide such operation for tokens.
59+
let parent = replacement.parent().unwrap().clone_subtree().clone_for_update();
60+
parent
61+
.children_with_tokens()
62+
.filter_map(NodeOrToken::into_token)
63+
.find(|it| it.kind() == replacement.kind())
64+
},
65+
|_| (),
66+
)
6367
}

0 commit comments

Comments
 (0)