Skip to content

Commit 34d572e

Browse files
committed
Auto merge of rust-lang#16130 - Veykril:syntax-fixup, r=Veykril
fix: Syntax fixup now removes subtrees with fake spans Fixes rust-lang/rust-analyzer#16118
2 parents 96f6608 + 4c45d23 commit 34d572e

File tree

5 files changed

+54
-14
lines changed

5 files changed

+54
-14
lines changed

crates/base-db/src/span.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -151,21 +151,26 @@ impl fmt::Debug for HirFileIdRepr {
151151

152152
impl From<FileId> for HirFileId {
153153
fn from(id: FileId) -> Self {
154-
assert!(id.index() < Self::MAX_FILE_ID, "FileId index {} is too large", id.index());
154+
_ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
155+
assert!(id.index() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.index());
155156
HirFileId(id.index())
156157
}
157158
}
158159

159160
impl From<MacroFileId> for HirFileId {
160161
fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
162+
_ = Self::ASSERT_MAX_FILE_ID_IS_SAME;
161163
let id = id.as_u32();
162-
assert!(id < Self::MAX_FILE_ID, "MacroCallId index {} is too large", id);
164+
assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {} is too large", id);
163165
HirFileId(id | Self::MACRO_FILE_TAG_MASK)
164166
}
165167
}
166168

