From a8b0a9101192b28cbcd4e2ee495613a5f07acbf2 Mon Sep 17 00:00:00 2001 From: Alex Good Date: Sat, 10 Jul 2021 15:11:17 +0100 Subject: [PATCH 1/4] copy proc_maco to proc_macro_nightly and modify for nightly ABI compatibility --- .../src/proc_macro_nightly/bridge/buffer.rs | 160 +++ .../src/proc_macro_nightly/bridge/client.rs | 483 +++++++++ .../src/proc_macro_nightly/bridge/closure.rs | 30 + .../src/proc_macro_nightly/bridge/handle.rs | 73 ++ .../src/proc_macro_nightly/bridge/mod.rs | 428 ++++++++ .../src/proc_macro_nightly/bridge/rpc.rs | 311 ++++++ .../proc_macro_nightly/bridge/scoped_cell.rs | 84 ++ .../src/proc_macro_nightly/bridge/server.rs | 351 +++++++ .../src/proc_macro_nightly/diagnostic.rs | 167 +++ .../src/proc_macro_nightly/mod.rs | 964 ++++++++++++++++++ .../src/rustc_server_nightly.rs | 836 +++++++++++++++ 11 files changed, 3887 insertions(+) create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/handle.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/mod.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/scoped_cell.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs create mode 100644 crates/proc_macro_srv/src/proc_macro_nightly/mod.rs create mode 100644 crates/proc_macro_srv/src/rustc_server_nightly.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs new file mode 100644 index 000000000000..a25feac20120 --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs @@ -0,0 +1,160 @@ +//! lib-proc-macro Buffer management for same-process client<->server communication. +//! +//! Copy from +//! augmented with removing unstable features + +use std::io::{self, Write}; +use std::mem; +use std::ops::{Deref, DerefMut}; +use std::slice; + +#[repr(C)] +struct Slice<'a, T> { + data: &'a [T; 0], + len: usize, +} + +unsafe impl<'a, T: Sync> Sync for Slice<'a, T> {} +unsafe impl<'a, T: Sync> Send for Slice<'a, T> {} + +impl<'a, T> Copy for Slice<'a, T> {} +impl<'a, T> Clone for Slice<'a, T> { + fn clone(&self) -> Self { + *self + } +} + +impl<'a, T> From<&'a [T]> for Slice<'a, T> { + fn from(xs: &'a [T]) -> Self { + Slice { data: unsafe { &*(xs.as_ptr() as *const [T; 0]) }, len: xs.len() } + } +} + +impl<'a, T> Deref for Slice<'a, T> { + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) } + } +} + +#[repr(C)] +pub struct Buffer { + data: *mut T, + 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 { + fn default() -> Self { + Self::from(vec![]) + } +} + +impl Deref for Buffer { + type Target = [T]; + fn deref(&self) -> &[T] { + unsafe { slice::from_raw_parts(self.data as *const T, self.len) } + } +} + +impl DerefMut for Buffer { + fn deref_mut(&mut self) -> &mut [T] { + unsafe { slice::from_raw_parts_mut(self.data, self.len) } + } +} + +impl Buffer { + pub(super) fn new() -> Self { + Self::default() + } + + pub(super) fn clear(&mut self) { + self.len = 0; + } + + pub(super) fn take(&mut self) -> Self { + mem::take(self) + } + + pub(super) fn extend_from_slice(&mut self, xs: &[T]) { + if xs.len() > self.capacity.wrapping_sub(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(); + } + } + + pub(super) fn push(&mut self, v: T) { + // 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 { + fn write(&mut self, xs: &[u8]) -> io::Result { + self.extend_from_slice(xs); + Ok(xs.len()) + } + + fn write_all(&mut self, xs: &[u8]) -> io::Result<()> { + self.extend_from_slice(xs); + Ok(()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for Buffer { + 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/proc_macro_nightly/bridge/client.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs new file mode 100644 index 000000000000..c135cf7a2371 --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs @@ -0,0 +1,483 @@ +//! lib-proc-macro Client-side types. +//! +//! Copy from +//! augmented with removing unstable features + +use super::*; + +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 struct $oty(pub(crate) handle::Handle); + // impl !Send for $oty {} + // impl !Sync for $oty {} + + // Forward `Drop::drop` to the inherent `drop` method. + impl Drop for $oty { + fn drop(&mut self) { + $oty(self.0).drop(); + } + } + + impl Encode for $oty { + fn encode(self, w: &mut Writer, s: &mut S) { + let handle = self.0; + 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.0.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.0.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::decode(r, s)) + } + } + )* + + $( + #[repr(C)] + #[derive(Copy, Clone, PartialEq, Eq, Hash)] + pub(crate) struct $ity(handle::Handle); + // impl !Send for $ity {} + // impl !Sync for $ity {} + + impl Encode for $ity { + fn encode(self, w: &mut Writer, s: &mut S) { + self.0.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::decode(r, s)) + } + } + )* + } +} +define_handles! { + 'owned: + FreeFunctions, + TokenStream, + TokenStreamBuilder, + TokenStreamIter, + 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 TokenStreamIter { + 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 { + #[allow(unused)] + $(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* { + panic!("crates should be linked against the sysroot version of proc_macro, not this one from rust-analyzer"); + // Bridge::with(|bridge| { + // let mut b = bridge.cached_buffer.take(); + + // b.clear(); + // api_tags::Method::$name(api_tags::$name::$method).encode(&mut b, &mut ()); + // reverse_encode!(b; $($arg),*); + + // b = bridge.dispatch.call(b); + + // let r = Result::<_, PanicMessage>::decode(&mut &b[..], &mut ()); + + // bridge.cached_buffer = b; + + // 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<'_> { + 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, + // Something weird is going on, so don't suppress any backtraces + BridgeState::InUse => true, + BridgeState::Connected(bridge) => 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 "global object" (usually a function pointer), +/// which may be using a different `proc_macro` from the one +/// used by the server, but can be interacted with compatibly. +/// +/// N.B., `F` must have FFI-friendly memory layout (e.g., a pointer). +/// The call ABI of function pointers used for `F` doesn't +/// need to match between server and client, since it's only +/// passed between them and (eventually) called by the client. +#[repr(C)] +#[derive(Copy, Clone)] +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<'_>, F) -> Buffer, + pub(super) f: F, +} + +/// 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 b = bridge.cached_buffer.take(); + + panic::catch_unwind(panic::AssertUnwindSafe(|| { + bridge.enter(|| { + let reader = &mut &b[..]; + let input = A::decode(reader, &mut ()); + + // Put the `cached_buffer` back in the `Bridge`, for requests. + Bridge::with(|bridge| bridge.cached_buffer = b.take()); + + let output = f(input); + + // Take the `cached_buffer` back out, for the output value. + b = 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 may not + // at the moment, so this is also potentially preventing UB). + b.clear(); + Ok::<_, ()>(output).encode(&mut b, &mut ()); + }) + })) + .map_err(PanicMessage::from) + .unwrap_or_else(|e| { + b.clear(); + Err::<(), _>(e).encode(&mut b, &mut ()); + }); + b +} + +impl Client crate::TokenStream> { + pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + extern "C" fn run( + bridge: Bridge<'_>, + f: impl FnOnce(crate::TokenStream) -> crate::TokenStream, + ) -> Buffer { + run_client(bridge, |input| f(crate::TokenStream(input)).0) + } + Client { get_handle_counters: HandleCounters::get, run, f } + } +} + +impl Client crate::TokenStream> { + pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self { + extern "C" fn run( + bridge: Bridge<'_>, + f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + ) -> Buffer { + run_client(bridge, |(input, input2)| { + f(crate::TokenStream(input), crate::TokenStream(input2)).0 + }) + } + Client { get_handle_counters: HandleCounters::get, run, f } + } +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum ProcMacro { + CustomDerive { + trait_name: &'static str, + attributes: &'static [&'static str], + client: Client crate::TokenStream>, + }, + + Attr { + name: &'static str, + client: Client crate::TokenStream>, + }, + + Bang { + name: &'static str, + client: Client crate::TokenStream>, + }, +} + +impl std::fmt::Debug for ProcMacro { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ProcMacro {{ name: {} }}", self.name()) + } +} + +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 fn custom_derive( + trait_name: &'static str, + attributes: &'static [&'static str], + expand: fn(crate::TokenStream) -> crate::TokenStream, + ) -> Self { + ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } + } + + pub fn attr( + name: &'static str, + expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + ) -> Self { + ProcMacro::Attr { name, client: Client::expand2(expand) } + } + + pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + ProcMacro::Bang { name, client: Client::expand1(expand) } + } +} diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs new file mode 100644 index 000000000000..f5b6d897e43c --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs @@ -0,0 +1,30 @@ +//! lib-proc-macro Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`. +//! +//! Copy from +//! augmented with removing unstable features + +#[repr(C)] +pub struct Closure<'a, A, R> { + call: unsafe extern "C" fn(&mut Env, A) -> R, + env: &'a mut Env, +} + +struct Env; + +// impl<'a, A, R> !Sync for Closure<'a, A, R> {} +// impl<'a, A, R> !Send for Closure<'a, A, R> {} + +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: unsafe { &mut *(f as *mut _ as *mut Env) } } + } +} + +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/proc_macro_nightly/bridge/handle.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/handle.rs new file mode 100644 index 000000000000..d2a65d249b5e --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/handle.rs @@ -0,0 +1,73 @@ +//! lib-proc-macro Server-side handles and storage for per-handle data. +//! +//! Copy from +//! augmented with removing unstable features + +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; +use std::num::NonZeroU32; +use std::ops::{Index, IndexMut}; +use std::sync::atomic::{AtomicUsize, Ordering}; + +pub(super) type Handle = NonZeroU32; + +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") + } +} + +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::new() } + } + + 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/proc_macro_nightly/bridge/mod.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/mod.rs new file mode 100644 index 000000000000..8d97bddfd389 --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/mod.rs @@ -0,0 +1,428 @@ +//! lib-proc-macro Internal interface for communicating between a `proc_macro` client +//! +//! Copy from +//! augmented with removing unstable features +//! +//! 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>); + }, + TokenStream { + fn drop($self: $S::TokenStream); + fn clone($self: &$S::TokenStream) -> $S::TokenStream; + fn new() -> $S::TokenStream; + fn is_empty($self: &$S::TokenStream) -> bool; + 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 into_iter($self: $S::TokenStream) -> $S::TokenStreamIter; + }, + TokenStreamBuilder { + fn drop($self: $S::TokenStreamBuilder); + fn new() -> $S::TokenStreamBuilder; + fn push($self: &mut $S::TokenStreamBuilder, stream: $S::TokenStream); + fn build($self: $S::TokenStreamBuilder) -> $S::TokenStream; + }, + TokenStreamIter { + fn drop($self: $S::TokenStreamIter); + fn clone($self: &$S::TokenStreamIter) -> $S::TokenStreamIter; + fn next( + $self: &mut $S::TokenStreamIter, + ) -> Option>; + }, + Group { + fn drop($self: $S::Group); + fn clone($self: &$S::Group) -> $S::Group; + fn new(delimiter: Delimiter, stream: $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 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 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; + }, + } + }; +} + +// FIXME(eddyb) this calls `encode` for each argument, but in reverse, +// to avoid borrow conflicts from borrows started by `&mut` arguments. +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; +#[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, +} + +// impl<'a> !Sync for Bridge<'a> {} +// impl<'a> !Send for Bridge<'a> {} + +#[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 `cov_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 Option { + type Unmarked = Option; + fn mark(unmarked: Self::Unmarked) -> Self { + unmarked.map(T::mark) + } +} +impl Unmark for Option { + type Unmarked = Option; + fn unmark(self) -> Self::Unmarked { + self.map(T::unmark) + } +} +impl Mark for Result { + type Unmarked = Result; + fn mark(unmarked: Self::Unmarked) -> Self { + unmarked.map(T::mark).map_err(E::mark) + } +} +impl Unmark for Result { + type Unmarked = Result; + fn unmark(self) -> Self::Unmarked { + self.map(T::unmark).map_err(E::unmark) + } +} + +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, + Delimiter, + Level, + LineColumn, + Spacing, + Bound, +} + +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, + } +); + +#[derive(Clone)] +pub enum TokenTree { + Group(G), + Punct(P), + Ident(I), + Literal(L), +} + +impl Mark for TokenTree { + type Unmarked = TokenTree; + fn mark(unmarked: Self::Unmarked) -> Self { + match unmarked { + TokenTree::Group(tt) => TokenTree::Group(G::mark(tt)), + TokenTree::Punct(tt) => TokenTree::Punct(P::mark(tt)), + TokenTree::Ident(tt) => TokenTree::Ident(I::mark(tt)), + TokenTree::Literal(tt) => TokenTree::Literal(L::mark(tt)), + } + } +} +impl Unmark for TokenTree { + type Unmarked = TokenTree; + fn unmark(self) -> Self::Unmarked { + match self { + TokenTree::Group(tt) => TokenTree::Group(tt.unmark()), + TokenTree::Punct(tt) => TokenTree::Punct(tt.unmark()), + TokenTree::Ident(tt) => TokenTree::Ident(tt.unmark()), + TokenTree::Literal(tt) => TokenTree::Literal(tt.unmark()), + } + } +} + +rpc_encode_decode!( + enum TokenTree { + Group(tt), + Punct(tt), + Ident(tt), + Literal(tt), + } +); diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs new file mode 100644 index 000000000000..69928ec845ab --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs @@ -0,0 +1,311 @@ +//! lib-proc-macro Serialization for client-server communication. +//! +//! Copy from +//! augmented with removing unstable features +//! +//! Serialization for client-server communication. + +use std::any::Any; +use std::char; +use std::io::Write; +use std::num::NonZeroU32; +use std::ops::Bound; +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.write_all(&self.to_le_bytes()).unwrap(); + } + } + + 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 { $($field:ident),* $(,)? }) => { + impl Encode for $name { + fn encode(self, w: &mut Writer, s: &mut S) { + $(self.$field.encode(w, s);)* + } + } + + impl DecodeMut<'_, '_, S> for $name { + fn decode(r: &mut Reader<'_>, 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.write_all(&[self]).unwrap(); + } +} + +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)) + } +} + +rpc_encode_decode!( + enum Bound { + Included(x), + Excluded(x), + Unbounded, + } +); + +rpc_encode_decode!( + enum Option { + None, + Some(x), + } +); + +rpc_encode_decode!( + enum Result { + Ok(x), + Err(e), + } +); + +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() + } +} + +/// Simplified version of panic payloads, ignoring +/// types other than `&'static str` and `String`. +#[derive(Debug)] +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/proc_macro_nightly/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/scoped_cell.rs new file mode 100644 index 000000000000..0436bc41836c --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/scoped_cell.rs @@ -0,0 +1,84 @@ +//! lib-proc-macro `Cell` variant for (scoped) existential lifetimes. +//! +//! Copy from +//! augmented with removing unstable features + +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 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/proc_macro_nightly/bridge/server.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs new file mode 100644 index 000000000000..cc9afd84b1ce --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs @@ -0,0 +1,351 @@ +//! lib-proc-macro server-side traits +//! +//! Copy from +//! augmented with removing unstable features + +use super::*; + +// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. +use super::client::HandleStore; + +/// Declare an associated item of one of the traits below, optionally +/// adjusting it (i.e., adding bounds to types and default bodies to methods). +macro_rules! associated_item { + (type FreeFunctions) => + (type FreeFunctions: 'static;); + (type TokenStream) => + (type TokenStream: 'static + Clone;); + (type TokenStreamBuilder) => + (type TokenStreamBuilder: 'static;); + (type TokenStreamIter) => + (type TokenStreamIter: 'static + Clone;); + (type Group) => + (type Group: 'static + Clone;); + (type Punct) => + (type Punct: 'static + Copy + Eq + Hash;); + (type Ident) => + (type Ident: 'static + Copy + Eq + Hash;); + (type Literal) => + (type Literal: 'static + Clone;); + (type SourceFile) => + (type SourceFile: 'static + Clone;); + (type MultiSpan) => + (type MultiSpan: 'static;); + (type Diagnostic) => + (type Diagnostic: 'static;); + (type Span) => + (type Span: 'static + Copy + Eq + Hash;); + (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 Types { + $(associated_item!(type $name);)* + } + + $(pub trait $name: Types { + $(associated_item!(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, b: Buffer) -> Buffer; + } + + impl DispatcherTrait for Dispatcher> { + $(type $name = as Types>::$name;)* + fn dispatch(&mut self, mut b: Buffer) -> Buffer { + let Dispatcher { handle_store, server } = self; + + let mut reader = &b[..]; + 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) + }; + + b.clear(); + r.encode(&mut b, handle_store); + })* + }),* + } + b + } + } + } +} +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<'_>, D) -> Buffer, + client_data: D, + 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<'_>, D) -> Buffer, + client_data: D, + force_show_panics: bool, + ) -> Buffer { + let mut dispatch = |b| dispatcher.dispatch(b); + + run_client( + Bridge { cached_buffer: input, dispatch: (&mut dispatch).into(), force_show_panics }, + client_data, + ) + } +} + +// 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<'_>, D) -> Buffer, + client_data: D, + 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 = |b| { + req_tx.send(b).unwrap(); + res_rx.recv().unwrap() + }; + + run_client( + Bridge { + cached_buffer: input, + dispatch: (&mut dispatch).into(), + force_show_panics, + }, + client_data, + ) + }); + + 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<'_>, D) -> Buffer, + client_data: D, + 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, + }, + client_data, + ); + + // 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>>, + D: Copy + Send + 'static, +>( + strategy: &impl ExecutionStrategy, + handle_counters: &'static client::HandleCounters, + server: S, + input: I, + run_client: extern "C" fn(Bridge<'_>, D) -> Buffer, + client_data: D, + force_show_panics: bool, +) -> Result { + let mut dispatcher = + Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) }; + + let mut b = Buffer::new(); + input.encode(&mut b, &mut dispatcher.handle_store); + + b = strategy.run_bridge_and_client( + &mut dispatcher, + b, + run_client, + client_data, + force_show_panics, + ); + + Result::decode(&mut &b[..], &mut dispatcher.handle_store) +} + +impl client::Client crate::TokenStream> { + pub fn run( + &self, + strategy: &impl ExecutionStrategy, + server: S, + input: S::TokenStream, + force_show_panics: bool, + ) -> Result { + let client::Client { get_handle_counters, run, f } = *self; + run_server( + strategy, + get_handle_counters(), + server, + as Types>::TokenStream::mark(input), + run, + f, + force_show_panics, + ) + .map( as Types>::TokenStream::unmark) + } +} + +impl client::Client crate::TokenStream> { + pub fn run( + &self, + strategy: &impl ExecutionStrategy, + server: S, + input: S::TokenStream, + input2: S::TokenStream, + force_show_panics: bool, + ) -> Result { + let client::Client { get_handle_counters, run, f } = *self; + run_server( + strategy, + get_handle_counters(), + server, + ( + as Types>::TokenStream::mark(input), + as Types>::TokenStream::mark(input2), + ), + run, + f, + force_show_panics, + ) + .map( as Types>::TokenStream::unmark) + } +} diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs b/crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs new file mode 100644 index 000000000000..6953b1ecf4cb --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs @@ -0,0 +1,167 @@ +//! 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) => { + /// Adds a new child diagnostic message to `self` with the level + /// identified by this method's name with 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 + } + + /// Adds a new child diagnostic message to `self` with the level + /// identified by this method's name with 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/proc_macro_nightly/mod.rs b/crates/proc_macro_srv/src/proc_macro_nightly/mod.rs new file mode 100644 index 000000000000..5814267cfc57 --- /dev/null +++ b/crates/proc_macro_srv/src/proc_macro_nightly/mod.rs @@ -0,0 +1,964 @@ +//! lib-proc-macro main module +//! +//! Copy from +//! augmented with removing unstable features + +// NOTE(@edwin0cheng): +// Because we just copy the bridge module from rustc for ABI compatible +// There are some unused stuffs inside it. +// We suppress these warning here. +#[doc(hidden)] +#[allow(unused_macros)] +#[allow(unused_variables)] +pub mod bridge; + +mod diagnostic; + +pub use diagnostic::{Diagnostic, Level, MultiSpan}; + +use std::ops::{Bound, RangeBounds}; +use std::path::PathBuf; +use std::str::FromStr; +use std::{fmt, iter, mem}; + +/// 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(bridge::client::TokenStream); + +/// Error returned from `TokenStream::from_str` +#[derive(Debug)] +pub struct LexError { + _inner: (), +} +impl LexError { + fn new() -> Self { + LexError { _inner: () } + } +} + +impl TokenStream { + /// Returns an empty `TokenStream` containing no token trees. + pub fn new() -> TokenStream { + TokenStream(bridge::client::TokenStream::new()) + } + + /// Checks if this `TokenStream` is empty. + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +/// 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(bridge::client::TokenStream::from_str(src))) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for TokenStream { +// fn to_string(&self) -> String { +// self.0.to_string() +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +/// 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() + } +} + +/// Creates a token stream containing a single token tree. +impl From for TokenStream { + fn from(tree: TokenTree) -> TokenStream { + TokenStream(bridge::client::TokenStream::from_token_tree(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), + })) + } +} + +/// Collects a number of token trees into a single stream. +impl iter::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 iter::FromIterator for TokenStream { + fn from_iter>(streams: I) -> Self { + let mut builder = bridge::client::TokenStreamBuilder::new(); + streams.into_iter().for_each(|stream| builder.push(stream.0)); + TokenStream(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(bridge::client::TokenStreamIter); + + 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.into_iter()) + } + } +} + +/// 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() + } + + /// Gets the ending line/column in the source file for this span. + pub fn end(&self) -> LineColumn { + self.0.end() + } + + /// 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() + } + + 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 0-indexed column (in UTF-8 characters) in the source file on which + /// the span starts or ends (inclusive). + pub column: usize, +} + +/// 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 may 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 may not actually be valid. + /// + /// [`is_real`]: #method.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) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for TokenTree { +// fn to_string(&self) -> String { +// match *self { +// TokenTree::Group(ref t) => t.to_string(), +// TokenTree::Ident(ref t) => t.to_string(), +// TokenTree::Punct(ref t) => t.to_string(), +// TokenTree::Literal(ref t) => t.to_string(), +// } +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +/// 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 implicit 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`. + /// Implicit delimiters may 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(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); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for Group { +// fn to_string(&self) -> String { +// TokenStream::from(TokenTree::from(self.clone())).to_string() +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +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() + } +} + +/// An `Punct` is an single punctuation character like `+`, `-` 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); + +/// Whether an `Punct` is followed immediately by another `Punct` or +/// followed by another token or whitespace. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Spacing { + /// e.g., `+` is `Alone` in `+ =`, `+ident` or `+()`. + Alone, + /// e.g., `+` is `Joint` in `+=` or `'#`. + /// 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); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for Punct { +// fn to_string(&self) -> String { +// TokenStream::from(TokenTree::from(self.clone())).to_string() +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +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() + } +} + +/// An identifier (`ident`). +#[derive(Clone, PartialEq, Eq, Hash)] +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, 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`). + 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 `as_str`. + 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); + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for Ident { +// fn to_string(&self) -> String { +// TokenStream::from(TokenTree::from(self.clone())).to_string() +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +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 may 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 may 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 may 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); + } + Literal(bridge::client::Literal::float(&n.to_string())) + } + + /// 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 may 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 may 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); + } + Literal(bridge::client::Literal::float(&n.to_string())) + } + + /// 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 may 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 { + // HACK(eddyb) something akin to `Option::cloned`, but for `Bound<&T>`. + fn cloned_bound(bound: Bound<&T>) -> Bound { + match bound { + Bound::Included(x) => Bound::Included(x.clone()), + Bound::Excluded(x) => Bound::Excluded(x.clone()), + Bound::Unbounded => Bound::Unbounded, + } + } + + self.0.subspan(cloned_bound(range.start_bound()), cloned_bound(range.end_bound())).map(Span) + } +} + +// N.B., the bridge only provides `to_string`, implement `fmt::Display` +// based on it (the reverse of the usual relationship between the two). +// impl ToString for Literal { +// fn to_string(&self) -> String { +// TokenStream::from(TokenTree::from(self.clone())).to_string() +// } +// } + +/// 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 { + f.write_str(&self.to_string()) + } +} + +impl fmt::Debug for Literal { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + // FIXME(eddyb) `Literal` should not expose internal `Debug` impls. + self.0.fmt(f) + } +} + +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::new()), + } + } +} + +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 { + use std::ops::Deref; + + let key: &str = key.as_ref(); + let value = env::var(key); + super::bridge::client::FreeFunctions::track_env_var( + key, + value.as_ref().map(|t| t.deref()).ok(), + ); + value + } +} diff --git a/crates/proc_macro_srv/src/rustc_server_nightly.rs b/crates/proc_macro_srv/src/rustc_server_nightly.rs new file mode 100644 index 000000000000..2f7a4a31c39f --- /dev/null +++ b/crates/proc_macro_srv/src/rustc_server_nightly.rs @@ -0,0 +1,836 @@ +//! 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 crate::proc_macro_nightly::bridge::{self, server}; + +use std::collections::HashMap; +use std::hash::Hash; +use std::iter::FromIterator; +use std::ops::Bound; +use std::{ascii, vec::IntoIter}; + +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, Clone)] +pub struct TokenStream { + pub token_trees: Vec, +} + +impl TokenStream { + pub fn new() -> Self { + TokenStream { token_trees: Default::default() } + } + + pub fn with_subtree(subtree: tt::Subtree) -> Self { + if subtree.delimiter.is_some() { + TokenStream { token_trees: vec![TokenTree::Subtree(subtree)] } + } else { + TokenStream { token_trees: subtree.token_trees } + } + } + + pub fn into_subtree(self) -> tt::Subtree { + tt::Subtree { delimiter: None, 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.is_none() => { + self.token_trees.extend(subtree.token_trees); + } + _ => { + self.token_trees.push(tkn); + } + } + } + } + } +} + +type Level = crate::proc_macro_nightly::Level; +type LineColumn = crate::proc_macro_nightly::LineColumn; +type SourceFile = crate::proc_macro_nightly::SourceFile; + +/// 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::{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 { + return tokentrees_to_text(&self.token_trees[..]); + + fn tokentrees_to_text(tkns: &[tt::TokenTree]) -> String { + tkns.iter() + .fold((String::new(), true), |(last, last_to_joint), tkn| { + let s = [last, tokentree_to_text(tkn)].join(if last_to_joint { + "" + } else { + " " + }); + let mut is_joint = false; + if let tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) = tkn { + if punct.spacing == tt::Spacing::Joint { + is_joint = true; + } + } + (s, is_joint) + }) + .0 + } + + fn tokentree_to_text(tkn: &tt::TokenTree) -> String { + match tkn { + tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => ident.text.clone().into(), + tt::TokenTree::Leaf(tt::Leaf::Literal(literal)) => literal.text.clone().into(), + tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => format!("{}", punct.char), + tt::TokenTree::Subtree(subtree) => { + let content = tokentrees_to_text(&subtree.token_trees); + let (open, close) = match subtree.delimiter.map(|it| it.kind) { + None => ("", ""), + Some(tt::DelimiterKind::Brace) => ("{", "}"), + Some(tt::DelimiterKind::Parenthesis) => ("(", ")"), + Some(tt::DelimiterKind::Bracket) => ("[", "]"), + }; + format!("{}{}{}", open, content, close) + } + } + } + } + } + + fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree { + tt::Subtree { + delimiter: subtree + .delimiter + .map(|d| tt::Delimiter { id: tt::TokenId::unspecified(), ..d }), + 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 { id: tt::TokenId::unspecified(), ..lit }) + } + tt::Leaf::Punct(punct) => { + tt::Leaf::Punct(tt::Punct { id: tt::TokenId::unspecified(), ..punct }) + } + tt::Leaf::Ident(ident) => { + tt::Leaf::Ident(tt::Ident { id: 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 Rustc { + ident_interner: IdentInterner, + // FIXME: store span information here. +} + +impl server::Types for Rustc { + type FreeFunctions = FreeFunctions; + type TokenStream = TokenStream; + type TokenStreamBuilder = TokenStreamBuilder; + type TokenStreamIter = TokenStreamIter; + 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 Rustc { + fn track_env_var(&mut self, _var: &str, _value: Option<&str>) { + // FIXME: track env var accesses + // https://github.com/rust-lang/rust/pull/71858 + } +} + +impl server::TokenStream for Rustc { + fn new(&mut self) -> Self::TokenStream { + Self::TokenStream::new() + } + + 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 into_iter(&mut self, stream: Self::TokenStream) -> Self::TokenStreamIter { + let trees: Vec = stream.into_iter().collect(); + TokenStreamIter { trees: trees.into_iter() } + } +} + +impl server::TokenStreamBuilder for Rustc { + fn new(&mut self) -> Self::TokenStreamBuilder { + Self::TokenStreamBuilder::new() + } + fn push(&mut self, builder: &mut Self::TokenStreamBuilder, stream: Self::TokenStream) { + builder.push(stream) + } + fn build(&mut self, builder: Self::TokenStreamBuilder) -> Self::TokenStream { + builder.build() + } +} + +impl server::TokenStreamIter for Rustc { + fn next( + &mut self, + iter: &mut Self::TokenStreamIter, + ) -> Option> { + iter.trees.next().map(|tree| match tree { + TokenTree::Subtree(group) => bridge::TokenTree::Group(group), + TokenTree::Leaf(tt::Leaf::Ident(ident)) => { + bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident)))) + } + TokenTree::Leaf(tt::Leaf::Literal(literal)) => bridge::TokenTree::Literal(literal), + TokenTree::Leaf(tt::Leaf::Punct(punct)) => bridge::TokenTree::Punct(punct), + }) + } +} + +fn delim_to_internal(d: bridge::Delimiter) -> Option { + 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 => return None, + }; + Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind }) +} + +fn delim_to_external(d: Option) -> bridge::Delimiter { + match d.map(|it| it.kind) { + Some(tt::DelimiterKind::Parenthesis) => bridge::Delimiter::Parenthesis, + Some(tt::DelimiterKind::Brace) => bridge::Delimiter::Brace, + Some(tt::DelimiterKind::Bracket) => bridge::Delimiter::Bracket, + None => 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 Rustc { + fn new(&mut self, delimiter: bridge::Delimiter, stream: Self::TokenStream) -> Self::Group { + Self::Group { delimiter: delim_to_internal(delimiter), token_trees: stream.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.map(|it| it.id).unwrap_or_else(tt::TokenId::unspecified) + } + + fn set_span(&mut self, _group: &mut Self::Group, _span: Self::Span) { + // FIXME handle span + } + + fn span_open(&mut self, _group: &Self::Group) -> Self::Span { + // FIXME handle span + // MySpan(self.span_interner.intern(&MySpanData(group.span_open()))) + tt::TokenId::unspecified() + } + + fn span_close(&mut self, _group: &Self::Group) -> Self::Span { + // FIXME handle span + tt::TokenId::unspecified() + } +} + +impl server::Punct for Rustc { + fn new(&mut self, ch: char, spacing: bridge::Spacing) -> Self::Punct { + tt::Punct { + char: ch, + spacing: spacing_to_internal(spacing), + id: 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 { + // FIXME handle span + tt::TokenId::unspecified() + } + fn with_span(&mut self, punct: Self::Punct, _span: Self::Span) -> Self::Punct { + // FIXME handle span + punct + } +} + +impl server::Ident for Rustc { + fn new(&mut self, string: &str, _span: Self::Span, _is_raw: bool) -> Self::Ident { + IdentId( + self.ident_interner.intern(&IdentData(tt::Ident { + text: string.into(), + id: tt::TokenId::unspecified(), + })), + ) + } + + fn span(&mut self, _ident: Self::Ident) -> Self::Span { + // FIXME handle span + tt::TokenId::unspecified() + } + fn with_span(&mut self, ident: Self::Ident, _span: Self::Span) -> Self::Ident { + // FIXME handle span + ident + } +} + +impl server::Literal for Rustc { + 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 { + unimplemented!() + } + fn symbol(&mut self, literal: &Self::Literal) -> String { + literal.text.to_string() + } + fn suffix(&mut self, _literal: &Self::Literal) -> Option { + None + } + + fn integer(&mut self, n: &str) -> Self::Literal { + let n = if let Ok(n) = n.parse::() { + n.to_string() + } else { + n.parse::().unwrap().to_string() + }; + Literal { text: n.into(), id: 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(), id: 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(), id: tt::TokenId::unspecified() } + } + + fn f32(&mut self, n: &str) -> Self::Literal { + let n: f32 = n.parse().unwrap(); + let text = format!("{}f32", n); + Literal { text: text.into(), id: tt::TokenId::unspecified() } + } + + fn f64(&mut self, n: &str) -> Self::Literal { + let n: f64 = n.parse().unwrap(); + let text = format!("{}f64", n); + Literal { text: text.into(), id: 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(), id: tt::TokenId::unspecified() } + } + + fn character(&mut self, ch: char) -> Self::Literal { + Literal { text: format!("'{}'", ch).into(), id: 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(), id: tt::TokenId::unspecified() } + } + + fn span(&mut self, literal: &Self::Literal) -> Self::Span { + literal.id + } + + fn set_span(&mut self, _literal: &mut Self::Literal, _span: Self::Span) { + // FIXME handle span + } + + fn subspan( + &mut self, + _literal: &Self::Literal, + _start: Bound, + _end: Bound, + ) -> Option { + // FIXME handle span + None + } +} + +impl server::SourceFile for Rustc { + fn eq(&mut self, file1: &Self::SourceFile, file2: &Self::SourceFile) -> bool { + file1.eq(file2) + } + fn path(&mut self, file: &Self::SourceFile) -> String { + String::from( + file.path().to_str().expect("non-UTF8 file path in `proc_macro::SourceFile::path`"), + ) + } + fn is_real(&mut self, file: &Self::SourceFile) -> bool { + file.is_real() + } +} + +impl server::Diagnostic for Rustc { + 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 Rustc { + 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 { + // let MySpanData(span) = self.span_interner.get(span.0); + unimplemented!() + } + + /// 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 { + None + } + 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() + } +} + +impl server::MultiSpan for Rustc { + 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::*; + use crate::proc_macro_nightly::bridge::server::Literal; + + #[test] + fn test_rustc_server_literals() { + let mut srv = Rustc { 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_rustc_server_to_string() { + let s = TokenStream { + token_trees: vec![ + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "struct".into(), + id: tt::TokenId::unspecified(), + })), + tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "T".into(), + id: tt::TokenId::unspecified(), + })), + tt::TokenTree::Subtree(tt::Subtree { + delimiter: Some(tt::Delimiter { + id: tt::TokenId::unspecified(), + kind: tt::DelimiterKind::Brace, + }), + token_trees: vec![], + }), + ], + }; + + assert_eq!(s.to_string(), "struct T {}"); + } + + #[test] + fn test_rustc_server_from_str() { + use std::str::FromStr; + let subtree_paren_a = tt::TokenTree::Subtree(tt::Subtree { + delimiter: Some(tt::Delimiter { + id: tt::TokenId::unspecified(), + kind: tt::DelimiterKind::Parenthesis, + }), + token_trees: vec![tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { + text: "a".into(), + id: 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(), + id: tt::TokenId::unspecified(), + })) + ); + } +} From 39b8d10b93155e80c5757e644706bf27455cf96a Mon Sep 17 00:00:00 2001 From: Alex Good Date: Sat, 10 Jul 2021 15:12:41 +0100 Subject: [PATCH 2/4] Use rustc version of dylib to choose which proc macro ABI to use --- crates/proc_macro_srv/src/dylib.rs | 353 ++++++++++++++---- crates/proc_macro_srv/src/lib.rs | 7 + .../src/proc_macro_nightly/bridge/buffer.rs | 4 +- 3 files changed, 290 insertions(+), 74 deletions(-) diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index 5133e7c50b20..c5ac2c402cb9 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs @@ -1,6 +1,8 @@ //! Handles dynamic library loading for proc macro use std::{ + convert::{TryFrom, TryInto}, + fmt, fs::File, io, path::{Path, PathBuf}, @@ -9,9 +11,16 @@ use std::{ use libloading::Library; use memmap2::Mmap; use object::Object; -use proc_macro_api::ProcMacroKind; +use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo}; -use crate::{proc_macro::bridge, rustc_server::TokenStream}; +use crate::{ + proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro}, + rustc_server::TokenStream as StableTokenStream, +}; +use crate::{ + proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro}, + rustc_server_nightly::TokenStream as NightlyTokenStream, +}; const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; @@ -74,26 +83,117 @@ fn load_library(file: &Path) -> Result { unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } } -struct ProcMacroLibraryLibloading { - // Hold the dylib to prevent it from unloading - _lib: Library, - exported_macros: Vec, +enum ProcMacroABI { + Stable, + Nightly, +} + +impl TryFrom for ProcMacroABI { + type Error = LoadProcMacroDylibError; + + fn try_from(info: RustCInfo) -> Result { + if info.version.0 < 1 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 47 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 54 { + Ok(ProcMacroABI::Stable) + } else { + Ok(ProcMacroABI::Nightly) + } + } +} + +#[derive(Debug)] +pub enum LoadProcMacroDylibError { + Io(io::Error), + UnsupportedABI, +} + +impl fmt::Display for LoadProcMacroDylibError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Io(e) => e.fmt(f), + Self::UnsupportedABI => write!(f, "unsupported ABI version"), + } + } +} + +impl From for LoadProcMacroDylibError { + fn from(e: io::Error) -> Self { + LoadProcMacroDylibError::Io(e) + } +} + +enum ProcMacroLibraryLibloading { + StableProcMacroLibrary { + _lib: Library, + exported_macros: Vec, + }, + NightlyProcMacroLibrary { + _lib: Library, + exported_macros: Vec, + }, } impl ProcMacroLibraryLibloading { - fn open(file: &Path) -> io::Result { + fn open(file: &Path) -> Result { let symbol_name = find_registrar_symbol(file)?.ok_or_else(|| { invalid_data_err(format!("Cannot find registrar symbol in file {}", file.display())) })?; + let version_info = read_dylib_info(file)?; + let macro_abi: ProcMacroABI = version_info.try_into()?; + let lib = load_library(file).map_err(invalid_data_err)?; - let exported_macros = { - let macros: libloading::Symbol<&&[bridge::client::ProcMacro]> = - unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; - macros.to_vec() - }; + match macro_abi { + ProcMacroABI::Stable => { + let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> = + unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; + let macros_vec = macros.to_vec(); + Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary { + _lib: lib, + exported_macros: macros_vec, + }) + } + ProcMacroABI::Nightly => { + let macros: libloading::Symbol< + &&[crate::proc_macro_nightly::bridge::client::ProcMacro], + > = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; + let macros_vec = macros.to_vec(); + Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary { + _lib: lib, + exported_macros: macros_vec, + }) + } + } + } +} + +#[derive(Debug)] +pub enum PanicMessage { + Stable(stable_bridge::PanicMessage), + Nightly(nightly_bridge::PanicMessage), +} + +impl From for PanicMessage { + fn from(p: stable_bridge::PanicMessage) -> Self { + PanicMessage::Stable(p) + } +} + +impl From for PanicMessage { + fn from(p: nightly_bridge::PanicMessage) -> Self { + PanicMessage::Nightly(p) + } +} - Ok(ProcMacroLibraryLibloading { _lib: lib, exported_macros }) +impl PanicMessage { + pub fn as_str(&self) -> Option<&str> { + match self { + Self::Stable(p) => p.as_str(), + Self::Nightly(p) => p.as_str(), + } } } @@ -102,7 +202,7 @@ pub struct Expander { } impl Expander { - pub fn new(lib: &Path) -> io::Result { + pub fn new(lib: &Path) -> Result { // Some libraries for dynamic loading require canonicalized path even when it is // already absolute let lib = lib.canonicalize()?; @@ -119,69 +219,28 @@ impl Expander { 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(crate::rustc_server::TokenStream::new(), |attr| { - TokenStream::with_subtree(attr.clone()) - }); - - for proc_macro in &self.inner.exported_macros { - match proc_macro { - bridge::client::ProcMacro::CustomDerive { trait_name, client, .. } - if *trait_name == macro_name => - { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_attributes, - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - _ => continue, + ) -> Result { + match &self.inner { + ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { + expand_stable(macro_name, macro_body, attributes, &exported_macros[..]) + .map_err(PanicMessage::from) + } + ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { + expand_nightly(macro_name, macro_body, attributes, &exported_macros[..]) + .map_err(PanicMessage::from) } } - - Err(bridge::PanicMessage::String("Nothing to expand".to_string())) } pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { - self.inner - .exported_macros - .iter() - .map(|proc_macro| match proc_macro { - bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() + match &self.inner { + ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { + list_macros_stable(&exported_macros[..]) + } + ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { + list_macros_nightly(&exported_macros[..]) + } + } } } @@ -217,3 +276,153 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { Ok(path.to_path_buf()) } + +fn expand_nightly( + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + macros: &[NightlyProcMacro], +) -> Result { + let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes + .map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| { + NightlyTokenStream::with_subtree(attr.clone()) + }); + + for proc_macro in macros { + match proc_macro { + crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { + trait_name, + client, + .. + } if *trait_name == macro_name => { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro_nightly::bridge::server::SameThread, + crate::rustc_server_nightly::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + _ => continue, + } + } + + Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string())) +} + +fn expand_stable( + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + macros: &[StableProcMacro], +) -> Result { + let parsed_body = StableTokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| { + StableTokenStream::with_subtree(attr.clone()) + }); + + for proc_macro in macros { + match proc_macro { + crate::proc_macro::bridge::client::ProcMacro::CustomDerive { + trait_name, + client, + .. + } if *trait_name == macro_name => { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro::bridge::client::ProcMacro::Bang { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + crate::proc_macro::bridge::client::ProcMacro::Attr { name, client } + if *name == macro_name => + { + let res = client.run( + &crate::proc_macro::bridge::server::SameThread, + crate::rustc_server::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + return res.map(|it| it.into_subtree()); + } + _ => continue, + } + } + + Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string())) +} + +pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> { + macros + .iter() + .map(|proc_macro| match proc_macro { + crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { + (trait_name.to_string(), ProcMacroKind::CustomDerive) + } + crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => { + (name.to_string(), ProcMacroKind::FuncLike) + } + crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => { + (name.to_string(), ProcMacroKind::Attr) + } + }) + .collect() +} + +pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> { + macros + .iter() + .map(|proc_macro| match proc_macro { + crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { + trait_name, + .. + } => (trait_name.to_string(), ProcMacroKind::CustomDerive), + crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => { + (name.to_string(), ProcMacroKind::FuncLike) + } + crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => { + (name.to_string(), ProcMacroKind::Attr) + } + }) + .collect() +} diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs index f54cbcd61532..deb1d1e526eb 100644 --- a/crates/proc_macro_srv/src/lib.rs +++ b/crates/proc_macro_srv/src/lib.rs @@ -15,9 +15,16 @@ #[doc(hidden)] mod proc_macro; +#[allow(dead_code)] +#[doc(hidden)] +mod proc_macro_nightly; + #[doc(hidden)] mod rustc_server; +#[doc(hidden)] +mod rustc_server_nightly; + mod dylib; use proc_macro::bridge::client::TokenStream; diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs index a25feac20120..1565db187c6d 100644 --- a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs +++ b/crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs @@ -91,7 +91,7 @@ impl Buffer { self.len += xs.len(); } } - + pub(super) fn push(&mut self, v: T) { // 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 @@ -145,7 +145,7 @@ impl From> for Buffer { } } - extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { + extern "C" fn reserve(b: Buffer, additional: usize) -> Buffer { let mut v = to_vec(b); v.reserve(additional); Buffer::from(v) From 14a51d28b530331151e636986e57912da7e406ec Mon Sep 17 00:00:00 2001 From: alexjg Date: Sat, 10 Jul 2021 17:07:12 +0100 Subject: [PATCH 3/4] check rustc major version == 1 not < 1 Co-authored-by: bjorn3 --- crates/proc_macro_srv/src/dylib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index c5ac2c402cb9..69a645119f4e 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs @@ -92,7 +92,7 @@ impl TryFrom for ProcMacroABI { type Error = LoadProcMacroDylibError; fn try_from(info: RustCInfo) -> Result { - if info.version.0 < 1 { + if info.version.0 != 1 { Err(LoadProcMacroDylibError::UnsupportedABI) } else if info.version.1 < 47 { Err(LoadProcMacroDylibError::UnsupportedABI) From e240eb67a86bb4deff2762e3c46f47278ccd975c Mon Sep 17 00:00:00 2001 From: Alex Good Date: Mon, 12 Jul 2021 15:47:47 +0100 Subject: [PATCH 4/4] Introduce proc_macro_srv::abis, impl 1.47 and 1.55 Rather than a "Stable" and "Nightly" ABI we instead name ABIs based on the version of the rust compiler in which they were introduced. We place these ABIs in a new module - `proc_macro_srv::abis` - where we also add some mchinery to abstract over ABIs. This should make it easy to add new ABIs at a later date as the rust compiler evolves. --- .../proc_macro_srv/src/abis/abi_1_47/mod.rs | 106 +++++++ .../abi_1_47}/proc_macro/bridge/buffer.rs | 0 .../abi_1_47}/proc_macro/bridge/client.rs | 29 +- .../abi_1_47}/proc_macro/bridge/closure.rs | 0 .../abi_1_47}/proc_macro/bridge/handle.rs | 0 .../abi_1_47}/proc_macro/bridge/mod.rs | 2 +- .../abi_1_47}/proc_macro/bridge/rpc.rs | 0 .../proc_macro/bridge/scoped_cell.rs | 0 .../abi_1_47}/proc_macro/bridge/server.rs | 5 +- .../abi_1_47/proc_macro}/diagnostic.rs | 0 .../src/{ => abis/abi_1_47}/proc_macro/mod.rs | 2 +- .../src/{ => abis/abi_1_47}/rustc_server.rs | 10 +- .../proc_macro_srv/src/abis/abi_1_55/mod.rs | 104 +++++++ .../abi_1_55/proc_macro}/bridge/buffer.rs | 0 .../abi_1_55/proc_macro}/bridge/client.rs | 29 +- .../abi_1_55/proc_macro}/bridge/closure.rs | 0 .../abi_1_55/proc_macro}/bridge/handle.rs | 0 .../abi_1_55/proc_macro}/bridge/mod.rs | 0 .../abi_1_55/proc_macro}/bridge/rpc.rs | 0 .../proc_macro}/bridge/scoped_cell.rs | 0 .../abi_1_55/proc_macro}/bridge/server.rs | 5 +- .../abi_1_55}/proc_macro/diagnostic.rs | 8 +- .../abi_1_55/proc_macro}/mod.rs | 0 .../abi_1_55/rustc_server.rs} | 10 +- crates/proc_macro_srv/src/abis/mod.rs | 97 ++++++ crates/proc_macro_srv/src/dylib.rs | 278 ++---------------- crates/proc_macro_srv/src/lib.rs | 22 +- crates/proc_macro_srv/src/tests/utils.rs | 10 +- 28 files changed, 390 insertions(+), 327 deletions(-) create mode 100644 crates/proc_macro_srv/src/abis/abi_1_47/mod.rs rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/buffer.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/client.rs (93%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/closure.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/handle.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/mod.rs (99%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/rpc.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/scoped_cell.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/bridge/server.rs (98%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_47/proc_macro}/diagnostic.rs (100%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/proc_macro/mod.rs (99%) rename crates/proc_macro_srv/src/{ => abis/abi_1_47}/rustc_server.rs (99%) create mode 100644 crates/proc_macro_srv/src/abis/abi_1_55/mod.rs rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/buffer.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/client.rs (93%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/closure.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/handle.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/mod.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/rpc.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/scoped_cell.rs (100%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/bridge/server.rs (98%) rename crates/proc_macro_srv/src/{ => abis/abi_1_55}/proc_macro/diagnostic.rs (94%) rename crates/proc_macro_srv/src/{proc_macro_nightly => abis/abi_1_55/proc_macro}/mod.rs (100%) rename crates/proc_macro_srv/src/{rustc_server_nightly.rs => abis/abi_1_55/rustc_server.rs} (99%) create mode 100644 crates/proc_macro_srv/src/abis/mod.rs diff --git a/crates/proc_macro_srv/src/abis/abi_1_47/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_47/mod.rs new file mode 100644 index 000000000000..6bbdcc586865 --- /dev/null +++ b/crates/proc_macro_srv/src/abis/abi_1_47/mod.rs @@ -0,0 +1,106 @@ +//! Macro ABI for version 1.47 of rustc + +#[allow(dead_code)] +#[doc(hidden)] +mod proc_macro; + +#[allow(dead_code)] +#[doc(hidden)] +mod rustc_server; +use libloading::Library; + +use proc_macro_api::ProcMacroKind; + +use super::PanicMessage; + +pub use rustc_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 = rustc_server::TokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes.map_or(rustc_server::TokenStream::new(), |attr| { + rustc_server::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, + rustc_server::Rustc::default(), + parsed_body, + false, + ); + 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, + rustc_server::Rustc::default(), + parsed_body, + false, + ); + 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, + rustc_server::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + 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/proc_macro/bridge/buffer.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/buffer.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro/bridge/buffer.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/buffer.rs diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/client.rs similarity index 93% rename from crates/proc_macro_srv/src/proc_macro/bridge/client.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/client.rs index c135cf7a2371..9f74fb06689f 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/client.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/client.rs @@ -3,6 +3,7 @@ //! Copy from //! augmented with removing unstable features +use super::super::TokenStream as CrateTokenStream; use super::*; macro_rules! define_handles { @@ -401,26 +402,26 @@ fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( b } -impl Client crate::TokenStream> { - pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { +impl Client CrateTokenStream> { + pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self { extern "C" fn run( bridge: Bridge<'_>, - f: impl FnOnce(crate::TokenStream) -> crate::TokenStream, + f: impl FnOnce(CrateTokenStream) -> CrateTokenStream, ) -> Buffer { - run_client(bridge, |input| f(crate::TokenStream(input)).0) + run_client(bridge, |input| f(CrateTokenStream(input)).0) } Client { get_handle_counters: HandleCounters::get, run, f } } } -impl Client crate::TokenStream> { - pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self { +impl Client CrateTokenStream> { + pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self { extern "C" fn run( bridge: Bridge<'_>, - f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream, ) -> Buffer { run_client(bridge, |(input, input2)| { - f(crate::TokenStream(input), crate::TokenStream(input2)).0 + f(CrateTokenStream(input), CrateTokenStream(input2)).0 }) } Client { get_handle_counters: HandleCounters::get, run, f } @@ -433,17 +434,17 @@ pub enum ProcMacro { CustomDerive { trait_name: &'static str, attributes: &'static [&'static str], - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, Attr { name: &'static str, - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, Bang { name: &'static str, - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, } @@ -465,19 +466,19 @@ impl ProcMacro { pub fn custom_derive( trait_name: &'static str, attributes: &'static [&'static str], - expand: fn(crate::TokenStream) -> crate::TokenStream, + expand: fn(CrateTokenStream) -> CrateTokenStream, ) -> Self { ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } } pub fn attr( name: &'static str, - expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream, ) -> Self { ProcMacro::Attr { name, client: Client::expand2(expand) } } - pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + pub fn bang(name: &'static str, expand: fn(CrateTokenStream) -> CrateTokenStream) -> Self { ProcMacro::Bang { name, client: Client::expand1(expand) } } } diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/closure.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/closure.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro/bridge/closure.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/closure.rs diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/handle.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/handle.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro/bridge/handle.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/handle.rs diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/mod.rs similarity index 99% rename from crates/proc_macro_srv/src/proc_macro/bridge/mod.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/mod.rs index 375396d1b441..be5402d6eed4 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/mod.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/mod.rs @@ -13,7 +13,7 @@ #![deny(unsafe_code)] -pub use crate::proc_macro::{Delimiter, Level, LineColumn, Spacing}; +pub use super::{Delimiter, Level, LineColumn, Spacing}; use std::fmt; use std::hash::Hash; use std::marker; diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/rpc.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro/bridge/rpc.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/rpc.rs diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/scoped_cell.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro/bridge/scoped_cell.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/scoped_cell.rs diff --git a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/server.rs similarity index 98% rename from crates/proc_macro_srv/src/proc_macro/bridge/server.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/server.rs index 731d302196f3..a580419fd13f 100644 --- a/crates/proc_macro_srv/src/proc_macro/bridge/server.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/bridge/server.rs @@ -3,6 +3,7 @@ //! Copy from //! augmented with removing unstable features +use super::super::TokenStream as ProcMacroTokenStream; use super::*; // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. @@ -308,7 +309,7 @@ fn run_server< Result::decode(&mut &b[..], &mut dispatcher.handle_store) } -impl client::Client crate::TokenStream> { +impl client::Client ProcMacroTokenStream> { pub fn run( &self, strategy: &impl ExecutionStrategy, @@ -330,7 +331,7 @@ impl client::Client crate::TokenStream> { } } -impl client::Client crate::TokenStream> { +impl client::Client ProcMacroTokenStream> { pub fn run( &self, strategy: &impl ExecutionStrategy, diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/diagnostic.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/diagnostic.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/diagnostic.rs diff --git a/crates/proc_macro_srv/src/proc_macro/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/mod.rs similarity index 99% rename from crates/proc_macro_srv/src/proc_macro/mod.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/mod.rs index b7c1d04b50e4..aaa710375671 100644 --- a/crates/proc_macro_srv/src/proc_macro/mod.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_47/proc_macro/mod.rs @@ -133,7 +133,7 @@ impl Extend for TokenStream { /// Public implementation details for the `TokenStream` type, such as iterators. pub mod token_stream { - use crate::proc_macro::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree}; + 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, diff --git a/crates/proc_macro_srv/src/rustc_server.rs b/crates/proc_macro_srv/src/abis/abi_1_47/rustc_server.rs similarity index 99% rename from crates/proc_macro_srv/src/rustc_server.rs rename to crates/proc_macro_srv/src/abis/abi_1_47/rustc_server.rs index e252e89a5697..088cc694e839 100644 --- a/crates/proc_macro_srv/src/rustc_server.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_47/rustc_server.rs @@ -8,7 +8,7 @@ //! //! FIXME: No span and source file information is implemented yet -use crate::proc_macro::bridge::{self, server}; +use super::proc_macro::bridge::{self, server}; use std::collections::HashMap; use std::hash::Hash; @@ -97,9 +97,9 @@ impl Extend for TokenStream { } } -type Level = crate::proc_macro::Level; -type LineColumn = crate::proc_macro::LineColumn; -type SourceFile = crate::proc_macro::SourceFile; +type Level = super::proc_macro::Level; +type LineColumn = super::proc_macro::LineColumn; +type SourceFile = super::proc_macro::SourceFile; /// A structure representing a diagnostic message and associated children /// messages. @@ -734,8 +734,8 @@ impl server::MultiSpan for Rustc { #[cfg(test)] mod tests { + use super::super::proc_macro::bridge::server::Literal; use super::*; - use crate::proc_macro::bridge::server::Literal; #[test] fn test_rustc_server_literals() { diff --git a/crates/proc_macro_srv/src/abis/abi_1_55/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_55/mod.rs new file mode 100644 index 000000000000..812601620756 --- /dev/null +++ b/crates/proc_macro_srv/src/abis/abi_1_55/mod.rs @@ -0,0 +1,104 @@ +//! Macro ABI for version 1.55 of rustc + +#[allow(dead_code)] +#[doc(hidden)] +mod proc_macro; + +#[allow(dead_code)] +#[doc(hidden)] +mod rustc_server; +use libloading::Library; + +use proc_macro_api::ProcMacroKind; + +use super::PanicMessage; + +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 = rustc_server::TokenStream::with_subtree(macro_body.clone()); + + let parsed_attributes = attributes.map_or(rustc_server::TokenStream::new(), |attr| { + rustc_server::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, + rustc_server::Rustc::default(), + parsed_body, + false, + ); + 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, + rustc_server::Rustc::default(), + parsed_body, + false, + ); + 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, + rustc_server::Rustc::default(), + parsed_attributes, + parsed_body, + false, + ); + 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/proc_macro_nightly/bridge/buffer.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/buffer.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/buffer.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/buffer.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/client.rs similarity index 93% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/client.rs index c135cf7a2371..9f74fb06689f 100644 --- a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/client.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/client.rs @@ -3,6 +3,7 @@ //! Copy from //! augmented with removing unstable features +use super::super::TokenStream as CrateTokenStream; use super::*; macro_rules! define_handles { @@ -401,26 +402,26 @@ fn run_client DecodeMut<'a, 's, ()>, R: Encode<()>>( b } -impl Client crate::TokenStream> { - pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { +impl Client CrateTokenStream> { + pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self { extern "C" fn run( bridge: Bridge<'_>, - f: impl FnOnce(crate::TokenStream) -> crate::TokenStream, + f: impl FnOnce(CrateTokenStream) -> CrateTokenStream, ) -> Buffer { - run_client(bridge, |input| f(crate::TokenStream(input)).0) + run_client(bridge, |input| f(CrateTokenStream(input)).0) } Client { get_handle_counters: HandleCounters::get, run, f } } } -impl Client crate::TokenStream> { - pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self { +impl Client CrateTokenStream> { + pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self { extern "C" fn run( bridge: Bridge<'_>, - f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream, ) -> Buffer { run_client(bridge, |(input, input2)| { - f(crate::TokenStream(input), crate::TokenStream(input2)).0 + f(CrateTokenStream(input), CrateTokenStream(input2)).0 }) } Client { get_handle_counters: HandleCounters::get, run, f } @@ -433,17 +434,17 @@ pub enum ProcMacro { CustomDerive { trait_name: &'static str, attributes: &'static [&'static str], - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, Attr { name: &'static str, - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, Bang { name: &'static str, - client: Client crate::TokenStream>, + client: Client CrateTokenStream>, }, } @@ -465,19 +466,19 @@ impl ProcMacro { pub fn custom_derive( trait_name: &'static str, attributes: &'static [&'static str], - expand: fn(crate::TokenStream) -> crate::TokenStream, + expand: fn(CrateTokenStream) -> CrateTokenStream, ) -> Self { ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } } pub fn attr( name: &'static str, - expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, + expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream, ) -> Self { ProcMacro::Attr { name, client: Client::expand2(expand) } } - pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self { + pub fn bang(name: &'static str, expand: fn(CrateTokenStream) -> CrateTokenStream) -> Self { ProcMacro::Bang { name, client: Client::expand1(expand) } } } diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/closure.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/closure.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/closure.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/handle.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/handle.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/handle.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/handle.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/mod.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/mod.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/mod.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/rpc.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/rpc.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/rpc.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/scoped_cell.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/scoped_cell.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/scoped_cell.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/scoped_cell.rs diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/server.rs similarity index 98% rename from crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/server.rs index cc9afd84b1ce..21563fe60498 100644 --- a/crates/proc_macro_srv/src/proc_macro_nightly/bridge/server.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/bridge/server.rs @@ -3,6 +3,7 @@ //! Copy from //! augmented with removing unstable features +use super::super::TokenStream as CrateTokenStream; use super::*; // FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. @@ -302,7 +303,7 @@ fn run_server< Result::decode(&mut &b[..], &mut dispatcher.handle_store) } -impl client::Client crate::TokenStream> { +impl client::Client CrateTokenStream> { pub fn run( &self, strategy: &impl ExecutionStrategy, @@ -324,7 +325,7 @@ impl client::Client crate::TokenStream> { } } -impl client::Client crate::TokenStream> { +impl client::Client CrateTokenStream> { pub fn run( &self, strategy: &impl ExecutionStrategy, diff --git a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/diagnostic.rs similarity index 94% rename from crates/proc_macro_srv/src/proc_macro/diagnostic.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/diagnostic.rs index 3c5b7bc01673..6953b1ecf4cb 100644 --- a/crates/proc_macro_srv/src/proc_macro/diagnostic.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/diagnostic.rs @@ -3,7 +3,7 @@ //! Copy from //! augmented with removing unstable features -use crate::proc_macro::Span; +use super::Span; /// An enum representing a diagnostic level. #[derive(Copy, Clone, Debug)] @@ -146,15 +146,15 @@ impl Diagnostic { /// Emit the diagnostic. pub fn emit(self) { - fn to_internal(spans: Vec) -> crate::proc_macro::bridge::client::MultiSpan { - let mut multi_span = crate::proc_macro::bridge::client::MultiSpan::new(); + 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 = crate::proc_macro::bridge::client::Diagnostic::new( + let mut diag = super::bridge::client::Diagnostic::new( self.level, &self.message[..], to_internal(self.spans), diff --git a/crates/proc_macro_srv/src/proc_macro_nightly/mod.rs b/crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/mod.rs similarity index 100% rename from crates/proc_macro_srv/src/proc_macro_nightly/mod.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/proc_macro/mod.rs diff --git a/crates/proc_macro_srv/src/rustc_server_nightly.rs b/crates/proc_macro_srv/src/abis/abi_1_55/rustc_server.rs similarity index 99% rename from crates/proc_macro_srv/src/rustc_server_nightly.rs rename to crates/proc_macro_srv/src/abis/abi_1_55/rustc_server.rs index 2f7a4a31c39f..8dfd65ade1c8 100644 --- a/crates/proc_macro_srv/src/rustc_server_nightly.rs +++ b/crates/proc_macro_srv/src/abis/abi_1_55/rustc_server.rs @@ -8,7 +8,7 @@ //! //! FIXME: No span and source file information is implemented yet -use crate::proc_macro_nightly::bridge::{self, server}; +use super::proc_macro::bridge::{self, server}; use std::collections::HashMap; use std::hash::Hash; @@ -97,9 +97,9 @@ impl Extend for TokenStream { } } -type Level = crate::proc_macro_nightly::Level; -type LineColumn = crate::proc_macro_nightly::LineColumn; -type SourceFile = crate::proc_macro_nightly::SourceFile; +type Level = super::proc_macro::Level; +type LineColumn = super::proc_macro::LineColumn; +type SourceFile = super::proc_macro::SourceFile; /// A structure representing a diagnostic message and associated children /// messages. @@ -737,8 +737,8 @@ impl server::MultiSpan for Rustc { #[cfg(test)] mod tests { + use super::super::proc_macro::bridge::server::Literal; use super::*; - use crate::proc_macro_nightly::bridge::server::Literal; #[test] fn test_rustc_server_literals() { diff --git a/crates/proc_macro_srv/src/abis/mod.rs b/crates/proc_macro_srv/src/abis/mod.rs new file mode 100644 index 000000000000..3b30aaa90bd2 --- /dev/null +++ b/crates/proc_macro_srv/src/abis/mod.rs @@ -0,0 +1,97 @@ +//! 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 +//! + +// pub(crate) so tests can use the TokenStream, more notes in test/utils.rs +pub(crate) mod abi_1_47; +mod abi_1_55; + +use super::dylib::LoadProcMacroDylibError; +pub(crate) use abi_1_47::Abi as Abi_1_47; +pub(crate) use abi_1_55::Abi as Abi_1_55; +use libloading::Library; +use proc_macro_api::{ProcMacroKind, RustCInfo}; + +pub struct PanicMessage { + message: Option, +} + +impl PanicMessage { + pub fn as_str(&self) -> Option { + self.message.clone() + } +} + +pub(crate) enum Abi { + Abi1_47(Abi_1_47), + Abi1_55(Abi_1_55), +} + +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 { + if info.version.0 != 1 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 47 { + Err(LoadProcMacroDylibError::UnsupportedABI) + } else if info.version.1 < 54 { + let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?; + Ok(Abi::Abi1_47(inner)) + } else { + let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?; + Ok(Abi::Abi1_55(inner)) + } + } + + pub fn expand( + &self, + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + ) -> Result { + match self { + Self::Abi1_55(abi) => abi.expand(macro_name, macro_body, attributes), + Self::Abi1_47(abi) => abi.expand(macro_name, macro_body, attributes), + } + } + + pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { + match self { + Self::Abi1_47(abi) => abi.list_macros(), + Self::Abi1_55(abi) => abi.list_macros(), + } + } +} diff --git a/crates/proc_macro_srv/src/dylib.rs b/crates/proc_macro_srv/src/dylib.rs index 69a645119f4e..5f0b0b061e19 100644 --- a/crates/proc_macro_srv/src/dylib.rs +++ b/crates/proc_macro_srv/src/dylib.rs @@ -1,7 +1,6 @@ //! Handles dynamic library loading for proc macro use std::{ - convert::{TryFrom, TryInto}, fmt, fs::File, io, @@ -11,16 +10,9 @@ use std::{ use libloading::Library; use memmap2::Mmap; use object::Object; -use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo}; +use proc_macro_api::{read_dylib_info, ProcMacroKind}; -use crate::{ - proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro}, - rustc_server::TokenStream as StableTokenStream, -}; -use crate::{ - proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro}, - rustc_server_nightly::TokenStream as NightlyTokenStream, -}; +use super::abis::Abi; const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; @@ -83,30 +75,10 @@ fn load_library(file: &Path) -> Result { unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) } } -enum ProcMacroABI { - Stable, - Nightly, -} - -impl TryFrom for ProcMacroABI { - type Error = LoadProcMacroDylibError; - - fn try_from(info: RustCInfo) -> Result { - if info.version.0 != 1 { - Err(LoadProcMacroDylibError::UnsupportedABI) - } else if info.version.1 < 47 { - Err(LoadProcMacroDylibError::UnsupportedABI) - } else if info.version.1 < 54 { - Ok(ProcMacroABI::Stable) - } else { - Ok(ProcMacroABI::Nightly) - } - } -} - #[derive(Debug)] pub enum LoadProcMacroDylibError { Io(io::Error), + LibLoading(libloading::Error), UnsupportedABI, } @@ -115,6 +87,7 @@ impl fmt::Display for LoadProcMacroDylibError { match self { Self::Io(e) => e.fmt(f), Self::UnsupportedABI => write!(f, "unsupported ABI version"), + Self::LibLoading(e) => e.fmt(f), } } } @@ -125,15 +98,16 @@ impl From for LoadProcMacroDylibError { } } -enum ProcMacroLibraryLibloading { - StableProcMacroLibrary { - _lib: Library, - exported_macros: Vec, - }, - NightlyProcMacroLibrary { - _lib: Library, - exported_macros: Vec, - }, +impl From for LoadProcMacroDylibError { + fn from(e: libloading::Error) -> Self { + LoadProcMacroDylibError::LibLoading(e) + } +} + +struct ProcMacroLibraryLibloading { + // Hold on to the library so it doesn't unload + _lib: Library, + abi: Abi, } impl ProcMacroLibraryLibloading { @@ -143,57 +117,10 @@ impl ProcMacroLibraryLibloading { })?; let version_info = read_dylib_info(file)?; - let macro_abi: ProcMacroABI = version_info.try_into()?; let lib = load_library(file).map_err(invalid_data_err)?; - match macro_abi { - ProcMacroABI::Stable => { - let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> = - unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; - let macros_vec = macros.to_vec(); - Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary { - _lib: lib, - exported_macros: macros_vec, - }) - } - ProcMacroABI::Nightly => { - let macros: libloading::Symbol< - &&[crate::proc_macro_nightly::bridge::client::ProcMacro], - > = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?; - let macros_vec = macros.to_vec(); - Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary { - _lib: lib, - exported_macros: macros_vec, - }) - } - } - } -} - -#[derive(Debug)] -pub enum PanicMessage { - Stable(stable_bridge::PanicMessage), - Nightly(nightly_bridge::PanicMessage), -} - -impl From for PanicMessage { - fn from(p: stable_bridge::PanicMessage) -> Self { - PanicMessage::Stable(p) - } -} - -impl From for PanicMessage { - fn from(p: nightly_bridge::PanicMessage) -> Self { - PanicMessage::Nightly(p) - } -} - -impl PanicMessage { - pub fn as_str(&self) -> Option<&str> { - match self { - Self::Stable(p) => p.as_str(), - Self::Nightly(p) => p.as_str(), - } + let abi = Abi::from_lib(&lib, symbol_name, version_info)?; + Ok(ProcMacroLibraryLibloading { _lib: lib, abi }) } } @@ -219,28 +146,13 @@ impl Expander { macro_name: &str, macro_body: &tt::Subtree, attributes: Option<&tt::Subtree>, - ) -> Result { - match &self.inner { - ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { - expand_stable(macro_name, macro_body, attributes, &exported_macros[..]) - .map_err(PanicMessage::from) - } - ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { - expand_nightly(macro_name, macro_body, attributes, &exported_macros[..]) - .map_err(PanicMessage::from) - } - } + ) -> Result { + let result = self.inner.abi.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)> { - match &self.inner { - ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { - list_macros_stable(&exported_macros[..]) - } - ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => { - list_macros_nightly(&exported_macros[..]) - } - } + self.inner.abi.list_macros() } } @@ -276,153 +188,3 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { fn ensure_file_with_lock_free_access(path: &Path) -> io::Result { Ok(path.to_path_buf()) } - -fn expand_nightly( - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - macros: &[NightlyProcMacro], -) -> Result { - let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone()); - - let parsed_attributes = attributes - .map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| { - NightlyTokenStream::with_subtree(attr.clone()) - }); - - for proc_macro in macros { - match proc_macro { - crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { - trait_name, - client, - .. - } if *trait_name == macro_name => { - let res = client.run( - &crate::proc_macro_nightly::bridge::server::SameThread, - crate::rustc_server_nightly::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client } - if *name == macro_name => - { - let res = client.run( - &crate::proc_macro_nightly::bridge::server::SameThread, - crate::rustc_server_nightly::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client } - if *name == macro_name => - { - let res = client.run( - &crate::proc_macro_nightly::bridge::server::SameThread, - crate::rustc_server_nightly::Rustc::default(), - parsed_attributes, - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - _ => continue, - } - } - - Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string())) -} - -fn expand_stable( - macro_name: &str, - macro_body: &tt::Subtree, - attributes: Option<&tt::Subtree>, - macros: &[StableProcMacro], -) -> Result { - let parsed_body = StableTokenStream::with_subtree(macro_body.clone()); - - let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| { - StableTokenStream::with_subtree(attr.clone()) - }); - - for proc_macro in macros { - match proc_macro { - crate::proc_macro::bridge::client::ProcMacro::CustomDerive { - trait_name, - client, - .. - } if *trait_name == macro_name => { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - crate::proc_macro::bridge::client::ProcMacro::Bang { name, client } - if *name == macro_name => - { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - crate::proc_macro::bridge::client::ProcMacro::Attr { name, client } - if *name == macro_name => - { - let res = client.run( - &crate::proc_macro::bridge::server::SameThread, - crate::rustc_server::Rustc::default(), - parsed_attributes, - parsed_body, - false, - ); - return res.map(|it| it.into_subtree()); - } - _ => continue, - } - } - - Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string())) -} - -pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> { - macros - .iter() - .map(|proc_macro| match proc_macro { - crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => { - (trait_name.to_string(), ProcMacroKind::CustomDerive) - } - crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() -} - -pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> { - macros - .iter() - .map(|proc_macro| match proc_macro { - crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive { - trait_name, - .. - } => (trait_name.to_string(), ProcMacroKind::CustomDerive), - crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => { - (name.to_string(), ProcMacroKind::FuncLike) - } - crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => { - (name.to_string(), ProcMacroKind::Attr) - } - }) - .collect() -} diff --git a/crates/proc_macro_srv/src/lib.rs b/crates/proc_macro_srv/src/lib.rs index deb1d1e526eb..eb9080e998e0 100644 --- a/crates/proc_macro_srv/src/lib.rs +++ b/crates/proc_macro_srv/src/lib.rs @@ -11,23 +11,10 @@ //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)… #![allow(unreachable_pub)] -#[allow(dead_code)] -#[doc(hidden)] -mod proc_macro; - -#[allow(dead_code)] -#[doc(hidden)] -mod proc_macro_nightly; - -#[doc(hidden)] -mod rustc_server; - -#[doc(hidden)] -mod rustc_server_nightly; - mod dylib; -use proc_macro::bridge::client::TokenStream; +mod abis; + use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; use std::{ collections::{hash_map::Entry, HashMap}, @@ -62,10 +49,7 @@ impl ProcMacroSrv { match result { Ok(expansion) => Ok(ExpansionResult { expansion }), - Err(msg) => { - let msg = msg.as_str().unwrap_or(""); - Err(format!("proc-macro panicked: {}", msg)) - } + Err(msg) => Err(format!("proc-macro panicked: {}", msg)), } } diff --git a/crates/proc_macro_srv/src/tests/utils.rs b/crates/proc_macro_srv/src/tests/utils.rs index 2c093aa0ad8e..150e26fe7590 100644 --- a/crates/proc_macro_srv/src/tests/utils.rs +++ b/crates/proc_macro_srv/src/tests/utils.rs @@ -12,8 +12,14 @@ pub mod fixtures { } } -fn parse_string(code: &str) -> Option { - Some(crate::rustc_server::TokenStream::from_str(code).unwrap()) +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::abi_1_47::TokenStream::from_str(code).ok() } pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {