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

Commit 890eb17

Browse files
committed
Replace ID based TokenMap with proper relative text-ranges / spans
1 parent f79439c commit 890eb17

Some content is hidden

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

80 files changed

+1819
-2049
lines changed

Cargo.lock

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/base-db/src/fixture.rs

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,12 @@ use test_utils::{
88
ESCAPED_CURSOR_MARKER,
99
};
1010
use triomphe::Arc;
11-
use tt::token_id::{Leaf, Subtree, TokenTree};
11+
use tt::{Leaf, Subtree, TokenTree};
1212
use vfs::{file_set::FileSet, VfsPath};
1313

1414
use crate::{
1515
input::{CrateName, CrateOrigin, LangCrateOrigin},
16+
span::SpanData,
1617
Change, CrateDisplayName, CrateGraph, CrateId, Dependency, DependencyKind, Edition, Env,
1718
FileId, FilePosition, FileRange, ProcMacro, ProcMacroExpander, ProcMacroExpansionError,
1819
ProcMacros, ReleaseChannel, SourceDatabaseExt, SourceRoot, SourceRootId,
@@ -539,10 +540,10 @@ struct IdentityProcMacroExpander;
539540
impl ProcMacroExpander for IdentityProcMacroExpander {
540541
fn expand(
541542
&self,
542-
subtree: &Subtree,
543-
_: Option<&Subtree>,
543+
subtree: &Subtree<SpanData>,
544+
_: Option<&Subtree<SpanData>>,
544545
_: &Env,
545-
) -> Result<Subtree, ProcMacroExpansionError> {
546+
) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
546547
Ok(subtree.clone())
547548
}
548549
}
@@ -553,10 +554,10 @@ struct AttributeInputReplaceProcMacroExpander;
553554
impl ProcMacroExpander for AttributeInputReplaceProcMacroExpander {
554555
fn expand(
555556
&self,
556-
_: &Subtree,
557-
attrs: Option<&Subtree>,
557+
_: &Subtree<SpanData>,
558+
attrs: Option<&Subtree<SpanData>>,
558559
_: &Env,
559-
) -> Result<Subtree, ProcMacroExpansionError> {
560+
) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
560561
attrs
561562
.cloned()
562563
.ok_or_else(|| ProcMacroExpansionError::Panic("Expected attribute input".into()))
@@ -568,11 +569,11 @@ struct MirrorProcMacroExpander;
568569
impl ProcMacroExpander for MirrorProcMacroExpander {
569570
fn expand(
570571
&self,
571-
input: &Subtree,
572-
_: Option<&Subtree>,
572+
input: &Subtree<SpanData>,
573+
_: Option<&Subtree<SpanData>>,
573574
_: &Env,
574-
) -> Result<Subtree, ProcMacroExpansionError> {
575-
fn traverse(input: &Subtree) -> Subtree {
575+
) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
576+
fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
576577
let mut token_trees = vec![];
577578
for tt in input.token_trees.iter().rev() {
578579
let tt = match tt {
@@ -595,13 +596,13 @@ struct ShortenProcMacroExpander;
595596
impl ProcMacroExpander for ShortenProcMacroExpander {
596597
fn expand(
597598
&self,
598-
input: &Subtree,
599-
_: Option<&Subtree>,
599+
input: &Subtree<SpanData>,
600+
_: Option<&Subtree<SpanData>>,
600601
_: &Env,
601-
) -> Result<Subtree, ProcMacroExpansionError> {
602+
) -> Result<Subtree<SpanData>, ProcMacroExpansionError> {
602603
return Ok(traverse(input));
603604

604-
fn traverse(input: &Subtree) -> Subtree {
605+
fn traverse(input: &Subtree<SpanData>) -> Subtree<SpanData> {
605606
let token_trees = input
606607
.token_trees
607608
.iter()
@@ -613,7 +614,7 @@ impl ProcMacroExpander for ShortenProcMacroExpander {
613614
Subtree { delimiter: input.delimiter, token_trees }
614615
}
615616

616-
fn modify_leaf(leaf: &Leaf) -> Leaf {
617+
fn modify_leaf(leaf: &Leaf<SpanData>) -> Leaf<SpanData> {
617618
let mut leaf = leaf.clone();
618619
match &mut leaf {
619620
Leaf::Literal(it) => {

crates/base-db/src/input.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,10 @@ use la_arena::{Arena, Idx};
1313
use rustc_hash::{FxHashMap, FxHashSet};
1414
use syntax::SmolStr;
1515
use triomphe::Arc;
16-
use tt::token_id::Subtree;
1716
use vfs::{file_set::FileSet, AbsPathBuf, AnchoredPath, FileId, VfsPath};
1817

18+
use crate::span::SpanData;
19+
1920
// Map from crate id to the name of the crate and path of the proc-macro. If the value is `None`,
2021
// then the crate for the proc-macro hasn't been build yet as the build data is missing.
2122
pub type ProcMacroPaths = FxHashMap<CrateId, Result<(Option<String>, AbsPathBuf), String>>;
@@ -255,10 +256,10 @@ pub enum ProcMacroKind {
255256
pub trait ProcMacroExpander: fmt::Debug + Send + Sync + RefUnwindSafe {
256257
fn expand(
257258
&self,
258-
subtree: &Subtree,
259-
attrs: Option<&Subtree>,
259+
subtree: &tt::Subtree<SpanData>,
260+
attrs: Option<&tt::Subtree<SpanData>>,
260261
env: &Env,
261-
) -> Result<Subtree, ProcMacroExpansionError>;
262+
) -> Result<tt::Subtree<SpanData>, ProcMacroExpansionError>;
262263
}
263264

264265
#[derive(Debug)]

crates/base-db/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
mod input;
66
mod change;
77
pub mod fixture;
8+
pub mod span;
89

910
use std::panic;
1011

crates/base-db/src/span.rs

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
use std::fmt;
2+
3+
use salsa::InternId;
4+
use vfs::FileId;
5+
6+
pub type ErasedFileAstId = la_arena::Idx<syntax::SyntaxNodePtr>;
7+
8+
// The first inde is always the root node's AstId
9+
pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
10+
la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0));
11+
12+
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
13+
pub struct SyntaxContext;
14+
15+
pub type SpanData = tt::SpanData<SpanAnchor>;
16+
17+
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
18+
pub struct SpanAnchor {
19+
pub file_id: HirFileId,
20+
pub ast_id: ErasedFileAstId,
21+
}
22+
23+
impl fmt::Debug for SpanAnchor {
24+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
25+
f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
26+
}
27+
}
28+
29+
impl tt::Span for SpanAnchor {
30+
const DUMMY: Self = SpanAnchor { file_id: HirFileId(0), ast_id: ROOT_ERASED_FILE_AST_ID };
31+
}
32+
33+
/// Input to the analyzer is a set of files, where each file is identified by
34+
/// `FileId` and contains source code. However, another source of source code in
35+
/// Rust are macros: each macro can be thought of as producing a "temporary
36+
/// file". To assign an id to such a file, we use the id of the macro call that
37+
/// produced the file. So, a `HirFileId` is either a `FileId` (source code
38+
/// written by user), or a `MacroCallId` (source code produced by macro).
39+
///
40+
/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
41+
/// containing the call plus the offset of the macro call in the file. Note that
42+
/// this is a recursive definition! However, the size_of of `HirFileId` is
43+
/// finite (because everything bottoms out at the real `FileId`) and small
44+
/// (`MacroCallId` uses the location interning. You can check details here:
45+
/// <https://en.wikipedia.org/wiki/String_interning>).
46+
///
47+
/// The two variants are encoded in a single u32 which are differentiated by the MSB.
48+
/// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a
49+
/// `MacroCallId`.
50+
#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
51+
pub struct HirFileId(u32);
52+
53+
impl From<HirFileId> for u32 {
54+
fn from(value: HirFileId) -> Self {
55+
value.0
56+
}
57+
}
58+
59+
impl From<u32> for HirFileId {
60+
fn from(value: u32) -> Self {
61+
HirFileId(value)
62+
}
63+
}
64+
65+
impl From<MacroCallId> for HirFileId {
66+
fn from(value: MacroCallId) -> Self {
67+
value.as_file()
68+
}
69+
}
70+
71+
impl fmt::Debug for HirFileId {
72+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
73+
self.repr().fmt(f)
74+
}
75+
}
76+
77+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
78+
pub struct MacroFile {
79+
pub macro_call_id: MacroCallId,
80+
}
81+
82+
/// `MacroCallId` identifies a particular macro invocation, like
83+
/// `println!("Hello, {}", world)`.
84+
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
85+
pub struct MacroCallId(salsa::InternId);
86+
crate::impl_intern_key!(MacroCallId);
87+
88+
impl MacroCallId {
89+
pub fn as_file(self) -> HirFileId {
90+
MacroFile { macro_call_id: self }.into()
91+
}
92+
93+
pub fn as_macro_file(self) -> MacroFile {
94+
MacroFile { macro_call_id: self }
95+
}
96+
}
97+
98+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
99+
pub enum HirFileIdRepr {
100+
FileId(FileId),
101+
MacroFile(MacroFile),
102+
}
103+
104+
impl fmt::Debug for HirFileIdRepr {
105+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
106+
match self {
107+
Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.0).finish(),
108+
Self::MacroFile(arg0) => {
109+
f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish()
110+
}
111+
}
112+
}
113+
}
114+
115+
impl From<FileId> for HirFileId {
116+
fn from(FileId(id): FileId) -> Self {
117+
assert!(id < Self::MAX_FILE_ID);
118+
HirFileId(id)
119+
}
120+
}
121+
122+
impl From<MacroFile> for HirFileId {
123+
fn from(MacroFile { macro_call_id: MacroCallId(id) }: MacroFile) -> Self {
124+
let id = id.as_u32();
125+
assert!(id < Self::MAX_FILE_ID);
126+
HirFileId(id | Self::MACRO_FILE_TAG_MASK)
127+
}
128+
}
129+
130+
impl HirFileId {
131+
const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
132+
const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
133+
134+
#[inline]
135+
pub fn is_macro(self) -> bool {
136+
self.0 & Self::MACRO_FILE_TAG_MASK != 0
137+
}
138+
139+
#[inline]
140+
pub fn macro_file(self) -> Option<MacroFile> {
141+
match self.0 & Self::MACRO_FILE_TAG_MASK {
142+
0 => None,
143+
_ => Some(MacroFile {
144+
macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
145+
}),
146+
}
147+
}
148+
149+
#[inline]
150+
pub fn file_id(self) -> Option<FileId> {
151+
match self.0 & Self::MACRO_FILE_TAG_MASK {
152+
0 => Some(FileId(self.0)),
153+
_ => None,
154+
}
155+
}
156+
157+
#[inline]
158+
pub fn repr(self) -> HirFileIdRepr {
159+
match self.0 & Self::MACRO_FILE_TAG_MASK {
160+
0 => HirFileIdRepr::FileId(FileId(self.0)),
161+
_ => HirFileIdRepr::MacroFile(MacroFile {
162+
macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)),
163+
}),
164+
}
165+
}
166+
}

crates/cfg/src/tests.rs

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,37 @@ use arbitrary::{Arbitrary, Unstructured};
22
use expect_test::{expect, Expect};
33
use mbe::syntax_node_to_token_tree;
44
use syntax::{ast, AstNode};
5+
use tt::Span;
56

67
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
78

9+
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
10+
struct DummyFile;
11+
impl Span for DummyFile {
12+
const DUMMY: Self = DummyFile;
13+
}
14+
815
fn assert_parse_result(input: &str, expected: CfgExpr) {
9-
let (tt, _) = {
10-
let source_file = ast::SourceFile::parse(input).ok().unwrap();
11-
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
12-
syntax_node_to_token_tree(tt.syntax())
13-
};
16+
let source_file = ast::SourceFile::parse(input).ok().unwrap();
17+
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
18+
let tt = syntax_node_to_token_tree(tt.syntax(), DummyFile, 0.into(), &Default::default());
1419
let cfg = CfgExpr::parse(&tt);
1520
assert_eq!(cfg, expected);
1621
}
1722

1823
fn check_dnf(input: &str, expect: Expect) {
19-
let (tt, _) = {
20-
let source_file = ast::SourceFile::parse(input).ok().unwrap();
21-
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
22-
syntax_node_to_token_tree(tt.syntax())
23-
};
24+
let source_file = ast::SourceFile::parse(input).ok().unwrap();
25+
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
26+
let tt = syntax_node_to_token_tree(tt.syntax(), DummyFile, 0.into(), &Default::default());
2427
let cfg = CfgExpr::parse(&tt);
2528
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
2629
expect.assert_eq(&actual);
2730
}
2831

2932
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
30-
let (tt, _) = {
31-
let source_file = ast::SourceFile::parse(input).ok().unwrap();
32-
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
33-
syntax_node_to_token_tree(tt.syntax())
34-
};
33+
let source_file = ast::SourceFile::parse(input).ok().unwrap();
34+
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
35+
let tt = syntax_node_to_token_tree(tt.syntax(), DummyFile, 0.into(), &Default::default());
3536
let cfg = CfgExpr::parse(&tt);
3637
let dnf = DnfExpr::new(cfg);
3738
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
@@ -40,11 +41,9 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
4041

4142
#[track_caller]
4243
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
43-
let (tt, _) = {
44-
let source_file = ast::SourceFile::parse(input).ok().unwrap();
45-
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
46-
syntax_node_to_token_tree(tt.syntax())
47-
};
44+
let source_file = ast::SourceFile::parse(input).ok().unwrap();
45+
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
46+
let tt = syntax_node_to_token_tree(tt.syntax(), DummyFile, 0.into(), &Default::default());
4847
let cfg = CfgExpr::parse(&tt);
4948
let dnf = DnfExpr::new(cfg);
5049
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();

0 commit comments

Comments
 (0)