1use 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
18pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0);
23
24pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
27 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#[derive(Clone, Copy, PartialEq, Eq, Hash)]
47pub struct SpanData<Ctx> {
48 pub range: TextRange,
52 pub anchor: SpanAnchor,
54 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#[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").field(&self.file_id()).field(&self.edition()).finish()
116 }
117}
118
119impl From<EditionedFileId> for FileId {
120 fn from(value: EditionedFileId) -> Self {
121 value.file_id()
122 }
123}
124
125const _: () = assert!(
126 EditionedFileId::RESERVED_HIGH_BITS
127 + EditionedFileId::EDITION_BITS
128 + EditionedFileId::FILE_ID_BITS
129 == u32::BITS
130);
131const _: () = assert!(
132 EditionedFileId::RESERVED_MASK ^ EditionedFileId::EDITION_MASK ^ EditionedFileId::FILE_ID_MASK
133 == 0xFFFF_FFFF
134);
135
136impl EditionedFileId {
137 pub const RESERVED_MASK: u32 = 0x8000_0000;
138 pub const EDITION_MASK: u32 = 0x7F80_0000;
139 pub const FILE_ID_MASK: u32 = 0x007F_FFFF;
140
141 pub const MAX_FILE_ID: u32 = Self::FILE_ID_MASK;
142
143 pub const RESERVED_HIGH_BITS: u32 = Self::RESERVED_MASK.count_ones();
144 pub const FILE_ID_BITS: u32 = Self::FILE_ID_MASK.count_ones();
145 pub const EDITION_BITS: u32 = Self::EDITION_MASK.count_ones();
146
147 pub const fn current_edition(file_id: FileId) -> Self {
148 Self::new(file_id, Edition::CURRENT)
149 }
150
151 pub const fn new(file_id: FileId, edition: Edition) -> Self {
152 let file_id = file_id.index();
153 let edition = edition as u32;
154 assert!(file_id <= Self::MAX_FILE_ID);
155 Self(file_id | (edition << Self::FILE_ID_BITS))
156 }
157
158 pub fn from_raw(u32: u32) -> Self {
159 assert!(u32 & Self::RESERVED_MASK == 0);
160 assert!((u32 & Self::EDITION_MASK) >> Self::FILE_ID_BITS <= Edition::LATEST as u32);
161 Self(u32)
162 }
163
164 pub const fn as_u32(self) -> u32 {
165 self.0
166 }
167
168 pub const fn file_id(self) -> FileId {
169 FileId::from_raw(self.0 & Self::FILE_ID_MASK)
170 }
171
172 pub const fn unpack(self) -> (FileId, Edition) {
173 (self.file_id(), self.edition())
174 }
175
176 pub const fn edition(self) -> Edition {
177 let edition = (self.0 & Self::EDITION_MASK) >> Self::FILE_ID_BITS;
178 debug_assert!(edition <= Edition::LATEST as u32);
179 unsafe { std::mem::transmute(edition as u8) }
180 }
181}
182
183#[cfg(not(feature = "salsa"))]
184mod salsa {
185 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
186 pub(crate) struct Id(u32);
187
188 impl Id {
189 pub(crate) const fn from_u32(u32: u32) -> Self {
190 Self(u32)
191 }
192
193 pub(crate) const fn as_u32(self) -> u32 {
194 self.0
195 }
196 }
197}
198
199#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
218pub struct HirFileId(salsa::Id);
219
220#[cfg(feature = "salsa")]
221impl salsa::plumbing::AsId for HirFileId {
222 fn as_id(&self) -> salsa::Id {
223 self.0
224 }
225}
226
227#[cfg(feature = "salsa")]
228impl salsa::plumbing::FromId for HirFileId {
229 fn from_id(id: salsa::Id) -> Self {
230 HirFileId(id)
231 }
232}
233
234impl From<HirFileId> for u32 {
235 fn from(value: HirFileId) -> Self {
236 value.0.as_u32()
237 }
238}
239
240impl From<MacroCallId> for HirFileId {
241 fn from(value: MacroCallId) -> Self {
242 value.as_file()
243 }
244}
245
246impl fmt::Debug for HirFileId {
247 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
248 self.repr().fmt(f)
249 }
250}
251
252impl PartialEq<FileId> for HirFileId {
253 fn eq(&self, &other: &FileId) -> bool {
254 self.file_id().map(EditionedFileId::file_id) == Some(other)
255 }
256}
257impl PartialEq<HirFileId> for FileId {
258 fn eq(&self, other: &HirFileId) -> bool {
259 other.file_id().map(EditionedFileId::file_id) == Some(*self)
260 }
261}
262
263impl PartialEq<EditionedFileId> for HirFileId {
264 fn eq(&self, &other: &EditionedFileId) -> bool {
265 *self == HirFileId::from(other)
266 }
267}
268impl PartialEq<HirFileId> for EditionedFileId {
269 fn eq(&self, &other: &HirFileId) -> bool {
270 other == HirFileId::from(*self)
271 }
272}
273impl PartialEq<EditionedFileId> for FileId {
274 fn eq(&self, &other: &EditionedFileId) -> bool {
275 *self == FileId::from(other)
276 }
277}
278impl PartialEq<FileId> for EditionedFileId {
279 fn eq(&self, &other: &FileId) -> bool {
280 other == FileId::from(*self)
281 }
282}
283
284#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
285pub struct MacroFileId {
286 pub macro_call_id: MacroCallId,
287}
288
289#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
292pub struct MacroCallId(salsa::Id);
293
294#[cfg(feature = "salsa")]
295impl salsa::plumbing::AsId for MacroCallId {
296 fn as_id(&self) -> salsa::Id {
297 self.0
298 }
299}
300
301#[cfg(feature = "salsa")]
302impl salsa::plumbing::FromId for MacroCallId {
303 fn from_id(id: salsa::Id) -> Self {
304 MacroCallId(id)
305 }
306}
307
308impl MacroCallId {
309 pub const MAX_ID: u32 = 0x7fff_ffff;
310
311 pub fn as_file(self) -> HirFileId {
312 MacroFileId { macro_call_id: self }.into()
313 }
314
315 pub fn as_macro_file(self) -> MacroFileId {
316 MacroFileId { macro_call_id: self }
317 }
318}
319
320#[derive(Clone, Copy, PartialEq, Eq, Hash)]
321pub enum HirFileIdRepr {
322 FileId(EditionedFileId),
323 MacroFile(MacroFileId),
324}
325
326impl fmt::Debug for HirFileIdRepr {
327 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
328 match self {
329 Self::FileId(arg0) => arg0.fmt(f),
330 Self::MacroFile(arg0) => {
331 f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish()
332 }
333 }
334 }
335}
336
337impl From<EditionedFileId> for HirFileId {
338 #[allow(clippy::let_unit_value)]
339 fn from(id: EditionedFileId) -> Self {
340 assert!(id.as_u32() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.as_u32());
341 HirFileId(salsa::Id::from_u32(id.0))
342 }
343}
344
345impl From<MacroFileId> for HirFileId {
346 #[allow(clippy::let_unit_value)]
347 fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self {
348 let id: u32 = id.as_u32();
349 assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {id} is too large");
350 HirFileId(salsa::Id::from_u32(id | Self::MACRO_FILE_TAG_MASK))
351 }
352}
353
354impl HirFileId {
355 const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK;
356 const MACRO_FILE_TAG_MASK: u32 = 1 << 31;
357
358 #[inline]
359 pub fn is_macro(self) -> bool {
360 self.0.as_u32() & Self::MACRO_FILE_TAG_MASK != 0
361 }
362
363 #[inline]
364 pub fn macro_file(self) -> Option<MacroFileId> {
365 match self.0.as_u32() & Self::MACRO_FILE_TAG_MASK {
366 0 => None,
367 _ => Some(MacroFileId {
368 macro_call_id: MacroCallId(salsa::Id::from_u32(
369 self.0.as_u32() ^ Self::MACRO_FILE_TAG_MASK,
370 )),
371 }),
372 }
373 }
374
375 #[inline]
376 pub fn file_id(self) -> Option<EditionedFileId> {
377 match self.0.as_u32() & Self::MACRO_FILE_TAG_MASK {
378 0 => Some(EditionedFileId(self.0.as_u32())),
379 _ => None,
380 }
381 }
382
383 #[inline]
384 pub fn repr(self) -> HirFileIdRepr {
385 match self.0.as_u32() & Self::MACRO_FILE_TAG_MASK {
386 0 => HirFileIdRepr::FileId(EditionedFileId(self.0.as_u32())),
387 _ => HirFileIdRepr::MacroFile(MacroFileId {
388 macro_call_id: MacroCallId(salsa::Id::from_u32(
389 self.0.as_u32() ^ Self::MACRO_FILE_TAG_MASK,
390 )),
391 }),
392 }
393 }
394}
395
396#[derive(Clone, Copy, PartialEq, Eq, Hash)]
400pub struct TokenId(pub u32);
401
402impl std::fmt::Debug for TokenId {
403 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
404 self.0.fmt(f)
405 }
406}