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