167169
impl HirFileId {
168-
const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
170+
const ASSERT_MAX_FILE_ID_IS_SAME: () =
171+
[()][(Self::MAX_HIR_FILE_ID != FileId::MAX_FILE_ID) as usize];
172+
173+
const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
169174
const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
170175

171176
#[inline]

crates/hir-expand/src/db.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use crate::{
2020
attrs::{collect_attrs, RawAttrs},
2121
builtin_attr_macro::pseudo_derive_attr_expansion,
2222
builtin_fn_macro::EagerExpander,
23-
fixup::{self, SyntaxFixupUndoInfo},
23+
fixup::{self, reverse_fixups, SyntaxFixupUndoInfo},
2424
hygiene::{apply_mark, SyntaxContextData, Transparency},
2525
span::{RealSpanMap, SpanMap, SpanMapRef},
2626
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
@@ -421,6 +421,15 @@ fn macro_arg(
421421
syntax::NodeOrToken::Token(_) => true,
422422
});
423423
fixups.remove.extend(censor);
424+
{
425+
let mut tt = mbe::syntax_node_to_token_tree_modified(
426+
&syntax,
427+
map.as_ref(),
428+
fixups.append.clone(),
429+
fixups.remove.clone(),
430+
);
431+
reverse_fixups(&mut tt, &fixups.undo_info);
432+
}
424433
(
425434
mbe::syntax_node_to_token_tree_modified(
426435
&syntax,

crates/hir-expand/src/fixup.rs

+30-9
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ use base_db::{
88
use la_arena::RawIdx;
99
use rustc_hash::{FxHashMap, FxHashSet};
1010
use smallvec::SmallVec;
11+
use stdx::never;
1112
use syntax::{
1213
ast::{self, AstNode, HasLoopBody},
1314
match_ast, SyntaxElement, SyntaxKind, SyntaxNode, TextRange, TextSize,
1415
};
1516
use triomphe::Arc;
16-
use tt::Spacing;
17+
use tt::{Spacing, Span};
1718

1819
use crate::{
1920
span::SpanMapRef,
@@ -45,19 +46,20 @@ impl SyntaxFixupUndoInfo {
4546
// replacement -> censor + append
4647
// append -> insert a fake node, here we need to assemble some dummy span that we can figure out how
4748
// to remove later
49+
const FIXUP_DUMMY_FILE: FileId = FileId::from_raw(FileId::MAX_FILE_ID);
50+
const FIXUP_DUMMY_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(RawIdx::from_u32(!0));
51+
const FIXUP_DUMMY_RANGE: TextRange = TextRange::empty(TextSize::new(0));
52+
const FIXUP_DUMMY_RANGE_END: TextSize = TextSize::new(!0);
4853

4954
pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> SyntaxFixups {
5055
let mut append = FxHashMap::<SyntaxElement, _>::default();
5156
let mut remove = FxHashSet::<SyntaxNode>::default();
5257
let mut preorder = node.preorder();
5358
let mut original = Vec::new();
54-
let dummy_range = TextRange::empty(TextSize::new(0));
59+
let dummy_range = FIXUP_DUMMY_RANGE;
5560
// we use a file id of `FileId(!0)` to signal a fake node, and the text range's start offset as
5661
// the index into the replacement vec but only if the end points to !0
57-
let dummy_anchor = SpanAnchor {
58-
file_id: FileId::from_raw(!0),
59-
ast_id: ErasedFileAstId::from_raw(RawIdx::from(!0)),
60-
};
62+
let dummy_anchor = SpanAnchor { file_id: FIXUP_DUMMY_FILE, ast_id: FIXUP_DUMMY_AST_ID };
6163
let fake_span = |range| SpanData {
6264
range: dummy_range,
6365
anchor: dummy_anchor,
@@ -76,7 +78,7 @@ pub(crate) fn fixup_syntax(span_map: SpanMapRef<'_>, node: &SyntaxNode) -> Synta
7678
let replacement = Leaf::Ident(Ident {
7779
text: "__ra_fixup".into(),
7880
span: SpanData {
79-
range: TextRange::new(TextSize::new(idx), TextSize::new(!0)),
81+
range: TextRange::new(TextSize::new(idx), FIXUP_DUMMY_RANGE_END),
8082
anchor: dummy_anchor,
8183
ctx: span_map.span_for_range(node_range).ctx,
8284
},
@@ -299,6 +301,13 @@ fn has_error_to_handle(node: &SyntaxNode) -> bool {
299301
pub(crate) fn reverse_fixups(tt: &mut Subtree, undo_info: &SyntaxFixupUndoInfo) {
300302
let Some(undo_info) = undo_info.original.as_deref() else { return };
301303
let undo_info = &**undo_info;
304+
if never!(
305+
tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
306+
|| tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
307+
) {
308+
tt.delimiter.close = SpanData::DUMMY;
309+
tt.delimiter.open = SpanData::DUMMY;
310+
}
302311
reverse_fixups_(tt, undo_info);
303312
}
304313

@@ -310,24 +319,36 @@ fn reverse_fixups_(tt: &mut Subtree, undo_info: &[Subtree]) {
310319
.filter(|tt| match tt {
311320
tt::TokenTree::Leaf(leaf) => {
312321
let span = leaf.span();
313-
span.anchor.file_id != FileId::from_raw(!0) || span.range.end() == TextSize::new(!0)
322+
let is_real_leaf = span.anchor.file_id != FIXUP_DUMMY_FILE;
323+
let is_replaced_node = span.range.end() == FIXUP_DUMMY_RANGE_END;
324+
is_real_leaf || is_replaced_node
314325
}
315326
tt::TokenTree::Subtree(_) => true,
316327
})
317328
.flat_map(|tt| match tt {
318329
tt::TokenTree::Subtree(mut tt) => {
330+
if tt.delimiter.close.anchor.file_id == FIXUP_DUMMY_FILE
331+
|| tt.delimiter.open.anchor.file_id == FIXUP_DUMMY_FILE
332+
{
333+
// Even though fixup never creates subtrees with fixup spans, the old proc-macro server
334+
// might copy them if the proc-macro asks for it, so we need to filter those out
335+
// here as well.
336+
return SmallVec::new_const();
337+
}
319338
reverse_fixups_(&mut tt, undo_info);
320339
SmallVec::from_const([tt.into()])
321340
}
322341
tt::TokenTree::Leaf(leaf) => {
323-
if leaf.span().anchor.file_id == FileId::from_raw(!0) {
342+
if leaf.span().anchor.file_id == FIXUP_DUMMY_FILE {
343+
// we have a fake node here, we need to replace it again with the original
324344
let original = undo_info[u32::from(leaf.span().range.start()) as usize].clone();
325345
if original.delimiter.kind == tt::DelimiterKind::Invisible {
326346
original.token_trees.into()
327347
} else {
328348
SmallVec::from_const([original.into()])
329349
}
330350
} else {
351+
// just a normal leaf
331352
SmallVec::from_const([leaf.into()])
332353
}
333354
}

crates/rust-analyzer/src/config.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1354,6 +1354,7 @@ impl Config {
13541354
}
13551355
}
13561356

1357+
// FIXME: This should be an AbsolutePathBuf
13571358
fn target_dir_from_config(&self) -> Option<PathBuf> {
13581359
self.data.rust_analyzerTargetDir.as_ref().and_then(|target_dir| match target_dir {
13591360
TargetDirectory::UseSubdirectory(yes) if *yes => {

crates/vfs/src/lib.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,17 @@ pub use paths::{AbsPath, AbsPathBuf};
6161
/// Most functions in rust-analyzer use this when they need to refer to a file.
6262
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
6363
pub struct FileId(u32);
64+
// pub struct FileId(NonMaxU32);
6465

6566
impl FileId {
6667
/// Think twice about using this outside of tests. If this ends up in a wrong place it will cause panics!
68+
// FIXME: To be removed once we get rid of all `SpanData::DUMMY` usages.
6769
pub const BOGUS: FileId = FileId(0xe4e4e);
70+
pub const MAX_FILE_ID: u32 = 0x7fff_ffff;
6871

6972
#[inline]
70-
pub fn from_raw(raw: u32) -> FileId {
73+
pub const fn from_raw(raw: u32) -> FileId {
74+
assert!(raw <= Self::MAX_FILE_ID);
7175
FileId(raw)
7276
}
7377

0 commit comments

Comments
 (0)