Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 9e8e124

Browse files
committed
Special case fixup spans in server::Span impl, they are immutable
1 parent 2e52aa1 commit 9e8e124

File tree

6 files changed

+67
-38
lines changed

6 files changed

+67
-38
lines changed

crates/hir-expand/src/builtin_fn_macro.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -776,7 +776,7 @@ fn quote_expand(
776776
_db: &dyn ExpandDatabase,
777777
_arg_id: MacroCallId,
778778
_tt: &tt::Subtree,
779-
span: SpanData,
779+
span: Span,
780780
) -> ExpandResult<tt::Subtree> {
781781
ExpandResult::new(
782782
tt::Subtree::empty(tt::DelimSpan { open: span, close: span }),

crates/hir-expand/src/fixup.rs

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
11
//! To make attribute macros work reliably when typing, we need to take care to
22
//! fix up syntax errors in the code we're passing to them.
33
4-
use la_arena::RawIdx;
54
use rustc_hash::{FxHashMap, FxHashSet};
65
use smallvec::SmallVec;
7-
use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SpanData};
6+
use span::{ErasedFileAstId, Span, SpanAnchor, SpanData, FIXUP_ERASED_FILE_AST_ID_MARKER};
87
use stdx::never;
98
use syntax::{
109
ast::{self, AstNode, HasLoopBody},
@@ -39,13 +38,11 @@ impl SyntaxFixupUndoInfo {
3938
pub(crate) const NONE: Self = SyntaxFixupUndoInfo { original: None };
4039
}
4140

42-
// censoring -> just don't convert the node
43-
// replacement -> censor + append
44-
// append -> insert a fake node, here we need to assemble some dummy span that we can figure out how
45-
// to remove later
46-
const FIXUP_DUMMY_FILE: FileId = FileId::from_raw(FileId::MAX_FILE_ID);
47-
const FIXUP_DUMMY_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(RawIdx::from_u32(!0));
41+
// We mark spans with `FIXUP_DUMMY_AST_ID` to indicate that they are fake.
42+
const FIXUP_DUMMY_AST_ID: ErasedFileAstId = FIXUP_ERASED_FILE_AST_ID_MARKER;
4843
const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0));
44+
// If the fake span has this range end, that means that the range start is an index into the
45+
// `original` list in `SyntaxFixupUndoInfo`.
4946
const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0);
5047

