1use 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 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 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#[derive(Clone, Copy, PartialEq, Eq, Hash)]
64pub struct SpanData<Ctx> {
65 pub range: TextRange,
69 pub anchor: SpanAnchor,
71 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#[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#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
227pub struct HirFileId(pub salsa::Id);
228
229#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
232pub struct MacroCallId(pub salsa::Id);