/// File and span related types. // FIXME: This should probably be moved into its own crate. use std::fmt; use salsa::InternId; use tt::SyntaxContext; use vfs::FileId; pub type ErasedFileAstId = la_arena::Idx; // The first inde is always the root node's AstId pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = la_arena::Idx::from_raw(la_arena::RawIdx::from_u32(0)); pub type SpanData = tt::SpanData; #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct SyntaxContextId(InternId); crate::impl_intern_key!(SyntaxContextId); impl fmt::Display for SyntaxContextId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "{}", self.0.as_u32()) } } impl SyntaxContext for SyntaxContextId { const DUMMY: Self = Self::ROOT; // veykril(HACK): salsa doesn't allow us fetching the id of the current input to be allocated so // we need a special value that behaves as the current context. } // inherent trait impls please tyvm impl SyntaxContextId { // TODO: This is very much UB, salsa exposes no way to create an InternId in a const context // currently (which kind of makes sense but we need it here!) pub const ROOT: Self = SyntaxContextId(unsafe { InternId::new_unchecked(0) }); // TODO: This is very much UB, salsa exposes no way to create an InternId in a const context // currently (which kind of makes sense but we need it here!) pub const SELF_REF: Self = SyntaxContextId(unsafe { InternId::new_unchecked(InternId::MAX - 1) }); /// Used syntax fixups pub const FAKE: Self = SyntaxContextId(unsafe { InternId::new_unchecked(InternId::MAX - 2) }); pub fn is_root(self) -> bool { self == Self::ROOT } } #[derive(Copy, Clone, PartialEq, Eq, Hash)] pub struct SpanAnchor { pub file_id: FileId, pub ast_id: ErasedFileAstId, } impl fmt::Debug for SpanAnchor { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish() } } impl tt::SpanAnchor for SpanAnchor { const DUMMY: Self = SpanAnchor { file_id: FileId(0), ast_id: ROOT_ERASED_FILE_AST_ID }; } /// 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 /// file". To assign an id to such a file, we use the id of the macro call that /// produced the file. So, a `HirFileId` is either a `FileId` (source code /// written by user), or a `MacroCallId` (source code produced by macro). /// /// What is a `MacroCallId`? Simplifying, it's a `HirFileId` of a file /// containing the call plus the offset of the macro call in the file. Note that /// this is a recursive definition! However, the size_of of `HirFileId` is /// finite (because everything bottoms out at the real `FileId`) and small /// (`MacroCallId` uses the location interning. You can check details here: /// ). /// /// The two variants are encoded in a single u32 which are differentiated by the MSB. /// If the MSB is 0, the value represents a `FileId`, otherwise the remaining 31 bits represent a /// `MacroCallId`. #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct HirFileId(u32); impl From for u32 { fn from(value: HirFileId) -> Self { value.0 } } impl From for HirFileId { fn from(value: u32) -> Self { HirFileId(value) } } impl From for HirFileId { fn from(value: MacroCallId) -> Self { value.as_file() } } impl fmt::Debug for HirFileId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { self.repr().fmt(f) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroFileId { pub macro_call_id: MacroCallId, } /// `MacroCallId` identifies a particular macro invocation, like /// `println!("Hello, {}", world)`. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct MacroCallId(salsa::InternId); crate::impl_intern_key!(MacroCallId); impl MacroCallId { pub fn as_file(self) -> HirFileId { MacroFileId { macro_call_id: self }.into() } pub fn as_macro_file(self) -> MacroFileId { MacroFileId { macro_call_id: self } } } #[derive(Clone, Copy, PartialEq, Eq, Hash)] pub enum HirFileIdRepr { FileId(FileId), 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.0).finish(), Self::MacroFile(arg0) => { f.debug_tuple("MacroFile").field(&arg0.macro_call_id.0).finish() } } } } impl From for HirFileId { fn from(FileId(id): FileId) -> Self { assert!(id < Self::MAX_FILE_ID); HirFileId(id) } } impl From for HirFileId { fn from(MacroFileId { macro_call_id: MacroCallId(id) }: MacroFileId) -> Self { let id = id.as_u32(); assert!(id < Self::MAX_FILE_ID); HirFileId(id | Self::MACRO_FILE_TAG_MASK) } } impl HirFileId { const MAX_FILE_ID: u32 = u32::MAX ^ Self::MACRO_FILE_TAG_MASK; const MACRO_FILE_TAG_MASK: u32 = 1 << 31; #[inline] pub fn is_macro(self) -> bool { self.0 & Self::MACRO_FILE_TAG_MASK != 0 } #[inline] pub fn macro_file(self) -> Option { match self.0 & Self::MACRO_FILE_TAG_MASK { 0 => None, _ => Some(MacroFileId { macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)), }), } } #[inline] pub fn file_id(self) -> Option { match self.0 & Self::MACRO_FILE_TAG_MASK { 0 => Some(FileId(self.0)), _ => None, } } #[inline] pub fn repr(self) -> HirFileIdRepr { match self.0 & Self::MACRO_FILE_TAG_MASK { 0 => HirFileIdRepr::FileId(FileId(self.0)), _ => HirFileIdRepr::MacroFile(MacroFileId { macro_call_id: MacroCallId(InternId::from(self.0 ^ Self::MACRO_FILE_TAG_MASK)), }), } } }