5148
pub(crate) fn fixup_syntax(
@@ -58,13 +55,13 @@ pub(crate) fn fixup_syntax(
5855
let mut preorder = node.preorder();
5956
let mut original = Vec::new();
6057
let dummy_range = FIXUP_DUMMY_RANGE;
61-
// we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as
62-
// the index into the replacement vec but only if the end points to !0
63-
let dummy_anchor = SpanAnchor { file_id: FIXUP_DUMMY_FILE, ast_id: FIXUP_DUMMY_AST_ID };
64-
let fake_span = |range| SpanData {
65-
range: dummy_range,
66-
anchor: dummy_anchor,
67-
ctx: span_map.span_for_range(range).ctx,
58+
let fake_span = |range| {
59+
let span = span_map.span_for_range(range);
60+
SpanData {
61+
range: dummy_range,
62+
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
63+
ctx: span.ctx,
64+
}
6865
};
6966
while let Some(event) = preorder.next() {
7067
let syntax::WalkEvent::Enter(node) = event else { continue };
@@ -76,12 +73,13 @@ pub(crate) fn fixup_syntax(
7673
let original_tree = mbe::syntax_node_to_token_tree(&node, span_map, call_site);
7774
let idx = original.len() as u32;
7875
original.push(original_tree);
76+
let span = span_map.span_for_range(node_range);
7977
let replacement = Leaf::Ident(Ident {
8078
text: "__ra_fixup".into(),
8179
span: SpanData {
8280
range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
83-
anchor: dummy_anchor,
84-
ctx: span_map.span_for_range(node_range).ctx,
81+
anchor: SpanAnchor { ast_id: FIXUP_DUMMY_AST_ID, ..span.anchor },
82+
ctx: span.ctx,
8583
},
8684
});
8785
append.insert(node.clone().into(), vec![replacement]);
@@ -304,8 +302,8 @@ pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo)
304302
let undo_info = &**undo_info;
305303
#[allow(deprecated)]
306304
if never!(
307-
tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
308-
|| tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
305+
tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
306+
|| tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
309307
) {
310308
tt.delimiter.close = SpanData::DUMMY;
311309
tt.delimiter.open = SpanData::DUMMY;
@@ -321,16 +319,16 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
321319
.filter(|tt| match tt {
322320
tt::TokenTree::Leaf(leaf) => {
323321
let span = leaf.span();
324-
let is_real_leaf = span.anchor.file_id != FIXUP_DUMMY_FILE;
322+
let is_real_leaf = span.anchor.ast_id != FIXUP_DUMMY_AST_ID;
325323
let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END;
326324
is_real_leaf || is_replaced_node
327325
}
328326
tt::TokenTree::Subtree(_) => true,
329327
})
330328
.flat_map(|tt| match tt {
331329
tt::TokenTree::Subtree(mut tt) => {
332-
if tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
333-
|| tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
330+
if tt.delimiter.close.anchor.ast_id == FIXUP_DUMMY_AST_ID
331+
|| tt.delimiter.open.anchor.ast_id == FIXUP_DUMMY_AST_ID
334332
{
335333
// Even though fixup never creates subtrees with fixup spans, the old proc-macro server
336334
// might copy them if the proc-macro asks for it, so we need to filter those out
@@ -341,7 +339,7 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
341339
SmallVec::from_const([tt.into()])
342340
}
343341
tt::TokenTree::Leaf(leaf) => {
344-
if leaf.span().anchor.file_id == FIXUP_DUMMY_FILE {
342+
if leaf.span().anchor.ast_id == FIXUP_DUMMY_AST_ID {
345343
// we have a fake node here, we need to replace it again with the original
346344
let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone();
347345
if original.delimiter.kind == tt::DelimiterKind::Invisible {

crates/proc-macro-srv/src/server/rust_analyzer_span.rs

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use std::{
1212

1313
use ::tt::{TextRange, TextSize};
1414
use proc_macro::bridge::{self, server};
15-
use span::Span;
15+
use span::{Span, FIXUP_ERASED_FILE_AST_ID_MARKER};
1616

1717
use crate::server::{
1818
delim_to_external, delim_to_internal, token_stream::TokenStreamBuilder, LiteralFormatter,
@@ -55,6 +55,10 @@ impl server::Types for RaSpanServer {
5555
}
5656

5757
impl server::FreeFunctions for RaSpanServer {
58+
fn injected_env_var(&mut self, _: &str) -> Option<std::string::String> {
59+
None
60+
}
61+
5862
fn track_env_var(&mut self, var: &str, value: Option<&str>) {
5963
self.tracked_env_vars.insert(var.into(), value.map(Into::into));
6064
}
@@ -124,9 +128,7 @@ impl server::TokenStream for RaSpanServer {
124128
});
125129

126130
let literal = tt::Literal { text, span: literal.0.span };
127-
let leaf: ::tt::Leaf<
128-
::tt::SpanData<base_db::span::SpanAnchor, base_db::span::SyntaxContextId>,
129-
> = tt::Leaf::from(literal);
131+
let leaf: tt::Leaf = tt::Leaf::from(literal);
130132
let tree = tt::TokenTree::from(leaf);
131133
Self::TokenStream::from_iter(iter::once(tree))
132134
}
@@ -246,6 +248,7 @@ impl server::Span for RaSpanServer {
246248
format!("{:?}", span)
247249
}
248250
fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
251+
// FIXME stub, requires db
249252
SourceFile {}
250253
}
251254
fn save_span(&mut self, _span: Self::Span) -> usize {
@@ -261,7 +264,7 @@ impl server::Span for RaSpanServer {
261264
/// See PR:
262265
/// https://github.com/rust-lang/rust/pull/55780
263266
fn source_text(&mut self, _span: Self::Span) -> Option<String> {
264-
// FIXME requires db
267+
// FIXME requires db, needs special handling wrt fixup spans
265268
None
266269
}
267270

@@ -278,9 +281,20 @@ impl server::Span for RaSpanServer {
278281
Range { start: span.range.start().into(), end: span.range.end().into() }
279282
}
280283
fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
284+
// We can't modify the span range for fixup spans, those are meaningful to fixup, so just
285+
// prefer the non-fixup span.
286+
if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
287+
return Some(second);
288+
}
289+
if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
290+
return Some(first);
291+
}
292+
// FIXME: Once we can talk back to the client, implement a "long join" request for anchors
293+
// that differ in [AstId]s as joining those spans requires resolving the AstIds.
281294
if first.anchor != second.anchor {
282295
return None;
283296
}
297+
// Differing context, we can't merge these so prefer the one that's root
284298
if first.ctx != second.ctx {
285299
if first.ctx.is_root() {
286300
return Some(second);
@@ -300,8 +314,10 @@ impl server::Span for RaSpanServer {
300314
start: Bound<usize>,
301315
end: Bound<usize>,
302316
) -> Option<Self::Span> {
303-
// FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL as it works on absolute
304-
// ranges
317+
// We can't modify the span range for fixup spans, those are meaningful to fixup.
318+
if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
319+
return Some(span);
320+
}
305321
let length = span.range.len().into();
306322

307323
let start: u32 = match start {
@@ -341,10 +357,18 @@ impl server::Span for RaSpanServer {
341357
}
342358

343359
fn end(&mut self, span: Self::Span) -> Self::Span {
360+
// We can't modify the span range for fixup spans, those are meaningful to fixup.
361+
if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
362+
return span;
363+
}
344364
Span { range: TextRange::empty(span.range.end()), ..span }
345365
}
346366

347367
fn start(&mut self, span: Self::Span) -> Self::Span {
368+
// We can't modify the span range for fixup spans, those are meaningful to fixup.
369+
if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
370+
return span;
371+
}
348372
Span { range: TextRange::empty(span.range.start()), ..span }
349373
}
350374

crates/proc-macro-srv/src/server/token_id.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,9 @@ impl server::Types for TokenIdServer {
5353
}
5454

5555
impl server::FreeFunctions for TokenIdServer {
56+
fn injected_env_var(&mut self, _: &str) -> Option<std::string::String> {
57+
None
58+
}
5659
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {}
5760
fn track_path(&mut self, _path: &str) {}
5861
fn literal_from_str(

crates/proc-macro-srv/src/tests/utils.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
//! utils used in proc-macro tests
22
3-
use base_db::{
4-
span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId},
5-
FileId,
6-
};
73
use expect_test::Expect;
84
use proc_macro_api::msg::TokenId;
5+
use span::{ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContextId};
96
use tt::TextRange;
107

118
use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv};
@@ -20,7 +17,7 @@ fn parse_string_spanned(
2017
anchor: SpanAnchor,
2118
call_site: SyntaxContextId,
2219
src: &str,
23-
) -> crate::server::TokenStream<SpanData> {
20+
) -> crate::server::TokenStream<Span> {
2421
crate::server::TokenStream::with_subtree(
2522
mbe::parse_to_token_tree(anchor, call_site, src).unwrap(),
2623
)
@@ -68,15 +65,15 @@ fn assert_expand_impl(
6865
.unwrap();
6966
expect.assert_eq(&format!("{res:?}"));
7067

71-
let def_site = SpanData {
68+
let def_site = Span {
7269
range: TextRange::new(0.into(), 150.into()),
7370
anchor: SpanAnchor {
7471
file_id: FileId::from_raw(41),
7572
ast_id: ErasedFileAstId::from_raw(From::from(1)),
7673
},
7774
ctx: SyntaxContextId::ROOT,
7875
};
79-
let call_site = SpanData {
76+
let call_site = Span {
8077
range: TextRange::new(0.into(), 100.into()),
8178
anchor: SpanAnchor {
8279
file_id: FileId::from_raw(42),

crates/span/src/lib.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,13 @@ pub type ErasedFileAstId = la_arena::Idx<syntax::SyntaxNodePtr>;
3030
pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
3131
la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0));
3232

33+
/// FileId used as the span for syntax node fixups. Any Span containing this file id is to be
34+
/// considered fake.
35+
pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
36+
// we pick the second to last for this in case we every consider making this a NonMaxU32, this
37+
// is required to be stable for the proc-macro-server
38+
la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(!0 - 1));
39+
3340
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
3441
pub struct SpanData<Ctx> {
3542
/// The text range of this span, relative to the anchor.

0 commit comments

Comments
 (0)