Merge pull request #19375 from ChayimFriedman2/do-not-complete

feat: Allow crate authors to control completion of their things
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 8479d00..610f8d0 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -70,6 +70,9 @@
       - name: Test
         run: cargo test --features sysroot-abi -p proc-macro-srv -p proc-macro-srv-cli -p proc-macro-api -- --quiet
 
+      - name: Check salsa dependency
+        run: "! (cargo tree -p proc-macro-srv-cli | grep -q salsa)"
+
   rust:
     if: github.repository == 'rust-lang/rust-analyzer'
     name: Rust
diff --git a/crates/base-db/src/change.rs b/crates/base-db/src/change.rs
index b5964ff..114b4cb 100644
--- a/crates/base-db/src/change.rs
+++ b/crates/base-db/src/change.rs
@@ -55,7 +55,7 @@
         if let Some(roots) = self.roots {
             for (idx, root) in roots.into_iter().enumerate() {
                 let root_id = SourceRootId(idx as u32);
-                let durability = durability(&root);
+                let durability = source_root_durability(&root);
                 for file_id in root.iter() {
                     db.set_file_source_root_with_durability(file_id, root_id, durability);
                 }
@@ -68,7 +68,7 @@
             let source_root_id = db.file_source_root(file_id);
             let source_root = db.source_root(source_root_id.source_root_id(db));
 
-            let durability = durability(&source_root.source_root(db));
+            let durability = file_text_durability(&source_root.source_root(db));
             // XXX: can't actually remove the file, just reset the text
             let text = text.unwrap_or_default();
             db.set_file_text_with_durability(file_id, &text, durability)
@@ -81,6 +81,10 @@
     }
 }
 
-fn durability(source_root: &SourceRoot) -> Durability {
+fn source_root_durability(source_root: &SourceRoot) -> Durability {
+    if source_root.is_library { Durability::MEDIUM } else { Durability::LOW }
+}
+
+fn file_text_durability(source_root: &SourceRoot) -> Durability {
     if source_root.is_library { Durability::HIGH } else { Durability::LOW }
 }
diff --git a/crates/base-db/src/input.rs b/crates/base-db/src/input.rs
index 2fe4f68..343aba1 100644
--- a/crates/base-db/src/input.rs
+++ b/crates/base-db/src/input.rs
@@ -491,7 +491,7 @@
         if **old_all_crates != *all_crates {
             db.set_all_crates_with_durability(
                 Arc::new(all_crates.into_boxed_slice()),
-                Durability::HIGH,
+                Durability::MEDIUM,
             );
         }
 
@@ -549,30 +549,30 @@
                 Entry::Occupied(entry) => {
                     let old_crate = *entry.get();
                     if crate_data != *old_crate.data(db) {
-                        old_crate.set_data(db).with_durability(Durability::HIGH).to(crate_data);
+                        old_crate.set_data(db).with_durability(Durability::MEDIUM).to(crate_data);
                     }
                     if krate.extra != *old_crate.extra_data(db) {
                         old_crate
                             .set_extra_data(db)
-                            .with_durability(Durability::HIGH)
+                            .with_durability(Durability::MEDIUM)
                             .to(krate.extra.clone());
                     }
                     if krate.cfg_options != *old_crate.cfg_options(db) {
                         old_crate
                             .set_cfg_options(db)
-                            .with_durability(Durability::HIGH)
+                            .with_durability(Durability::MEDIUM)
                             .to(krate.cfg_options.clone());
                     }
                     if krate.env != *old_crate.env(db) {
                         old_crate
                             .set_env(db)
-                            .with_durability(Durability::HIGH)
+                            .with_durability(Durability::MEDIUM)
                             .to(krate.env.clone());
                     }
                     if krate.ws_data != *old_crate.workspace_data(db) {
                         old_crate
                             .set_workspace_data(db)
-                            .with_durability(Durability::HIGH)
+                            .with_durability(Durability::MEDIUM)
                             .to(krate.ws_data.clone());
                     }
                     old_crate
@@ -585,7 +585,7 @@
                         krate.cfg_options.clone(),
                         krate.env.clone(),
                     )
-                    .durability(Durability::HIGH)
+                    .durability(Durability::MEDIUM)
                     .new(db);
                     entry.insert(input);
                     input
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 86f31dc..8059023 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2400,7 +2400,8 @@
             Some(FormatCount::Literal(n)) => {
                 let args = self.alloc_expr_desugared(Expr::Literal(Literal::Uint(
                     *n as u128,
-                    Some(BuiltinUint::Usize),
+                    // FIXME: Change this to Some(BuiltinUint::U16) once we drop support for toolchains < 1.88
+                    None,
                 )));
                 let count_is = match LangItem::FormatCount.ty_rel_path(
                     self.db,
diff --git a/crates/hir-def/src/expr_store/tests.rs b/crates/hir-def/src/expr_store/tests.rs
index a6fcfaa..55b95eb 100644
--- a/crates/hir-def/src/expr_store/tests.rs
+++ b/crates/hir-def/src/expr_store/tests.rs
@@ -216,7 +216,7 @@
                         8u32,
                         builtin#lang(Count::Implied),
                         builtin#lang(Count::Is)(
-                            2usize,
+                            2,
                         ),
                     ), builtin#lang(Placeholder::new)(
                         1usize,
diff --git a/crates/hir-ty/src/method_resolution.rs b/crates/hir-ty/src/method_resolution.rs
index 895cc58..d7c2f00 100644
--- a/crates/hir-ty/src/method_resolution.rs
+++ b/crates/hir-ty/src/method_resolution.rs
@@ -876,21 +876,26 @@
         return true;
     }
 
