ra_ap_span/
lib.rs

1//! File and span related types.
2use std::fmt::{self, Write};
3
4mod ast_id;
5mod hygiene;
6mod map;
7
8pub use self::{
9    ast_id::{AstIdMap, AstIdNode, ErasedFileAstId, FileAstId},
10    hygiene::{SyntaxContext, Transparency},
11    map::{RealSpanMap, SpanMap},
12};
13
14pub use syntax::Edition;
15pub use text_size::{TextRange, TextSize};
16pub use vfs::FileId;
17
18// The first index is always the root node's AstId
19/// The root ast id always points to the encompassing file, using this in spans is discouraged as
20/// any range relative to it will be effectively absolute, ruining the entire point of anchored
21/// relative text ranges.
22pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0);
23
24/// FileId used as the span for syntax node fixups. Any Span containing this file id is to be
25/// considered fake.
26pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
27    // we pick the second to last for this in case we ever consider making this a NonMaxU32, this
28    // is required to be stable for the proc-macro-server
29    ErasedFileAstId::from_raw(!0 - 1);
30
31pub type Span = SpanData<SyntaxContext>;
32
33impl Span {
34    pub fn cover(self, other: Span) -> Span {
35        if self.anchor != other.anchor {
36            return self;
37        }
38        let range = self.range.cover(other.range);
39        Span { range, ..self }
40    }
41}
42
43/// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs
44/// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental
45/// friendly.
46#[derive(Clone, Copy, PartialEq, Eq, Hash)]
47pub struct SpanData<Ctx> {
48    /// The text range of this span, relative to the anchor.
49    /// We need the anchor for incrementality, as storing absolute ranges will require
50    /// recomputation on every change in a file at all times.
51    pub range: TextRange,
52    /// The anchor this span is relative to.
53    pub anchor: SpanAnchor,
54    /// The syntax context of the span.
55    pub ctx: Ctx,
56}
57
58impl<Ctx: fmt::Debug> fmt::Debug for SpanData<Ctx> {
59    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60        if f.alternate() {
61            fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
62            f.write_char(':')?;
63            fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
64            f.write_char('@')?;
65            fmt::Debug::fmt(&self.range, f)?;
66            f.write_char('#')?;
67            self.ctx.fmt(f)
68        } else {
69            f.debug_struct("SpanData")
70                .field("range", &self.range)
71                .field("anchor", &self.anchor)
72                .field("ctx", &self.ctx)
73                .finish()
74        }
75    }
76}
77
78impl<Ctx: Copy> SpanData<Ctx> {
79    pub fn eq_ignoring_ctx(self, other: Self) -> bool {
80        self.anchor == other.anchor && self.range == other.range
81    }
82}
83
84impl fmt::Display for Span {
85    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
86        fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
87        f.write_char(':')?;
88        fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
89        f.write_char('@')?;
90        fmt::Debug::fmt(&self.range, f)?;
91        f.write_char('#')?;
92        self.ctx.fmt(f)
93    }
94}
95
96#[derive(Copy, Clone, PartialEq, Eq, Hash)]
97pub struct SpanAnchor {
98    pub file_id: EditionedFileId,
99    pub ast_id: ErasedFileAstId,
100}
101
102impl fmt::Debug for SpanAnchor {
103    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104        f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
105    }
106}
107
108/// A [`FileId`] and [`Edition`] bundled up together.
109/// The MSB is reserved for `HirFileId` encoding, more upper bits are used to then encode the edition.
110#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
111pub struct EditionedFileId(u32);
112
113impl fmt::Debug for EditionedFileId {
114    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
115        f.debug_tuple("EditionedFileId")
116            .field(&self.file_id().index())
117            .field(&self.edition())
118            .finish()
119    }
120}
121
122impl From<EditionedFileId> for FileId {
123    fn from(value: EditionedFileId) -> Self {
124        value.file_id()
125    }
126}
127
128const _: () = assert!(
129    EditionedFileId::RESERVED_HIGH_BITS
130        + EditionedFileId::EDITION_BITS
131        + EditionedFileId::FILE_ID_BITS
132        == u32::BITS
133);
134const _: () = assert!(
135    EditionedFileId::RESERVED_MASK ^ EditionedFileId::EDITION_MASK ^ EditionedFileId::FILE_ID_MASK
136        == 0xFFFF_FFFF
137);
138
139impl EditionedFileId {
140    pub const RESERVED_MASK: u32 = 0x8000_0000;
141    pub const EDITION_MASK: u32 = 0x7F80_0000;
142    pub const FILE_ID_MASK: u32 = 0x007F_FFFF;
143
144    pub const MAX_FILE_ID: u32 = Self::FILE_ID_MASK;
145
146    pub const RESERVED_HIGH_BITS: u32 = Self::RESERVED_MASK.count_ones();
147    pub const FILE_ID_BITS: u32 = Self::FILE_ID_MASK.count_ones();
148    pub const EDITION_BITS: u32 = Self::EDITION_MASK.count_ones();
149
150    pub const fn current_edition(file_id: FileId) -> Self {
151        Self::new(file_id, Edition::CURRENT)
152    }
153
154    pub const fn new(file_id: FileId, edition: Edition) -> Self {
155        let file_id = file_id.index();
156        let edition = edition as u32;
157        assert!(file_id <= Self::MAX_FILE_ID);
158        Self(file_id | (edition << Self::FILE_ID_BITS))
159    }
160
161    pub fn from_raw(u32: u32) -> Self {
162        assert!(u32 & Self::RESERVED_MASK == 0);
163        assert!((u32 & Self::EDITION_MASK) >> Self::FILE_ID_BITS <= Edition::LATEST as u32);
164        Self(u32)
165    }
166
167    pub const fn as_u32(self) -> u32 {
168        self.0
169    }
170
171    pub const fn file_id(self) -> FileId {
172        FileId::from_raw(self.0 & Self::FILE_ID_MASK)
173    }
174
175    pub const fn unpack(self) -> (FileId, Edition) {
176        (self.file_id(), self.edition())
177    }
178
179    pub const fn edition(self) -> Edition {
180        let edition = (self.0 & Self::EDITION_MASK) >> Self::FILE_ID_BITS;
181        debug_assert!(edition <= Edition::LATEST as u32);
182        unsafe { std::mem::transmute(edition as u8) }
183    }
184}
185
186#[cfg(not(feature = "salsa"))]
187mod salsa {
188    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
189    pub struct Id(u32);
190}
191
192/// Input to the analyzer is a set of files, where each file is identified by
193/// `FileId` and contains source code. However, another source of source code in
194/// Rust are macros: each macro can be thought of as producing a "temporary
195/// file". To assign an id to such a file, we use the id of the macro call that
196/// produced the file. So, a `HirFileId` is either a `FileId` (source code
197/// written by user), or a `MacroCallId` (source code produced by macro).
198///
199/// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file
200/// containing the call plus the offset of the macro call in the file. Note that
201/// this is a recursive definition! However, the size_of of `HirFileId` is
202/// finite (because everything bottoms out at the real `FileId`) and small
203/// (`MacroCallId` uses the location interning. You can check details here:
204/// <https://en.wikipedia.org/wiki/String_interning>).
205///
206/// Internally this holds a `salsa::Id`, but we cannot use this definition here
207/// as it references things from base-db and hir-expand.
208// FIXME: Give this a better fitting name
209#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
210pub struct HirFileId(pub salsa::Id);
211
212/// `MacroCallId` identifies a particular macro invocation, like
213/// `println!("Hello, {}", world)`.
214#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
215pub struct MacroCallId(pub salsa::Id);
216
217/// Legacy span type, only defined here as it is still used by the proc-macro server.
218/// While rust-analyzer doesn't use this anymore at all, RustRover relies on the legacy type for
219/// proc-macro expansion.
220#[derive(Clone, Copy, PartialEq, Eq, Hash)]
221pub struct TokenId(pub u32);
222
223impl std::fmt::Debug for TokenId {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        self.0.fmt(f)
226    }
227}