Skip to content

Commit 9bd3aa9

Browse files
Merge pull request #21279 from J3m3/no-downmap
internal: add special `ErasedFileAstId` used for bypassing downmapping
2 parents 0023f19 + 55451d3 commit 9bd3aa9

File tree

4 files changed

+103
-36
lines changed

4 files changed

+103
-36
lines changed

crates/hir-def/src/macro_expansion_tests/mod.rs

Lines changed: 82 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,18 @@ use std::{any::TypeId, iter, ops::Range, sync};
1919
use base_db::RootQueryDb;
2020
use expect_test::Expect;
2121
use hir_expand::{
22-
AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
22+
AstId, ExpansionInfo, InFile, MacroCallId, MacroCallKind, MacroKind,
2323
builtin::quote::quote,
2424
db::ExpandDatabase,
2525
proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
2626
span_map::SpanMapRef,
2727
};
2828
use intern::{Symbol, sym};
2929
use itertools::Itertools;
30-
use span::{Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext};
30+
use span::{
31+
Edition, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor,
32+
SyntaxContext,
33+
};
3134
use stdx::{format_to, format_to_acc};
3235
use syntax::{
3336
AstNode, AstPtr,
@@ -97,37 +100,6 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
97100
},
98101
)];
99102

100-
fn resolve(
101-
db: &dyn DefDatabase,
102-
def_map: &DefMap,
103-
ast_id: AstId<ast::MacroCall>,
104-
ast_ptr: InFile<AstPtr<ast::MacroCall>>,
105-
) -> Option<MacroCallId> {
106-
def_map.modules().find_map(|module| {
107-
for decl in
108-
module.1.scope.declarations().chain(module.1.scope.unnamed_consts().map(Into::into))
109-
{
110-
let body = match decl {
111-
ModuleDefId::FunctionId(it) => it.into(),
112-
ModuleDefId::ConstId(it) => it.into(),
113-
ModuleDefId::StaticId(it) => it.into(),
114-
_ => continue,
115-
};
116-
117-
let (body, sm) = db.body_with_source_map(body);
118-
if let Some(it) =
119-
body.blocks(db).find_map(|block| resolve(db, block.1, ast_id, ast_ptr))
120-
{
121-
return Some(it);
122-
}
123-
if let Some((_, res)) = sm.macro_calls().find(|it| it.0 == ast_ptr) {
124-
return Some(res);
125-
}
126-
}
127-
module.1.scope.macro_invoc(ast_id)
128-
})
129-
}
130-
131103
let db = TestDB::with_files_extra_proc_macros(ra_fixture, extra_proc_macros);
132104
let krate = db.fetch_test_crate();
133105
let def_map = crate_def_map(&db, krate);
@@ -144,7 +116,7 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
144116
let ast_id = db.ast_id_map(source.file_id).ast_id(&macro_call_node);
145117
let ast_id = InFile::new(source.file_id, ast_id);
146118
let ptr = InFile::new(source.file_id, AstPtr::new(&macro_call_node));
147-
let macro_call_id = resolve(&db, def_map, ast_id, ptr)
119+
let macro_call_id = resolve_macro_call_id(&db, def_map, ast_id, ptr)
148120
.unwrap_or_else(|| panic!("unable to find semantic macro call {macro_call_node}"));
149121
let expansion_result = db.parse_macro_expansion(macro_call_id);
150122
expansions.push((macro_call_node.clone(), expansion_result));
@@ -278,6 +250,38 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
278250
expect.assert_eq(&expanded_text);
279251
}
280252