-    let unwrap_fundamental = |ty: Ty| match ty.kind(Interner) {
-        TyKind::Ref(_, _, referenced) => referenced.clone(),
-        &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => {
-            let struct_data = db.struct_data(s);
-            if struct_data.flags.contains(StructFlags::IS_FUNDAMENTAL) {
-                let next = subs.type_parameters(Interner).next();
-                match next {
-                    Some(ty) => ty,
-                    None => ty,
+    let unwrap_fundamental = |mut ty: Ty| {
+        // Unwrap all layers of fundamental types with a loop.
+        loop {
+            match ty.kind(Interner) {
+                TyKind::Ref(_, _, referenced) => ty = referenced.clone(),
+                &TyKind::Adt(AdtId(hir_def::AdtId::StructId(s)), ref subs) => {
+                    let struct_data = db.struct_data(s);
+                    if struct_data.flags.contains(StructFlags::IS_FUNDAMENTAL) {
+                        let next = subs.type_parameters(Interner).next();
+                        match next {
+                            Some(it) => ty = it,
+                            None => break ty,
+                        }
+                    } else {
+                        break ty;
+                    }
                 }
-            } else {
-                ty
+                _ => break ty,
             }
         }
-        _ => ty,
     };
     //   - At least one of the types `T0..=Tn`` must be a local type. Let `Ti`` be the first such type.
     let is_not_orphan = trait_ref.substitution.type_parameters(Interner).any(|ty| {
diff --git a/crates/ide-db/src/apply_change.rs b/crates/ide-db/src/apply_change.rs
index 36745b0..008b6fd 100644
--- a/crates/ide-db/src/apply_change.rs
+++ b/crates/ide-db/src/apply_change.rs
@@ -29,8 +29,8 @@
                     local_roots.insert(root_id);
                 }
             }
-            self.set_local_roots_with_durability(Arc::new(local_roots), Durability::HIGH);
-            self.set_library_roots_with_durability(Arc::new(library_roots), Durability::HIGH);
+            self.set_local_roots_with_durability(Arc::new(local_roots), Durability::MEDIUM);
+            self.set_library_roots_with_durability(Arc::new(library_roots), Durability::MEDIUM);
         }
         change.apply(self);
     }
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index c6bd803..b5b4a9e 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -220,9 +220,9 @@
         // This needs to be here otherwise `CrateGraphBuilder` will panic.
         db.set_all_crates(Arc::new(Box::new([])));
         CrateGraphBuilder::default().set_in_db(&mut db);
-        db.set_proc_macros_with_durability(Default::default(), Durability::HIGH);
-        db.set_local_roots_with_durability(Default::default(), Durability::HIGH);
-        db.set_library_roots_with_durability(Default::default(), Durability::HIGH);
+        db.set_proc_macros_with_durability(Default::default(), Durability::MEDIUM);
+        db.set_local_roots_with_durability(Default::default(), Durability::MEDIUM);
+        db.set_library_roots_with_durability(Default::default(), Durability::MEDIUM);
         db.set_expand_proc_attr_macros_with_durability(false, Durability::HIGH);
         db.update_base_query_lru_capacities(lru_capacity);
         db
diff --git a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
index 78a04e1..35dc9b0 100644
--- a/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
+++ b/crates/ide-diagnostics/src/handlers/trait_impl_orphan.rs
@@ -104,4 +104,17 @@
 "#,
         );
     }
