Skip to main content

ra_ap_span/
lib.rs

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