253+
fn resolve_macro_call_id(
254+
db: &dyn DefDatabase,
255+
def_map: &DefMap,
256+
ast_id: AstId<ast::MacroCall>,
257+
ast_ptr: InFile<AstPtr<ast::MacroCall>>,
258+
) -> Option<MacroCallId> {
259+
def_map.modules().find_map(|module| {
260+
for decl in
261+
module.1.scope.declarations().chain(module.1.scope.unnamed_consts().map(Into::into))
262+
{
263+
let body = match decl {
264+
ModuleDefId::FunctionId(it) => it.into(),
265+
ModuleDefId::ConstId(it) => it.into(),
266+
ModuleDefId::StaticId(it) => it.into(),
267+
_ => continue,
268+
};
269+
270+
let (body, sm) = db.body_with_source_map(body);
271+
if let Some(it) = body
272+
.blocks(db)
273+
.find_map(|block| resolve_macro_call_id(db, block.1, ast_id, ast_ptr))
274+
{
275+
return Some(it);
276+
}
277+
if let Some((_, res)) = sm.macro_calls().find(|it| it.0 == ast_ptr) {
278+
return Some(res);
279+
}
280+
}
281+
module.1.scope.macro_invoc(ast_id)
282+
})
283+
}
284+
281285
fn reindent(indent: IndentLevel, pp: String) -> String {
282286
if !pp.contains('\n') {
283287
return pp;
@@ -430,3 +434,47 @@ fn regression_20171() {
430434
Edition::CURRENT
431435
});
432436
}
437+
438+
#[test]
439+
fn no_downmap() {
440+
let fixture = r#"
441+
macro_rules! m {
442+
($func_name:ident) => {
443+
fn $func_name() { todo!() }
444+
};
445+
}
446+
m!(f);
447+
m!(g);
448+
"#;
449+
450+
let (db, file_id) = TestDB::with_single_file(fixture);
451+
let krate = file_id.krate(&db);
452+
let def_map = crate_def_map(&db, krate);
453+
let source = def_map[def_map.root].definition_source(&db);
454+
let source_file = match source.value {
455+
ModuleSource::SourceFile(it) => it,
456+
ModuleSource::Module(_) | ModuleSource::BlockExpr(_) => panic!(),
457+
};
458+
let no_downmap_spans: Vec<_> = source_file
459+
.syntax()
460+
.descendants()
461+
.map(|node| {
462+
let mut span = db.real_span_map(file_id).span_for_range(node.text_range());
463+
span.anchor.ast_id = NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER;
464+
span
465+
})
466+
.collect();
467+
468+
for macro_call_node in source_file.syntax().descendants().filter_map(ast::MacroCall::cast) {
469+
let ast_id = db.ast_id_map(source.file_id).ast_id(&macro_call_node);
470+
let ast_id = InFile::new(source.file_id, ast_id);
471+
let ptr = InFile::new(source.file_id, AstPtr::new(&macro_call_node));
472+
let macro_call_id = resolve_macro_call_id(&db, def_map, ast_id, ptr)
473+
.unwrap_or_else(|| panic!("unable to find semantic macro call {macro_call_node}"));
474+
let expansion_info = ExpansionInfo::new(&db, macro_call_id);
475+
for &span in no_downmap_spans.iter() {
476+
assert!(expansion_info.map_range_down(span).is_none());
477+
assert!(expansion_info.map_range_down_exact(span).is_none());
478+
}
479+
}
480+
}

crates/hir-expand/src/lib.rs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,9 @@ use std::{hash::Hash, ops};
3737

3838
use base_db::Crate;
3939
use either::Either;
40-
use span::{Edition, ErasedFileAstId, FileAstId, Span, SyntaxContext};
40+
use span::{
41+
Edition, ErasedFileAstId, FileAstId, NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, Span, SyntaxContext,
42+
};
4143
use syntax::{
4244
SyntaxNode, SyntaxToken, TextRange, TextSize,
4345
ast::{self, AstNode},
@@ -854,6 +856,10 @@ impl ExpansionInfo {
854856
&self,
855857
span: Span,
856858
) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
859+
if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
860+
return None;
861+
}
862+
857863
let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
858864
self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
859865
});
@@ -869,6 +875,10 @@ impl ExpansionInfo {
869875
&self,
870876
span: Span,
871877
) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContext)> + '_>> {
878+
if span.anchor.ast_id == NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER {
879+
return None;
880+
}
881+
872882
let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
873883
self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
874884
});

crates/span/src/ast_id.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
4848
pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
4949
ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::Fixup as u32));
5050

51+
/// [`ErasedFileAstId`] used as the span for syntax nodes that should not be mapped down to
52+
/// macro expansion. Any `Span` containing this file id is to be considered fake.
53+
pub const NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
54+
ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::NoDownmap as u32));
55+
5156
/// This is a type erased FileAstId.
5257
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
5358
pub struct ErasedFileAstId(u32);
@@ -95,6 +100,7 @@ impl fmt::Debug for ErasedFileAstId {
95100
BlockExpr,
96101
AsmExpr,
97102
Fixup,
103+
NoDownmap,
98104
);
99105
if f.alternate() {
100106
write!(f, "{kind}[{:04X}, {}]", self.hash_value(), self.index())
@@ -150,6 +156,9 @@ enum ErasedFileAstIdKind {
150156
// because incrementality is not a problem, they will always be the only item in the macro file,
151157
// and memory usage also not because they're rare.
152158
AsmExpr,
159+
/// Represents a fake [`ErasedFileAstId`] that should not be mapped down to macro expansion
160+
/// result.
161+
NoDownmap,
153162
/// Keep this last.
154163
Root,
155164
}

crates/span/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ mod map;
1414
pub use self::{
1515
ast_id::{
1616
AstIdMap, AstIdNode, ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, FileAstId,
17-
ROOT_ERASED_FILE_AST_ID,
17+
NO_DOWNMAP_ERASED_FILE_AST_ID_MARKER, ROOT_ERASED_FILE_AST_ID,
1818
},
1919
hygiene::{SyntaxContext, Transparency},
2020
map::{RealSpanMap, SpanMap},

0 commit comments

Comments
 (0)