+
+    #[test]
+    fn twice_fundamental() {
+        check_diagnostics(
+            r#"
+//- /foo.rs crate:foo
+pub trait Trait {}
+//- /bar.rs crate:bar deps:foo
+struct Foo;
+impl foo::Trait for &&Foo {}
+        "#,
+        );
+    }
 }
diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml
index 57a28b0..ab42102 100644
--- a/crates/proc-macro-srv-cli/Cargo.toml
+++ b/crates/proc-macro-srv-cli/Cargo.toml
@@ -8,6 +8,7 @@
 edition.workspace = true
 license.workspace = true
 rust-version.workspace = true
+publish = false
 
 [dependencies]
 proc-macro-srv.workspace = true
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index 245b064..c49159d 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -21,13 +21,32 @@
 /// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
 ///
 /// It seems that on Windows that behaviour is default, so we do nothing in that case.
+///
+/// # Safety
+///
+/// The caller is responsible for ensuring that the path is valid proc-macro library
 #[cfg(windows)]
-fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
+unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
+    // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
     unsafe { Library::new(file) }
 }
 
+/// Loads dynamic library in platform dependent manner.
+///
+/// For unix, you have to use RTLD_DEEPBIND flag to escape problems described
+/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample)
+/// and [here](https://github.com/rust-lang/rust/issues/60593).
+///
+/// Usage of RTLD_DEEPBIND
+/// [here](https://github.com/fedochet/rust-proc-macro-panic-inside-panic-expample/issues/1)
+///
+/// It seems that on Windows that behaviour is default, so we do nothing in that case.
+///
+/// # Safety
+///
+/// The caller is responsible for ensuring that the path is valid proc-macro library
 #[cfg(unix)]
-fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
+unsafe fn load_library(file: &Utf8Path) -> Result<Library, libloading::Error> {
     // not defined by POSIX, different values on mips vs other targets
     #[cfg(target_env = "gnu")]
     use libc::RTLD_DEEPBIND;
@@ -39,6 +58,7 @@
     #[cfg(not(target_env = "gnu"))]
     const RTLD_DEEPBIND: std::os::raw::c_int = 0x0;
 
+    // SAFETY: The caller is responsible for ensuring that the path is valid proc-macro library
     unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
 }
 
@@ -84,26 +104,32 @@
 impl ProcMacroLibrary {
     fn open(path: &Utf8Path) -> Result<Self, LoadProcMacroDylibError> {
         let file = fs::File::open(path)?;
+        #[allow(clippy::undocumented_unsafe_blocks)] // FIXME
         let file = unsafe { memmap2::Mmap::map(&file) }?;
         let obj = object::File::parse(&*file)
             .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
         let version_info = version::read_dylib_info(&obj)?;
+        if version_info.version_string != crate::RUSTC_VERSION_STRING {
+            return Err(LoadProcMacroDylibError::AbiMismatch(version_info.version_string));
+        }
+
         let symbol_name =
             find_registrar_symbol(&obj).map_err(invalid_data_err)?.ok_or_else(|| {
                 invalid_data_err(format!("Cannot find registrar symbol in file {path}"))
             })?;
 
-        let lib = load_library(path).map_err(invalid_data_err)?;
-        let proc_macros = unsafe {
-            // SAFETY: We extend the lifetime here to avoid referential borrow problems
-            // We never reveal proc_macros to the outside and drop it before _lib
-            std::mem::transmute::<&ProcMacros, &'static ProcMacros>(ProcMacros::from_lib(
-                &lib,
-                symbol_name,
-                &version_info.version_string,
-            )?)
-        };
-        Ok(ProcMacroLibrary { _lib: lib, proc_macros })
+        // SAFETY: We have verified the validity of the dylib as a proc-macro library
+        let lib = unsafe { load_library(path) }.map_err(invalid_data_err)?;
+        // SAFETY: We have verified the validity of the dylib as a proc-macro library
+        // The 'static lifetime is a lie, it's actually the lifetime of the library but unavoidable
+        // due to self-referentiality
+        // But we make sure that we do not drop it before the symbol is dropped
+        let proc_macros =
+            unsafe { lib.get::<&'static &'static ProcMacros>(symbol_name.as_bytes()) };
+        match proc_macros {
+            Ok(proc_macros) => Ok(ProcMacroLibrary { proc_macros: *proc_macros, _lib: lib }),
+            Err(e) => Err(e.into()),
+        }
     }
 }
 
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index 2623b2d..223c5a5 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -15,7 +15,7 @@
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 #![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
 #![allow(unreachable_pub, internal_features, clippy::disallowed_types, clippy::print_stderr)]
