diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index bb77324378a4..6293b11d1e89 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -24,6 +24,9 @@ jobs: runs-on: ${{ matrix.os }} env: CC: deny_c + # we want to build r-a on stable to check that it keeps building on stable, + # but we also want to test our proc-macro-srv which depends on nightly features + RUSTC_BOOTSTRAP: 1 strategy: fail-fast: false @@ -50,15 +53,15 @@ jobs: run: sed -i '/\[profile.dev]/a opt-level=1' Cargo.toml - name: Compile (tests) - run: cargo test --no-run --locked + run: cargo test --no-run --locked --features sysroot-abi # It's faster to `test` before `build` ¯\_(ツ)_/¯ - name: Compile (rust-analyzer) if: matrix.os == 'ubuntu-latest' - run: cargo build --quiet + run: cargo build --quiet --features sysroot-abi - name: Test - run: cargo test -- --nocapture --quiet + run: cargo test --features sysroot-abi -- --nocapture --quiet - name: Run analysis-stats on rust-analyzer if: matrix.os == 'ubuntu-latest' diff --git a/Cargo.lock b/Cargo.lock index f97591411ee0..48acc325daf3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1249,6 +1249,7 @@ dependencies = [ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ + "proc-macro-api", "proc-macro-srv", ] @@ -1456,7 +1457,7 @@ dependencies = [ "parking_lot 0.12.1", "parking_lot_core 0.9.6", "proc-macro-api", - "proc-macro-srv", + "proc-macro-srv-cli", "profile", "project-model", "rayon", diff --git a/crates/proc-macro-srv-cli/Cargo.toml b/crates/proc-macro-srv-cli/Cargo.toml index c402bc02253c..8f03c6ec7b57 100644 --- a/crates/proc-macro-srv-cli/Cargo.toml +++ b/crates/proc-macro-srv-cli/Cargo.toml @@ -10,6 +10,7 @@ rust-version.workspace = true [dependencies] proc-macro-srv.workspace = true +proc-macro-api.workspace = true [features] sysroot-abi = ["proc-macro-srv/sysroot-abi"] diff --git a/crates/proc-macro-srv/src/cli.rs b/crates/proc-macro-srv-cli/src/lib.rs similarity index 52% rename from crates/proc-macro-srv/src/cli.rs rename to crates/proc-macro-srv-cli/src/lib.rs index 05168feb629f..b307701e19fb 100644 --- a/crates/proc-macro-srv/src/cli.rs +++ b/crates/proc-macro-srv-cli/src/lib.rs @@ -3,10 +3,9 @@ use std::io; use proc_macro_api::msg::{self, Message}; -use crate::ProcMacroSrv; - +#[cfg(feature = "sysroot-abi")] pub fn run() -> io::Result<()> { - let mut srv = ProcMacroSrv::default(); + let mut srv = proc_macro_srv::ProcMacroSrv::default(); let mut buf = String::new(); while let Some(req) = read_request(&mut buf)? { @@ -24,6 +23,27 @@ pub fn run() -> io::Result<()> { Ok(()) } +#[cfg(not(feature = "sysroot-abi"))] +pub fn run() -> io::Result<()> { + let mut buf = String::new(); + + while let Some(req) = read_request(&mut buf)? { + let res = match req { + msg::Request::ListMacros { .. } => { + msg::Response::ListMacros(Err("server is built without sysroot support".to_owned())) + } + msg::Request::ExpandMacro(..) => msg::Response::ExpandMacro(Err(msg::PanicMessage( + "server is built without sysroot support".to_owned(), + ))), + msg::Request::ApiVersionCheck {} => { + msg::Response::ApiVersionCheck(proc_macro_api::msg::CURRENT_API_VERSION) + } + }; + write_response(res)? + } + + Ok(()) +} fn read_request(buf: &mut String) -> io::Result> { msg::Request::read(&mut io::stdin().lock(), buf) diff --git a/crates/proc-macro-srv-cli/src/main.rs b/crates/proc-macro-srv-cli/src/main.rs index ac9fa9f5a4ce..cc83c60f15c4 100644 --- a/crates/proc-macro-srv-cli/src/main.rs +++ b/crates/proc-macro-srv-cli/src/main.rs @@ -1,6 +1,5 @@ //! A standalone binary for `proc-macro-srv`. - -use proc_macro_srv::cli; +//! Driver for proc macro server fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -15,5 +14,5 @@ fn main() -> std::io::Result<()> { } } - cli::run() + proc_macro_srv_cli::run() } diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs deleted file mode 100644 index 93805c89354a..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/mod.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Macro ABI for version 1.63 of rustc - -#[allow(dead_code)] -#[doc(hidden)] -mod proc_macro; - -#[allow(dead_code)] -#[doc(hidden)] -mod ra_server; - -use libloading::Library; -use proc_macro_api::ProcMacroKind; - -use super::tt; -use super::PanicMessage; - -pub use ra_server::TokenStream; - -pub(crate) struct Abi { - exported_macros: Vec, -} - -impl From for PanicMessage { - fn from(p: proc_macro::bridge::PanicMessage) -> Self { - Self { message: p.as_str().map(|s| s.to_string()) } - } -} - -impl Abi { - pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result { - let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> = - lib.get(symbol_name.as_bytes())?; - Ok(Self { exported_macros: macros.to_vec() }) - } - - pub fn expand( - &self, - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - let parsed_body = TokenStream::with_subtree(macro_body.clone()); - - let parsed_attributes = - attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone())); - - for proc_macro in &self.exported_macros { - match proc_macro { - proc_macro::bridge::client::ProcMacro::CustomDerive { - trait_name, client, .. - } if *trait_name == macro_name => { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - proc_macro::bridge::client::ProcMacro::Bang { name, client } - if *name == macro_name => - { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - proc_macro::bridge::client::ProcMacro::Attr { name, client } - if *name == macro_name => - { - let res = client.run( - &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), - parsed_attributes, - parsed_body, - true, - ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); - } - _ => continue, - } - } - - Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into()) - } - - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.exported_macros - .iter() - .map(|proc_macro| match proc_macro { - proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - proc_macro::bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - proc_macro::bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs deleted file mode 100644 index 48030f8d82dc..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/buffer.rs +++ /dev/null @@ -1,156 +0,0 @@ -//! Buffer management for same-process client<->server communication. - -use std::io::{self, Write}; -use std::mem; -use std::ops::{Deref, DerefMut}; -use std::slice; - -#[repr(C)] -pub struct Buffer { - data: *mut u8, - len: usize, - capacity: usize, - reserve: extern "C" fn(Buffer, usize) -> Buffer, - drop: extern "C" fn(Buffer), -} - -unsafe impl Sync for Buffer {} -unsafe impl Send for Buffer {} - -impl Default for Buffer { - #[inline] - fn default() -> Self { - Self::from(vec![]) - } -} - -impl Deref for Buffer { - type Target = [u8]; - #[inline] - fn deref(&self) -> &[u8] { - unsafe { slice::from_raw_parts(self.data as *const u8, self.len) } - } -} - -impl DerefMut for Buffer { - #[inline] - fn deref_mut(&mut self) -> &mut [u8] { - unsafe { slice::from_raw_parts_mut(self.data, self.len) } - } -} - -impl Buffer { - #[inline] - pub(super) fn new() -> Self { - Self::default() - } - - #[inline] - pub(super) fn clear(&mut self) { - self.len = 0; - } - - #[inline] - pub(super) fn take(&mut self) -> Self { - mem::take(self) - } - - // We have the array method separate from extending from a slice. This is - // because in the case of small arrays, codegen can be more efficient - // (avoiding a memmove call). With extend_from_slice, LLVM at least - // currently is not able to make that optimization. - #[inline] - pub(super) fn extend_from_array(&mut self, xs: &[u8; N]) { - if xs.len() > (self.capacity - self.len) { - let b = self.take(); - *self = (b.reserve)(b, xs.len()); - } - unsafe { - xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); - self.len += xs.len(); - } - } - - #[inline] - pub(super) fn extend_from_slice(&mut self, xs: &[u8]) { - if xs.len() > (self.capacity - self.len) { - let b = self.take(); - *self = (b.reserve)(b, xs.len()); - } - unsafe { - xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len()); - self.len += xs.len(); - } - } - - #[inline] - pub(super) fn push(&mut self, v: u8) { - // The code here is taken from Vec::push, and we know that reserve() - // will panic if we're exceeding isize::MAX bytes and so there's no need - // to check for overflow. - if self.len == self.capacity { - let b = self.take(); - *self = (b.reserve)(b, 1); - } - unsafe { - *self.data.add(self.len) = v; - self.len += 1; - } - } -} - -impl Write for Buffer { - #[inline] - fn write(&mut self, xs: &[u8]) -> io::Result { - self.extend_from_slice(xs); - Ok(xs.len()) - } - - #[inline] - fn write_all(&mut self, xs: &[u8]) -> io::Result<()> { - self.extend_from_slice(xs); - Ok(()) - } - - #[inline] - fn flush(&mut self) -> io::Result<()> { - Ok(()) - } -} - -impl Drop for Buffer { - #[inline] - fn drop(&mut self) { - let b = self.take(); - (b.drop)(b); - } -} - -impl From> for Buffer { - fn from(mut v: Vec) -> Self { - let (data, len, capacity) = (v.as_mut_ptr(), v.len(), v.capacity()); - mem::forget(v); - - // This utility function is nested in here because it can *only* - // be safely called on `Buffer`s created by *this* `proc_macro`. - fn to_vec(b: Buffer) -> Vec { - unsafe { - let Buffer { data, len, capacity, .. } = b; - mem::forget(b); - Vec::from_raw_parts(data, len, capacity) - } - } - - extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { - let mut v = to_vec(b); - v.reserve(additional); - Buffer::from(v) - } - - extern "C" fn drop(b: Buffer) { - mem::drop(to_vec(b)); - } - - Buffer { data, len, capacity, reserve, drop } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs deleted file mode 100644 index b346c2c18969..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/client.rs +++ /dev/null @@ -1,510 +0,0 @@ -//! Client-side types. - -use super::*; - -use std::marker::PhantomData; - -macro_rules! define_handles { - ( - 'owned: $($oty:ident,)* - 'interned: $($ity:ident,)* - ) => { - #[repr(C)] - #[allow(non_snake_case)] - pub struct HandleCounters { - $($oty: AtomicUsize,)* - $($ity: AtomicUsize,)* - } - - impl HandleCounters { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - extern "C" fn get() -> &'static Self { - static COUNTERS: HandleCounters = HandleCounters { - $($oty: AtomicUsize::new(1),)* - $($ity: AtomicUsize::new(1),)* - }; - &COUNTERS - } - } - - // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. - #[repr(C)] - #[allow(non_snake_case)] - pub(super) struct HandleStore { - $($oty: handle::OwnedStore,)* - $($ity: handle::InternedStore,)* - } - - impl HandleStore { - pub(super) fn new(handle_counters: &'static HandleCounters) -> Self { - HandleStore { - $($oty: handle::OwnedStore::new(&handle_counters.$oty),)* - $($ity: handle::InternedStore::new(&handle_counters.$ity),)* - } - } - } - - $( - #[repr(C)] - pub(crate) struct $oty { - handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, - } - - // Forward `Drop::drop` to the inherent `drop` method. - impl Drop for $oty { - fn drop(&mut self) { - $oty { - handle: self.handle, - _marker: PhantomData, - }.drop(); - } - } - - impl Encode for $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - let handle = self.handle; - mem::forget(self); - handle.encode(w, s); - } - } - - impl DecodeMut<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$oty.take(handle::Handle::decode(r, &mut ())) - } - } - - impl Encode for &$oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl<'s, S: server::Types> Decode<'_, 's, HandleStore>> - for &'s Marked - { - fn decode(r: &mut Reader<'_>, s: &'s HandleStore>) -> Self { - &s.$oty[handle::Handle::decode(r, &mut ())] - } - } - - impl Encode for &mut $oty { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore>> - for &'s mut Marked - { - fn decode( - r: &mut Reader<'_>, - s: &'s mut HandleStore> - ) -> Self { - &mut s.$oty[handle::Handle::decode(r, &mut ())] - } - } - - impl Encode>> - for Marked - { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$oty.alloc(self).encode(w, s); - } - } - - impl DecodeMut<'_, '_, S> for $oty { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $oty { - handle: handle::Handle::decode(r, s), - _marker: PhantomData, - } - } - } - )* - - $( - #[repr(C)] - #[derive(Copy, Clone, PartialEq, Eq, Hash)] - pub(crate) struct $ity { - handle: handle::Handle, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual - // way of doing this, but that requires unstable features. - // rust-analyzer uses this code and avoids unstable features. - _marker: PhantomData<*mut ()>, - } - - impl Encode for $ity { - fn encode(self, w: &mut Writer, s: &mut S) { - self.handle.encode(w, s); - } - } - - impl DecodeMut<'_, '_, HandleStore>> - for Marked - { - fn decode(r: &mut Reader<'_>, s: &mut HandleStore>) -> Self { - s.$ity.copy(handle::Handle::decode(r, &mut ())) - } - } - - impl Encode>> - for Marked - { - fn encode(self, w: &mut Writer, s: &mut HandleStore>) { - s.$ity.alloc(self).encode(w, s); - } - } - - impl DecodeMut<'_, '_, S> for $ity { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - $ity { - handle: handle::Handle::decode(r, s), - _marker: PhantomData, - } - } - } - )* - } -} -define_handles! { - 'owned: - FreeFunctions, - TokenStream, - Group, - Literal, - SourceFile, - MultiSpan, - Diagnostic, - - 'interned: - Punct, - Ident, - Span, -} - -// FIXME(eddyb) generate these impls by pattern-matching on the -// names of methods - also could use the presence of `fn drop` -// to distinguish between 'owned and 'interned, above. -// Alternatively, special "modes" could be listed of types in with_api -// instead of pattern matching on methods, here and in server decl. - -impl Clone for TokenStream { - fn clone(&self) -> Self { - self.clone() - } -} - -impl Clone for Group { - fn clone(&self) -> Self { - self.clone() - } -} - -impl Clone for Literal { - fn clone(&self) -> Self { - self.clone() - } -} - -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Literal") - // format the kind without quotes, as in `kind: Float` - .field("kind", &format_args!("{}", &self.debug_kind())) - .field("symbol", &self.symbol()) - // format `Some("...")` on one line even in {:#?} mode - .field("suffix", &format_args!("{:?}", &self.suffix())) - .field("span", &self.span()) - .finish() - } -} - -impl Clone for SourceFile { - fn clone(&self) -> Self { - self.clone() - } -} - -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(&self.debug()) - } -} - -macro_rules! define_client_side { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $(impl $name { - $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { - Bridge::with(|bridge| { - let mut buf = bridge.cached_buffer.take(); - - buf.clear(); - api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ()); - reverse_encode!(buf; $($arg),*); - - buf = bridge.dispatch.call(buf); - - let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ()); - - bridge.cached_buffer = buf; - - r.unwrap_or_else(|e| panic::resume_unwind(e.into())) - }) - })* - })* - } -} -with_api!(self, self, define_client_side); - -enum BridgeState<'a> { - /// No server is currently connected to this client. - NotConnected, - - /// A server is connected and available for requests. - Connected(Bridge<'a>), - - /// Access to the bridge is being exclusively acquired - /// (e.g., during `BridgeState::with`). - InUse, -} - -enum BridgeStateL {} - -impl<'a> scoped_cell::ApplyL<'a> for BridgeStateL { - type Out = BridgeState<'a>; -} - -thread_local! { - static BRIDGE_STATE: scoped_cell::ScopedCell = - scoped_cell::ScopedCell::new(BridgeState::NotConnected); -} - -impl BridgeState<'_> { - /// Take exclusive control of the thread-local - /// `BridgeState`, and pass it to `f`, mutably. - /// The state will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - /// - /// N.B., while `f` is running, the thread-local state - /// is `BridgeState::InUse`. - fn with(f: impl FnOnce(&mut BridgeState<'_>) -> R) -> R { - BRIDGE_STATE.with(|state| { - state.replace(BridgeState::InUse, |mut state| { - // FIXME(#52812) pass `f` directly to `replace` when `RefMutL` is gone - f(&mut state) - }) - }) - } -} - -impl Bridge<'_> { - pub(crate) fn is_available() -> bool { - BridgeState::with(|state| match state { - BridgeState::Connected(_) | BridgeState::InUse => true, - BridgeState::NotConnected => false, - }) - } - - fn enter(self, f: impl FnOnce() -> R) -> R { - let force_show_panics = self.force_show_panics; - // Hide the default panic output within `proc_macro` expansions. - // NB. the server can't do this because it may use a different libstd. - static HIDE_PANICS_DURING_EXPANSION: Once = Once::new(); - HIDE_PANICS_DURING_EXPANSION.call_once(|| { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { - let show = BridgeState::with(|state| match state { - BridgeState::NotConnected => true, - BridgeState::Connected(_) | BridgeState::InUse => force_show_panics, - }); - if show { - prev(info) - } - })); - }); - - BRIDGE_STATE.with(|state| state.set(BridgeState::Connected(self), f)) - } - - fn with(f: impl FnOnce(&mut Bridge<'_>) -> R) -> R { - BridgeState::with(|state| match state { - BridgeState::NotConnected => { - panic!("procedural macro API is used outside of a procedural macro"); - } - BridgeState::InUse => { - panic!("procedural macro API is used while it's already in use"); - } - BridgeState::Connected(bridge) => f(bridge), - }) - } -} - -/// A client-side RPC entry-point, which may be using a different `proc_macro` -/// from the one used by the server, but can be invoked compatibly. -/// -/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters -/// decorate the `Client` with the RPC "interface" of the entry-point, but -/// do not themselves participate in ABI, at all, only facilitate type-checking. -/// -/// E.g. `Client` is the common proc macro interface, -/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`, -/// indicating that the RPC input and output will be serialized token streams, -/// and forcing the use of APIs that take/return `S::TokenStream`, server-side. -#[repr(C)] -pub struct Client { - // FIXME(eddyb) use a reference to the `static COUNTERS`, instead of - // a wrapper `fn` pointer, once `const fn` can reference `static`s. - pub(super) get_handle_counters: extern "C" fn() -> &'static HandleCounters, - - pub(super) run: extern "C" fn(Bridge<'_>) -> Buffer, - - pub(super) _marker: PhantomData O>, -} - -impl Copy for Client {} -impl Clone for Client { - fn clone(&self) -> Self { - *self - } -} - -/// Client-side helper for handling client panics, entering the bridge, -/// deserializing input and serializing output. -// FIXME(eddyb) maybe replace `Bridge::enter` with this? -fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( - mut bridge: Bridge<'_>, - f: impl FnOnce(A) -> R, -) -> Buffer { - // The initial `cached_buffer` contains the input. - let mut buf = bridge.cached_buffer.take(); - - panic::catch_unwind(panic::AssertUnwindSafe(|| { - bridge.enter(|| { - let reader = &mut &buf[..]; - let input = A::decode(reader, &mut ()); - - // Put the `cached_buffer` back in the `Bridge`, for requests. - Bridge::with(|bridge| bridge.cached_buffer = buf.take()); - - let output = f(input); - - // Take the `cached_buffer` back out, for the output value. - buf = Bridge::with(|bridge| bridge.cached_buffer.take()); - - // HACK(eddyb) Separate encoding a success value (`Ok(output)`) - // from encoding a panic (`Err(e: PanicMessage)`) to avoid - // having handles outside the `bridge.enter(|| ...)` scope, and - // to catch panics that could happen while encoding the success. - // - // Note that panics should be impossible beyond this point, but - // this is defensively trying to avoid any accidental panicking - // reaching the `extern "C"` (which should `abort` but might not - // at the moment, so this is also potentially preventing UB). - buf.clear(); - Ok::<_, ()>(output).encode(&mut buf, &mut ()); - }) - })) - .map_err(PanicMessage::from) - .unwrap_or_else(|e| { - buf.clear(); - Err::<(), _>(e).encode(&mut buf, &mut ()); - }); - buf -} - -impl Client { - pub const fn expand1( - f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - Client { - get_handle_counters: HandleCounters::get, - run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { - run_client(bridge, |input| f(super::super::TokenStream(input)).0) - }), - _marker: PhantomData, - } - } -} - -impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> { - pub const fn expand2( - f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream - + Copy, - ) -> Self { - Client { - get_handle_counters: HandleCounters::get, - run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| { - run_client(bridge, |(input, input2)| { - f(super::super::TokenStream(input), super::super::TokenStream(input2)).0 - }) - }), - _marker: PhantomData, - } - } -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum ProcMacro { - CustomDerive { - trait_name: &'static str, - attributes: &'static [&'static str], - client: Client, - }, - - Attr { - name: &'static str, - client: Client< - (super::super::TokenStream, super::super::TokenStream), - super::super::TokenStream, - >, - }, - - Bang { - name: &'static str, - client: Client, - }, -} - -impl ProcMacro { - pub fn name(&self) -> &'static str { - match self { - ProcMacro::CustomDerive { trait_name, .. } => trait_name, - ProcMacro::Attr { name, .. } => name, - ProcMacro::Bang { name, .. } => name, - } - } - - pub const fn custom_derive( - trait_name: &'static str, - attributes: &'static [&'static str], - expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } - } - - pub const fn attr( - name: &'static str, - expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream - + Copy, - ) -> Self { - ProcMacro::Attr { name, client: Client::expand2(expand) } - } - - pub const fn bang( - name: &'static str, - expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy, - ) -> Self { - ProcMacro::Bang { name, client: Client::expand1(expand) } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs deleted file mode 100644 index d371ae3cea09..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/closure.rs +++ /dev/null @@ -1,32 +0,0 @@ -//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. - -use std::marker::PhantomData; - -#[repr(C)] -pub struct Closure<'a, A, R> { - call: unsafe extern "C" fn(*mut Env, A) -> R, - env: *mut Env, - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. - // - // The `'a` lifetime parameter represents the lifetime of `Env`. - _marker: PhantomData<*mut &'a mut ()>, -} - -struct Env; - -impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> { - fn from(f: &'a mut F) -> Self { - unsafe extern "C" fn call R>(env: *mut Env, arg: A) -> R { - (*(env as *mut _ as *mut F))(arg) - } - Closure { call: call::, env: f as *mut _ as *mut Env, _marker: PhantomData } - } -} - -impl<'a, A, R> Closure<'a, A, R> { - pub fn call(&mut self, arg: A) -> R { - unsafe { (self.call)(self.env, arg) } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs deleted file mode 100644 index c219a9465d39..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/handle.rs +++ /dev/null @@ -1,89 +0,0 @@ -//! Server-side handles and storage for per-handle data. - -use std::collections::{BTreeMap, HashMap}; -use std::hash::{BuildHasher, Hash}; -use std::num::NonZeroU32; -use std::ops::{Index, IndexMut}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -pub(super) type Handle = NonZeroU32; - -/// A store that associates values of type `T` with numeric handles. A value can -/// be looked up using its handle. -pub(super) struct OwnedStore { - counter: &'static AtomicUsize, - data: BTreeMap, -} - -impl OwnedStore { - pub(super) fn new(counter: &'static AtomicUsize) -> Self { - // Ensure the handle counter isn't 0, which would panic later, - // when `NonZeroU32::new` (aka `Handle::new`) is called in `alloc`. - assert_ne!(counter.load(Ordering::SeqCst), 0); - - OwnedStore { counter, data: BTreeMap::new() } - } -} - -impl OwnedStore { - pub(super) fn alloc(&mut self, x: T) -> Handle { - let counter = self.counter.fetch_add(1, Ordering::SeqCst); - let handle = Handle::new(counter as u32).expect("`proc_macro` handle counter overflowed"); - assert!(self.data.insert(handle, x).is_none()); - handle - } - - pub(super) fn take(&mut self, h: Handle) -> T { - self.data.remove(&h).expect("use-after-free in `proc_macro` handle") - } -} - -impl Index for OwnedStore { - type Output = T; - fn index(&self, h: Handle) -> &T { - self.data.get(&h).expect("use-after-free in `proc_macro` handle") - } -} - -impl IndexMut for OwnedStore { - fn index_mut(&mut self, h: Handle) -> &mut T { - self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle") - } -} - -// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement -// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`). -#[derive(Clone)] -struct NonRandomState; - -impl BuildHasher for NonRandomState { - type Hasher = std::collections::hash_map::DefaultHasher; - #[inline] - fn build_hasher(&self) -> Self::Hasher { - Self::Hasher::new() - } -} - -/// Like `OwnedStore`, but avoids storing any value more than once. -pub(super) struct InternedStore { - owned: OwnedStore, - interner: HashMap, -} - -impl InternedStore { - pub(super) fn new(counter: &'static AtomicUsize) -> Self { - InternedStore { - owned: OwnedStore::new(counter), - interner: HashMap::with_hasher(NonRandomState), - } - } - - pub(super) fn alloc(&mut self, x: T) -> Handle { - let owned = &mut self.owned; - *self.interner.entry(x).or_insert_with(|| owned.alloc(x)) - } - - pub(super) fn copy(&mut self, h: Handle) -> T { - self.owned[h] - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs deleted file mode 100644 index 4967da4931a2..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/mod.rs +++ /dev/null @@ -1,451 +0,0 @@ -//! Internal interface for communicating between a `proc_macro` client -//! (a proc macro crate) and a `proc_macro` server (a compiler front-end). -//! -//! Serialization (with C ABI buffers) and unique integer handles are employed -//! to allow safely interfacing between two copies of `proc_macro` built -//! (from the same source) by different compilers with potentially mismatching -//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap). - -#![deny(unsafe_code)] - -pub use super::{Delimiter, Level, LineColumn, Spacing}; -use std::fmt; -use std::hash::Hash; -use std::marker; -use std::mem; -use std::ops::Bound; -use std::panic; -use std::sync::atomic::AtomicUsize; -use std::sync::Once; -use std::thread; - -/// Higher-order macro describing the server RPC API, allowing automatic -/// generation of type-safe Rust APIs, both client-side and server-side. -/// -/// `with_api!(MySelf, my_self, my_macro)` expands to: -/// ```rust,ignore (pseudo-code) -/// my_macro! { -/// // ... -/// Literal { -/// // ... -/// fn character(ch: char) -> MySelf::Literal; -/// // ... -/// fn span(my_self: &MySelf::Literal) -> MySelf::Span; -/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span); -/// }, -/// // ... -/// } -/// ``` -/// -/// The first two arguments serve to customize the arguments names -/// and argument/return types, to enable several different usecases: -/// -/// If `my_self` is just `self`, then each `fn` signature can be used -/// as-is for a method. If it's anything else (`self_` in practice), -/// then the signatures don't have a special `self` argument, and -/// can, therefore, have a different one introduced. -/// -/// If `MySelf` is just `Self`, then the types are only valid inside -/// a trait or a trait impl, where the trait has associated types -/// for each of the API types. If non-associated types are desired, -/// a module name (`self` in practice) can be used instead of `Self`. -macro_rules! with_api { - ($S:ident, $self:ident, $m:ident) => { - $m! { - FreeFunctions { - fn drop($self: $S::FreeFunctions); - fn track_env_var(var: &str, value: Option<&str>); - fn track_path(path: &str); - }, - TokenStream { - fn drop($self: $S::TokenStream); - fn clone($self: &$S::TokenStream) -> $S::TokenStream; - fn is_empty($self: &$S::TokenStream) -> bool; - fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>; - fn from_str(src: &str) -> $S::TokenStream; - fn to_string($self: &$S::TokenStream) -> String; - fn from_token_tree( - tree: TokenTree<$S::Group, $S::Punct, $S::Ident, $S::Literal>, - ) -> $S::TokenStream; - fn concat_trees( - base: Option<$S::TokenStream>, - trees: Vec>, - ) -> $S::TokenStream; - fn concat_streams( - base: Option<$S::TokenStream>, - streams: Vec<$S::TokenStream>, - ) -> $S::TokenStream; - fn into_trees( - $self: $S::TokenStream - ) -> Vec>; - }, - Group { - fn drop($self: $S::Group); - fn clone($self: &$S::Group) -> $S::Group; - fn new(delimiter: Delimiter, stream: Option<$S::TokenStream>) -> $S::Group; - fn delimiter($self: &$S::Group) -> Delimiter; - fn stream($self: &$S::Group) -> $S::TokenStream; - fn span($self: &$S::Group) -> $S::Span; - fn span_open($self: &$S::Group) -> $S::Span; - fn span_close($self: &$S::Group) -> $S::Span; - fn set_span($self: &mut $S::Group, span: $S::Span); - }, - Punct { - fn new(ch: char, spacing: Spacing) -> $S::Punct; - fn as_char($self: $S::Punct) -> char; - fn spacing($self: $S::Punct) -> Spacing; - fn span($self: $S::Punct) -> $S::Span; - fn with_span($self: $S::Punct, span: $S::Span) -> $S::Punct; - }, - Ident { - fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident; - fn span($self: $S::Ident) -> $S::Span; - fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident; - }, - Literal { - fn drop($self: $S::Literal); - fn clone($self: &$S::Literal) -> $S::Literal; - fn from_str(s: &str) -> Result<$S::Literal, ()>; - fn to_string($self: &$S::Literal) -> String; - fn debug_kind($self: &$S::Literal) -> String; - fn symbol($self: &$S::Literal) -> String; - fn suffix($self: &$S::Literal) -> Option; - fn integer(n: &str) -> $S::Literal; - fn typed_integer(n: &str, kind: &str) -> $S::Literal; - fn float(n: &str) -> $S::Literal; - fn f32(n: &str) -> $S::Literal; - fn f64(n: &str) -> $S::Literal; - fn string(string: &str) -> $S::Literal; - fn character(ch: char) -> $S::Literal; - fn byte_string(bytes: &[u8]) -> $S::Literal; - fn span($self: &$S::Literal) -> $S::Span; - fn set_span($self: &mut $S::Literal, span: $S::Span); - fn subspan( - $self: &$S::Literal, - start: Bound, - end: Bound, - ) -> Option<$S::Span>; - }, - SourceFile { - fn drop($self: $S::SourceFile); - fn clone($self: &$S::SourceFile) -> $S::SourceFile; - fn eq($self: &$S::SourceFile, other: &$S::SourceFile) -> bool; - fn path($self: &$S::SourceFile) -> String; - fn is_real($self: &$S::SourceFile) -> bool; - }, - MultiSpan { - fn drop($self: $S::MultiSpan); - fn new() -> $S::MultiSpan; - fn push($self: &mut $S::MultiSpan, span: $S::Span); - }, - Diagnostic { - fn drop($self: $S::Diagnostic); - fn new(level: Level, msg: &str, span: $S::MultiSpan) -> $S::Diagnostic; - fn sub( - $self: &mut $S::Diagnostic, - level: Level, - msg: &str, - span: $S::MultiSpan, - ); - fn emit($self: $S::Diagnostic); - }, - Span { - fn debug($self: $S::Span) -> String; - fn def_site() -> $S::Span; - fn call_site() -> $S::Span; - fn mixed_site() -> $S::Span; - fn source_file($self: $S::Span) -> $S::SourceFile; - fn parent($self: $S::Span) -> Option<$S::Span>; - fn source($self: $S::Span) -> $S::Span; - fn start($self: $S::Span) -> LineColumn; - fn end($self: $S::Span) -> LineColumn; - fn before($self: $S::Span) -> $S::Span; - fn after($self: $S::Span) -> $S::Span; - fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>; - fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span; - fn source_text($self: $S::Span) -> Option; - fn save_span($self: $S::Span) -> usize; - fn recover_proc_macro_span(id: usize) -> $S::Span; - }, - } - }; -} - -// FIXME(eddyb) this calls `encode` for each argument, but in reverse, -// to match the ordering in `reverse_decode`. -macro_rules! reverse_encode { - ($writer:ident;) => {}; - ($writer:ident; $first:ident $(, $rest:ident)*) => { - reverse_encode!($writer; $($rest),*); - $first.encode(&mut $writer, &mut ()); - } -} - -// FIXME(eddyb) this calls `decode` for each argument, but in reverse, -// to avoid borrow conflicts from borrows started by `&mut` arguments. -macro_rules! reverse_decode { - ($reader:ident, $s:ident;) => {}; - ($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => { - reverse_decode!($reader, $s; $($rest: $rest_ty),*); - let $first = <$first_ty>::decode(&mut $reader, $s); - } -} - -#[allow(unsafe_code)] -mod buffer; -#[forbid(unsafe_code)] -pub mod client; -#[allow(unsafe_code)] -mod closure; -#[forbid(unsafe_code)] -mod handle; -#[macro_use] -#[forbid(unsafe_code)] -mod rpc; -#[allow(unsafe_code)] -mod scoped_cell; -#[allow(unsafe_code)] -mod selfless_reify; -#[forbid(unsafe_code)] -pub mod server; - -use buffer::Buffer; -pub use rpc::PanicMessage; -use rpc::{Decode, DecodeMut, Encode, Reader, Writer}; - -/// An active connection between a server and a client. -/// The server creates the bridge (`Bridge::run_server` in `server.rs`), -/// then passes it to the client through the function pointer in the `run` -/// field of `client::Client`. The client holds its copy of the `Bridge` -/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`). -#[repr(C)] -pub struct Bridge<'a> { - /// Reusable buffer (only `clear`-ed, never shrunk), primarily - /// used for making requests, but also for passing input to client. - cached_buffer: Buffer, - - /// Server-side function that the client uses to make requests. - dispatch: closure::Closure<'a, Buffer, Buffer>, - - /// If 'true', always invoke the default panic hook - force_show_panics: bool, - - // Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing - // this, but that requires unstable features. rust-analyzer uses this code - // and avoids unstable features. - _marker: marker::PhantomData<*mut ()>, -} - -#[forbid(unsafe_code)] -#[allow(non_camel_case_types)] -mod api_tags { - use super::rpc::{DecodeMut, Encode, Reader, Writer}; - - macro_rules! declare_tags { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)* - }),* $(,)?) => { - $( - pub(super) enum $name { - $($method),* - } - rpc_encode_decode!(enum $name { $($method),* }); - )* - - pub(super) enum Method { - $($name($name)),* - } - rpc_encode_decode!(enum Method { $($name(m)),* }); - } - } - with_api!(self, self, declare_tags); -} - -/// Helper to wrap associated types to allow trait impl dispatch. -/// That is, normally a pair of impls for `T::Foo` and `T::Bar` -/// can overlap, but if the impls are, instead, on types like -/// `Marked` and `Marked`, they can't. -trait Mark { - type Unmarked; - fn mark(unmarked: Self::Unmarked) -> Self; -} - -/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details). -trait Unmark { - type Unmarked; - fn unmark(self) -> Self::Unmarked; -} - -#[derive(Copy, Clone, PartialEq, Eq, Hash)] -struct Marked { - value: T, - _marker: marker::PhantomData, -} - -impl Mark for Marked { - type Unmarked = T; - fn mark(unmarked: Self::Unmarked) -> Self { - Marked { value: unmarked, _marker: marker::PhantomData } - } -} -impl Unmark for Marked { - type Unmarked = T; - fn unmark(self) -> Self::Unmarked { - self.value - } -} -impl<'a, T, M> Unmark for &'a Marked { - type Unmarked = &'a T; - fn unmark(self) -> Self::Unmarked { - &self.value - } -} -impl<'a, T, M> Unmark for &'a mut Marked { - type Unmarked = &'a mut T; - fn unmark(self) -> Self::Unmarked { - &mut self.value - } -} - -impl Mark for Vec { - type Unmarked = Vec; - fn mark(unmarked: Self::Unmarked) -> Self { - // Should be a no-op due to std's in-place collect optimizations. - unmarked.into_iter().map(T::mark).collect() - } -} -impl Unmark for Vec { - type Unmarked = Vec; - fn unmark(self) -> Self::Unmarked { - // Should be a no-op due to std's in-place collect optimizations. - self.into_iter().map(T::unmark).collect() - } -} - -macro_rules! mark_noop { - ($($ty:ty),* $(,)?) => { - $( - impl Mark for $ty { - type Unmarked = Self; - fn mark(unmarked: Self::Unmarked) -> Self { - unmarked - } - } - impl Unmark for $ty { - type Unmarked = Self; - fn unmark(self) -> Self::Unmarked { - self - } - } - )* - } -} -mark_noop! { - (), - bool, - char, - &'_ [u8], - &'_ str, - String, - usize, - Delimiter, - Level, - LineColumn, - Spacing, -} - -rpc_encode_decode!( - enum Delimiter { - Parenthesis, - Brace, - Bracket, - None, - } -); -rpc_encode_decode!( - enum Level { - Error, - Warning, - Note, - Help, - } -); -rpc_encode_decode!(struct LineColumn { line, column }); -rpc_encode_decode!( - enum Spacing { - Alone, - Joint, - } -); - -macro_rules! mark_compound { - (enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => { - impl<$($T: Mark),+> Mark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn mark(unmarked: Self::Unmarked) -> Self { - match unmarked { - $($name::$variant $(($field))? => { - $name::$variant $((Mark::mark($field)))? - })* - } - } - } - - impl<$($T: Unmark),+> Unmark for $name <$($T),+> { - type Unmarked = $name <$($T::Unmarked),+>; - fn unmark(self) -> Self::Unmarked { - match self { - $($name::$variant $(($field))? => { - $name::$variant $((Unmark::unmark($field)))? - })* - } - } - } - } -} - -macro_rules! compound_traits { - ($($t:tt)*) => { - rpc_encode_decode!($($t)*); - mark_compound!($($t)*); - }; -} - -compound_traits!( - enum Bound { - Included(x), - Excluded(x), - Unbounded, - } -); - -compound_traits!( - enum Option { - Some(t), - None, - } -); - -compound_traits!( - enum Result { - Ok(t), - Err(e), - } -); - -#[derive(Clone)] -pub enum TokenTree { - Group(G), - Punct(P), - Ident(I), - Literal(L), -} - -compound_traits!( - enum TokenTree { - Group(tt), - Punct(tt), - Ident(tt), - Literal(tt), - } -); diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs deleted file mode 100644 index e9d7a46c06f6..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/rpc.rs +++ /dev/null @@ -1,304 +0,0 @@ -//! Serialization for client-server communication. - -use std::any::Any; -use std::char; -use std::io::Write; -use std::num::NonZeroU32; -use std::str; - -pub(super) type Writer = super::buffer::Buffer; - -pub(super) trait Encode: Sized { - fn encode(self, w: &mut Writer, s: &mut S); -} - -pub(super) type Reader<'a> = &'a [u8]; - -pub(super) trait Decode<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s S) -> Self; -} - -pub(super) trait DecodeMut<'a, 's, S>: Sized { - fn decode(r: &mut Reader<'a>, s: &'s mut S) -> Self; -} - -macro_rules! rpc_encode_decode { - (le $ty:ty) => { - impl Encode for $ty { - fn encode(self, w: &mut Writer, _: &mut S) { - w.extend_from_array(&self.to_le_bytes()); - } - } - - impl DecodeMut<'_, '_, S> for $ty { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { - const N: usize = ::std::mem::size_of::<$ty>(); - - let mut bytes = [0; N]; - bytes.copy_from_slice(&r[..N]); - *r = &r[N..]; - - Self::from_le_bytes(bytes) - } - } - }; - (struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { - $(self.$field.encode(w, s);)* - } - } - - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - $name { - $($field: DecodeMut::decode(r, s)),* - } - } - } - }; - (enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => { - impl),+)?> Encode for $name $(<$($T),+>)? { - fn encode(self, w: &mut Writer, s: &mut S) { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub const $variant: u8 = Tag::$variant as u8;)* - } - - match self { - $($name::$variant $(($field))* => { - tag::$variant.encode(w, s); - $($field.encode(w, s);)* - })* - } - } - } - - impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S> - for $name $(<$($T),+>)? - { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - // HACK(eddyb): `Tag` enum duplicated between the - // two impls as there's no other place to stash it. - #[allow(non_upper_case_globals)] - mod tag { - #[repr(u8)] enum Tag { $($variant),* } - - $(pub const $variant: u8 = Tag::$variant as u8;)* - } - - match u8::decode(r, s) { - $(tag::$variant => { - $(let $field = DecodeMut::decode(r, s);)* - $name::$variant $(($field))* - })* - _ => unreachable!(), - } - } - } - } -} - -impl Encode for () { - fn encode(self, _: &mut Writer, _: &mut S) {} -} - -impl DecodeMut<'_, '_, S> for () { - fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {} -} - -impl Encode for u8 { - fn encode(self, w: &mut Writer, _: &mut S) { - w.push(self); - } -} - -impl DecodeMut<'_, '_, S> for u8 { - fn decode(r: &mut Reader<'_>, _: &mut S) -> Self { - let x = r[0]; - *r = &r[1..]; - x - } -} - -rpc_encode_decode!(le u32); -rpc_encode_decode!(le usize); - -impl Encode for bool { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u8).encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for bool { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - match u8::decode(r, s) { - 0 => false, - 1 => true, - _ => unreachable!(), - } - } -} - -impl Encode for char { - fn encode(self, w: &mut Writer, s: &mut S) { - (self as u32).encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for char { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - char::from_u32(u32::decode(r, s)).unwrap() - } -} - -impl Encode for NonZeroU32 { - fn encode(self, w: &mut Writer, s: &mut S) { - self.get().encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for NonZeroU32 { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - Self::new(u32::decode(r, s)).unwrap() - } -} - -impl, B: Encode> Encode for (A, B) { - fn encode(self, w: &mut Writer, s: &mut S) { - self.0.encode(w, s); - self.1.encode(w, s); - } -} - -impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> - for (A, B) -{ - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - (DecodeMut::decode(r, s), DecodeMut::decode(r, s)) - } -} - -impl Encode for &[u8] { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - w.write_all(self).unwrap(); - } -} - -impl<'a, S> DecodeMut<'a, '_, S> for &'a [u8] { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - let len = usize::decode(r, s); - let xs = &r[..len]; - *r = &r[len..]; - xs - } -} - -impl Encode for &str { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_bytes().encode(w, s); - } -} - -impl<'a, S> DecodeMut<'a, '_, S> for &'a str { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - str::from_utf8(<&[u8]>::decode(r, s)).unwrap() - } -} - -impl Encode for String { - fn encode(self, w: &mut Writer, s: &mut S) { - self[..].encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for String { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - <&str>::decode(r, s).to_string() - } -} - -impl> Encode for Vec { - fn encode(self, w: &mut Writer, s: &mut S) { - self.len().encode(w, s); - for x in self { - x.encode(w, s); - } - } -} - -impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec { - fn decode(r: &mut Reader<'a>, s: &mut S) -> Self { - let len = usize::decode(r, s); - let mut vec = Vec::with_capacity(len); - for _ in 0..len { - vec.push(T::decode(r, s)); - } - vec - } -} - -/// Simplified version of panic payloads, ignoring -/// types other than `&'static str` and `String`. -pub enum PanicMessage { - StaticStr(&'static str), - String(String), - Unknown, -} - -impl From> for PanicMessage { - fn from(payload: Box) -> Self { - if let Some(s) = payload.downcast_ref::<&'static str>() { - return PanicMessage::StaticStr(s); - } - if let Ok(s) = payload.downcast::() { - return PanicMessage::String(*s); - } - PanicMessage::Unknown - } -} - -impl Into> for PanicMessage { - fn into(self) -> Box { - match self { - PanicMessage::StaticStr(s) => Box::new(s), - PanicMessage::String(s) => Box::new(s), - PanicMessage::Unknown => { - struct UnknownPanicMessage; - Box::new(UnknownPanicMessage) - } - } - } -} - -impl PanicMessage { - pub fn as_str(&self) -> Option<&str> { - match self { - PanicMessage::StaticStr(s) => Some(s), - PanicMessage::String(s) => Some(s), - PanicMessage::Unknown => None, - } - } -} - -impl Encode for PanicMessage { - fn encode(self, w: &mut Writer, s: &mut S) { - self.as_str().encode(w, s); - } -} - -impl DecodeMut<'_, '_, S> for PanicMessage { - fn decode(r: &mut Reader<'_>, s: &mut S) -> Self { - match Option::::decode(r, s) { - Some(s) => PanicMessage::String(s), - None => PanicMessage::Unknown, - } - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs deleted file mode 100644 index 2cde1f65adf9..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/scoped_cell.rs +++ /dev/null @@ -1,81 +0,0 @@ -//! `Cell` variant for (scoped) existential lifetimes. - -use std::cell::Cell; -use std::mem; -use std::ops::{Deref, DerefMut}; - -/// Type lambda application, with a lifetime. -#[allow(unused_lifetimes)] -pub trait ApplyL<'a> { - type Out; -} - -/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. -pub trait LambdaL: for<'a> ApplyL<'a> {} - -impl ApplyL<'a>> LambdaL for T {} - -// HACK(eddyb) work around projection limitations with a newtype -// FIXME(#52812) replace with `&'a mut >::Out` -pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut >::Out); - -impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { - type Target = >::Out; - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -pub struct ScopedCell(Cell<>::Out>); - -impl ScopedCell { - pub const fn new(value: >::Out) -> Self { - ScopedCell(Cell::new(value)) - } - - /// Sets the value in `self` to `replacement` while - /// running `f`, which gets the old value, mutably. - /// The old value will be restored after `f` exits, even - /// by panic, including modifications made to it by `f`. - pub fn replace<'a, R>( - &self, - replacement: >::Out, - f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, - ) -> R { - /// Wrapper that ensures that the cell always gets filled - /// (with the original state, optionally changed by `f`), - /// even if `f` had panicked. - struct PutBackOnDrop<'a, T: LambdaL> { - cell: &'a ScopedCell, - value: Option<>::Out>, - } - - impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { - fn drop(&mut self) { - self.cell.0.set(self.value.take().unwrap()); - } - } - - let mut put_back_on_drop = PutBackOnDrop { - cell: self, - value: Some(self.0.replace(unsafe { - let erased = mem::transmute_copy(&replacement); - mem::forget(replacement); - erased - })), - }; - - f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) - } - - /// Sets the value in `self` to `value` while running `f`. - pub fn set(&self, value: >::Out, f: impl FnOnce() -> R) -> R { - self.replace(value, |_| f()) - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs deleted file mode 100644 index 4ee4bb87c2bb..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/selfless_reify.rs +++ /dev/null @@ -1,83 +0,0 @@ -//! Abstraction for creating `fn` pointers from any callable that *effectively* -//! has the equivalent of implementing `Default`, even if the compiler neither -//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers) -//! other than those with absolutely no captures. -//! -//! More specifically, for a closure-like type to be "effectively `Default`": -//! * it must be a ZST (zero-sized type): no information contained within, so -//! that `Default`'s return value (if it were implemented) is unambiguous -//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar -//! types that would make duplicating values at will unsound -//! * combined with the ZST requirement, this confers a kind of "telecopy" -//! ability: similar to `Copy`, but without keeping the value around, and -//! instead "reconstructing" it (a noop given it's a ZST) when needed -//! * it must be *provably* inhabited: no captured uninhabited types or any -//! other types that cannot be constructed by the user of this abstraction -//! * the proof is a value of the closure-like type itself, in a sense the -//! "seed" for the "telecopy" process made possible by ZST + `Copy` -//! * this requirement is the only reason an abstraction limited to a specific -//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic -//! at the "attempted `::default()` call" time, but that doesn't guarantee -//! that the value can be soundly created, and attempting to use the typical -//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type -//! that is not proof of anything without a value (i.e. isomorphic to a -//! newtype of the type it's trying to prove the inhabitation of) -//! -//! A more flexible (and safer) solution to the general problem could exist once -//! `const`-generic parameters can have type parameters in their types: -//! -//! ```rust,ignore (needs future const-generics) -//! extern "C" fn ffi_wrapper< -//! A, R, -//! F: Fn(A) -> R, -//! const f: F, // <-- this `const`-generic is not yet allowed -//! >(arg: A) -> R { -//! f(arg) -//! } -//! ``` - -use std::mem; - -// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement. -macro_rules! define_reify_functions { - ($( - fn $name:ident $(<$($param:ident),*>)? - for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty; - )+) => { - $(pub const fn $name< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty { - // FIXME(eddyb) describe the `F` type (e.g. via `type_name::`) once panic - // formatting becomes possible in `const fn`. - assert!(mem::size_of::() == 0, "selfless_reify: closure must be zero-sized"); - - $(extern $abi)? fn wrapper< - $($($param,)*)? - F: Fn($($arg_ty),*) -> $ret_ty + Copy - >($($arg: $arg_ty),*) -> $ret_ty { - let f = unsafe { - // SAFETY: `F` satisfies all criteria for "out of thin air" - // reconstructability (see module-level doc comment). - mem::MaybeUninit::::uninit().assume_init() - }; - f($($arg),*) - } - let _f_proof = f; - wrapper::< - $($($param,)*)? - F - > - })+ - } -} - -define_reify_functions! { - fn _reify_to_extern_c_fn_unary for extern "C" fn(arg: A) -> R; - - // HACK(eddyb) this abstraction is used with `for<'a> fn(Bridge<'a>) -> T` - // but that doesn't work with just `reify_to_extern_c_fn_unary` because of - // the `fn` pointer type being "higher-ranked" (i.e. the `for<'a>` binder). - // FIXME(eddyb) try to remove the lifetime from `Bridge`, that'd help. - fn reify_to_extern_c_fn_hrt_bridge for extern "C" fn(bridge: super::Bridge<'_>) -> R; -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs deleted file mode 100644 index 0fb3c6985894..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/bridge/server.rs +++ /dev/null @@ -1,332 +0,0 @@ -//! Server-side traits. - -use super::*; - -// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. -use super::client::HandleStore; - -pub trait Types { - type FreeFunctions: 'static; - type TokenStream: 'static + Clone; - type Group: 'static + Clone; - type Punct: 'static + Copy + Eq + Hash; - type Ident: 'static + Copy + Eq + Hash; - type Literal: 'static + Clone; - type SourceFile: 'static + Clone; - type MultiSpan: 'static; - type Diagnostic: 'static; - type Span: 'static + Copy + Eq + Hash; -} - -/// Declare an associated fn of one of the traits below, adding necessary -/// default bodies. -macro_rules! associated_fn { - (fn drop(&mut self, $arg:ident: $arg_ty:ty)) => - (fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) }); - - (fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) => - (fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() }); - - ($($item:tt)*) => ($($item)*;) -} - -macro_rules! declare_server_traits { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - $(pub trait $name: Types { - $(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)* - })* - - pub trait Server: Types $(+ $name)* {} - impl Server for S {} - } -} -with_api!(Self, self_, declare_server_traits); - -pub(super) struct MarkedTypes(S); - -macro_rules! define_mark_types_impls { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - impl Types for MarkedTypes { - $(type $name = Marked;)* - } - - $(impl $name for MarkedTypes { - $(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)? { - <_>::mark($name::$method(&mut self.0, $($arg.unmark()),*)) - })* - })* - } -} -with_api!(Self, self_, define_mark_types_impls); - -struct Dispatcher { - handle_store: HandleStore, - server: S, -} - -macro_rules! define_dispatcher_impl { - ($($name:ident { - $(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)* - }),* $(,)?) => { - // FIXME(eddyb) `pub` only for `ExecutionStrategy` below. - pub trait DispatcherTrait { - // HACK(eddyb) these are here to allow `Self::$name` to work below. - $(type $name;)* - fn dispatch(&mut self, buf: Buffer) -> Buffer; - } - - impl DispatcherTrait for Dispatcher> { - $(type $name = as Types>::$name;)* - fn dispatch(&mut self, mut buf: Buffer) -> Buffer { - let Dispatcher { handle_store, server } = self; - - let mut reader = &buf[..]; - match api_tags::Method::decode(&mut reader, &mut ()) { - $(api_tags::Method::$name(m) => match m { - $(api_tags::$name::$method => { - let mut call_method = || { - reverse_decode!(reader, handle_store; $($arg: $arg_ty),*); - $name::$method(server, $($arg),*) - }; - // HACK(eddyb) don't use `panic::catch_unwind` in a panic. - // If client and server happen to use the same `libstd`, - // `catch_unwind` asserts that the panic counter was 0, - // even when the closure passed to it didn't panic. - let r = if thread::panicking() { - Ok(call_method()) - } else { - panic::catch_unwind(panic::AssertUnwindSafe(call_method)) - .map_err(PanicMessage::from) - }; - - buf.clear(); - r.encode(&mut buf, handle_store); - })* - }),* - } - buf - } - } - } -} -with_api!(Self, self_, define_dispatcher_impl); - -pub trait ExecutionStrategy { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer; -} - -pub struct SameThread; - -impl ExecutionStrategy for SameThread { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - let mut dispatch = |buf| dispatcher.dispatch(buf); - - run_client(Bridge { - cached_buffer: input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) - } -} - -// NOTE(eddyb) Two implementations are provided, the second one is a bit -// faster but neither is anywhere near as fast as same-thread execution. - -pub struct CrossThread1; - -impl ExecutionStrategy for CrossThread1 { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - use std::sync::mpsc::channel; - - let (req_tx, req_rx) = channel(); - let (res_tx, res_rx) = channel(); - - let join_handle = thread::spawn(move || { - let mut dispatch = |buf| { - req_tx.send(buf).unwrap(); - res_rx.recv().unwrap() - }; - - run_client(Bridge { - cached_buffer: input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }) - }); - - for b in req_rx { - res_tx.send(dispatcher.dispatch(b)).unwrap(); - } - - join_handle.join().unwrap() - } -} - -pub struct CrossThread2; - -impl ExecutionStrategy for CrossThread2 { - fn run_bridge_and_client( - &self, - dispatcher: &mut impl DispatcherTrait, - input: Buffer, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, - force_show_panics: bool, - ) -> Buffer { - use std::sync::{Arc, Mutex}; - - enum State { - Req(T), - Res(T), - } - - let mut state = Arc::new(Mutex::new(State::Res(Buffer::new()))); - - let server_thread = thread::current(); - let state2 = state.clone(); - let join_handle = thread::spawn(move || { - let mut dispatch = |b| { - *state2.lock().unwrap() = State::Req(b); - server_thread.unpark(); - loop { - thread::park(); - if let State::Res(b) = &mut *state2.lock().unwrap() { - break b.take(); - } - } - }; - - let r = run_client(Bridge { - cached_buffer: input, - dispatch: (&mut dispatch).into(), - force_show_panics, - _marker: marker::PhantomData, - }); - - // Wake up the server so it can exit the dispatch loop. - drop(state2); - server_thread.unpark(); - - r - }); - - // Check whether `state2` was dropped, to know when to stop. - while Arc::get_mut(&mut state).is_none() { - thread::park(); - let mut b = match &mut *state.lock().unwrap() { - State::Req(b) => b.take(), - _ => continue, - }; - b = dispatcher.dispatch(b.take()); - *state.lock().unwrap() = State::Res(b); - join_handle.thread().unpark(); - } - - join_handle.join().unwrap() - } -} - -fn run_server< - S: Server, - I: Encode>>, - O: for<'a, 's> DecodeMut<'a, 's, HandleStore>>, ->( - strategy: &impl ExecutionStrategy, - handle_counters: &'static client::HandleCounters, - server: S, - input: I, - run_client: extern "C" fn(Bridge<'_>) -> Buffer, - force_show_panics: bool, -) -> Result { - let mut dispatcher = - Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; - - let mut buf = Buffer::new(); - input.encode(&mut buf, &mut dispatcher.handle_store); - - buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics); - - Result::decode(&mut &buf[..], &mut dispatcher.handle_store) -} - -impl client::Client { - pub fn run( - &self, - strategy: &impl ExecutionStrategy, - server: S, - input: S::TokenStream, - force_show_panics: bool, - ) -> Result - where - S: Server, - S::TokenStream: Default, - { - let client::Client { get_handle_counters, run, _marker } = *self; - run_server( - strategy, - get_handle_counters(), - server, - as Types>::TokenStream::mark(input), - run, - force_show_panics, - ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) - } -} - -impl - client::Client< - (super::super::TokenStream, super::super::TokenStream), - super::super::TokenStream, - > -{ - pub fn run( - &self, - strategy: &impl ExecutionStrategy, - server: S, - input: S::TokenStream, - input2: S::TokenStream, - force_show_panics: bool, - ) -> Result - where - S: Server, - S::TokenStream: Default, - { - let client::Client { get_handle_counters, run, _marker } = *self; - run_server( - strategy, - get_handle_counters(), - server, - ( - as Types>::TokenStream::mark(input), - as Types>::TokenStream::mark(input2), - ), - run, - force_show_panics, - ) - .map(|s| as Types>::TokenStream>>::unmark(s).unwrap_or_default()) - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs deleted file mode 100644 index 3fade2dc4f9c..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/diagnostic.rs +++ /dev/null @@ -1,166 +0,0 @@ -//! lib-proc-macro diagnostic -//! -//! Copy from -//! augmented with removing unstable features - -use super::Span; - -/// An enum representing a diagnostic level. -#[derive(Copy, Clone, Debug)] -#[non_exhaustive] -pub enum Level { - /// An error. - Error, - /// A warning. - Warning, - /// A note. - Note, - /// A help message. - Help, -} - -/// Trait implemented by types that can be converted into a set of `Span`s. -pub trait MultiSpan { - /// Converts `self` into a `Vec`. - fn into_spans(self) -> Vec; -} - -impl MultiSpan for Span { - fn into_spans(self) -> Vec { - vec![self] - } -} - -impl MultiSpan for Vec { - fn into_spans(self) -> Vec { - self - } -} - -impl<'a> MultiSpan for &'a [Span] { - fn into_spans(self) -> Vec { - self.to_vec() - } -} - -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec, - children: Vec, -} - -macro_rules! diagnostic_child_methods { - ($spanned:ident, $regular:ident, $level:expr) => { - #[doc = concat!("Adds a new child diagnostics message to `self` with the [`", - stringify!($level), "`] level, and the given `spans` and `message`.")] - pub fn $spanned(mut self, spans: S, message: T) -> Diagnostic - where - S: MultiSpan, - T: Into, - { - self.children.push(Diagnostic::spanned(spans, $level, message)); - self - } - - #[doc = concat!("Adds a new child diagnostic message to `self` with the [`", - stringify!($level), "`] level, and the given `message`.")] - pub fn $regular>(mut self, message: T) -> Diagnostic { - self.children.push(Diagnostic::new($level, message)); - self - } - }; -} - -/// Iterator over the children diagnostics of a `Diagnostic`. -#[derive(Debug, Clone)] -pub struct Children<'a>(std::slice::Iter<'a, Diagnostic>); - -impl<'a> Iterator for Children<'a> { - type Item = &'a Diagnostic; - - fn next(&mut self) -> Option { - self.0.next() - } -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } - - /// Creates a new diagnostic with the given `level` and `message` pointing to - /// the given set of `spans`. - pub fn spanned(spans: S, level: Level, message: T) -> Diagnostic - where - S: MultiSpan, - T: Into, - { - Diagnostic { level, message: message.into(), spans: spans.into_spans(), children: vec![] } - } - - diagnostic_child_methods!(span_error, error, Level::Error); - diagnostic_child_methods!(span_warning, warning, Level::Warning); - diagnostic_child_methods!(span_note, note, Level::Note); - diagnostic_child_methods!(span_help, help, Level::Help); - - /// Returns the diagnostic `level` for `self`. - pub fn level(&self) -> Level { - self.level - } - - /// Sets the level in `self` to `level`. - pub fn set_level(&mut self, level: Level) { - self.level = level; - } - - /// Returns the message in `self`. - pub fn message(&self) -> &str { - &self.message - } - - /// Sets the message in `self` to `message`. - pub fn set_message>(&mut self, message: T) { - self.message = message.into(); - } - - /// Returns the `Span`s in `self`. - pub fn spans(&self) -> &[Span] { - &self.spans - } - - /// Sets the `Span`s in `self` to `spans`. - pub fn set_spans(&mut self, spans: S) { - self.spans = spans.into_spans(); - } - - /// Returns an iterator over the children diagnostics of `self`. - pub fn children(&self) -> Children<'_> { - Children(self.children.iter()) - } - - /// Emit the diagnostic. - pub fn emit(self) { - fn to_internal(spans: Vec) -> super::bridge::client::MultiSpan { - let mut multi_span = super::bridge::client::MultiSpan::new(); - for span in spans { - multi_span.push(span.0); - } - multi_span - } - - let mut diag = super::bridge::client::Diagnostic::new( - self.level, - &self.message[..], - to_internal(self.spans), - ); - for c in self.children { - diag.sub(c.level, &c.message[..], to_internal(c.spans)); - } - diag.emit(); - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs deleted file mode 100644 index 89bd10da5e48..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/mod.rs +++ /dev/null @@ -1,1106 +0,0 @@ -//! A support library for macro authors when defining new macros. -//! -//! This library, provided by the standard distribution, provides the types -//! consumed in the interfaces of procedurally defined macro definitions such as -//! function-like macros `#[proc_macro]`, macro attributes `#[proc_macro_attribute]` and -//! custom derive attributes`#[proc_macro_derive]`. -//! -//! See [the book] for more. -//! -//! [the book]: ../book/ch19-06-macros.html#procedural-macros-for-generating-code-from-attributes - -#[doc(hidden)] -pub mod bridge; - -mod diagnostic; - -pub use diagnostic::{Diagnostic, Level, MultiSpan}; - -use std::cmp::Ordering; -use std::ops::RangeBounds; -use std::path::PathBuf; -use std::str::FromStr; -use std::{error, fmt, iter, mem}; - -/// Determines whether proc_macro has been made accessible to the currently -/// running program. -/// -/// The proc_macro crate is only intended for use inside the implementation of -/// procedural macros. All the functions in this crate panic if invoked from -/// outside of a procedural macro, such as from a build script or unit test or -/// ordinary Rust binary. -/// -/// With consideration for Rust libraries that are designed to support both -/// macro and non-macro use cases, `proc_macro::is_available()` provides a -/// non-panicking way to detect whether the infrastructure required to use the -/// API of proc_macro is presently available. Returns true if invoked from -/// inside of a procedural macro, false if invoked from any other binary. -pub fn is_available() -> bool { - bridge::Bridge::is_available() -} - -/// The main type provided by this crate, representing an abstract stream of -/// tokens, or, more specifically, a sequence of token trees. -/// The type provide interfaces for iterating over those token trees and, conversely, -/// collecting a number of token trees into one stream. -/// -/// This is both the input and output of `#[proc_macro]`, `#[proc_macro_attribute]` -/// and `#[proc_macro_derive]` definitions. -#[derive(Clone)] -pub struct TokenStream(Option); - -/// Error returned from `TokenStream::from_str`. -#[non_exhaustive] -#[derive(Debug)] -pub struct LexError; - -impl fmt::Display for LexError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("cannot parse string into token stream") - } -} - -impl error::Error for LexError {} - -/// Error returned from `TokenStream::expand_expr`. -#[non_exhaustive] -#[derive(Debug)] -pub struct ExpandError; - -impl fmt::Display for ExpandError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("macro expansion failed") - } -} - -impl error::Error for ExpandError {} - -impl TokenStream { - /// Returns an empty `TokenStream` containing no token trees. - pub fn new() -> TokenStream { - TokenStream(None) - } - - /// Checks if this `TokenStream` is empty. - pub fn is_empty(&self) -> bool { - self.0.as_ref().map(|h| h.is_empty()).unwrap_or(true) - } - - /// Parses this `TokenStream` as an expression and attempts to expand any - /// macros within it. Returns the expanded `TokenStream`. - /// - /// Currently only expressions expanding to literals will succeed, although - /// this may be relaxed in the future. - /// - /// NOTE: In error conditions, `expand_expr` may leave macros unexpanded, - /// report an error, failing compilation, and/or return an `Err(..)`. The - /// specific behavior for any error condition, and what conditions are - /// considered errors, is unspecified and may change in the future. - pub fn expand_expr(&self) -> Result { - let stream = self.0.as_ref().ok_or(ExpandError)?; - match bridge::client::TokenStream::expand_expr(stream) { - Ok(stream) => Ok(TokenStream(Some(stream))), - Err(_) => Err(ExpandError), - } - } -} - -/// Attempts to break the string into tokens and parse those tokens into a token stream. -/// May fail for a number of reasons, for example, if the string contains unbalanced delimiters -/// or characters not existing in the language. -/// All tokens in the parsed stream get `Span::call_site()` spans. -/// -/// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to -/// change these errors into `LexError`s later. -impl FromStr for TokenStream { - type Err = LexError; - - fn from_str(src: &str) -> Result { - Ok(TokenStream(Some(bridge::client::TokenStream::from_str(src)))) - } -} - -/// Prints the token stream as a string that is supposed to be losslessly convertible back -/// into the same token stream (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -impl fmt::Display for TokenStream { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -/// Prints token in a form convenient for debugging. -impl fmt::Debug for TokenStream { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("TokenStream ")?; - f.debug_list().entries(self.clone()).finish() - } -} - -impl Default for TokenStream { - fn default() -> Self { - TokenStream::new() - } -} - -pub use quote::{quote, quote_span}; - -fn tree_to_bridge_tree( - tree: TokenTree, -) -> bridge::TokenTree< - bridge::client::Group, - bridge::client::Punct, - bridge::client::Ident, - bridge::client::Literal, -> { - match tree { - TokenTree::Group(tt) => bridge::TokenTree::Group(tt.0), - TokenTree::Punct(tt) => bridge::TokenTree::Punct(tt.0), - TokenTree::Ident(tt) => bridge::TokenTree::Ident(tt.0), - TokenTree::Literal(tt) => bridge::TokenTree::Literal(tt.0), - } -} - -/// Creates a token stream containing a single token tree. -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream(Some(bridge::client::TokenStream::from_token_tree(tree_to_bridge_tree(tree)))) - } -} - -/// Non-generic helper for implementing `FromIterator` and -/// `Extend` with less monomorphization in calling crates. -struct ConcatStreamsHelper { - streams: Vec, -} - -impl ConcatStreamsHelper { - fn new(capacity: usize) -> Self { - ConcatStreamsHelper { streams: Vec::with_capacity(capacity) } - } - - fn push(&mut self, stream: TokenStream) { - if let Some(stream) = stream.0 { - self.streams.push(stream); - } - } - - fn build(mut self) -> TokenStream { - if self.streams.len() <= 1 { - TokenStream(self.streams.pop()) - } else { - TokenStream(Some(bridge::client::TokenStream::concat_streams(None, self.streams))) - } - } - - fn append_to(mut self, stream: &mut TokenStream) { - if self.streams.is_empty() { - return; - } - let base = stream.0.take(); - if base.is_none() && self.streams.len() == 1 { - stream.0 = self.streams.pop(); - } else { - stream.0 = Some(bridge::client::TokenStream::concat_streams(base, self.streams)); - } - } -} - -/// Collects a number of token trees into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { - let iter = streams.into_iter(); - let mut builder = ConcatStreamsHelper::new(iter.size_hint().0); - iter.for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { - // FIXME(eddyb) Use an optimized implementation if/when possible. - *self = iter::once(mem::replace(self, Self::new())).chain(streams).collect(); - } -} - -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { - use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - #[derive(Clone)] - pub struct IntoIter( - std::vec::IntoIter< - bridge::TokenTree< - bridge::client::Group, - bridge::client::Punct, - bridge::client::Ident, - bridge::client::Literal, - >, - >, - ); - - impl Iterator for IntoIter { - type Item = TokenTree; - - fn next(&mut self) -> Option { - self.0.next().map(|tree| match tree { - bridge::TokenTree::Group(tt) => TokenTree::Group(Group(tt)), - bridge::TokenTree::Punct(tt) => TokenTree::Punct(Punct(tt)), - bridge::TokenTree::Ident(tt) => TokenTree::Ident(Ident(tt)), - bridge::TokenTree::Literal(tt) => TokenTree::Literal(Literal(tt)), - }) - } - } - - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - IntoIter(self.0.map(|v| v.into_trees()).unwrap_or_default().into_iter()) - } - } -} - -#[doc(hidden)] -mod quote; - -/// A region of source code, along with macro expansion information. -#[derive(Copy, Clone)] -pub struct Span(bridge::client::Span); - -macro_rules! diagnostic_method { - ($name:ident, $level:expr) => { - /// Creates a new `Diagnostic` with the given `message` at the span - /// `self`. - pub fn $name>(self, message: T) -> Diagnostic { - Diagnostic::spanned(self, $level, message) - } - }; -} - -impl Span { - /// A span that resolves at the macro definition site. - pub fn def_site() -> Span { - Span(bridge::client::Span::def_site()) - } - - /// The span of the invocation of the current procedural macro. - /// Identifiers created with this span will be resolved as if they were written - /// directly at the macro call location (call-site hygiene) and other code - /// at the macro call site will be able to refer to them as well. - pub fn call_site() -> Span { - Span(bridge::client::Span::call_site()) - } - - /// A span that represents `macro_rules` hygiene, and sometimes resolves at the macro - /// definition site (local variables, labels, `$crate`) and sometimes at the macro - /// call site (everything else). - /// The span location is taken from the call-site. - pub fn mixed_site() -> Span { - Span(bridge::client::Span::mixed_site()) - } - - /// The original source file into which this span points. - pub fn source_file(&self) -> SourceFile { - SourceFile(self.0.source_file()) - } - - /// The `Span` for the tokens in the previous macro expansion from which - /// `self` was generated from, if any. - pub fn parent(&self) -> Option { - self.0.parent().map(Span) - } - - /// The span for the origin source code that `self` was generated from. If - /// this `Span` wasn't generated from other macro expansions then the return - /// value is the same as `*self`. - pub fn source(&self) -> Span { - Span(self.0.source()) - } - - /// Gets the starting line/column in the source file for this span. - pub fn start(&self) -> LineColumn { - self.0.start().add_1_to_column() - } - - /// Gets the ending line/column in the source file for this span. - pub fn end(&self) -> LineColumn { - self.0.end().add_1_to_column() - } - - /// Creates an empty span pointing to directly before this span. - pub fn before(&self) -> Span { - Span(self.0.before()) - } - - /// Creates an empty span pointing to directly after this span. - pub fn after(&self) -> Span { - Span(self.0.after()) - } - - /// Creates a new span encompassing `self` and `other`. - /// - /// Returns `None` if `self` and `other` are from different files. - pub fn join(&self, other: Span) -> Option { - self.0.join(other.0).map(Span) - } - - /// Creates a new span with the same line/column information as `self` but - /// that resolves symbols as though it were at `other`. - pub fn resolved_at(&self, other: Span) -> Span { - Span(self.0.resolved_at(other.0)) - } - - /// Creates a new span with the same name resolution behavior as `self` but - /// with the line/column information of `other`. - pub fn located_at(&self, other: Span) -> Span { - other.resolved_at(*self) - } - - /// Compares to spans to see if they're equal. - pub fn eq(&self, other: &Span) -> bool { - self.0 == other.0 - } - - /// Returns the source text behind a span. This preserves the original source - /// code, including spaces and comments. It only returns a result if the span - /// corresponds to real source code. - /// - /// Note: The observable result of a macro should only rely on the tokens and - /// not on this source text. The result of this function is a best effort to - /// be used for diagnostics only. - pub fn source_text(&self) -> Option { - self.0.source_text() - } - - // Used by the implementation of `Span::quote` - #[doc(hidden)] - pub fn save_span(&self) -> usize { - self.0.save_span() - } - - // Used by the implementation of `Span::quote` - #[doc(hidden)] - pub fn recover_proc_macro_span(id: usize) -> Span { - Span(bridge::client::Span::recover_proc_macro_span(id)) - } - - diagnostic_method!(error, Level::Error); - diagnostic_method!(warning, Level::Warning); - diagnostic_method!(note, Level::Note); - diagnostic_method!(help, Level::Help); -} - -/// Prints a span in a form convenient for debugging. -impl fmt::Debug for Span { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// A line-column pair representing the start or end of a `Span`. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct LineColumn { - /// The 1-indexed line in the source file on which the span starts or ends (inclusive). - pub line: usize, - /// The 1-indexed column (number of bytes in UTF-8 encoding) in the source - /// file on which the span starts or ends (inclusive). - pub column: usize, -} - -impl LineColumn { - fn add_1_to_column(self) -> Self { - LineColumn { line: self.line, column: self.column + 1 } - } -} - -impl Ord for LineColumn { - fn cmp(&self, other: &Self) -> Ordering { - self.line.cmp(&other.line).then(self.column.cmp(&other.column)) - } -} - -impl PartialOrd for LineColumn { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } -} - -/// The source file of a given `Span`. -#[derive(Clone)] -pub struct SourceFile(bridge::client::SourceFile); - -impl SourceFile { - /// Gets the path to this source file. - /// - /// ### Note - /// If the code span associated with this `SourceFile` was generated by an external macro, this - /// macro, this might not be an actual path on the filesystem. Use [`is_real`] to check. - /// - /// Also note that even if `is_real` returns `true`, if `--remap-path-prefix` was passed on - /// the command line, the path as given might not actually be valid. - /// - /// [`is_real`]: Self::is_real - pub fn path(&self) -> PathBuf { - PathBuf::from(self.0.path()) - } - - /// Returns `true` if this source file is a real source file, and not generated by an external - /// macro's expansion. - pub fn is_real(&self) -> bool { - // This is a hack until intercrate spans are implemented and we can have real source files - // for spans generated in external macros. - // https://github.com/rust-lang/rust/pull/43604#issuecomment-333334368 - self.0.is_real() - } -} - -impl fmt::Debug for SourceFile { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("SourceFile") - .field("path", &self.path()) - .field("is_real", &self.is_real()) - .finish() - } -} - -impl PartialEq for SourceFile { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } -} - -impl Eq for SourceFile {} - -/// A single token or a delimited sequence of token trees (e.g., `[1, (), ..]`). -#[derive(Clone)] -pub enum TokenTree { - /// A token stream surrounded by bracket delimiters. - Group(Group), - /// An identifier. - Ident(Ident), - /// A single punctuation character (`+`, `,`, `$`, etc.). - Punct(Punct), - /// A literal character (`'a'`), string (`"hello"`), number (`2.3`), etc. - Literal(Literal), -} - -impl TokenTree { - /// Returns the span of this tree, delegating to the `span` method of - /// the contained token or a delimited stream. - pub fn span(&self) -> Span { - match *self { - TokenTree::Group(ref t) => t.span(), - TokenTree::Ident(ref t) => t.span(), - TokenTree::Punct(ref t) => t.span(), - TokenTree::Literal(ref t) => t.span(), - } - } - - /// Configures the span for *only this token*. - /// - /// Note that if this token is a `Group` then this method will not configure - /// the span of each of the internal tokens, this will simply delegate to - /// the `set_span` method of each variant. - pub fn set_span(&mut self, span: Span) { - match *self { - TokenTree::Group(ref mut t) => t.set_span(span), - TokenTree::Ident(ref mut t) => t.set_span(span), - TokenTree::Punct(ref mut t) => t.set_span(span), - TokenTree::Literal(ref mut t) => t.set_span(span), - } - } -} - -/// Prints token tree in a form convenient for debugging. -impl fmt::Debug for TokenTree { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - // Each of these has the name in the struct type in the derived debug, - // so don't bother with an extra layer of indirection - match *self { - TokenTree::Group(ref tt) => tt.fmt(f), - TokenTree::Ident(ref tt) => tt.fmt(f), - TokenTree::Punct(ref tt) => tt.fmt(f), - TokenTree::Literal(ref tt) => tt.fmt(f), - } - } -} - -impl From for TokenTree { - fn from(g: Group) -> TokenTree { - TokenTree::Group(g) - } -} - -impl From for TokenTree { - fn from(g: Ident) -> TokenTree { - TokenTree::Ident(g) - } -} - -impl From for TokenTree { - fn from(g: Punct) -> TokenTree { - TokenTree::Punct(g) - } -} - -impl From for TokenTree { - fn from(g: Literal) -> TokenTree { - TokenTree::Literal(g) - } -} - -/// Prints the token tree as a string that is supposed to be losslessly convertible back -/// into the same token tree (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters and negative numeric literals. -impl fmt::Display for TokenTree { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -/// A delimited token stream. -/// -/// A `Group` internally contains a `TokenStream` which is surrounded by `Delimiter`s. -#[derive(Clone)] -pub struct Group(bridge::client::Group); - -/// Describes how a sequence of token trees is delimited. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Delimiter { - /// `( ... )` - Parenthesis, - /// `{ ... }` - Brace, - /// `[ ... ]` - Bracket, - /// `Ø ... Ø` - /// An invisible delimiter, that may, for example, appear around tokens coming from a - /// "macro variable" `$var`. It is important to preserve operator priorities in cases like - /// `$var * 3` where `$var` is `1 + 2`. - /// Invisible delimiters might not survive roundtrip of a token stream through a string. - None, -} - -impl Group { - /// Creates a new `Group` with the given delimiter and token stream. - /// - /// This constructor will set the span for this group to - /// `Span::call_site()`. To change the span you can use the `set_span` - /// method below. - pub fn new(delimiter: Delimiter, stream: TokenStream) -> Group { - Group(bridge::client::Group::new(delimiter, stream.0)) - } - - /// Returns the delimiter of this `Group` - pub fn delimiter(&self) -> Delimiter { - self.0.delimiter() - } - - /// Returns the `TokenStream` of tokens that are delimited in this `Group`. - /// - /// Note that the returned token stream does not include the delimiter - /// returned above. - pub fn stream(&self) -> TokenStream { - TokenStream(Some(self.0.stream())) - } - - /// Returns the span for the delimiters of this token stream, spanning the - /// entire `Group`. - /// - /// ```text - /// pub fn span(&self) -> Span { - /// ^^^^^^^ - /// ``` - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Returns the span pointing to the opening delimiter of this group. - /// - /// ```text - /// pub fn span_open(&self) -> Span { - /// ^ - /// ``` - pub fn span_open(&self) -> Span { - Span(self.0.span_open()) - } - - /// Returns the span pointing to the closing delimiter of this group. - /// - /// ```text - /// pub fn span_close(&self) -> Span { - /// ^ - /// ``` - pub fn span_close(&self) -> Span { - Span(self.0.span_close()) - } - - /// Configures the span for this `Group`'s delimiters, but not its internal - /// tokens. - /// - /// This method will **not** set the span of all the internal tokens spanned - /// by this group, but rather it will only set the span of the delimiter - /// tokens at the level of the `Group`. - pub fn set_span(&mut self, span: Span) { - self.0.set_span(span.0); - } -} - -/// Prints the group as a string that should be losslessly convertible back -/// into the same group (modulo spans), except for possibly `TokenTree::Group`s -/// with `Delimiter::None` delimiters. -impl fmt::Display for Group { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -impl fmt::Debug for Group { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Group") - .field("delimiter", &self.delimiter()) - .field("stream", &self.stream()) - .field("span", &self.span()) - .finish() - } -} - -/// A `Punct` is a single punctuation character such as `+`, `-` or `#`. -/// -/// Multi-character operators like `+=` are represented as two instances of `Punct` with different -/// forms of `Spacing` returned. -#[derive(Clone)] -pub struct Punct(bridge::client::Punct); - -/// Describes whether a `Punct` is followed immediately by another `Punct` ([`Spacing::Joint`]) or -/// by a different token or whitespace ([`Spacing::Alone`]). -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Spacing { - /// A `Punct` is not immediately followed by another `Punct`. - /// E.g. `+` is `Alone` in `+ =`, `+ident` and `+()`. - Alone, - /// A `Punct` is immediately followed by another `Punct`. - /// E.g. `+` is `Joint` in `+=` and `++`. - /// - /// Additionally, single quote `'` can join with identifiers to form lifetimes: `'ident`. - Joint, -} - -impl Punct { - /// Creates a new `Punct` from the given character and spacing. - /// The `ch` argument must be a valid punctuation character permitted by the language, - /// otherwise the function will panic. - /// - /// The returned `Punct` will have the default span of `Span::call_site()` - /// which can be further configured with the `set_span` method below. - pub fn new(ch: char, spacing: Spacing) -> Punct { - Punct(bridge::client::Punct::new(ch, spacing)) - } - - /// Returns the value of this punctuation character as `char`. - pub fn as_char(&self) -> char { - self.0.as_char() - } - - /// Returns the spacing of this punctuation character, indicating whether it's immediately - /// followed by another `Punct` in the token stream, so they can potentially be combined into - /// a multi-character operator (`Joint`), or it's followed by some other token or whitespace - /// (`Alone`) so the operator has certainly ended. - pub fn spacing(&self) -> Spacing { - self.0.spacing() - } - - /// Returns the span for this punctuation character. - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configure the span for this punctuation character. - pub fn set_span(&mut self, span: Span) { - self.0 = self.0.with_span(span.0); - } -} - -/// Prints the punctuation character as a string that should be losslessly convertible -/// back into the same character. -impl fmt::Display for Punct { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -impl fmt::Debug for Punct { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Punct") - .field("ch", &self.as_char()) - .field("spacing", &self.spacing()) - .field("span", &self.span()) - .finish() - } -} - -impl PartialEq for Punct { - fn eq(&self, rhs: &char) -> bool { - self.as_char() == *rhs - } -} - -impl PartialEq for char { - fn eq(&self, rhs: &Punct) -> bool { - *self == rhs.as_char() - } -} - -/// An identifier (`ident`). -#[derive(Clone)] -pub struct Ident(bridge::client::Ident); - -impl Ident { - /// Creates a new `Ident` with the given `string` as well as the specified - /// `span`. - /// The `string` argument must be a valid identifier permitted by the - /// language (including keywords, e.g. `self` or `fn`). Otherwise, the function will panic. - /// - /// Note that `span`, currently in rustc, configures the hygiene information - /// for this identifier. - /// - /// As of this time `Span::call_site()` explicitly opts-in to "call-site" hygiene - /// meaning that identifiers created with this span will be resolved as if they were written - /// directly at the location of the macro call, and other code at the macro call site will be - /// able to refer to them as well. - /// - /// Later spans like `Span::def_site()` will allow to opt-in to "definition-site" hygiene - /// meaning that identifiers created with this span will be resolved at the location of the - /// macro definition and other code at the macro call site will not be able to refer to them. - /// - /// Due to the current importance of hygiene this constructor, unlike other - /// tokens, requires a `Span` to be specified at construction. - pub fn new(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, false)) - } - - /// Same as `Ident::new`, but creates a raw identifier (`r#ident`). - /// The `string` argument be a valid identifier permitted by the language - /// (including keywords, e.g. `fn`). Keywords which are usable in path segments - /// (e.g. `self`, `super`) are not supported, and will cause a panic. - pub fn new_raw(string: &str, span: Span) -> Ident { - Ident(bridge::client::Ident::new(string, span.0, true)) - } - - /// Returns the span of this `Ident`, encompassing the entire string returned - /// by [`to_string`](Self::to_string). - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span of this `Ident`, possibly changing its hygiene context. - pub fn set_span(&mut self, span: Span) { - self.0 = self.0.with_span(span.0); - } -} - -/// Prints the identifier as a string that should be losslessly convertible -/// back into the same identifier. -impl fmt::Display for Ident { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -impl fmt::Debug for Ident { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("Ident") - .field("ident", &self.to_string()) - .field("span", &self.span()) - .finish() - } -} - -/// A literal string (`"hello"`), byte string (`b"hello"`), -/// character (`'a'`), byte character (`b'a'`), an integer or floating point number -/// with or without a suffix (`1`, `1u8`, `2.3`, `2.3f32`). -/// Boolean literals like `true` and `false` do not belong here, they are `Ident`s. -#[derive(Clone)] -pub struct Literal(bridge::client::Literal); - -macro_rules! suffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new suffixed integer literal with the specified value. - /// - /// This function will create an integer like `1u32` where the integer - /// value specified is the first part of the token and the integral is - /// also suffixed at the end. - /// Literals created from negative numbers might not survive round-trips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::typed_integer(&n.to_string(), stringify!($kind))) - } - )*) -} - -macro_rules! unsuffixed_int_literals { - ($($name:ident => $kind:ident,)*) => ($( - /// Creates a new unsuffixed integer literal with the specified value. - /// - /// This function will create an integer like `1` where the integer - /// value specified is the first part of the token. No suffix is - /// specified on this token, meaning that invocations like - /// `Literal::i8_unsuffixed(1)` are equivalent to - /// `Literal::u32_unsuffixed(1)`. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// Literals created through this method have the `Span::call_site()` - /// span by default, which can be configured with the `set_span` method - /// below. - pub fn $name(n: $kind) -> Literal { - Literal(bridge::client::Literal::integer(&n.to_string())) - } - )*) -} - -impl Literal { - suffixed_int_literals! { - u8_suffixed => u8, - u16_suffixed => u16, - u32_suffixed => u32, - u64_suffixed => u64, - u128_suffixed => u128, - usize_suffixed => usize, - i8_suffixed => i8, - i16_suffixed => i16, - i32_suffixed => i32, - i64_suffixed => i64, - i128_suffixed => i128, - isize_suffixed => isize, - } - - unsuffixed_int_literals! { - u8_unsuffixed => u8, - u16_unsuffixed => u16, - u32_unsuffixed => u32, - u64_unsuffixed => u64, - u128_unsuffixed => u128, - usize_unsuffixed => usize, - i8_unsuffixed => i8, - i16_unsuffixed => i16, - i32_unsuffixed => i32, - i64_unsuffixed => i64, - i128_unsuffixed => i128, - isize_unsuffixed => isize, - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f32_unsuffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - let mut repr = n.to_string(); - if !repr.contains('.') { - repr.push_str(".0"); - } - Literal(bridge::client::Literal::float(&repr)) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f32` where the value - /// specified is the preceding part of the token and `f32` is the suffix of - /// the token. This token will always be inferred to be an `f32` in the - /// compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f32_suffixed(n: f32) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - Literal(bridge::client::Literal::f32(&n.to_string())) - } - - /// Creates a new unsuffixed floating-point literal. - /// - /// This constructor is similar to those like `Literal::i8_unsuffixed` where - /// the float's value is emitted directly into the token but no suffix is - /// used, so it may be inferred to be a `f64` later in the compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f64_unsuffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - let mut repr = n.to_string(); - if !repr.contains('.') { - repr.push_str(".0"); - } - Literal(bridge::client::Literal::float(&repr)) - } - - /// Creates a new suffixed floating-point literal. - /// - /// This constructor will create a literal like `1.0f64` where the value - /// specified is the preceding part of the token and `f64` is the suffix of - /// the token. This token will always be inferred to be an `f64` in the - /// compiler. - /// Literals created from negative numbers might not survive rountrips through - /// `TokenStream` or strings and may be broken into two tokens (`-` and positive literal). - /// - /// # Panics - /// - /// This function requires that the specified float is finite, for - /// example if it is infinity or NaN this function will panic. - pub fn f64_suffixed(n: f64) -> Literal { - if !n.is_finite() { - panic!("Invalid float literal {n}"); - } - Literal(bridge::client::Literal::f64(&n.to_string())) - } - - /// String literal. - pub fn string(string: &str) -> Literal { - Literal(bridge::client::Literal::string(string)) - } - - /// Character literal. - pub fn character(ch: char) -> Literal { - Literal(bridge::client::Literal::character(ch)) - } - - /// Byte string literal. - pub fn byte_string(bytes: &[u8]) -> Literal { - Literal(bridge::client::Literal::byte_string(bytes)) - } - - /// Returns the span encompassing this literal. - pub fn span(&self) -> Span { - Span(self.0.span()) - } - - /// Configures the span associated for this literal. - pub fn set_span(&mut self, span: Span) { - self.0.set_span(span.0); - } - - /// Returns a `Span` that is a subset of `self.span()` containing only the - /// source bytes in range `range`. Returns `None` if the would-be trimmed - /// span is outside the bounds of `self`. - // FIXME(SergioBenitez): check that the byte range starts and ends at a - // UTF-8 boundary of the source. otherwise, it's likely that a panic will - // occur elsewhere when the source text is printed. - // FIXME(SergioBenitez): there is no way for the user to know what - // `self.span()` actually maps to, so this method can currently only be - // called blindly. For example, `to_string()` for the character 'c' returns - // "'\u{63}'"; there is no way for the user to know whether the source text - // was 'c' or whether it was '\u{63}'. - pub fn subspan>(&self, range: R) -> Option { - self.0.subspan(range.start_bound().cloned(), range.end_bound().cloned()).map(Span) - } -} - -/// Parse a single literal from its stringified representation. -/// -/// In order to parse successfully, the input string must not contain anything -/// but the literal token. Specifically, it must not contain whitespace or -/// comments in addition to the literal. -/// -/// The resulting literal token will have a `Span::call_site()` span. -/// -/// NOTE: some errors may cause panics instead of returning `LexError`. We -/// reserve the right to change these errors into `LexError`s later. -impl FromStr for Literal { - type Err = LexError; - - fn from_str(src: &str) -> Result { - match bridge::client::Literal::from_str(src) { - Ok(literal) => Ok(Literal(literal)), - Err(()) => Err(LexError), - } - } -} - -/// Prints the literal as a string that should be losslessly convertible -/// back into the same literal (except for possible rounding for floating point literals). -impl fmt::Display for Literal { - fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result { - unimplemented!() - } -} - -impl fmt::Debug for Literal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -/// Tracked access to environment variables. -pub mod tracked_env { - use std::env::{self, VarError}; - use std::ffi::OsStr; - - /// Retrieve an environment variable and add it to build dependency info. - /// Build system executing the compiler will know that the variable was accessed during - /// compilation, and will be able to rerun the build when the value of that variable changes. - /// Besides the dependency tracking this function should be equivalent to `env::var` from the - /// standard library, except that the argument must be UTF-8. - pub fn var + AsRef>(key: K) -> Result { - let key: &str = key.as_ref(); - let value = env::var(key); - super::bridge::client::FreeFunctions::track_env_var(key, value.as_deref().ok()); - value - } -} - -/// Tracked access to additional files. -pub mod tracked_path { - - /// Track a file explicitly. - /// - /// Commonly used for tracking asset preprocessing. - pub fn path>(path: P) { - let path: &str = path.as_ref(); - super::bridge::client::FreeFunctions::track_path(path); - } -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs b/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs deleted file mode 100644 index 39309faa4121..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/proc_macro/quote.rs +++ /dev/null @@ -1,139 +0,0 @@ -//! # Quasiquoter -//! This file contains the implementation internals of the quasiquoter provided by `quote!`. - -//! This quasiquoter uses macros 2.0 hygiene to reliably access -//! items from `proc_macro`, to build a `proc_macro::TokenStream`. - -use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree}; - -macro_rules! quote_tt { - (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) }; - ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) }; - ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) }; - (,) => { Punct::new(',', Spacing::Alone) }; - (.) => { Punct::new('.', Spacing::Alone) }; - (;) => { Punct::new(';', Spacing::Alone) }; - (!) => { Punct::new('!', Spacing::Alone) }; - (<) => { Punct::new('<', Spacing::Alone) }; - (>) => { Punct::new('>', Spacing::Alone) }; - (&) => { Punct::new('&', Spacing::Alone) }; - (=) => { Punct::new('=', Spacing::Alone) }; - ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; -} - -macro_rules! quote_ts { - ((@ $($t:tt)*)) => { $($t)* }; - (::) => { - [ - TokenTree::from(Punct::new(':', Spacing::Joint)), - TokenTree::from(Punct::new(':', Spacing::Alone)), - ].iter() - .cloned() - .map(|mut x| { - x.set_span(Span::def_site()); - x - }) - .collect::() - }; - ($t:tt) => { TokenTree::from(quote_tt!($t)) }; -} - -/// Simpler version of the real `quote!` macro, implemented solely -/// through `macro_rules`, for bootstrapping the real implementation -/// (see the `quote` function), which does not have access to the -/// real `quote!` macro due to the `proc_macro` crate not being -/// able to depend on itself. -/// -/// Note: supported tokens are a subset of the real `quote!`, but -/// unquoting is different: instead of `$x`, this uses `(@ expr)`. -macro_rules! quote { - () => { TokenStream::new() }; - ($($t:tt)*) => { - [ - $(TokenStream::from(quote_ts!($t)),)* - ].iter().cloned().collect::() - }; -} - -/// Quote a `TokenStream` into a `TokenStream`. -/// This is the actual implementation of the `quote!()` proc macro. -/// -/// It is loaded by the compiler in `register_builtin_macros`. -pub fn quote(stream: TokenStream) -> TokenStream { - if stream.is_empty() { - return quote!(super::TokenStream::new()); - } - let proc_macro_crate = quote!(crate); - let mut after_dollar = false; - let tokens = stream - .into_iter() - .filter_map(|tree| { - if after_dollar { - after_dollar = false; - match tree { - TokenTree::Ident(_) => { - return Some(quote!(Into::::into( - Clone::clone(&(@ tree))),)); - } - TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} - _ => panic!("`$` must be followed by an ident or `$` in `quote!`"), - } - } else if let TokenTree::Punct(ref tt) = tree { - if tt.as_char() == '$' { - after_dollar = true; - return None; - } - } - - Some(quote!(super::TokenStream::from((@ match tree { - TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new( - (@ TokenTree::from(Literal::character(tt.as_char()))), - (@ match tt.spacing() { - Spacing::Alone => quote!(super::Spacing::Alone), - Spacing::Joint => quote!(super::Spacing::Joint), - }), - ))), - TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new( - (@ match tt.delimiter() { - Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis), - Delimiter::Brace => quote!(super::Delimiter::Brace), - Delimiter::Bracket => quote!(super::Delimiter::Bracket), - Delimiter::None => quote!(super::Delimiter::None), - }), - (@ quote(tt.stream())), - ))), - TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new( - (@ TokenTree::from(Literal::string(&tt.to_string()))), - (@ quote_span(proc_macro_crate.clone(), tt.span())), - ))), - TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({ - let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) - .parse::() - .unwrap() - .into_iter(); - if let (Some(super::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) - { - lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span()))); - lit - } else { - unreachable!() - } - })) - })),)) - }) - .collect::(); - - if after_dollar { - panic!("unexpected trailing `$` in `quote!`"); - } - - quote!([(@ tokens)].iter().cloned().collect::()) -} - -/// Quote a `Span` into a `TokenStream`. -/// This is needed to implement a custom quoter. -pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { - let id = span.save_span(); - quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) -} diff --git a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs b/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs deleted file mode 100644 index 30baf3a13f57..000000000000 --- a/crates/proc-macro-srv/src/abis/abi_1_63/ra_server.rs +++ /dev/null @@ -1,840 +0,0 @@ -//! Rustc proc-macro server implementation with tt -//! -//! Based on idea from -//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that -//! we could provide any TokenStream implementation. -//! The original idea from fedochet is using proc-macro2 as backend, -//! we use tt instead for better integration with RA. -//! -//! FIXME: No span and source file information is implemented yet - -use super::proc_macro::bridge::{self, server}; - -use std::collections::HashMap; -use std::hash::Hash; -use std::ops::Bound; -use std::{ascii, vec::IntoIter}; - -use crate::tt; - -type Group = tt::Subtree; -type TokenTree = tt::TokenTree; -type Punct = tt::Punct; -type Spacing = tt::Spacing; -type Literal = tt::Literal; -type Span = tt::TokenId; - -#[derive(Debug, Default, Clone)] -pub struct TokenStream { - pub token_trees: Vec, -} - -impl TokenStream { - pub fn new() -> Self { - TokenStream::default() - } - - pub fn with_subtree(subtree: tt::Subtree) -> Self { - if subtree.delimiter.kind != tt::DelimiterKind::Invisible { - TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } - } else { - TokenStream { token_trees: subtree.token_trees } - } - } - - pub fn into_subtree(self) -> tt::Subtree { - tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees } - } - - pub fn is_empty(&self) -> bool { - self.token_trees.is_empty() - } -} - -/// Creates a token stream containing a single token tree. -impl From for TokenStream { - fn from(tree: TokenTree) -> TokenStream { - TokenStream { token_trees: vec![tree] } - } -} - -/// Collects a number of token trees into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(trees: I) -> Self { - trees.into_iter().map(TokenStream::from).collect() - } -} - -/// A "flattening" operation on token streams, collects token trees -/// from multiple token streams into a single stream. -impl FromIterator for TokenStream { - fn from_iter>(streams: I) -> Self { - let mut builder = TokenStreamBuilder::new(); - streams.into_iter().for_each(|stream| builder.push(stream)); - builder.build() - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, trees: I) { - self.extend(trees.into_iter().map(TokenStream::from)); - } -} - -impl Extend for TokenStream { - fn extend>(&mut self, streams: I) { - for item in streams { - for tkn in item { - match tkn { - tt::TokenTree::Subtree(subtree) - if subtree.delimiter.kind == tt::DelimiterKind::Invisible => - { - self.token_trees.extend(subtree.token_trees); - } - _ => { - self.token_trees.push(tkn); - } - } - } - } - } -} - -#[derive(Clone)] -pub struct SourceFile { - // FIXME stub -} - -type Level = super::proc_macro::Level; -type LineColumn = super::proc_macro::LineColumn; - -/// A structure representing a diagnostic message and associated children -/// messages. -#[derive(Clone, Debug)] -pub struct Diagnostic { - level: Level, - message: String, - spans: Vec, - children: Vec, -} - -impl Diagnostic { - /// Creates a new diagnostic with the given `level` and `message`. - pub fn new>(level: Level, message: T) -> Diagnostic { - Diagnostic { level, message: message.into(), spans: vec![], children: vec![] } - } -} - -// Rustc Server Ident has to be `Copyable` -// We use a stub here for bypassing -#[derive(Hash, Eq, PartialEq, Copy, Clone)] -pub struct IdentId(u32); - -#[derive(Clone, Hash, Eq, PartialEq)] -struct IdentData(tt::Ident); - -#[derive(Default)] -struct IdentInterner { - idents: HashMap, - ident_data: Vec, -} - -impl IdentInterner { - fn intern(&mut self, data: &IdentData) -> u32 { - if let Some(index) = self.idents.get(data) { - return *index; - } - - let index = self.idents.len() as u32; - self.ident_data.push(data.clone()); - self.idents.insert(data.clone(), index); - index - } - - fn get(&self, index: u32) -> &IdentData { - &self.ident_data[index as usize] - } - - #[allow(unused)] - fn get_mut(&mut self, index: u32) -> &mut IdentData { - self.ident_data.get_mut(index as usize).expect("Should be consistent") - } -} - -pub struct TokenStreamBuilder { - acc: TokenStream, -} - -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { - use std::str::FromStr; - - use super::{tt, TokenStream, TokenTree}; - - /// An iterator over `TokenStream`'s `TokenTree`s. - /// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, - /// and returns whole groups as token trees. - impl IntoIterator for TokenStream { - type Item = TokenTree; - type IntoIter = super::IntoIter; - - fn into_iter(self) -> Self::IntoIter { - self.token_trees.into_iter() - } - } - - type LexError = String; - - /// Attempts to break the string into tokens and parse those tokens into a token stream. - /// May fail for a number of reasons, for example, if the string contains unbalanced delimiters - /// or characters not existing in the language. - /// All tokens in the parsed stream get `Span::call_site()` spans. - /// - /// NOTE: some errors may cause panics instead of returning `LexError`. We reserve the right to - /// change these errors into `LexError`s later. - impl FromStr for TokenStream { - type Err = LexError; - - fn from_str(src: &str) -> Result { - let (subtree, _token_map) = - mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?; - - let subtree = subtree_replace_token_ids_with_unspecified(subtree); - Ok(TokenStream::with_subtree(subtree)) - } - } - - impl ToString for TokenStream { - fn to_string(&self) -> String { - ::tt::pretty(&self.token_trees) - } - } - - fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { - tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId::UNSPECIFIED, - close: tt::TokenId::UNSPECIFIED, - ..subtree.delimiter - }, - token_trees: subtree - .token_trees - .into_iter() - .map(token_tree_replace_token_ids_with_unspecified) - .collect(), - } - } - - fn token_tree_replace_token_ids_with_unspecified(tt: tt::TokenTree) -> tt::TokenTree { - match tt { - tt::TokenTree::Leaf(leaf) => { - tt::TokenTree::Leaf(leaf_replace_token_ids_with_unspecified(leaf)) - } - tt::TokenTree::Subtree(subtree) => { - tt::TokenTree::Subtree(subtree_replace_token_ids_with_unspecified(subtree)) - } - } - } - - fn leaf_replace_token_ids_with_unspecified(leaf: tt::Leaf) -> tt::Leaf { - match leaf { - tt::Leaf::Literal(lit) => { - tt::Leaf::Literal(tt::Literal { span: tt::TokenId::unspecified(), ..lit }) - } - tt::Leaf::Punct(punct) => { - tt::Leaf::Punct(tt::Punct { span: tt::TokenId::unspecified(), ..punct }) - } - tt::Leaf::Ident(ident) => { - tt::Leaf::Ident(tt::Ident { span: tt::TokenId::unspecified(), ..ident }) - } - } - } -} - -impl TokenStreamBuilder { - fn new() -> TokenStreamBuilder { - TokenStreamBuilder { acc: TokenStream::new() } - } - - fn push(&mut self, stream: TokenStream) { - self.acc.extend(stream.into_iter()) - } - - fn build(self) -> TokenStream { - self.acc - } -} - -pub struct FreeFunctions; - -#[derive(Clone)] -pub struct TokenStreamIter { - trees: IntoIter, -} - -#[derive(Default)] -pub struct RustAnalyzer { - ident_interner: IdentInterner, - // FIXME: store span information here. -} - -impl server::Types for RustAnalyzer { - type FreeFunctions = FreeFunctions; - type TokenStream = TokenStream; - type Group = Group; - type Punct = Punct; - type Ident = IdentId; - type Literal = Literal; - type SourceFile = SourceFile; - type Diagnostic = Diagnostic; - type Span = Span; - type MultiSpan = Vec; -} - -impl server::FreeFunctions for RustAnalyzer { - fn track_env_var(&mut self, _var: &str, _value: Option<&str>) { - // FIXME: track env var accesses - // https://github.com/rust-lang/rust/pull/71858 - } - fn track_path(&mut self, _path: &str) {} -} - -impl server::TokenStream for RustAnalyzer { - fn is_empty(&mut self, stream: &Self::TokenStream) -> bool { - stream.is_empty() - } - fn from_str(&mut self, src: &str) -> Self::TokenStream { - use std::str::FromStr; - - Self::TokenStream::from_str(src).expect("cannot parse string") - } - fn to_string(&mut self, stream: &Self::TokenStream) -> String { - stream.to_string() - } - fn from_token_tree( - &mut self, - tree: bridge::TokenTree, - ) -> Self::TokenStream { - match tree { - bridge::TokenTree::Group(group) => { - let tree = TokenTree::from(group); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Ident(IdentId(index)) => { - let IdentData(ident) = self.ident_interner.get(index).clone(); - let ident: tt::Ident = ident; - let leaf = tt::Leaf::from(ident); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Literal(literal) => { - let leaf = tt::Leaf::from(literal); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - - bridge::TokenTree::Punct(p) => { - let leaf = tt::Leaf::from(p); - let tree = TokenTree::from(leaf); - Self::TokenStream::from_iter(vec![tree]) - } - } - } - - fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result { - Ok(self_.clone()) - } - - fn concat_trees( - &mut self, - base: Option, - trees: Vec>, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for tree in trees { - builder.push(self.from_token_tree(tree)); - } - builder.build() - } - - fn concat_streams( - &mut self, - base: Option, - streams: Vec, - ) -> Self::TokenStream { - let mut builder = TokenStreamBuilder::new(); - if let Some(base) = base { - builder.push(base); - } - for stream in streams { - builder.push(stream); - } - builder.build() - } - - fn into_trees( - &mut self, - stream: Self::TokenStream, - ) -> Vec> { - stream - .into_iter() - .map(|tree| match tree { - tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { - bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident)))) - } - tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit), - tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct), - tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(subtree), - }) - .collect() - } -} - -fn delim_to_internal(d: bridge::Delimiter) -> tt::Delimiter { - let kind = match d { - bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis, - bridge::Delimiter::Brace => tt::DelimiterKind::Brace, - bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket, - bridge::Delimiter::None => tt::DelimiterKind::Invisible, - }; - tt::Delimiter { open: tt::TokenId::unspecified(), close: tt::TokenId::unspecified(), kind } -} - -fn delim_to_external(d: tt::Delimiter) -> bridge::Delimiter { - match d.kind { - tt::DelimiterKind::Parenthesis => bridge::Delimiter::Parenthesis, - tt::DelimiterKind::Brace => bridge::Delimiter::Brace, - tt::DelimiterKind::Bracket => bridge::Delimiter::Bracket, - tt::DelimiterKind::Invisible => bridge::Delimiter::None, - } -} - -fn spacing_to_internal(spacing: bridge::Spacing) -> Spacing { - match spacing { - bridge::Spacing::Alone => Spacing::Alone, - bridge::Spacing::Joint => Spacing::Joint, - } -} - -fn spacing_to_external(spacing: Spacing) -> bridge::Spacing { - match spacing { - Spacing::Alone => bridge::Spacing::Alone, - Spacing::Joint => bridge::Spacing::Joint, - } -} - -impl server::Group for RustAnalyzer { - fn new( - &mut self, - delimiter: bridge::Delimiter, - stream: Option, - ) -> Self::Group { - Self::Group { - delimiter: delim_to_internal(delimiter), - token_trees: stream.unwrap_or_default().token_trees, - } - } - fn delimiter(&mut self, group: &Self::Group) -> bridge::Delimiter { - delim_to_external(group.delimiter) - } - - // NOTE: Return value of do not include delimiter - fn stream(&mut self, group: &Self::Group) -> Self::TokenStream { - TokenStream { token_trees: group.token_trees.clone() } - } - - fn span(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.open - } - - fn set_span(&mut self, group: &mut Self::Group, span: Self::Span) { - group.delimiter.open = span; - } - - fn span_open(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.open - } - - fn span_close(&mut self, group: &Self::Group) -> Self::Span { - group.delimiter.close - } -} - -impl server::Punct for RustAnalyzer { - fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct { - tt::Punct { - char: ch, - spacing: spacing_to_internal(spacing), - span: tt::TokenId::unspecified(), - } - } - fn as_char(&mut self, punct: Self::Punct) -> char { - punct.char - } - fn spacing(&mut self, punct: Self::Punct) -> bridge::Spacing { - spacing_to_external(punct.spacing) - } - fn span(&mut self, punct: Self::Punct) -> Self::Span { - punct.span - } - fn with_span(&mut self, punct: Self::Punct, span: Self::Span) -> Self::Punct { - tt::Punct { span: span, ..punct } - } -} - -impl server::Ident for RustAnalyzer { - fn new(&mut self, string: &str, span: Self::Span, is_raw: bool) -> Self::Ident { - IdentId(self.ident_interner.intern(&IdentData(tt::Ident { - text: if is_raw { ::tt::SmolStr::from_iter(["r#", string]) } else { string.into() }, - span, - }))) - } - - fn span(&mut self, ident: Self::Ident) -> Self::Span { - self.ident_interner.get(ident.0).0.span - } - fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident { - let data = self.ident_interner.get(ident.0); - let new = IdentData(tt::Ident { span: span, ..data.0.clone() }); - IdentId(self.ident_interner.intern(&new)) - } -} - -impl server::Literal for RustAnalyzer { - fn debug_kind(&mut self, _literal: &Self::Literal) -> String { - // r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these. - // They must still be present to be ABI-compatible and work with upstream proc_macro. - "".to_owned() - } - fn from_str(&mut self, s: &str) -> Result { - Ok(Literal { text: s.into(), span: tt::TokenId::unspecified() }) - } - fn symbol(&mut self, literal: &Self::Literal) -> String { - literal.text.to_string() - } - fn suffix(&mut self, _literal: &Self::Literal) -> Option { - None - } - - fn to_string(&mut self, literal: &Self::Literal) -> String { - literal.to_string() - } - - fn integer(&mut self, n: &str) -> Self::Literal { - let n = match n.parse::() { - Ok(n) => n.to_string(), - Err(_) => n.parse::().unwrap().to_string(), - }; - Literal { text: n.into(), span: tt::TokenId::unspecified() } - } - - fn typed_integer(&mut self, n: &str, kind: &str) -> Self::Literal { - macro_rules! def_suffixed_integer { - ($kind:ident, $($ty:ty),*) => { - match $kind { - $( - stringify!($ty) => { - let n: $ty = n.parse().unwrap(); - format!(concat!("{}", stringify!($ty)), n) - } - )* - _ => unimplemented!("unknown args for typed_integer: n {}, kind {}", n, $kind), - } - } - } - - let text = def_suffixed_integer! {kind, u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize}; - - Literal { text: text.into(), span: tt::TokenId::unspecified() } - } - - fn float(&mut self, n: &str) -> Self::Literal { - let n: f64 = n.parse().unwrap(); - let mut text = f64::to_string(&n); - if !text.contains('.') { - text += ".0" - } - Literal { text: text.into(), span: tt::TokenId::unspecified() } - } - - fn f32(&mut self, n: &str) -> Self::Literal { - let n: f32 = n.parse().unwrap(); - let text = format!("{n}f32"); - Literal { text: text.into(), span: tt::TokenId::unspecified() } - } - - fn f64(&mut self, n: &str) -> Self::Literal { - let n: f64 = n.parse().unwrap(); - let text = format!("{n}f64"); - Literal { text: text.into(), span: tt::TokenId::unspecified() } - } - - fn string(&mut self, string: &str) -> Self::Literal { - let mut escaped = String::new(); - for ch in string.chars() { - escaped.extend(ch.escape_debug()); - } - Literal { text: format!("\"{escaped}\"").into(), span: tt::TokenId::unspecified() } - } - - fn character(&mut self, ch: char) -> Self::Literal { - Literal { text: format!("'{ch}'").into(), span: tt::TokenId::unspecified() } - } - - fn byte_string(&mut self, bytes: &[u8]) -> Self::Literal { - let string = bytes - .iter() - .cloned() - .flat_map(ascii::escape_default) - .map(Into::::into) - .collect::(); - - Literal { text: format!("b\"{string}\"").into(), span: tt::TokenId::unspecified() } - } - - fn span(&mut self, literal: &Self::Literal) -> Self::Span { - literal.span - } - - fn set_span(&mut self, literal: &mut Self::Literal, span: Self::Span) { - literal.span = span; - } - - fn subspan( - &mut self, - _literal: &Self::Literal, - _start: Bound, - _end: Bound, - ) -> Option { - // FIXME handle span - None - } -} - -impl server::SourceFile for RustAnalyzer { - // FIXME these are all stubs - fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool { - true - } - fn path(&mut self, _file: &Self::SourceFile) -> String { - String::new() - } - fn is_real(&mut self, _file: &Self::SourceFile) -> bool { - true - } -} - -impl server::Diagnostic for RustAnalyzer { - fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic { - let mut diag = Diagnostic::new(level, msg); - diag.spans = spans; - diag - } - - fn sub( - &mut self, - _diag: &mut Self::Diagnostic, - _level: Level, - _msg: &str, - _spans: Self::MultiSpan, - ) { - // FIXME handle diagnostic - // - } - - fn emit(&mut self, _diag: Self::Diagnostic) { - // FIXME handle diagnostic - // diag.emit() - } -} - -impl server::Span for RustAnalyzer { - fn debug(&mut self, span: Self::Span) -> String { - format!("{:?}", span.0) - } - fn def_site(&mut self) -> Self::Span { - // MySpan(self.span_interner.intern(&MySpanData(Span::def_site()))) - // FIXME handle span - tt::TokenId::unspecified() - } - fn call_site(&mut self) -> Self::Span { - // MySpan(self.span_interner.intern(&MySpanData(Span::call_site()))) - // FIXME handle span - tt::TokenId::unspecified() - } - fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile { - SourceFile {} - } - fn save_span(&mut self, _span: Self::Span) -> usize { - // FIXME stub - 0 - } - fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span { - // FIXME stub - tt::TokenId::unspecified() - } - /// Recent feature, not yet in the proc_macro - /// - /// See PR: - /// https://github.com/rust-lang/rust/pull/55780 - fn source_text(&mut self, _span: Self::Span) -> Option { - None - } - - fn parent(&mut self, _span: Self::Span) -> Option { - // FIXME handle span - None - } - fn source(&mut self, span: Self::Span) -> Self::Span { - // FIXME handle span - span - } - fn start(&mut self, _span: Self::Span) -> LineColumn { - // FIXME handle span - LineColumn { line: 0, column: 0 } - } - fn end(&mut self, _span: Self::Span) -> LineColumn { - // FIXME handle span - LineColumn { line: 0, column: 0 } - } - fn join(&mut self, first: Self::Span, _second: Self::Span) -> Option { - // Just return the first span again, because some macros will unwrap the result. - Some(first) - } - fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span { - // FIXME handle span - tt::TokenId::unspecified() - } - - fn mixed_site(&mut self) -> Self::Span { - // FIXME handle span - tt::TokenId::unspecified() - } - - fn after(&mut self, _self_: Self::Span) -> Self::Span { - tt::TokenId::unspecified() - } - - fn before(&mut self, _self_: Self::Span) -> Self::Span { - tt::TokenId::unspecified() - } -} - -impl server::MultiSpan for RustAnalyzer { - fn new(&mut self) -> Self::MultiSpan { - // FIXME handle span - vec![] - } - - fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) { - //TODP - other.push(span) - } -} - -#[cfg(test)] -mod tests { - use super::super::proc_macro::bridge::server::Literal; - use super::*; - - #[test] - fn test_ra_server_literals() { - let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() }; - assert_eq!(srv.integer("1234").text, "1234"); - - assert_eq!(srv.typed_integer("12", "u8").text, "12u8"); - assert_eq!(srv.typed_integer("255", "u16").text, "255u16"); - assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32"); - assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64"); - assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128"); - assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize"); - assert_eq!(srv.typed_integer("127", "i8").text, "127i8"); - assert_eq!(srv.typed_integer("255", "i16").text, "255i16"); - assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32"); - assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64"); - assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128"); - assert_eq!(srv.float("0").text, "0.0"); - assert_eq!(srv.float("15684.5867").text, "15684.5867"); - assert_eq!(srv.f32("15684.58").text, "15684.58f32"); - assert_eq!(srv.f64("15684.58").text, "15684.58f64"); - - assert_eq!(srv.string("hello_world").text, "\"hello_world\""); - assert_eq!(srv.character('c').text, "'c'"); - assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\""); - - // u128::max - assert_eq!( - srv.integer("340282366920938463463374607431768211455").text, - "340282366920938463463374607431768211455" - ); - // i128::min - assert_eq!( - srv.integer("-170141183460469231731687303715884105728").text, - "-170141183460469231731687303715884105728" - ); - } - - #[test] - fn test_ra_server_to_string() { - let s = TokenStream { - token_trees: vec![ - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "struct".into(), - span: tt::TokenId::unspecified(), - })), - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "T".into(), - span: tt::TokenId::unspecified(), - })), - tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId::unspecified(), - close: tt::TokenId::unspecified(), - kind: tt::DelimiterKind::Brace, - }, - token_trees: vec![], - }), - ], - }; - - assert_eq!(s.to_string(), "struct T {}"); - } - - #[test] - fn test_ra_server_from_str() { - use std::str::FromStr; - let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { - delimiter: tt::Delimiter { - open: tt::TokenId::unspecified(), - close: tt::TokenId::unspecified(), - kind: tt::DelimiterKind::Parenthesis, - }, - token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "a".into(), - span: tt::TokenId::unspecified(), - }))], - }); - - let t1 = TokenStream::from_str("(a)").unwrap(); - assert_eq!(t1.token_trees.len(), 1); - assert_eq!(t1.token_trees[0], subtree_paren_a); - - let t2 = TokenStream::from_str("(a);").unwrap(); - assert_eq!(t2.token_trees.len(), 2); - assert_eq!(t2.token_trees[0], subtree_paren_a); - - let underscore = TokenStream::from_str("_").unwrap(); - assert_eq!( - underscore.token_trees[0], - tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { - text: "_".into(), - span: tt::TokenId::unspecified(), - })) - ); - } -} diff --git a/crates/proc-macro-srv/src/abis/mod.rs b/crates/proc-macro-srv/src/abis/mod.rs deleted file mode 100644 index 04be39cffa4a..000000000000 --- a/crates/proc-macro-srv/src/abis/mod.rs +++ /dev/null @@ -1,146 +0,0 @@ -//! Procedural macros are implemented by compiling the macro providing crate -//! to a dynamic library with a particular ABI which the compiler uses to expand -//! macros. Unfortunately this ABI is not specified and can change from version -//! to version of the compiler. To support this we copy the ABI from the rust -//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47). -//! -//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple -//! interface the rest of rust-analyzer can use to talk to the macro -//! provider. -//! -//! # Adding a new ABI -//! -//! To add a new ABI you'll need to copy the source of the target proc_macro -//! crate from the source tree of the Rust compiler into this directory tree. -//! Then you'll need to modify it -//! - Remove any feature! or other things which won't compile on stable -//! - change any absolute imports to relative imports within the ABI tree -//! -//! Then you'll need to add a branch to the `Abi` enum and an implementation of -//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See -//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll -//! need to update the conditionals in `Abi::from_lib` to return your new ABI -//! for the relevant versions of the rust compiler -//! - -mod abi_1_63; -#[cfg(feature = "sysroot-abi")] -mod abi_sysroot; - -// see `build.rs` -include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); - -// Used by `test/utils.rs` -#[cfg(all(test, feature = "sysroot-abi"))] -pub(crate) use abi_sysroot::TokenStream as TestTokenStream; - -use super::dylib::LoadProcMacroDylibError; -pub(crate) use abi_1_63::Abi as Abi_1_63; -#[cfg(feature = "sysroot-abi")] -pub(crate) use abi_sysroot::Abi as Abi_Sysroot; -use libloading::Library; -use proc_macro_api::{ProcMacroKind, RustCInfo}; - -use crate::tt; - -pub struct PanicMessage { - message: Option, -} - -impl PanicMessage { - pub fn as_str(&self) -> Option { - self.message.clone() - } -} - -pub(crate) enum Abi { - Abi1_63(Abi_1_63), - #[cfg(feature = "sysroot-abi")] - AbiSysroot(Abi_Sysroot), -} - -impl Abi { - /// 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 fn from_lib( - lib: &Library, - symbol_name: String, - info: RustCInfo, - ) -> Result { - // the sysroot ABI relies on `extern proc_macro` with unstable features, - // instead of a snapshot of the proc macro bridge's source code. it's only - // enabled if we have an exact version match. - #[cfg(feature = "sysroot-abi")] - { - if info.version_string == RUSTC_VERSION_STRING { - let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?; - return Ok(Abi::AbiSysroot(inner)); - } - - // if we reached this point, versions didn't match. in testing, we - // want that to panic - this could mean that the format of `rustc - // --version` no longer matches the format of the version string - // stored in the `.rustc` section, and we want to catch that in-tree - // with `x.py test` - #[cfg(test)] - { - let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH"); - if let Ok("1") = allow_mismatch.as_deref() { - // only used by rust-analyzer developers, when working on the - // sysroot ABI from the rust-analyzer repository - which should - // only happen pre-subtree. this can be removed later. - } else { - panic!( - "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", - info.version_string, RUSTC_VERSION_STRING - ); - } - } - } - - // FIXME: this should use exclusive ranges when they're stable - // https://github.com/rust-lang/rust/issues/37854 - match (info.version.0, info.version.1) { - (1, 63) => { - let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?; - Ok(Abi::Abi1_63(inner)) - } - _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)), - } - } - - pub fn expand( - &self, - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - match self { - Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes), - #[cfg(feature = "sysroot-abi")] - Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes), - } - } - - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - match self { - Self::Abi1_63(abi) => abi.list_macros(), - #[cfg(feature = "sysroot-abi")] - Self::AbiSysroot(abi) => abi.list_macros(), - } - } -} - -#[test] -fn test_version_check() { - let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path()); - let info = proc_macro_api::read_dylib_info(&path).unwrap(); - assert!(info.version.1 >= 50); -} diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs index 89ffd1f493e2..dd05e250c2de 100644 --- a/crates/proc-macro-srv/src/dylib.rs +++ b/crates/proc-macro-srv/src/dylib.rs @@ -13,10 +13,6 @@ use object::Object; use paths::AbsPath; use proc_macro_api::{read_dylib_info, ProcMacroKind}; -use crate::tt; - -use super::abis::Abi; - const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; fn invalid_data_err(e: impl Into>) -> io::Error { @@ -82,14 +78,17 @@ fn load_library(file: &Path) -> Result { pub enum LoadProcMacroDylibError { Io(io::Error), LibLoading(libloading::Error), - UnsupportedABI(String), + AbiMismatch(String), } impl fmt::Display for LoadProcMacroDylibError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Io(e) => e.fmt(f), - Self::UnsupportedABI(v) => write!(f, "unsupported ABI `{v}`"), + Self::AbiMismatch(v) => { + use crate::RUSTC_VERSION_STRING; + write!(f, "mismatched ABI expected: `{RUSTC_VERSION_STRING}`, got `{v}`") + } Self::LibLoading(e) => e.fmt(f), } } @@ -110,7 +109,7 @@ impl From for LoadProcMacroDylibError { struct ProcMacroLibraryLibloading { // Hold on to the library so it doesn't unload _lib: Library, - abi: Abi, + proc_macros: crate::proc_macros::ProcMacros, } impl ProcMacroLibraryLibloading { @@ -125,8 +124,9 @@ impl ProcMacroLibraryLibloading { let version_info = read_dylib_info(abs_file)?; let lib = load_library(file).map_err(invalid_data_err)?; - let abi = Abi::from_lib(&lib, symbol_name, version_info)?; - Ok(ProcMacroLibraryLibloading { _lib: lib, abi }) + let proc_macros = + crate::proc_macros::ProcMacros::from_lib(&lib, symbol_name, version_info)?; + Ok(ProcMacroLibraryLibloading { _lib: lib, proc_macros }) } } @@ -150,15 +150,15 @@ impl Expander { pub fn expand( &self, macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - ) -> Result { - let result = self.inner.abi.expand(macro_name, macro_body, attributes); + macro_body: &crate::tt::Subtree, + attributes: Option<&crate::tt::Subtree>, + ) -> Result { + let result = self.inner.proc_macros.expand(macro_name, macro_body, attributes); result.map_err(|e| e.as_str().unwrap_or_else(|| "".to_string())) } pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.inner.abi.list_macros() + self.inner.proc_macros.list_macros() } } diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs index ee70fe7d4f54..2aac37903130 100644 --- a/crates/proc-macro-srv/src/lib.rs +++ b/crates/proc-macro-srv/src/lib.rs @@ -10,17 +10,16 @@ //! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable` //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… +#![cfg(feature = "sysroot-abi")] +#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)] #![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)] -#![cfg_attr( - feature = "sysroot-abi", - feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span) -)] #![allow(unreachable_pub)] -mod dylib; -mod abis; +extern crate proc_macro; -pub mod cli; +mod dylib; +mod server; +mod proc_macros; use std::{ collections::{hash_map::Entry, HashMap}, @@ -32,25 +31,25 @@ use std::{ time::SystemTime, }; -use proc_macro_api::{ - msg::{ExpandMacro, FlatTree, PanicMessage}, - ProcMacroKind, -}; +use proc_macro_api::{msg, ProcMacroKind}; use ::tt::token_id as tt; +// see `build.rs` +include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); + #[derive(Default)] -pub(crate) struct ProcMacroSrv { +pub struct ProcMacroSrv { expanders: HashMap<(PathBuf, SystemTime), dylib::Expander>, } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; impl ProcMacroSrv { - pub fn expand(&mut self, task: ExpandMacro) -> Result { + pub fn expand(&mut self, task: msg::ExpandMacro) -> Result { let expander = self.expander(task.lib.as_ref()).map_err(|err| { debug_assert!(false, "should list macros before asking to expand"); - PanicMessage(format!("failed to load macro: {err}")) + msg::PanicMessage(format!("failed to load macro: {err}")) })?; let prev_env = EnvSnapshot::new(); @@ -77,7 +76,7 @@ impl ProcMacroSrv { .spawn_scoped(s, || { expander .expand(&task.macro_name, ¯o_body, attributes.as_ref()) - .map(|it| FlatTree::new(&it)) + .map(|it| msg::FlatTree::new(&it)) }); let res = match thread { Ok(handle) => handle.join(), @@ -102,10 +101,10 @@ impl ProcMacroSrv { } } - result.map_err(PanicMessage) + result.map_err(msg::PanicMessage) } - pub(crate) fn list_macros( + pub fn list_macros( &mut self, dylib_path: &Path, ) -> Result, String> { @@ -129,6 +128,16 @@ impl ProcMacroSrv { } } +pub struct PanicMessage { + message: Option, +} + +impl PanicMessage { + pub fn as_str(&self) -> Option { + self.message.clone() + } +} + struct EnvSnapshot { vars: HashMap, } @@ -138,10 +147,13 @@ impl EnvSnapshot { EnvSnapshot { vars: env::vars_os().collect() } } - fn rollback(self) { - let mut old_vars = self.vars; + fn rollback(self) {} +} + +impl Drop for EnvSnapshot { + fn drop(&mut self) { for (name, value) in env::vars_os() { - let old_value = old_vars.remove(&name); + let old_value = self.vars.remove(&name); if old_value != Some(value) { match old_value { None => env::remove_var(name), @@ -149,13 +161,13 @@ impl EnvSnapshot { } } } - for (name, old_value) in old_vars { + for (name, old_value) in self.vars.drain() { env::set_var(name, old_value) } } } -#[cfg(all(feature = "sysroot-abi", test))] +#[cfg(test)] mod tests; #[cfg(test)] diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs b/crates/proc-macro-srv/src/proc_macros.rs similarity index 52% rename from crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs rename to crates/proc-macro-srv/src/proc_macros.rs index 0a3b8866a7fd..3c6f32033192 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/mod.rs +++ b/crates/proc-macro-srv/src/proc_macros.rs @@ -1,45 +1,55 @@ //! Proc macro ABI -extern crate proc_macro; - -#[allow(dead_code)] -#[doc(hidden)] -mod ra_server; - use libloading::Library; -use proc_macro_api::ProcMacroKind; +use proc_macro_api::{ProcMacroKind, RustCInfo}; -use super::{tt, PanicMessage}; +use crate::{dylib::LoadProcMacroDylibError, server::SYMBOL_INTERNER, tt}; -pub use ra_server::TokenStream; - -pub(crate) struct Abi { +pub(crate) struct ProcMacros { exported_macros: Vec, } -impl From for PanicMessage { +impl From for crate::PanicMessage { fn from(p: proc_macro::bridge::PanicMessage) -> Self { Self { message: p.as_str().map(|s| s.to_string()) } } } -impl Abi { - pub unsafe fn from_lib(lib: &Library, symbol_name: String) -> Result { - let macros: libloading::Symbol<'_, &&[proc_macro::bridge::client::ProcMacro]> = - lib.get(symbol_name.as_bytes())?; - Ok(Self { exported_macros: macros.to_vec() }) +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( + lib: &Library, + symbol_name: String, + info: RustCInfo, + ) -> Result { + if info.version_string == crate::RUSTC_VERSION_STRING { + let macros = unsafe { + lib.get::<&&[proc_macro::bridge::client::ProcMacro]>(symbol_name.as_bytes()) + }?; + + return Ok(Self { exported_macros: macros.to_vec() }); + } + Err(LoadProcMacroDylibError::AbiMismatch(info.version_string)) } - pub fn expand( + pub(crate) fn expand( &self, macro_name: &str, macro_body: &tt::Subtree, attributes: Option<&tt::Subtree>, - ) -> Result { - let parsed_body = ra_server::TokenStream::with_subtree(macro_body.clone()); + ) -> Result { + let parsed_body = crate::server::TokenStream::with_subtree(macro_body.clone()); - let parsed_attributes = attributes.map_or(ra_server::TokenStream::new(), |attr| { - ra_server::TokenStream::with_subtree(attr.clone()) + let parsed_attributes = attributes.map_or(crate::server::TokenStream::new(), |attr| { + crate::server::TokenStream::with_subtree(attr.clone()) }); for proc_macro in &self.exported_macros { @@ -49,34 +59,34 @@ impl Abi { } if *trait_name == macro_name => { let res = client.run( &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from); } proc_macro::bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { let res = client.run( &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from); } proc_macro::bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { let res = client.run( &proc_macro::bridge::server::SameThread, - ra_server::RustAnalyzer::default(), + crate::server::RustAnalyzer { interner: &SYMBOL_INTERNER }, parsed_attributes, parsed_body, true, ); - return res.map(|it| it.into_subtree()).map_err(PanicMessage::from); + return res.map(|it| it.into_subtree()).map_err(crate::PanicMessage::from); } _ => continue, } @@ -85,7 +95,7 @@ impl Abi { Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into()) } - pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { + pub(crate) fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { self.exported_macros .iter() .map(|proc_macro| match proc_macro { @@ -102,3 +112,16 @@ impl Abi { .collect() } } + +#[test] +fn test_version_check() { + let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path()); + let info = proc_macro_api::read_dylib_info(&path).unwrap(); + assert_eq!( + info.version_string, + crate::RUSTC_VERSION_STRING, + "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", + info.version_string, + crate::RUSTC_VERSION_STRING, + ); +} diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs b/crates/proc-macro-srv/src/server.rs similarity index 91% rename from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs rename to crates/proc-macro-srv/src/server.rs index d258a0247290..5b659ab84981 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server.rs +++ b/crates/proc-macro-srv/src/server.rs @@ -8,9 +8,9 @@ //! //! FIXME: No span and source file information is implemented yet -use super::proc_macro::{ - self, +use proc_macro::{ bridge::{self, server}, + LineColumn, }; mod token_stream; @@ -26,8 +26,10 @@ use crate::tt; type Group = tt::Subtree; type TokenTree = tt::TokenTree; +#[allow(unused)] type Punct = tt::Punct; type Spacing = tt::Spacing; +#[allow(unused)] type Literal = tt::Literal; type Span = tt::TokenId; @@ -36,14 +38,11 @@ pub struct SourceFile { // FIXME stub } -type Level = super::proc_macro::Level; -type LineColumn = super::proc_macro::LineColumn; - pub struct FreeFunctions; -#[derive(Default)] pub struct RustAnalyzer { // FIXME: store span information here. + pub(crate) interner: SymbolInternerRef, } impl server::Types for RustAnalyzer { @@ -68,7 +67,7 @@ impl server::FreeFunctions for RustAnalyzer { // FIXME: keep track of LitKind and Suffix Ok(bridge::Literal { kind: bridge::LitKind::Err, - symbol: Symbol::intern(s), + symbol: Symbol::intern(self.interner, s), suffix: None, span: tt::TokenId::unspecified(), }) @@ -109,7 +108,7 @@ impl server::TokenStream for RustAnalyzer { } bridge::TokenTree::Ident(ident) => { - let text = ident.sym.text(); + let text = ident.sym.text(self.interner); let text = if ident.is_raw { ::tt::SmolStr::from_iter(["r#", &text]) } else { text }; let ident: tt::Ident = tt::Ident { text, span: ident.span }; @@ -120,8 +119,9 @@ impl server::TokenStream for RustAnalyzer { bridge::TokenTree::Literal(literal) => { let literal = LiteralFormatter(literal); - let text = literal - .with_stringify_parts(|parts| ::tt::SmolStr::from_iter(parts.iter().copied())); + let text = literal.with_stringify_parts(self.interner, |parts| { + ::tt::SmolStr::from_iter(parts.iter().copied()) + }); let literal = tt::Literal { text, span: literal.0.span }; let leaf = tt::Leaf::from(literal); @@ -185,7 +185,7 @@ impl server::TokenStream for RustAnalyzer { .map(|tree| match tree { tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => { bridge::TokenTree::Ident(bridge::Ident { - sym: Symbol::intern(ident.text.trim_start_matches("r#")), + sym: Symbol::intern(self.interner, ident.text.trim_start_matches("r#")), is_raw: ident.text.starts_with("r#"), span: ident.span, }) @@ -194,7 +194,7 @@ impl server::TokenStream for RustAnalyzer { bridge::TokenTree::Literal(bridge::Literal { // FIXME: handle literal kinds kind: bridge::LitKind::Err, - symbol: Symbol::intern(&lit.text), + symbol: Symbol::intern(self.interner, &lit.text), // FIXME: handle suffixes suffix: None, span: lit.span, @@ -240,6 +240,7 @@ fn delim_to_external(d: tt::Delimiter) -> proc_macro::Delimiter { } } +#[allow(unused)] fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { match spacing { proc_macro::Spacing::Alone => Spacing::Alone, @@ -247,6 +248,7 @@ fn spacing_to_internal(spacing: proc_macro::Spacing) -> Spacing { } } +#[allow(unused)] fn spacing_to_external(spacing: Spacing) -> proc_macro::Spacing { match spacing { Spacing::Alone => proc_macro::Spacing::Alone, @@ -350,11 +352,13 @@ impl server::Server for RustAnalyzer { } fn intern_symbol(ident: &str) -> Self::Symbol { - Symbol::intern(&::tt::SmolStr::from(ident)) + // FIXME: should be self.interner once the proc-macro api allows is + Symbol::intern(&SYMBOL_INTERNER, &::tt::SmolStr::from(ident)) } fn with_symbol_string(symbol: &Self::Symbol, f: impl FnOnce(&str)) { - f(symbol.text().as_str()) + // FIXME: should be self.interner once the proc-macro api allows is + f(symbol.text(&SYMBOL_INTERNER).as_str()) } } @@ -365,7 +369,11 @@ impl LiteralFormatter { /// literal's representation. This is done to allow the `ToString` and /// `Display` implementations to borrow references to symbol values, and /// both be optimized to reduce overhead. - fn with_stringify_parts(&self, f: impl FnOnce(&[&str]) -> R) -> R { + fn with_stringify_parts( + &self, + interner: SymbolInternerRef, + f: impl FnOnce(&[&str]) -> R, + ) -> R { /// Returns a string containing exactly `num` '#' characters. /// Uses a 256-character source string literal which is always safe to /// index with a `u8` index. @@ -380,7 +388,7 @@ impl LiteralFormatter { &HASHES[..num as usize] } - self.with_symbol_and_suffix(|symbol, suffix| match self.0.kind { + self.with_symbol_and_suffix(interner, |symbol, suffix| match self.0.kind { bridge::LitKind::Byte => f(&["b'", symbol, "'", suffix]), bridge::LitKind::Char => f(&["'", symbol, "'", suffix]), bridge::LitKind::Str => f(&["\"", symbol, "\"", suffix]), @@ -397,9 +405,13 @@ impl LiteralFormatter { }) } - fn with_symbol_and_suffix(&self, f: impl FnOnce(&str, &str) -> R) -> R { - let symbol = self.0.symbol.text(); - let suffix = self.0.suffix.map(|s| s.text()).unwrap_or_default(); + fn with_symbol_and_suffix( + &self, + interner: SymbolInternerRef, + f: impl FnOnce(&str, &str) -> R, + ) -> R { + let symbol = self.0.symbol.text(interner); + let suffix = self.0.suffix.map(|s| s.text(interner)).unwrap_or_default(); f(symbol.as_str(), suffix.as_str()) } } diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs b/crates/proc-macro-srv/src/server/symbol.rs similarity index 58% rename from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs rename to crates/proc-macro-srv/src/server/symbol.rs index 51dfba2ea9fb..540d06457f2f 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/symbol.rs +++ b/crates/proc-macro-srv/src/server/symbol.rs @@ -1,28 +1,30 @@ //! Symbol interner for proc-macro-srv -use std::{cell::RefCell, collections::HashMap}; +use std::{cell::RefCell, collections::HashMap, thread::LocalKey}; use tt::SmolStr; thread_local! { - static SYMBOL_INTERNER: RefCell = Default::default(); + pub(crate) static SYMBOL_INTERNER: RefCell = Default::default(); } // ID for an interned symbol. #[derive(Hash, Eq, PartialEq, Copy, Clone)] pub struct Symbol(u32); +pub(crate) type SymbolInternerRef = &'static LocalKey>; + impl Symbol { - pub fn intern(data: &str) -> Symbol { - SYMBOL_INTERNER.with(|i| i.borrow_mut().intern(data)) + pub(super) fn intern(interner: SymbolInternerRef, data: &str) -> Symbol { + interner.with(|i| i.borrow_mut().intern(data)) } - pub fn text(&self) -> SmolStr { - SYMBOL_INTERNER.with(|i| i.borrow().get(self).clone()) + pub(super) fn text(&self, interner: SymbolInternerRef) -> SmolStr { + interner.with(|i| i.borrow().get(self).clone()) } } #[derive(Default)] -struct SymbolInterner { +pub(crate) struct SymbolInterner { idents: HashMap, ident_data: Vec, } diff --git a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs b/crates/proc-macro-srv/src/server/token_stream.rs similarity index 93% rename from crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs rename to crates/proc-macro-srv/src/server/token_stream.rs index d091d431900a..2589d8b64d48 100644 --- a/crates/proc-macro-srv/src/abis/abi_sysroot/ra_server/token_stream.rs +++ b/crates/proc-macro-srv/src/server/token_stream.rs @@ -4,15 +4,15 @@ use crate::tt::{self, TokenTree}; #[derive(Debug, Default, Clone)] pub struct TokenStream { - pub token_trees: Vec, + pub(super) token_trees: Vec, } impl TokenStream { - pub fn new() -> Self { + pub(crate) fn new() -> Self { TokenStream::default() } - pub fn with_subtree(subtree: tt::Subtree) -> Self { + pub(crate) fn with_subtree(subtree: tt::Subtree) -> Self { if subtree.delimiter.kind != tt::DelimiterKind::Invisible { TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } } else { @@ -20,11 +20,11 @@ impl TokenStream { } } - pub fn into_subtree(self) -> tt::Subtree { + pub(crate) fn into_subtree(self) -> tt::Subtree { tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: self.token_trees } } - pub fn is_empty(&self) -> bool { + pub(super) fn is_empty(&self) -> bool { self.token_trees.is_empty() } } @@ -78,12 +78,12 @@ impl Extend for TokenStream { } } -pub struct TokenStreamBuilder { +pub(super) struct TokenStreamBuilder { acc: TokenStream, } -/// Public implementation details for the `TokenStream` type, such as iterators. -pub mod token_stream { +/// pub(super)lic implementation details for the `TokenStream` type, such as iterators. +pub(super) mod token_stream { use std::str::FromStr; use super::{tt, TokenStream, TokenTree}; diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs index efbeb90ca9dd..49b4d973b63b 100644 --- a/crates/proc-macro-srv/src/tests/utils.rs +++ b/crates/proc-macro-srv/src/tests/utils.rs @@ -5,14 +5,14 @@ use std::str::FromStr; use crate::{dylib, proc_macro_test_dylib_path, ProcMacroSrv}; -fn parse_string(code: &str) -> Option { +fn parse_string(code: &str) -> Option { // This is a bit strange. We need to parse a string into a token stream into // order to create a tt::SubTree from it in fixtures. `into_subtree` is // implemented by all the ABIs we have so we arbitrarily choose one ABI to // write a `parse_string` function for and use that. The tests don't really // care which ABI we're using as the `into_subtree` function isn't part of // the ABI and shouldn't change between ABI versions. - crate::abis::TestTokenStream::from_str(code).ok() + crate::server::TokenStream::from_str(code).ok() } pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) { diff --git a/crates/rust-analyzer/Cargo.toml b/crates/rust-analyzer/Cargo.toml index 1a4155d3bbb3..3f2cfed806f6 100644 --- a/crates/rust-analyzer/Cargo.toml +++ b/crates/rust-analyzer/Cargo.toml @@ -67,7 +67,7 @@ ide-db.workspace = true ide-ssr.workspace = true ide.workspace = true proc-macro-api.workspace = true -proc-macro-srv.workspace = true +proc-macro-srv-cli.workspace = true profile.workspace = true project-model.workspace = true stdx.workspace = true @@ -95,8 +95,9 @@ mbe.workspace = true [features] jemalloc = ["jemallocator", "profile/jemalloc"] force-always-assert = ["always-assert/force"] +sysroot-abi = ["proc-macro-srv-cli/sysroot-abi"] in-rust-tree = [ - "proc-macro-srv/sysroot-abi", + "sysroot-abi", "ide/in-rust-tree", "syntax/in-rust-tree", ] diff --git a/crates/rust-analyzer/src/bin/main.rs b/crates/rust-analyzer/src/bin/main.rs index 4de022b6ed60..3506bed2c74e 100644 --- a/crates/rust-analyzer/src/bin/main.rs +++ b/crates/rust-analyzer/src/bin/main.rs @@ -77,7 +77,7 @@ fn try_main(flags: flags::RustAnalyzer) -> Result<()> { with_extra_thread("LspServer", run_server)?; } flags::RustAnalyzerCmd::ProcMacro(flags::ProcMacro) => { - with_extra_thread("MacroExpander", || proc_macro_srv::cli::run().map_err(Into::into))?; + with_extra_thread("MacroExpander", || proc_macro_srv_cli::run().map_err(Into::into))?; } flags::RustAnalyzerCmd::Parse(cmd) => cmd.run()?, flags::RustAnalyzerCmd::Symbols(cmd) => cmd.run()?, diff --git a/crates/rust-analyzer/src/reload.rs b/crates/rust-analyzer/src/reload.rs index 987eb8aad6da..f90df169bee0 100644 --- a/crates/rust-analyzer/src/reload.rs +++ b/crates/rust-analyzer/src/reload.rs @@ -108,6 +108,10 @@ impl GlobalState { status.health = lsp_ext::Health::Warning; message.push_str("Failed to run build scripts of some packages.\n\n"); } + if self.proc_macro_clients.iter().any(|it| it.is_err()) { + status.health = lsp_ext::Health::Warning; + message.push_str("Failed to spawn one or more proc-macro servers.\n\n"); + } if !self.config.cargo_autoreload() && self.is_quiescent() && self.fetch_workspaces_queue.op_requested() @@ -376,18 +380,23 @@ impl GlobalState { .workspaces .iter() .map(|ws| { - let (path, args): (_, &[_]) = if path_manually_set { + let path = if path_manually_set { tracing::debug!( "Pro-macro server path explicitly set: {}", path.display() ); - (path.clone(), &[]) + path.clone() } else { match ws.find_sysroot_proc_macro_srv() { - Some(server_path) => (server_path, &[]), - None => (path.clone(), &["proc-macro"]), + Some(server_path) => server_path, + None => path.clone(), } }; + let args: &[_] = if path.file_stem() == Some("rust-analyzer".as_ref()) { + &["proc-macro"] + } else { + &[] + }; tracing::info!(?args, "Using proc-macro server at {}", path.display(),); ProcMacroServer::spawn(path.clone(), args).map_err(|err| { diff --git a/crates/rust-analyzer/tests/slow-tests/main.rs b/crates/rust-analyzer/tests/slow-tests/main.rs index 587d640969aa..54a91df23e5d 100644 --- a/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/crates/rust-analyzer/tests/slow-tests/main.rs @@ -59,7 +59,7 @@ use std::collections::Spam; "#, ) .with_config(serde_json::json!({ - "cargo": { "sysroot": "discover" } + "cargo": { "sysroot": "discover" }, })) .server() .wait_until_workspace_is_loaded(); @@ -508,7 +508,7 @@ fn main() {} #[test] fn test_missing_module_code_action_in_json_project() { if skip_slow_tests() { - // return; + return; } let tmp_dir = TestDir::new(); @@ -612,7 +612,7 @@ fn main() {{}} "# )) .with_config(serde_json::json!({ - "cargo": { "sysroot": "discover" } + "cargo": { "sysroot": "discover" }, })) .server() .wait_until_workspace_is_loaded(); @@ -685,7 +685,7 @@ version = \"0.0.0\" #[test] fn out_dirs_check() { if skip_slow_tests() { - // return; + return; } let server = Project::with_fixture( @@ -711,10 +711,21 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); } //- /src/main.rs -#[rustc_builtin_macro] macro_rules! include {} -#[rustc_builtin_macro] macro_rules! include_str {} -#[rustc_builtin_macro] macro_rules! concat {} -#[rustc_builtin_macro] macro_rules! env {} +#![allow(warnings)] +#![feature(rustc_attrs)] +#[rustc_builtin_macro] macro_rules! include { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! include_str { + ($file:expr $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! concat { + ($($e:ident),+ $(,)?) => {{ /* compiler built-in */ }}; +} +#[rustc_builtin_macro] macro_rules! env { + ($name:expr $(,)?) => {{ /* compiler built-in */ }}; + ($name:expr, $error_msg:expr $(,)?) => {{ /* compiler built-in */ }}; +} include!(concat!(env!("OUT_DIR"), "/hello.rs")); @@ -749,7 +760,7 @@ fn main() { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(19, 10), + Position::new(30, 10), ), work_done_progress_params: Default::default(), }); @@ -758,7 +769,7 @@ fn main() { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(20, 10), + Position::new(31, 10), ), work_done_progress_params: Default::default(), }); @@ -768,23 +779,23 @@ fn main() { GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(17, 9), + Position::new(28, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 17 }, - "start": { "character": 8, "line": 17 } + "end": { "character": 10, "line": 28 }, + "start": { "character": 8, "line": 28 } }, "targetRange": { - "end": { "character": 9, "line": 8 }, - "start": { "character": 0, "line": 7 } + "end": { "character": 9, "line": 19 }, + "start": { "character": 0, "line": 18 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 8 }, - "start": { "character": 7, "line": 8 } + "end": { "character": 8, "line": 19 }, + "start": { "character": 7, "line": 19 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -794,23 +805,23 @@ fn main() { GotoDefinitionParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("src/main.rs"), - Position::new(18, 9), + Position::new(29, 9), ), work_done_progress_params: Default::default(), partial_result_params: Default::default(), }, json!([{ "originSelectionRange": { - "end": { "character": 10, "line": 18 }, - "start": { "character": 8, "line": 18 } + "end": { "character": 10, "line": 29 }, + "start": { "character": 8, "line": 29 } }, "targetRange": { - "end": { "character": 9, "line": 12 }, - "start": { "character": 0, "line":11 } + "end": { "character": 9, "line": 23 }, + "start": { "character": 0, "line": 22 } }, "targetSelectionRange": { - "end": { "character": 8, "line": 12 }, - "start": { "character": 7, "line": 12 } + "end": { "character": 8, "line": 23 }, + "start": { "character": 7, "line": 23 } }, "targetUri": "file:///[..]src/main.rs" }]), @@ -818,8 +829,7 @@ fn main() { } #[test] -// FIXME: Re-enable once we can run proc-macro tests on rust-lang/rust-analyzer again -#[cfg(any())] +#[cfg(feature = "sysroot-abi")] fn resolve_proc_macro() { use expect_test::expect; if skip_slow_tests() { @@ -837,6 +847,7 @@ edition = "2021" bar = {path = "../bar"} //- /foo/src/main.rs +#![feature(rustc_attrs, decl_macro)] use bar::Bar; #[rustc_builtin_macro] @@ -913,7 +924,7 @@ pub fn foo(_input: TokenStream) -> TokenStream { let res = server.send_request::(HoverParams { text_document_position_params: TextDocumentPositionParams::new( server.doc_id("foo/src/main.rs"), - Position::new(10, 9), + Position::new(11, 9), ), work_done_progress_params: Default::default(), }); @@ -1083,10 +1094,18 @@ version = "0.0.0" //- /bar/src/lib.rs pub fn bar() {} + +//- /baz/Cargo.toml +[package] +name = "baz" +version = "0.0.0" + +//- /baz/src/lib.rs "#, ) .root("foo") .root("bar") + .root("baz") .with_config(json!({ "files": { "excludeDirs": ["foo", "bar"] diff --git a/crates/rust-analyzer/tests/slow-tests/support.rs b/crates/rust-analyzer/tests/slow-tests/support.rs index 037fc89ace09..e5f0e57d2a0e 100644 --- a/crates/rust-analyzer/tests/slow-tests/support.rs +++ b/crates/rust-analyzer/tests/slow-tests/support.rs @@ -37,8 +37,12 @@ impl<'a> Project<'a> { "sysroot": null, // Can't use test binary as rustc wrapper. "buildScripts": { - "useRustcWrapper": false + "useRustcWrapper": false, + "enable": false, }, + }, + "procMacro": { + "enable": false, } }), } @@ -251,6 +255,9 @@ impl Server { .clone() .extract::("experimental/serverStatus") .unwrap(); + if status.health != lsp_ext::Health::Ok { + panic!("server errored/warned while loading workspace: {:?}", status.message); + } status.quiescent } _ => false,