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