-#![deny(deprecated_safe)]
+#![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)]
 
 extern crate proc_macro;
 #[cfg(feature = "in-rust-tree")]
diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/proc_macros.rs
index a5fa7f6..42474ab 100644
--- a/crates/proc-macro-srv/src/proc_macros.rs
+++ b/crates/proc-macro-srv/src/proc_macros.rs
@@ -2,11 +2,7 @@
 
 use proc_macro::bridge;
 
-use libloading::Library;
-
-use crate::{
-    ProcMacroKind, ProcMacroSrvSpan, dylib::LoadProcMacroDylibError, server_impl::TopSubtree,
-};
+use crate::{ProcMacroKind, ProcMacroSrvSpan, server_impl::TopSubtree};
 
 #[repr(transparent)]
 pub(crate) struct ProcMacros([bridge::client::ProcMacro]);
@@ -18,28 +14,6 @@
 }
 
 impl ProcMacros {
-    /// Load a new ABI.
-    ///
-    /// # Arguments
-    ///
-    /// *`lib` - The dynamic library containing the macro implementations
-    /// *`symbol_name` - The symbol name the macros can be found attributes
-    /// *`info` - RustCInfo about the compiler that was used to compile the
-    ///           macro crate. This is the information we use to figure out
-    ///           which ABI to return
-    pub(crate) fn from_lib<'l>(
-        lib: &'l Library,
-        symbol_name: String,
-        version_string: &str,
-    ) -> Result<&'l ProcMacros, LoadProcMacroDylibError> {
-        if version_string != crate::RUSTC_VERSION_STRING {
-            return Err(LoadProcMacroDylibError::AbiMismatch(version_string.to_owned()));
-        }
-        unsafe { lib.get::<&'l &'l ProcMacros>(symbol_name.as_bytes()) }
-            .map(|it| **it)
-            .map_err(Into::into)
-    }
-
     pub(crate) fn expand<S: ProcMacroSrvSpan>(
         &self,
         macro_name: &str,
diff --git a/crates/span/Cargo.toml b/crates/span/Cargo.toml
index 3381dac..b3b401c 100644
--- a/crates/span/Cargo.toml
+++ b/crates/span/Cargo.toml
@@ -12,7 +12,7 @@
 
 [dependencies]
 la-arena.workspace = true
-salsa.workspace = true
+salsa = { workspace = true, optional = true }
 rustc-hash.workspace = true
 hashbrown.workspace = true
 text-size.workspace = true
@@ -22,5 +22,8 @@
 syntax.workspace = true
 stdx.workspace = true
 
+[features]
+default = ["salsa"]
+
 [lints]
 workspace = true
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 9ecd188..a2923cd 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -21,16 +21,19 @@
 //! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer.
 use std::fmt;
 
-use crate::{Edition, MacroCallId};
+use crate::Edition;
 
 /// A syntax context describes a hierarchy tracking order of macro definitions.
+#[cfg(feature = "salsa")]
 #[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
 pub struct SyntaxContext(
     salsa::Id,
     std::marker::PhantomData<&'static salsa::plumbing::interned::Value<SyntaxContext>>,
 );
 
+#[cfg(feature = "salsa")]
 const _: () = {
+    use crate::MacroCallId;
     use salsa::plumbing as zalsa_;
     use salsa::plumbing::interned as zalsa_struct_;
 
@@ -291,8 +294,6 @@
 };
 
 impl SyntaxContext {
-    const MAX_ID: u32 = salsa::Id::MAX_U32 - 1;
-
     pub fn is_root(self) -> bool {
         (SyntaxContext::MAX_ID - Edition::LATEST as u32) <= self.into_u32()
             && self.into_u32() <= (SyntaxContext::MAX_ID - Edition::Edition2015 as u32)
@@ -308,20 +309,43 @@
     /// The root context, which is the parent of all other contexts. All [`FileId`]s have this context.
     pub const fn root(edition: Edition) -> Self {
         let edition = edition as u32;
-        SyntaxContext(
-            salsa::Id::from_u32(SyntaxContext::MAX_ID - edition),
-            std::marker::PhantomData,
-        )
+        SyntaxContext::from_u32(SyntaxContext::MAX_ID - edition)
     }
+}
 
-    pub fn into_u32(self) -> u32 {
+#[cfg(feature = "salsa")]
+impl SyntaxContext {
+    const MAX_ID: u32 = salsa::Id::MAX_U32 - 1;
+
+    pub const fn into_u32(self) -> u32 {
         self.0.as_u32()
     }
 
-    pub fn from_u32(u32: u32) -> Self {
+    pub const fn from_u32(u32: u32) -> Self {
         Self(salsa::Id::from_u32(u32), std::marker::PhantomData)
     }
 }
+#[cfg(not(feature = "salsa"))]
+#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
+pub struct SyntaxContext(u32);
+
+#[allow(dead_code)]
+const SALSA_MAX_ID_MIRROR: u32 = u32::MAX - 0xFF;
+#[cfg(feature = "salsa")]
+const _: () = assert!(salsa::Id::MAX_U32 == SALSA_MAX_ID_MIRROR);
+
+#[cfg(not(feature = "salsa"))]
+impl SyntaxContext {
+    const MAX_ID: u32 = SALSA_MAX_ID_MIRROR - 1;
+
+    pub const fn into_u32(self) -> u32 {
+        self.0
+    }
+
+    pub const fn from_u32(u32: u32) -> Self {
+        Self(u32)
+    }
+}
 
 /// A property of a macro expansion that determines how identifiers
 /// produced by that expansion are resolved.
@@ -354,9 +378,9 @@
 impl fmt::Display for SyntaxContext {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         if self.is_root() {
-            write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.0.as_u32()).number())
+            write!(f, "ROOT{}", Edition::from_u32(SyntaxContext::MAX_ID - self.into_u32()).number())
         } else {
-            write!(f, "{}", self.0.as_u32())
+            write!(f, "{}", self.into_u32())
         }
     }
 }
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index fbd1b25..f3f6d80 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -180,6 +180,22 @@
     }
 }
 
