Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/span/src/lib.rs')
| -rw-r--r-- | crates/span/src/lib.rs | 158 |
1 files changed, 129 insertions, 29 deletions
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs index bbaf1b2a6d..b4e21d64f8 100644 --- a/crates/span/src/lib.rs +++ b/crates/span/src/lib.rs @@ -17,18 +17,6 @@ pub use syntax::Edition; pub use text_size::{TextRange, TextSize}; pub use vfs::FileId; -#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] -pub struct FilePosition { - pub file_id: FileId, - pub offset: TextSize, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct FileRange { - pub file_id: FileId, - pub range: TextRange, -} - // The first index is always the root node's AstId /// The root ast id always points to the encompassing file, using this in spans is discouraged as /// any range relative to it will be effectively absolute, ruining the entire point of anchored @@ -45,6 +33,16 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId = pub type Span = SpanData<SyntaxContextId>; +impl Span { + pub fn cover(self, other: Span) -> Span { + if self.anchor != other.anchor { + return self; + } + let range = self.range.cover(other.range); + Span { range, ..self } + } +} + /// Spans represent a region of code, used by the IDE to be able link macro inputs and outputs /// together. Positions in spans are relative to some [`SpanAnchor`] to make them more incremental /// friendly. @@ -63,7 +61,7 @@ pub struct SpanData<Ctx> { impl<Ctx: fmt::Debug> fmt::Debug for SpanData<Ctx> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { if f.alternate() { - fmt::Debug::fmt(&self.anchor.file_id.index(), f)?; + fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?; f.write_char(':')?; fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?; f.write_char('@')?; @@ -88,7 +86,7 @@ impl<Ctx: Copy> SpanData<Ctx> { impl fmt::Display for Span { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.anchor.file_id.index(), f)?; + fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?; f.write_char(':')?; fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?; f.write_char('@')?; @@ -100,7 +98,7 @@ impl fmt::Display for Span { #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SpanAnchor { - pub file_id: FileId, + pub file_id: EditionedFileId, pub ast_id: ErasedFileAstId, } @@ -110,6 +108,81 @@ impl fmt::Debug for SpanAnchor { } } +/// A [`FileId`] and [`Edition`] bundled up together. +/// The MSB is reserved for `HirFileId` encoding, more upper bits are used to then encode the edition. +#[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub struct EditionedFileId(u32); + +impl fmt::Debug for EditionedFileId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("EditionedFileId").field(&self.file_id()).field(&self.edition()).finish() + } +} + +impl From<EditionedFileId> for FileId { + fn from(value: EditionedFileId) -> Self { + value.file_id() + } +} + +const _: () = assert!( + EditionedFileId::RESERVED_HIGH_BITS + + EditionedFileId::EDITION_BITS + + EditionedFileId::FILE_ID_BITS + == u32::BITS +); +const _: () = assert!( + EditionedFileId::RESERVED_MASK ^ EditionedFileId::EDITION_MASK ^ EditionedFileId::FILE_ID_MASK + == 0xFFFF_FFFF +); + +impl EditionedFileId { + pub const RESERVED_MASK: u32 = 0x8000_0000; + pub const EDITION_MASK: u32 = 0x7F80_0000; + pub const FILE_ID_MASK: u32 = 0x007F_FFFF; + + pub const MAX_FILE_ID: u32 = Self::FILE_ID_MASK; + + pub const RESERVED_HIGH_BITS: u32 = Self::RESERVED_MASK.count_ones(); + pub const FILE_ID_BITS: u32 = Self::FILE_ID_MASK.count_ones(); + pub const EDITION_BITS: u32 = Self::EDITION_MASK.count_ones(); + + pub const fn current_edition(file_id: FileId) -> Self { + Self::new(file_id, Edition::CURRENT) + } + + pub const fn new(file_id: FileId, edition: Edition) -> Self { + let file_id = file_id.index(); + let edition = edition as u32; + assert!(file_id <= Self::MAX_FILE_ID); + Self(file_id | (edition << Self::FILE_ID_BITS)) + } + + pub fn from_raw(u32: u32) -> Self { + assert!(u32 & Self::RESERVED_MASK == 0); + assert!((u32 & Self::EDITION_MASK) >> Self::FILE_ID_BITS <= Edition::LATEST as u32); + Self(u32) + } + + pub const fn as_u32(self) -> u32 { + self.0 + } + + pub const fn file_id(self) -> FileId { + FileId::from_raw(self.0 & Self::FILE_ID_MASK) + } + + pub const fn unpack(self) -> (FileId, Edition) { + (self.file_id(), self.edition()) + } + + pub const fn edition(self) -> Edition { + let edition = (self.0 & Self::EDITION_MASK) >> Self::FILE_ID_BITS; + debug_assert!(edition <= Edition::LATEST as u32); + unsafe { std::mem::transmute(edition as u8) } + } +} + /// Input to the analyzer is a set of files, where each file is identified by /// `FileId` and contains source code. However, another source of source code in /// Rust are macros: each macro can be thought of as producing a "temporary @@ -149,6 +222,38 @@ impl fmt::Debug for HirFileId { } } +impl PartialEq<FileId> for HirFileId { + fn eq(&self, &other: &FileId) -> bool { + self.file_id().map(EditionedFileId::file_id) == Some(other) + } +} +impl PartialEq<HirFileId> for FileId { + fn eq(&self, other: &HirFileId) -> bool { + other.file_id().map(EditionedFileId::file_id) == Some(*self) + } +} + +impl PartialEq<EditionedFileId> for HirFileId { + fn eq(&self, &other: &EditionedFileId) -> bool { + *self == HirFileId::from(other) + } +} +impl PartialEq<HirFileId> for EditionedFileId { + fn eq(&self, &other: &HirFileId) -> bool { + other == HirFileId::from(*self) + } +} +impl PartialEq<EditionedFileId> for FileId { + fn eq(&self, &other: &EditionedFileId) -> bool { + *self == FileId::from(other) + } +} +impl PartialEq<FileId> for EditionedFileId { + fn eq(&self, &other: &FileId) -> bool { + other == FileId::from(*self) + } +} + #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroFileId { pub macro_call_id: MacroCallId, @@ -182,14 +287,14 @@ impl MacroCallId { #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum HirFileIdRepr { - FileId(FileId), + FileId(EditionedFileId), MacroFile(MacroFileId), } impl fmt::Debug for HirFileIdRepr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - Self::FileId(arg0) => f.debug_tuple("FileId").field(&arg0.index()).finish(), + Self::FileId(arg0) => arg0.fmt(f), Self::MacroFile(arg0) => { f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish() } @@ -197,19 +302,17 @@ impl fmt::Debug for HirFileIdRepr { } } -impl From<FileId> for HirFileId { +impl From<EditionedFileId> for HirFileId { #[allow(clippy::let_unit_value)] - fn from(id: FileId) -> Self { - _ = Self::ASSERT_MAX_FILE_ID_IS_SAME; - assert!(id.index() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.index()); - HirFileId(id.index()) + fn from(id: EditionedFileId) -> Self { + assert!(id.as_u32() <= Self::MAX_HIR_FILE_ID, "FileId index {} is too large", id.as_u32()); + HirFileId(id.as_u32()) } } impl From<MacroFileId> for HirFileId { #[allow(clippy::let_unit_value)] fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self { - _ = Self::ASSERT_MAX_FILE_ID_IS_SAME; let id = id.as_u32(); assert!(id <= Self::MAX_HIR_FILE_ID, "MacroCallId index {id} is too large"); HirFileId(id | Self::MACRO_FILE_TAG_MASK) @@ -217,9 +320,6 @@ impl From<MacroFileId> for HirFileId { } impl HirFileId { - const ASSERT_MAX_FILE_ID_IS_SAME: () = - [()][(Self::MAX_HIR_FILE_ID != FileId::MAX_FILE_ID) as usize]; - const MAX_HIR_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK; const MACRO_FILE_TAG_MASK: u32 = 1 << 31; @@ -239,9 +339,9 @@ impl HirFileId { } #[inline] - pub fn file_id(self) -> Option<FileId> { + pub fn file_id(self) -> Option<EditionedFileId> { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => Some(FileId::from_raw(self.0)), + 0 => Some(EditionedFileId(self.0)), _ => None, } } @@ -249,7 +349,7 @@ impl HirFileId { #[inline] pub fn repr(self) -> HirFileIdRepr { match self.0 & Self::MACRO_FILE_TAG_MASK { - 0 => HirFileIdRepr::FileId(FileId::from_raw(self.0)), + 0 => HirFileIdRepr::FileId(EditionedFileId(self.0)), _ => HirFileIdRepr::MacroFile(MacroFileId { macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)), }), |