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)