+#[cfg(not(feature = "salsa"))]
+mod salsa {
+    #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
+    pub(crate) struct Id(u32);
+
+    impl Id {
+        pub(crate) const fn from_u32(u32: u32) -> Self {
+            Self(u32)
+        }
+
+        pub(crate) const fn as_u32(self) -> u32 {
+            self.0
+        }
+    }
+}
+
 /// 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
@@ -201,12 +217,14 @@
 #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct HirFileId(salsa::Id);
 
+#[cfg(feature = "salsa")]
 impl salsa::plumbing::AsId for HirFileId {
     fn as_id(&self) -> salsa::Id {
         self.0
     }
 }
 
+#[cfg(feature = "salsa")]
 impl salsa::plumbing::FromId for HirFileId {
     fn from_id(id: salsa::Id) -> Self {
         HirFileId(id)
@@ -273,12 +291,14 @@
 #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct MacroCallId(salsa::Id);
 
+#[cfg(feature = "salsa")]
 impl salsa::plumbing::AsId for MacroCallId {
     fn as_id(&self) -> salsa::Id {
         self.0
     }
 }
 
+#[cfg(feature = "salsa")]
 impl salsa::plumbing::FromId for MacroCallId {
     fn from_id(id: salsa::Id) -> Self {
         MacroCallId(id)