Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/base-db/src/editioned_file_id.rs')
-rw-r--r--crates/base-db/src/editioned_file_id.rs301
1 files changed, 21 insertions, 280 deletions
diff --git a/crates/base-db/src/editioned_file_id.rs b/crates/base-db/src/editioned_file_id.rs
index 13fb05d565..db3730bccd 100644
--- a/crates/base-db/src/editioned_file_id.rs
+++ b/crates/base-db/src/editioned_file_id.rs
@@ -1,305 +1,46 @@
//! Defines [`EditionedFileId`], an interned wrapper around [`span::EditionedFileId`] that
//! is interned (so queries can take it) and remembers its crate.
-use core::fmt;
-use std::hash::{Hash, Hasher};
+use std::hash::Hash;
+use salsa::Database;
use span::Edition;
use vfs::FileId;
-use crate::{Crate, RootQueryDb};
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
-pub struct EditionedFileId(
- salsa::Id,
- std::marker::PhantomData<&'static salsa::plumbing::interned::Value<EditionedFileId>>,
-);
-
-const _: () = {
- use salsa::plumbing as zalsa_;
- use zalsa_::interned as zalsa_struct_;
- type Configuration_ = EditionedFileId;
-
- #[derive(Debug, Clone, PartialEq, Eq)]
- pub struct EditionedFileIdData {
- editioned_file_id: span::EditionedFileId,
- krate: Crate,
- }
-
- // FIXME: This poses an invalidation problem, if one constructs an `EditionedFileId` with a
- // different crate then whatever the input of a memo used, it will invalidate the memo causing
- // it to recompute even if the crate is not really used.
- /// We like to include the origin crate in an `EditionedFileId` (for use in the item tree),
- /// but this poses us a problem.
- ///
- /// Spans contain `EditionedFileId`s, and we don't want to make them store the crate too
- /// because that will increase their size, which will increase memory usage significantly.
- /// Furthermore, things using spans do not generally need the crate: they are using the
- /// file id for queries like `ast_id_map` or `parse`, which do not care about the crate.
- ///
- /// To solve this, we hash **only the `span::EditionedFileId`**, but on still compare
- /// the crate in equality check. This preserves the invariant of `Hash` and `Eq` -
- /// although same hashes can be used for different items, same file ids used for multiple
- /// crates is a rare thing, and different items always have different hashes. Then,
- /// when we only have a `span::EditionedFileId`, we use the `intern()` method to
- /// reuse existing file ids, and create new one only if needed. See [`from_span_guess_origin`].
- ///
- /// See this for more info: https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/Letting.20EditionedFileId.20know.20its.20crate/near/530189401
- ///
- /// [`from_span_guess_origin`]: EditionedFileId::from_span_guess_origin
- #[derive(Hash, PartialEq, Eq)]
- struct WithoutCrate {
- editioned_file_id: span::EditionedFileId,
- }
-
- impl Hash for EditionedFileIdData {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- let EditionedFileIdData { editioned_file_id, krate: _ } = *self;
- editioned_file_id.hash(state);
- }
- }
-
- impl zalsa_struct_::HashEqLike<WithoutCrate> for EditionedFileIdData {
- #[inline]
- fn hash<H: Hasher>(&self, state: &mut H) {
- Hash::hash(self, state);
- }
-
- #[inline]
- fn eq(&self, data: &WithoutCrate) -> bool {
- let EditionedFileIdData { editioned_file_id, krate: _ } = *self;
- editioned_file_id == data.editioned_file_id
- }
- }
-
- impl zalsa_::HasJar for EditionedFileId {
- type Jar = zalsa_struct_::JarImpl<EditionedFileId>;
- const KIND: zalsa_::JarKind = zalsa_::JarKind::Struct;
- }
-
- zalsa_::register_jar! {
- zalsa_::ErasedJar::erase::<EditionedFileId>()
- }
-
- impl zalsa_struct_::Configuration for EditionedFileId {
- const LOCATION: salsa::plumbing::Location =
- salsa::plumbing::Location { file: file!(), line: line!() };
- const DEBUG_NAME: &'static str = "EditionedFileId";
- const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX;
- const PERSIST: bool = false;
-
- type Fields<'a> = EditionedFileIdData;
- type Struct<'db> = EditionedFileId;
-
- fn serialize<S>(_: &Self::Fields<'_>, _: S) -> Result<S::Ok, S::Error>
- where
- S: zalsa_::serde::Serializer,
- {
- unimplemented!("attempted to serialize value that set `PERSIST` to false")
- }
-
- fn deserialize<'de, D>(_: D) -> Result<Self::Fields<'static>, D::Error>
- where
- D: zalsa_::serde::Deserializer<'de>,
- {
- unimplemented!("attempted to deserialize value that cannot set `PERSIST` to false");
- }
- }
-
- impl Configuration_ {
- pub fn ingredient(zalsa: &zalsa_::Zalsa) -> &zalsa_struct_::IngredientImpl<Self> {
- static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<EditionedFileId>> =
- zalsa_::IngredientCache::new();
-
- // SAFETY: `lookup_jar_by_type` returns a valid ingredient index, and the only
- // ingredient created by our jar is the struct ingredient.
- unsafe {
- CACHE.get_or_create(zalsa, || {
- zalsa.lookup_jar_by_type::<zalsa_struct_::JarImpl<EditionedFileId>>()
- })
- }
- }
- }
-
- impl zalsa_::AsId for EditionedFileId {
- fn as_id(&self) -> salsa::Id {
- self.0.as_id()
- }
- }
- impl zalsa_::FromId for EditionedFileId {
- fn from_id(id: salsa::Id) -> Self {
- Self(<salsa::Id>::from_id(id), std::marker::PhantomData)
- }
- }
-
- unsafe impl Send for EditionedFileId {}
- unsafe impl Sync for EditionedFileId {}
-
- impl std::fmt::Debug for EditionedFileId {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- Self::default_debug_fmt(*self, f)
- }
- }
-
- impl zalsa_::SalsaStructInDb for EditionedFileId {
- type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex;
-
- fn lookup_ingredient_index(aux: &zalsa_::Zalsa) -> salsa::plumbing::IngredientIndices {
- aux.lookup_jar_by_type::<zalsa_struct_::JarImpl<EditionedFileId>>().into()
- }
-
- fn entries(zalsa: &zalsa_::Zalsa) -> impl Iterator<Item = zalsa_::DatabaseKeyIndex> + '_ {
- let _ingredient_index =
- zalsa.lookup_jar_by_type::<zalsa_struct_::JarImpl<EditionedFileId>>();
- <EditionedFileId>::ingredient(zalsa).entries(zalsa).map(|entry| entry.key())
- }
-
- #[inline]
- fn cast(id: salsa::Id, type_id: std::any::TypeId) -> Option<Self> {
- if type_id == std::any::TypeId::of::<EditionedFileId>() {
- Some(<Self as salsa::plumbing::FromId>::from_id(id))
- } else {
- None
- }
- }
-
- #[inline]
- unsafe fn memo_table(
- zalsa: &zalsa_::Zalsa,
- id: zalsa_::Id,
- current_revision: zalsa_::Revision,
- ) -> zalsa_::MemoTableWithTypes<'_> {
- // SAFETY: Guaranteed by caller.
- unsafe {
- zalsa.table().memos::<zalsa_struct_::Value<EditionedFileId>>(id, current_revision)
- }
- }
- }
-
- unsafe impl zalsa_::Update for EditionedFileId {
- unsafe fn maybe_update(old_pointer: *mut Self, new_value: Self) -> bool {
- if unsafe { *old_pointer } != new_value {
- unsafe { *old_pointer = new_value };
- true
- } else {
- false
- }
- }
- }
-
- impl EditionedFileId {
- pub fn from_span(
- db: &(impl salsa::Database + ?Sized),
- editioned_file_id: span::EditionedFileId,
- krate: Crate,
- ) -> Self {
- let (zalsa, zalsa_local) = db.zalsas();
- Configuration_::ingredient(zalsa).intern(
- zalsa,
- zalsa_local,
- EditionedFileIdData { editioned_file_id, krate },
- |_, data| data,
- )
- }
-
- /// Guesses the crate for the file.
- ///
- /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases:
- ///
- /// 1. The file is not in the module tree.
- /// 2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin
- /// (e.g. on enter feature, folding, etc.).
- pub fn from_span_guess_origin(
- db: &dyn RootQueryDb,
- editioned_file_id: span::EditionedFileId,
- ) -> Self {
- let (zalsa, zalsa_local) = db.zalsas();
- Configuration_::ingredient(zalsa).intern(
- zalsa,
- zalsa_local,
- WithoutCrate { editioned_file_id },
- |_, _| {
- // FileId not in the database.
- let krate = db
- .relevant_crates(editioned_file_id.file_id())
- .first()
- .copied()
- .or_else(|| db.all_crates().first().copied())
- .unwrap_or_else(|| {
- // What we're doing here is a bit fishy. We rely on the fact that we only need
- // the crate in the item tree, and we should not create an `EditionedFileId`
- // without a crate except in cases where it does not matter. The chances that
- // `all_crates()` will be empty are also very slim, but it can occur during startup.
- // In the very unlikely case that there is a bug and we'll use this crate, Salsa
- // will panic.
-
- // SAFETY: 0 is less than `Id::MAX_U32`.
- salsa::plumbing::FromId::from_id(unsafe { salsa::Id::from_index(0) })
- });
- EditionedFileIdData { editioned_file_id, krate }
- },
- )
- }
-
- pub fn editioned_file_id(self, db: &dyn salsa::Database) -> span::EditionedFileId {
- let zalsa = db.zalsa();
- let fields = Configuration_::ingredient(zalsa).fields(zalsa, self);
- fields.editioned_file_id
- }
-
- pub fn krate(self, db: &dyn salsa::Database) -> Crate {
- let zalsa = db.zalsa();
- let fields = Configuration_::ingredient(zalsa).fields(zalsa, self);
- fields.krate
- }
+#[salsa::interned(debug, constructor = from_span_file_id, no_lifetime)]
+#[derive(PartialOrd, Ord)]
+pub struct EditionedFileId {
+ field: span::EditionedFileId,
+}
- /// Default debug formatting for this struct (may be useful if you define your own `Debug` impl)
- pub fn default_debug_fmt(this: Self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- zalsa_::with_attached_database(|db| {
- let zalsa = db.zalsa();
- let fields = Configuration_::ingredient(zalsa).fields(zalsa, this);
- fmt::Debug::fmt(fields, f)
- })
- .unwrap_or_else(|| {
- f.debug_tuple("EditionedFileId").field(&zalsa_::AsId::as_id(&this)).finish()
- })
- }
+impl EditionedFileId {
+ #[inline]
+ pub fn new(db: &dyn Database, file_id: FileId, edition: Edition) -> Self {
+ Self::from_span_file_id(db, span::EditionedFileId::new(file_id, edition))
}
-};
-impl EditionedFileId {
#[inline]
- pub fn new(db: &dyn salsa::Database, file_id: FileId, edition: Edition, krate: Crate) -> Self {
- EditionedFileId::from_span(db, span::EditionedFileId::new(file_id, edition), krate)
+ pub fn current_edition(db: &dyn Database, file_id: FileId) -> Self {
+ Self::from_span_file_id(db, span::EditionedFileId::current_edition(file_id))
}
- /// Attaches the current edition and guesses the crate for the file.
- ///
- /// Only use this if you cannot precisely determine the origin. This can happen in one of two cases:
- ///
- /// 1. The file is not in the module tree.
- /// 2. You are latency sensitive and cannot afford calling the def map to precisely compute the origin
- /// (e.g. on enter feature, folding, etc.).
#[inline]
- pub fn current_edition_guess_origin(db: &dyn RootQueryDb, file_id: FileId) -> Self {
- Self::from_span_guess_origin(db, span::EditionedFileId::current_edition(file_id))
+ pub fn file_id(self, db: &dyn Database) -> vfs::FileId {
+ self.field(db).file_id()
}
#[inline]
- pub fn file_id(self, db: &dyn salsa::Database) -> vfs::FileId {
- let id = self.editioned_file_id(db);
- id.file_id()
+ pub fn span_file_id(self, db: &dyn Database) -> span::EditionedFileId {
+ self.field(db)
}
#[inline]
- pub fn unpack(self, db: &dyn salsa::Database) -> (vfs::FileId, span::Edition) {
- let id = self.editioned_file_id(db);
- (id.file_id(), id.edition())
+ pub fn unpack(self, db: &dyn Database) -> (vfs::FileId, span::Edition) {
+ self.field(db).unpack()
}
#[inline]
- pub fn edition(self, db: &dyn salsa::Database) -> Edition {
- self.editioned_file_id(db).edition()
+ pub fn edition(self, db: &dyn Database) -> Edition {
+ self.field(db).edition()
}
}