mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-30 05:45:12 +00:00
Encode edition within FileId in the hir layer
This commit is contained in:
parent
92268627a8
commit
5264f86242
160 changed files with 1117 additions and 824 deletions
|
@ -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
|
||||
|
@ -63,7 +51,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 +76,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 +88,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 +98,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 +212,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 +277,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 +292,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 +310,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 +329,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 +339,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)),
|
||||
}),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue