mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-01 23:12:02 +00:00
Auto merge of #12989 - lnicola:rm-1-64-abi, r=Veykril
internal: Remove incomplete 1.64 ABI This no longer works for current nightlies and should not be needed any more, since we can use the toolchain's proc macro server instead.
This commit is contained in:
commit
3eb340fa4d
@ -1,105 +0,0 @@
|
||||
//! Proc macro ABI.
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
mod proc_macro;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[doc(hidden)]
|
||||
mod ra_server;
|
||||
|
||||
use libloading::Library;
|
||||
use proc_macro_api::ProcMacroKind;
|
||||
|
||||
use super::PanicMessage;
|
||||
|
||||
pub use ra_server::TokenStream;
|
||||
|
||||
pub(crate) struct Abi {
|
||||
exported_macros: Vec<proc_macro::bridge::client::ProcMacro>,
|
||||
}
|
||||
|
||||
impl From<proc_macro::bridge::PanicMessage> 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<Abi, libloading::Error> {
|
||||
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<tt::Subtree, PanicMessage> {
|
||||
let parsed_body = TokenStream::with_subtree(macro_body.clone());
|
||||
|
||||
let parsed_attributes =
|
||||
attributes.map_or(TokenStream::new(), |attr| TokenStream::with_subtree(attr.clone()));
|
||||
|
||||
for proc_macro in &self.exported_macros {
|
||||
match proc_macro {
|
||||
proc_macro::bridge::client::ProcMacro::CustomDerive {
|
||||
trait_name, client, ..
|
||||
} if *trait_name == macro_name => {
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
ra_server::RustAnalyzer::default(),
|
||||
parsed_body,
|
||||
true,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Bang { name, client }
|
||||
if *name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
ra_server::RustAnalyzer::default(),
|
||||
parsed_body,
|
||||
true,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Attr { name, client }
|
||||
if *name == macro_name =>
|
||||
{
|
||||
let res = client.run(
|
||||
&proc_macro::bridge::server::SameThread,
|
||||
ra_server::RustAnalyzer::default(),
|
||||
parsed_attributes,
|
||||
parsed_body,
|
||||
true,
|
||||
);
|
||||
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
|
||||
}
|
||||
_ => continue,
|
||||
}
|
||||
}
|
||||
|
||||
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
|
||||
}
|
||||
|
||||
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
|
||||
self.exported_macros
|
||||
.iter()
|
||||
.map(|proc_macro| match proc_macro {
|
||||
proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
|
||||
(trait_name.to_string(), ProcMacroKind::CustomDerive)
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::FuncLike)
|
||||
}
|
||||
proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
|
||||
(name.to_string(), ProcMacroKind::Attr)
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
@ -1,156 +0,0 @@
|
||||
//! Buffer management for same-process client<->server communication.
|
||||
|
||||
use std::io::{self, Write};
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
use std::slice;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Buffer {
|
||||
data: *mut u8,
|
||||
len: usize,
|
||||
capacity: usize,
|
||||
reserve: extern "C" fn(Buffer, usize) -> Buffer,
|
||||
drop: extern "C" fn(Buffer),
|
||||
}
|
||||
|
||||
unsafe impl Sync for Buffer {}
|
||||
unsafe impl Send for Buffer {}
|
||||
|
||||
impl Default for Buffer {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self::from(vec![])
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for Buffer {
|
||||
type Target = [u8];
|
||||
#[inline]
|
||||
fn deref(&self) -> &[u8] {
|
||||
unsafe { slice::from_raw_parts(self.data as *const u8, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl DerefMut for Buffer {
|
||||
#[inline]
|
||||
fn deref_mut(&mut self) -> &mut [u8] {
|
||||
unsafe { slice::from_raw_parts_mut(self.data, self.len) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Buffer {
|
||||
#[inline]
|
||||
pub(super) fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn clear(&mut self) {
|
||||
self.len = 0;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn take(&mut self) -> Self {
|
||||
mem::take(self)
|
||||
}
|
||||
|
||||
// We have the array method separate from extending from a slice. This is
|
||||
// because in the case of small arrays, codegen can be more efficient
|
||||
// (avoiding a memmove call). With extend_from_slice, LLVM at least
|
||||
// currently is not able to make that optimization.
|
||||
#[inline]
|
||||
pub(super) fn extend_from_array<const N: usize>(&mut self, xs: &[u8; N]) {
|
||||
if xs.len() > (self.capacity - self.len) {
|
||||
let b = self.take();
|
||||
*self = (b.reserve)(b, xs.len());
|
||||
}
|
||||
unsafe {
|
||||
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
|
||||
self.len += xs.len();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn extend_from_slice(&mut self, xs: &[u8]) {
|
||||
if xs.len() > (self.capacity - self.len) {
|
||||
let b = self.take();
|
||||
*self = (b.reserve)(b, xs.len());
|
||||
}
|
||||
unsafe {
|
||||
xs.as_ptr().copy_to_nonoverlapping(self.data.add(self.len), xs.len());
|
||||
self.len += xs.len();
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(super) fn push(&mut self, v: u8) {
|
||||
// The code here is taken from Vec::push, and we know that reserve()
|
||||
// will panic if we're exceeding isize::MAX bytes and so there's no need
|
||||
// to check for overflow.
|
||||
if self.len == self.capacity {
|
||||
let b = self.take();
|
||||
*self = (b.reserve)(b, 1);
|
||||
}
|
||||
unsafe {
|
||||
*self.data.add(self.len) = v;
|
||||
self.len += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Write for Buffer {
|
||||
#[inline]
|
||||
fn write(&mut self, xs: &[u8]) -> io::Result<usize> {
|
||||
self.extend_from_slice(xs);
|
||||
Ok(xs.len())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn write_all(&mut self, xs: &[u8]) -> io::Result<()> {
|
||||
self.extend_from_slice(xs);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Buffer {
|
||||
#[inline]
|
||||
fn drop(&mut self) {
|
||||
let b = self.take();
|
||||
(b.drop)(b);
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<u8>> for Buffer {
|
||||
fn from(mut v: Vec<u8>) -> 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<u8> {
|
||||
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 }
|
||||
}
|
||||
}
|
@ -1,529 +0,0 @@
|
||||
//! Client-side types.
|
||||
|
||||
use super::*;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
macro_rules! define_handles {
|
||||
(
|
||||
'owned: $($oty:ident,)*
|
||||
'interned: $($ity:ident,)*
|
||||
) => {
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
pub struct HandleCounters {
|
||||
$($oty: AtomicUsize,)*
|
||||
$($ity: AtomicUsize,)*
|
||||
}
|
||||
|
||||
impl HandleCounters {
|
||||
// FIXME(eddyb) use a reference to the `static COUNTERS`, instead of
|
||||
// a wrapper `fn` pointer, once `const fn` can reference `static`s.
|
||||
extern "C" fn get() -> &'static Self {
|
||||
static COUNTERS: HandleCounters = HandleCounters {
|
||||
$($oty: AtomicUsize::new(1),)*
|
||||
$($ity: AtomicUsize::new(1),)*
|
||||
};
|
||||
&COUNTERS
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
|
||||
#[repr(C)]
|
||||
#[allow(non_snake_case)]
|
||||
pub(super) struct HandleStore<S: server::Types> {
|
||||
$($oty: handle::OwnedStore<S::$oty>,)*
|
||||
$($ity: handle::InternedStore<S::$ity>,)*
|
||||
}
|
||||
|
||||
impl<S: server::Types> HandleStore<S> {
|
||||
pub(super) fn new(handle_counters: &'static HandleCounters) -> Self {
|
||||
HandleStore {
|
||||
$($oty: handle::OwnedStore::new(&handle_counters.$oty),)*
|
||||
$($ity: handle::InternedStore::new(&handle_counters.$ity),)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
#[repr(C)]
|
||||
pub(crate) struct $oty {
|
||||
handle: handle::Handle,
|
||||
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
|
||||
// way of doing this, but that requires unstable features.
|
||||
// rust-analyzer uses this code and avoids unstable features.
|
||||
_marker: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
// Forward `Drop::drop` to the inherent `drop` method.
|
||||
impl Drop for $oty {
|
||||
fn drop(&mut self) {
|
||||
$oty {
|
||||
handle: self.handle,
|
||||
_marker: PhantomData,
|
||||
}.drop();
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for $oty {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
let handle = self.handle;
|
||||
mem::forget(self);
|
||||
handle.encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
|
||||
for Marked<S::$oty, $oty>
|
||||
{
|
||||
fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
|
||||
s.$oty.take(handle::Handle::decode(r, &mut ()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for &$oty {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.handle.encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: server::Types> Decode<'_, 's, HandleStore<server::MarkedTypes<S>>>
|
||||
for &'s Marked<S::$oty, $oty>
|
||||
{
|
||||
fn decode(r: &mut Reader<'_>, s: &'s HandleStore<server::MarkedTypes<S>>) -> Self {
|
||||
&s.$oty[handle::Handle::decode(r, &mut ())]
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for &mut $oty {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.handle.encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'s, S: server::Types> DecodeMut<'_, 's, HandleStore<server::MarkedTypes<S>>>
|
||||
for &'s mut Marked<S::$oty, $oty>
|
||||
{
|
||||
fn decode(
|
||||
r: &mut Reader<'_>,
|
||||
s: &'s mut HandleStore<server::MarkedTypes<S>>
|
||||
) -> Self {
|
||||
&mut s.$oty[handle::Handle::decode(r, &mut ())]
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
|
||||
for Marked<S::$oty, $oty>
|
||||
{
|
||||
fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
|
||||
s.$oty.alloc(self).encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for $oty {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
$oty {
|
||||
handle: handle::Handle::decode(r, s),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct $ity {
|
||||
handle: handle::Handle,
|
||||
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual
|
||||
// way of doing this, but that requires unstable features.
|
||||
// rust-analyzer uses this code and avoids unstable features.
|
||||
_marker: PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for $ity {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.handle.encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: server::Types> DecodeMut<'_, '_, HandleStore<server::MarkedTypes<S>>>
|
||||
for Marked<S::$ity, $ity>
|
||||
{
|
||||
fn decode(r: &mut Reader<'_>, s: &mut HandleStore<server::MarkedTypes<S>>) -> Self {
|
||||
s.$ity.copy(handle::Handle::decode(r, &mut ()))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: server::Types> Encode<HandleStore<server::MarkedTypes<S>>>
|
||||
for Marked<S::$ity, $ity>
|
||||
{
|
||||
fn encode(self, w: &mut Writer, s: &mut HandleStore<server::MarkedTypes<S>>) {
|
||||
s.$ity.alloc(self).encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for $ity {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
$ity {
|
||||
handle: handle::Handle::decode(r, s),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
define_handles! {
|
||||
'owned:
|
||||
FreeFunctions,
|
||||
TokenStream,
|
||||
Literal,
|
||||
SourceFile,
|
||||
MultiSpan,
|
||||
Diagnostic,
|
||||
|
||||
'interned:
|
||||
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 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 Span {
|
||||
pub(crate) fn def_site() -> Span {
|
||||
Bridge::with(|bridge| bridge.globals.def_site)
|
||||
}
|
||||
|
||||
pub(crate) fn call_site() -> Span {
|
||||
Bridge::with(|bridge| bridge.globals.call_site)
|
||||
}
|
||||
|
||||
pub(crate) fn mixed_site() -> Span {
|
||||
Bridge::with(|bridge| bridge.globals.mixed_site)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Span {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&self.debug())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_client_side {
|
||||
($($name:ident {
|
||||
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
|
||||
}),* $(,)?) => {
|
||||
$(impl $name {
|
||||
$(pub(crate) fn $method($($arg: $arg_ty),*) $(-> $ret_ty)* {
|
||||
Bridge::with(|bridge| {
|
||||
let mut buf = bridge.cached_buffer.take();
|
||||
|
||||
buf.clear();
|
||||
api_tags::Method::$name(api_tags::$name::$method).encode(&mut buf, &mut ());
|
||||
reverse_encode!(buf; $($arg),*);
|
||||
|
||||
buf = bridge.dispatch.call(buf);
|
||||
|
||||
let r = Result::<_, PanicMessage>::decode(&mut &buf[..], &mut ());
|
||||
|
||||
bridge.cached_buffer = buf;
|
||||
|
||||
r.unwrap_or_else(|e| panic::resume_unwind(e.into()))
|
||||
})
|
||||
})*
|
||||
})*
|
||||
}
|
||||
}
|
||||
with_api!(self, self, define_client_side);
|
||||
|
||||
struct Bridge<'a> {
|
||||
/// Reusable buffer (only `clear`-ed, never shrunk), primarily
|
||||
/// used for making requests.
|
||||
cached_buffer: Buffer,
|
||||
|
||||
/// Server-side function that the client uses to make requests.
|
||||
dispatch: closure::Closure<'a, Buffer, Buffer>,
|
||||
|
||||
/// Provided globals for this macro expansion.
|
||||
globals: ExpnGlobals<Span>,
|
||||
}
|
||||
|
||||
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<BridgeStateL> =
|
||||
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<R>(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 with<R>(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),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn is_available() -> bool {
|
||||
BridgeState::with(|state| match state {
|
||||
BridgeState::Connected(_) | BridgeState::InUse => true,
|
||||
BridgeState::NotConnected => false,
|
||||
})
|
||||
}
|
||||
|
||||
/// A client-side RPC entry-point, which may be using a different `proc_macro`
|
||||
/// from the one used by the server, but can be invoked compatibly.
|
||||
///
|
||||
/// Note that the (phantom) `I` ("input") and `O` ("output") type parameters
|
||||
/// decorate the `Client<I, O>` with the RPC "interface" of the entry-point, but
|
||||
/// do not themselves participate in ABI, at all, only facilitate type-checking.
|
||||
///
|
||||
/// E.g. `Client<TokenStream, TokenStream>` is the common proc macro interface,
|
||||
/// used for `#[proc_macro] fn foo(input: TokenStream) -> TokenStream`,
|
||||
/// indicating that the RPC input and output will be serialized token streams,
|
||||
/// and forcing the use of APIs that take/return `S::TokenStream`, server-side.
|
||||
#[repr(C)]
|
||||
pub struct Client<I, O> {
|
||||
// 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(BridgeConfig<'_>) -> Buffer,
|
||||
|
||||
pub(super) _marker: PhantomData<fn(I) -> O>,
|
||||
}
|
||||
|
||||
impl<I, O> Copy for Client<I, O> {}
|
||||
impl<I, O> Clone for Client<I, O> {
|
||||
fn clone(&self) -> Self {
|
||||
*self
|
||||
}
|
||||
}
|
||||
|
||||
fn maybe_install_panic_hook(force_show_panics: bool) {
|
||||
// Hide the default panic output within `proc_macro` expansions.
|
||||
// NB. the server can't do this because it may use a different libstd.
|
||||
static HIDE_PANICS_DURING_EXPANSION: Once = Once::new();
|
||||
HIDE_PANICS_DURING_EXPANSION.call_once(|| {
|
||||
let prev = panic::take_hook();
|
||||
panic::set_hook(Box::new(move |info| {
|
||||
let show = BridgeState::with(|state| match state {
|
||||
BridgeState::NotConnected => true,
|
||||
BridgeState::Connected(_) | BridgeState::InUse => force_show_panics,
|
||||
});
|
||||
if show {
|
||||
prev(info)
|
||||
}
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
/// 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<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
|
||||
config: BridgeConfig<'_>,
|
||||
f: impl FnOnce(A) -> R,
|
||||
) -> Buffer {
|
||||
let BridgeConfig { input: mut buf, dispatch, force_show_panics, .. } = config;
|
||||
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(|| {
|
||||
maybe_install_panic_hook(force_show_panics);
|
||||
|
||||
let reader = &mut &buf[..];
|
||||
let (globals, input) = <(ExpnGlobals<Span>, A)>::decode(reader, &mut ());
|
||||
|
||||
// Put the buffer we used for input back in the `Bridge` for requests.
|
||||
let new_state =
|
||||
BridgeState::Connected(Bridge { cached_buffer: buf.take(), dispatch, globals });
|
||||
|
||||
BRIDGE_STATE.with(|state| {
|
||||
state.set(new_state, || {
|
||||
let output = f(input);
|
||||
|
||||
// Take the `cached_buffer` back out, for the output value.
|
||||
buf = Bridge::with(|bridge| bridge.cached_buffer.take());
|
||||
|
||||
// HACK(eddyb) Separate encoding a success value (`Ok(output)`)
|
||||
// from encoding a panic (`Err(e: PanicMessage)`) to avoid
|
||||
// having handles outside the `bridge.enter(|| ...)` scope, and
|
||||
// to catch panics that could happen while encoding the success.
|
||||
//
|
||||
// Note that panics should be impossible beyond this point, but
|
||||
// this is defensively trying to avoid any accidental panicking
|
||||
// reaching the `extern "C"` (which should `abort` but might not
|
||||
// at the moment, so this is also potentially preventing UB).
|
||||
buf.clear();
|
||||
Ok::<_, ()>(output).encode(&mut buf, &mut ());
|
||||
})
|
||||
})
|
||||
}))
|
||||
.map_err(PanicMessage::from)
|
||||
.unwrap_or_else(|e| {
|
||||
buf.clear();
|
||||
Err::<(), _>(e).encode(&mut buf, &mut ());
|
||||
});
|
||||
buf
|
||||
}
|
||||
|
||||
impl Client<super::super::TokenStream, super::super::TokenStream> {
|
||||
pub const fn expand1(
|
||||
f: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
|
||||
) -> Self {
|
||||
Client {
|
||||
get_handle_counters: HandleCounters::get,
|
||||
run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
|
||||
run_client(bridge, |input| f(super::super::TokenStream(input)).0)
|
||||
}),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Client<(super::super::TokenStream, super::super::TokenStream), super::super::TokenStream> {
|
||||
pub const fn expand2(
|
||||
f: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
|
||||
+ Copy,
|
||||
) -> Self {
|
||||
Client {
|
||||
get_handle_counters: HandleCounters::get,
|
||||
run: super::selfless_reify::reify_to_extern_c_fn_hrt_bridge(move |bridge| {
|
||||
run_client(bridge, |(input, input2)| {
|
||||
f(super::super::TokenStream(input), super::super::TokenStream(input2)).0
|
||||
})
|
||||
}),
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub enum ProcMacro {
|
||||
CustomDerive {
|
||||
trait_name: &'static str,
|
||||
attributes: &'static [&'static str],
|
||||
client: Client<super::super::TokenStream, super::super::TokenStream>,
|
||||
},
|
||||
|
||||
Attr {
|
||||
name: &'static str,
|
||||
client: Client<
|
||||
(super::super::TokenStream, super::super::TokenStream),
|
||||
super::super::TokenStream,
|
||||
>,
|
||||
},
|
||||
|
||||
Bang {
|
||||
name: &'static str,
|
||||
client: Client<super::super::TokenStream, super::super::TokenStream>,
|
||||
},
|
||||
}
|
||||
|
||||
impl ProcMacro {
|
||||
pub fn name(&self) -> &'static str {
|
||||
match self {
|
||||
ProcMacro::CustomDerive { trait_name, .. } => trait_name,
|
||||
ProcMacro::Attr { name, .. } => name,
|
||||
ProcMacro::Bang { name, .. } => name,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn custom_derive(
|
||||
trait_name: &'static str,
|
||||
attributes: &'static [&'static str],
|
||||
expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
|
||||
) -> Self {
|
||||
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
|
||||
}
|
||||
|
||||
pub const fn attr(
|
||||
name: &'static str,
|
||||
expand: impl Fn(super::super::TokenStream, super::super::TokenStream) -> super::super::TokenStream
|
||||
+ Copy,
|
||||
) -> Self {
|
||||
ProcMacro::Attr { name, client: Client::expand2(expand) }
|
||||
}
|
||||
|
||||
pub const fn bang(
|
||||
name: &'static str,
|
||||
expand: impl Fn(super::super::TokenStream) -> super::super::TokenStream + Copy,
|
||||
) -> Self {
|
||||
ProcMacro::Bang { name, client: Client::expand1(expand) }
|
||||
}
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
//! Closure type (equivalent to `&mut dyn FnMut(A) -> R`) that's `repr(C)`.
|
||||
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct Closure<'a, A, R> {
|
||||
call: unsafe extern "C" fn(*mut Env, A) -> R,
|
||||
env: *mut Env,
|
||||
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
|
||||
// this, but that requires unstable features. rust-analyzer uses this code
|
||||
// and avoids unstable features.
|
||||
//
|
||||
// The `'a` lifetime parameter represents the lifetime of `Env`.
|
||||
_marker: PhantomData<*mut &'a mut ()>,
|
||||
}
|
||||
|
||||
struct Env;
|
||||
|
||||
impl<'a, A, R, F: FnMut(A) -> R> From<&'a mut F> for Closure<'a, A, R> {
|
||||
fn from(f: &'a mut F) -> Self {
|
||||
unsafe extern "C" fn call<A, R, F: FnMut(A) -> R>(env: *mut Env, arg: A) -> R {
|
||||
(*(env as *mut _ as *mut F))(arg)
|
||||
}
|
||||
Closure { call: call::<A, R, F>, env: f as *mut _ as *mut Env, _marker: PhantomData }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, A, R> Closure<'a, A, R> {
|
||||
pub fn call(&mut self, arg: A) -> R {
|
||||
unsafe { (self.call)(self.env, arg) }
|
||||
}
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
//! Server-side handles and storage for per-handle data.
|
||||
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::hash::{BuildHasher, Hash};
|
||||
use std::num::NonZeroU32;
|
||||
use std::ops::{Index, IndexMut};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
|
||||
pub(super) type Handle = NonZeroU32;
|
||||
|
||||
/// A store that associates values of type `T` with numeric handles. A value can
|
||||
/// be looked up using its handle.
|
||||
pub(super) struct OwnedStore<T: 'static> {
|
||||
counter: &'static AtomicUsize,
|
||||
data: BTreeMap<Handle, T>,
|
||||
}
|
||||
|
||||
impl<T> OwnedStore<T> {
|
||||
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<T> OwnedStore<T> {
|
||||
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<T> Index<Handle> for OwnedStore<T> {
|
||||
type Output = T;
|
||||
fn index(&self, h: Handle) -> &T {
|
||||
self.data.get(&h).expect("use-after-free in `proc_macro` handle")
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> IndexMut<Handle> for OwnedStore<T> {
|
||||
fn index_mut(&mut self, h: Handle) -> &mut T {
|
||||
self.data.get_mut(&h).expect("use-after-free in `proc_macro` handle")
|
||||
}
|
||||
}
|
||||
|
||||
// HACK(eddyb) deterministic `std::collections::hash_map::RandomState` replacement
|
||||
// that doesn't require adding any dependencies to `proc_macro` (like `rustc-hash`).
|
||||
#[derive(Clone)]
|
||||
struct NonRandomState;
|
||||
|
||||
impl BuildHasher for NonRandomState {
|
||||
type Hasher = std::collections::hash_map::DefaultHasher;
|
||||
#[inline]
|
||||
fn build_hasher(&self) -> Self::Hasher {
|
||||
Self::Hasher::new()
|
||||
}
|
||||
}
|
||||
|
||||
/// Like `OwnedStore`, but avoids storing any value more than once.
|
||||
pub(super) struct InternedStore<T: 'static> {
|
||||
owned: OwnedStore<T>,
|
||||
interner: HashMap<T, Handle, NonRandomState>,
|
||||
}
|
||||
|
||||
impl<T: Copy + Eq + Hash> InternedStore<T> {
|
||||
pub(super) fn new(counter: &'static AtomicUsize) -> Self {
|
||||
InternedStore {
|
||||
owned: OwnedStore::new(counter),
|
||||
interner: HashMap::with_hasher(NonRandomState),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn alloc(&mut self, x: T) -> Handle {
|
||||
let owned = &mut self.owned;
|
||||
*self.interner.entry(x).or_insert_with(|| owned.alloc(x))
|
||||
}
|
||||
|
||||
pub(super) fn copy(&mut self, h: Handle) -> T {
|
||||
self.owned[h]
|
||||
}
|
||||
}
|
@ -1,493 +0,0 @@
|
||||
//! Internal interface for communicating between a `proc_macro` client
|
||||
//! (a proc macro crate) and a `proc_macro` server (a compiler front-end).
|
||||
//!
|
||||
//! Serialization (with C ABI buffers) and unique integer handles are employed
|
||||
//! to allow safely interfacing between two copies of `proc_macro` built
|
||||
//! (from the same source) by different compilers with potentially mismatching
|
||||
//! Rust ABIs (e.g., stage0/bin/rustc vs stage1/bin/rustc during bootstrap).
|
||||
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
pub use super::{Delimiter, Level, LineColumn, Spacing};
|
||||
use std::fmt;
|
||||
use std::hash::Hash;
|
||||
use std::marker;
|
||||
use std::mem;
|
||||
use std::ops::Bound;
|
||||
use std::panic;
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::Once;
|
||||
use std::thread;
|
||||
|
||||
/// Higher-order macro describing the server RPC API, allowing automatic
|
||||
/// generation of type-safe Rust APIs, both client-side and server-side.
|
||||
///
|
||||
/// `with_api!(MySelf, my_self, my_macro)` expands to:
|
||||
/// ```rust,ignore (pseudo-code)
|
||||
/// my_macro! {
|
||||
/// // ...
|
||||
/// Literal {
|
||||
/// // ...
|
||||
/// fn character(ch: char) -> MySelf::Literal;
|
||||
/// // ...
|
||||
/// fn span(my_self: &MySelf::Literal) -> MySelf::Span;
|
||||
/// fn set_span(my_self: &mut MySelf::Literal, span: MySelf::Span);
|
||||
/// },
|
||||
/// // ...
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// The first two arguments serve to customize the arguments names
|
||||
/// and argument/return types, to enable several different usecases:
|
||||
///
|
||||
/// If `my_self` is just `self`, then each `fn` signature can be used
|
||||
/// as-is for a method. If it's anything else (`self_` in practice),
|
||||
/// then the signatures don't have a special `self` argument, and
|
||||
/// can, therefore, have a different one introduced.
|
||||
///
|
||||
/// If `MySelf` is just `Self`, then the types are only valid inside
|
||||
/// a trait or a trait impl, where the trait has associated types
|
||||
/// for each of the API types. If non-associated types are desired,
|
||||
/// a module name (`self` in practice) can be used instead of `Self`.
|
||||
macro_rules! with_api {
|
||||
($S:ident, $self:ident, $m:ident) => {
|
||||
$m! {
|
||||
FreeFunctions {
|
||||
fn drop($self: $S::FreeFunctions);
|
||||
fn track_env_var(var: &str, value: Option<&str>);
|
||||
fn track_path(path: &str);
|
||||
},
|
||||
TokenStream {
|
||||
fn drop($self: $S::TokenStream);
|
||||
fn clone($self: &$S::TokenStream) -> $S::TokenStream;
|
||||
fn is_empty($self: &$S::TokenStream) -> bool;
|
||||
fn expand_expr($self: &$S::TokenStream) -> Result<$S::TokenStream, ()>;
|
||||
fn from_str(src: &str) -> $S::TokenStream;
|
||||
fn to_string($self: &$S::TokenStream) -> String;
|
||||
fn from_token_tree(
|
||||
tree: TokenTree<$S::TokenStream, $S::Span, $S::Ident, $S::Literal>,
|
||||
) -> $S::TokenStream;
|
||||
fn concat_trees(
|
||||
base: Option<$S::TokenStream>,
|
||||
trees: Vec<TokenTree<$S::TokenStream, $S::Span, $S::Ident, $S::Literal>>,
|
||||
) -> $S::TokenStream;
|
||||
fn concat_streams(
|
||||
base: Option<$S::TokenStream>,
|
||||
streams: Vec<$S::TokenStream>,
|
||||
) -> $S::TokenStream;
|
||||
fn into_trees(
|
||||
$self: $S::TokenStream
|
||||
) -> Vec<TokenTree<$S::TokenStream, $S::Span, $S::Ident, $S::Literal>>;
|
||||
},
|
||||
Ident {
|
||||
fn new(string: &str, span: $S::Span, is_raw: bool) -> $S::Ident;
|
||||
fn span($self: $S::Ident) -> $S::Span;
|
||||
fn with_span($self: $S::Ident, span: $S::Span) -> $S::Ident;
|
||||
},
|
||||
Literal {
|
||||
fn drop($self: $S::Literal);
|
||||
fn clone($self: &$S::Literal) -> $S::Literal;
|
||||
fn from_str(s: &str) -> Result<$S::Literal, ()>;
|
||||
fn to_string($self: &$S::Literal) -> String;
|
||||
fn debug_kind($self: &$S::Literal) -> String;
|
||||
fn symbol($self: &$S::Literal) -> String;
|
||||
fn suffix($self: &$S::Literal) -> Option<String>;
|
||||
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<usize>,
|
||||
end: Bound<usize>,
|
||||
) -> 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 source_file($self: $S::Span) -> $S::SourceFile;
|
||||
fn parent($self: $S::Span) -> Option<$S::Span>;
|
||||
fn source($self: $S::Span) -> $S::Span;
|
||||
fn start($self: $S::Span) -> LineColumn;
|
||||
fn end($self: $S::Span) -> LineColumn;
|
||||
fn before($self: $S::Span) -> $S::Span;
|
||||
fn after($self: $S::Span) -> $S::Span;
|
||||
fn join($self: $S::Span, other: $S::Span) -> Option<$S::Span>;
|
||||
fn resolved_at($self: $S::Span, at: $S::Span) -> $S::Span;
|
||||
fn source_text($self: $S::Span) -> Option<String>;
|
||||
fn save_span($self: $S::Span) -> usize;
|
||||
fn recover_proc_macro_span(id: usize) -> $S::Span;
|
||||
},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this calls `encode` for each argument, but in reverse,
|
||||
// to match the ordering in `reverse_decode`.
|
||||
macro_rules! reverse_encode {
|
||||
($writer:ident;) => {};
|
||||
($writer:ident; $first:ident $(, $rest:ident)*) => {
|
||||
reverse_encode!($writer; $($rest),*);
|
||||
$first.encode(&mut $writer, &mut ());
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME(eddyb) this calls `decode` for each argument, but in reverse,
|
||||
// to avoid borrow conflicts from borrows started by `&mut` arguments.
|
||||
macro_rules! reverse_decode {
|
||||
($reader:ident, $s:ident;) => {};
|
||||
($reader:ident, $s:ident; $first:ident: $first_ty:ty $(, $rest:ident: $rest_ty:ty)*) => {
|
||||
reverse_decode!($reader, $s; $($rest: $rest_ty),*);
|
||||
let $first = <$first_ty>::decode(&mut $reader, $s);
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unsafe_code)]
|
||||
mod buffer;
|
||||
#[forbid(unsafe_code)]
|
||||
pub mod client;
|
||||
#[allow(unsafe_code)]
|
||||
mod closure;
|
||||
#[forbid(unsafe_code)]
|
||||
mod handle;
|
||||
#[macro_use]
|
||||
#[forbid(unsafe_code)]
|
||||
mod rpc;
|
||||
#[allow(unsafe_code)]
|
||||
mod scoped_cell;
|
||||
#[allow(unsafe_code)]
|
||||
mod selfless_reify;
|
||||
#[forbid(unsafe_code)]
|
||||
pub mod server;
|
||||
|
||||
use buffer::Buffer;
|
||||
pub use rpc::PanicMessage;
|
||||
use rpc::{Decode, DecodeMut, Encode, Reader, Writer};
|
||||
|
||||
/// Configuration for establishing an active connection between a server and a
|
||||
/// client. The server creates the bridge config (`run_server` in `server.rs`),
|
||||
/// then passes it to the client through the function pointer in the `run` field
|
||||
/// of `client::Client`. The client constructs a local `Bridge` from the config
|
||||
/// in TLS during its execution (`Bridge::{enter, with}` in `client.rs`).
|
||||
#[repr(C)]
|
||||
pub struct BridgeConfig<'a> {
|
||||
/// Buffer used to pass initial input to the client.
|
||||
input: Buffer,
|
||||
|
||||
/// Server-side function that the client uses to make requests.
|
||||
dispatch: closure::Closure<'a, Buffer, Buffer>,
|
||||
|
||||
/// If 'true', always invoke the default panic hook
|
||||
force_show_panics: bool,
|
||||
|
||||
// Prevent Send and Sync impls. `!Send`/`!Sync` is the usual way of doing
|
||||
// this, but that requires unstable features. rust-analyzer uses this code
|
||||
// and avoids unstable features.
|
||||
_marker: marker::PhantomData<*mut ()>,
|
||||
}
|
||||
|
||||
#[forbid(unsafe_code)]
|
||||
#[allow(non_camel_case_types)]
|
||||
mod api_tags {
|
||||
use super::rpc::{DecodeMut, Encode, Reader, Writer};
|
||||
|
||||
macro_rules! declare_tags {
|
||||
($($name:ident {
|
||||
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)*;)*
|
||||
}),* $(,)?) => {
|
||||
$(
|
||||
pub(super) enum $name {
|
||||
$($method),*
|
||||
}
|
||||
rpc_encode_decode!(enum $name { $($method),* });
|
||||
)*
|
||||
|
||||
pub(super) enum Method {
|
||||
$($name($name)),*
|
||||
}
|
||||
rpc_encode_decode!(enum Method { $($name(m)),* });
|
||||
}
|
||||
}
|
||||
with_api!(self, self, declare_tags);
|
||||
}
|
||||
|
||||
/// Helper to wrap associated types to allow trait impl dispatch.
|
||||
/// That is, normally a pair of impls for `T::Foo` and `T::Bar`
|
||||
/// can overlap, but if the impls are, instead, on types like
|
||||
/// `Marked<T::Foo, Foo>` and `Marked<T::Bar, Bar>`, they can't.
|
||||
trait Mark {
|
||||
type Unmarked;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self;
|
||||
}
|
||||
|
||||
/// Unwrap types wrapped by `Mark::mark` (see `Mark` for details).
|
||||
trait Unmark {
|
||||
type Unmarked;
|
||||
fn unmark(self) -> Self::Unmarked;
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||
struct Marked<T, M> {
|
||||
value: T,
|
||||
_marker: marker::PhantomData<M>,
|
||||
}
|
||||
|
||||
impl<T, M> Mark for Marked<T, M> {
|
||||
type Unmarked = T;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
Marked { value: unmarked, _marker: marker::PhantomData }
|
||||
}
|
||||
}
|
||||
impl<T, M> Unmark for Marked<T, M> {
|
||||
type Unmarked = T;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
self.value
|
||||
}
|
||||
}
|
||||
impl<'a, T, M> Unmark for &'a Marked<T, M> {
|
||||
type Unmarked = &'a T;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
&self.value
|
||||
}
|
||||
}
|
||||
impl<'a, T, M> Unmark for &'a mut Marked<T, M> {
|
||||
type Unmarked = &'a mut T;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
&mut self.value
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Mark> Mark for Vec<T> {
|
||||
type Unmarked = Vec<T::Unmarked>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
// Should be a no-op due to std's in-place collect optimizations.
|
||||
unmarked.into_iter().map(T::mark).collect()
|
||||
}
|
||||
}
|
||||
impl<T: Unmark> Unmark for Vec<T> {
|
||||
type Unmarked = Vec<T::Unmarked>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
// Should be a no-op due to std's in-place collect optimizations.
|
||||
self.into_iter().map(T::unmark).collect()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! mark_noop {
|
||||
($($ty:ty),* $(,)?) => {
|
||||
$(
|
||||
impl Mark for $ty {
|
||||
type Unmarked = Self;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
unmarked
|
||||
}
|
||||
}
|
||||
impl Unmark for $ty {
|
||||
type Unmarked = Self;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
self
|
||||
}
|
||||
}
|
||||
)*
|
||||
}
|
||||
}
|
||||
mark_noop! {
|
||||
(),
|
||||
bool,
|
||||
char,
|
||||
&'_ [u8],
|
||||
&'_ str,
|
||||
String,
|
||||
u8,
|
||||
usize,
|
||||
Delimiter,
|
||||
Level,
|
||||
LineColumn,
|
||||
Spacing,
|
||||
}
|
||||
|
||||
rpc_encode_decode!(
|
||||
enum Delimiter {
|
||||
Parenthesis,
|
||||
Brace,
|
||||
Bracket,
|
||||
None,
|
||||
}
|
||||
);
|
||||
rpc_encode_decode!(
|
||||
enum Level {
|
||||
Error,
|
||||
Warning,
|
||||
Note,
|
||||
Help,
|
||||
}
|
||||
);
|
||||
rpc_encode_decode!(struct LineColumn { line, column });
|
||||
rpc_encode_decode!(
|
||||
enum Spacing {
|
||||
Alone,
|
||||
Joint,
|
||||
}
|
||||
);
|
||||
|
||||
macro_rules! mark_compound {
|
||||
(struct $name:ident <$($T:ident),+> { $($field:ident),* $(,)? }) => {
|
||||
impl<$($T: Mark),+> Mark for $name <$($T),+> {
|
||||
type Unmarked = $name <$($T::Unmarked),+>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
$name {
|
||||
$($field: Mark::mark(unmarked.$field)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
|
||||
type Unmarked = $name <$($T::Unmarked),+>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
$name {
|
||||
$($field: Unmark::unmark(self.$field)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(enum $name:ident <$($T:ident),+> { $($variant:ident $(($field:ident))?),* $(,)? }) => {
|
||||
impl<$($T: Mark),+> Mark for $name <$($T),+> {
|
||||
type Unmarked = $name <$($T::Unmarked),+>;
|
||||
fn mark(unmarked: Self::Unmarked) -> Self {
|
||||
match unmarked {
|
||||
$($name::$variant $(($field))? => {
|
||||
$name::$variant $((Mark::mark($field)))?
|
||||
})*
|
||||
}
|
||||
}
|
||||
}
|
||||
impl<$($T: Unmark),+> Unmark for $name <$($T),+> {
|
||||
type Unmarked = $name <$($T::Unmarked),+>;
|
||||
fn unmark(self) -> Self::Unmarked {
|
||||
match self {
|
||||
$($name::$variant $(($field))? => {
|
||||
$name::$variant $((Unmark::unmark($field)))?
|
||||
})*
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! compound_traits {
|
||||
($($t:tt)*) => {
|
||||
rpc_encode_decode!($($t)*);
|
||||
mark_compound!($($t)*);
|
||||
};
|
||||
}
|
||||
|
||||
compound_traits!(
|
||||
enum Bound<T> {
|
||||
Included(x),
|
||||
Excluded(x),
|
||||
Unbounded,
|
||||
}
|
||||
);
|
||||
|
||||
compound_traits!(
|
||||
enum Option<T> {
|
||||
Some(t),
|
||||
None,
|
||||
}
|
||||
);
|
||||
|
||||
compound_traits!(
|
||||
enum Result<T, E> {
|
||||
Ok(t),
|
||||
Err(e),
|
||||
}
|
||||
);
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct DelimSpan<Span> {
|
||||
pub open: Span,
|
||||
pub close: Span,
|
||||
pub entire: Span,
|
||||
}
|
||||
|
||||
impl<Span: Copy> DelimSpan<Span> {
|
||||
pub fn from_single(span: Span) -> Self {
|
||||
DelimSpan { open: span, close: span, entire: span }
|
||||
}
|
||||
}
|
||||
|
||||
compound_traits!(struct DelimSpan<Span> { open, close, entire });
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Group<TokenStream, Span> {
|
||||
pub delimiter: Delimiter,
|
||||
pub stream: Option<TokenStream>,
|
||||
pub span: DelimSpan<Span>,
|
||||
}
|
||||
|
||||
compound_traits!(struct Group<TokenStream, Span> { delimiter, stream, span });
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Punct<Span> {
|
||||
pub ch: u8,
|
||||
pub joint: bool,
|
||||
pub span: Span,
|
||||
}
|
||||
|
||||
compound_traits!(struct Punct<Span> { ch, joint, span });
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum TokenTree<TokenStream, Span, Ident, Literal> {
|
||||
Group(Group<TokenStream, Span>),
|
||||
Punct(Punct<Span>),
|
||||
Ident(Ident),
|
||||
Literal(Literal),
|
||||
}
|
||||
|
||||
compound_traits!(
|
||||
enum TokenTree<TokenStream, Span, Ident, Literal> {
|
||||
Group(tt),
|
||||
Punct(tt),
|
||||
Ident(tt),
|
||||
Literal(tt),
|
||||
}
|
||||
);
|
||||
|
||||
/// Globals provided alongside the initial inputs for a macro expansion.
|
||||
/// Provides values such as spans which are used frequently to avoid RPC.
|
||||
#[derive(Clone)]
|
||||
pub struct ExpnGlobals<Span> {
|
||||
pub def_site: Span,
|
||||
pub call_site: Span,
|
||||
pub mixed_site: Span,
|
||||
}
|
||||
|
||||
compound_traits!(
|
||||
struct ExpnGlobals<Span> { def_site, call_site, mixed_site }
|
||||
);
|
@ -1,304 +0,0 @@
|
||||
//! Serialization for client-server communication.
|
||||
|
||||
use std::any::Any;
|
||||
use std::char;
|
||||
use std::io::Write;
|
||||
use std::num::NonZeroU32;
|
||||
use std::str;
|
||||
|
||||
pub(super) type Writer = super::buffer::Buffer;
|
||||
|
||||
pub(super) trait Encode<S>: 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<S> Encode<S> for $ty {
|
||||
fn encode(self, w: &mut Writer, _: &mut S) {
|
||||
w.extend_from_array(&self.to_le_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for $ty {
|
||||
fn decode(r: &mut Reader<'_>, _: &mut S) -> Self {
|
||||
const N: usize = ::std::mem::size_of::<$ty>();
|
||||
|
||||
let mut bytes = [0; N];
|
||||
bytes.copy_from_slice(&r[..N]);
|
||||
*r = &r[N..];
|
||||
|
||||
Self::from_le_bytes(bytes)
|
||||
}
|
||||
}
|
||||
};
|
||||
(struct $name:ident $(<$($T:ident),+>)? { $($field:ident),* $(,)? }) => {
|
||||
impl<S, $($($T: Encode<S>),+)?> Encode<S> for $name $(<$($T),+>)? {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
$(self.$field.encode(w, s);)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, $($($T: for<'s> DecodeMut<'a, 's, S>),+)?> DecodeMut<'a, '_, S>
|
||||
for $name $(<$($T),+>)?
|
||||
{
|
||||
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
|
||||
$name {
|
||||
$($field: DecodeMut::decode(r, s)),*
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
(enum $name:ident $(<$($T:ident),+>)? { $($variant:ident $(($field:ident))*),* $(,)? }) => {
|
||||
impl<S, $($($T: Encode<S>),+)?> Encode<S> 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<S> Encode<S> for () {
|
||||
fn encode(self, _: &mut Writer, _: &mut S) {}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for () {
|
||||
fn decode(_: &mut Reader<'_>, _: &mut S) -> Self {}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for u8 {
|
||||
fn encode(self, w: &mut Writer, _: &mut S) {
|
||||
w.push(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> 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<S> Encode<S> for bool {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
(self as u8).encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for bool {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
match u8::decode(r, s) {
|
||||
0 => false,
|
||||
1 => true,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for char {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
(self as u32).encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for char {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
char::from_u32(u32::decode(r, s)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> for NonZeroU32 {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.get().encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for NonZeroU32 {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
Self::new(u32::decode(r, s)).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, A: Encode<S>, B: Encode<S>> Encode<S> for (A, B) {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.0.encode(w, s);
|
||||
self.1.encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, A: for<'s> DecodeMut<'a, 's, S>, B: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S>
|
||||
for (A, B)
|
||||
{
|
||||
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
|
||||
(DecodeMut::decode(r, s), DecodeMut::decode(r, s))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Encode<S> 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<S> Encode<S> 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<S> Encode<S> for String {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self[..].encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for String {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
<&str>::decode(r, s).to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T: Encode<S>> Encode<S> for Vec<T> {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.len().encode(w, s);
|
||||
for x in self {
|
||||
x.encode(w, s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S, T: for<'s> DecodeMut<'a, 's, S>> DecodeMut<'a, '_, S> for Vec<T> {
|
||||
fn decode(r: &mut Reader<'a>, s: &mut S) -> Self {
|
||||
let len = usize::decode(r, s);
|
||||
let mut vec = Vec::with_capacity(len);
|
||||
for _ in 0..len {
|
||||
vec.push(T::decode(r, s));
|
||||
}
|
||||
vec
|
||||
}
|
||||
}
|
||||
|
||||
/// Simplified version of panic payloads, ignoring
|
||||
/// types other than `&'static str` and `String`.
|
||||
pub enum PanicMessage {
|
||||
StaticStr(&'static str),
|
||||
String(String),
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl From<Box<dyn Any + Send>> for PanicMessage {
|
||||
fn from(payload: Box<dyn Any + Send + 'static>) -> Self {
|
||||
if let Some(s) = payload.downcast_ref::<&'static str>() {
|
||||
return PanicMessage::StaticStr(s);
|
||||
}
|
||||
if let Ok(s) = payload.downcast::<String>() {
|
||||
return PanicMessage::String(*s);
|
||||
}
|
||||
PanicMessage::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<Box<dyn Any + Send>> for PanicMessage {
|
||||
fn into(self) -> Box<dyn Any + Send> {
|
||||
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<S> Encode<S> for PanicMessage {
|
||||
fn encode(self, w: &mut Writer, s: &mut S) {
|
||||
self.as_str().encode(w, s);
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> DecodeMut<'_, '_, S> for PanicMessage {
|
||||
fn decode(r: &mut Reader<'_>, s: &mut S) -> Self {
|
||||
match Option::<String>::decode(r, s) {
|
||||
Some(s) => PanicMessage::String(s),
|
||||
None => PanicMessage::Unknown,
|
||||
}
|
||||
}
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
//! `Cell` variant for (scoped) existential lifetimes.
|
||||
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
|
||||
/// Type lambda application, with a lifetime.
|
||||
#[allow(unused_lifetimes)]
|
||||
pub trait ApplyL<'a> {
|
||||
type Out;
|
||||
}
|
||||
|
||||
/// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
|
||||
pub trait LambdaL: for<'a> ApplyL<'a> {}
|
||||
|
||||
impl<T: for<'a> ApplyL<'a>> LambdaL for T {}
|
||||
|
||||
// HACK(eddyb) work around projection limitations with a newtype
|
||||
// FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out`
|
||||
pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
|
||||
|
||||
impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> {
|
||||
type Target = <T as ApplyL<'b>>::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<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
|
||||
|
||||
impl<T: LambdaL> ScopedCell<T> {
|
||||
pub const fn new(value: <T as ApplyL<'static>>::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: <T as ApplyL<'a>>::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<T>,
|
||||
value: Option<<T as ApplyL<'static>>::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<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R {
|
||||
self.replace(value, |_| f())
|
||||
}
|
||||
}
|
@ -1,84 +0,0 @@
|
||||
//! Abstraction for creating `fn` pointers from any callable that *effectively*
|
||||
//! has the equivalent of implementing `Default`, even if the compiler neither
|
||||
//! provides `Default` nor allows reifying closures (i.e. creating `fn` pointers)
|
||||
//! other than those with absolutely no captures.
|
||||
//!
|
||||
//! More specifically, for a closure-like type to be "effectively `Default`":
|
||||
//! * it must be a ZST (zero-sized type): no information contained within, so
|
||||
//! that `Default`'s return value (if it were implemented) is unambiguous
|
||||
//! * it must be `Copy`: no captured "unique ZST tokens" or any other similar
|
||||
//! types that would make duplicating values at will unsound
|
||||
//! * combined with the ZST requirement, this confers a kind of "telecopy"
|
||||
//! ability: similar to `Copy`, but without keeping the value around, and
|
||||
//! instead "reconstructing" it (a noop given it's a ZST) when needed
|
||||
//! * it must be *provably* inhabited: no captured uninhabited types or any
|
||||
//! other types that cannot be constructed by the user of this abstraction
|
||||
//! * the proof is a value of the closure-like type itself, in a sense the
|
||||
//! "seed" for the "telecopy" process made possible by ZST + `Copy`
|
||||
//! * this requirement is the only reason an abstraction limited to a specific
|
||||
//! usecase is required: ZST + `Copy` can be checked with *at worst* a panic
|
||||
//! at the "attempted `::default()` call" time, but that doesn't guarantee
|
||||
//! that the value can be soundly created, and attempting to use the typical
|
||||
//! "proof ZST token" approach leads yet again to having a ZST + `Copy` type
|
||||
//! that is not proof of anything without a value (i.e. isomorphic to a
|
||||
//! newtype of the type it's trying to prove the inhabitation of)
|
||||
//!
|
||||
//! A more flexible (and safer) solution to the general problem could exist once
|
||||
//! `const`-generic parameters can have type parameters in their types:
|
||||
//!
|
||||
//! ```rust,ignore (needs future const-generics)
|
||||
//! extern "C" fn ffi_wrapper<
|
||||
//! A, R,
|
||||
//! F: Fn(A) -> R,
|
||||
//! const f: F, // <-- this `const`-generic is not yet allowed
|
||||
//! >(arg: A) -> R {
|
||||
//! f(arg)
|
||||
//! }
|
||||
//! ```
|
||||
|
||||
use std::mem;
|
||||
|
||||
// FIXME(eddyb) this could be `trait` impls except for the `const fn` requirement.
|
||||
macro_rules! define_reify_functions {
|
||||
($(
|
||||
fn $name:ident $(<$($param:ident),*>)?
|
||||
for $(extern $abi:tt)? fn($($arg:ident: $arg_ty:ty),*) -> $ret_ty:ty;
|
||||
)+) => {
|
||||
$(pub const fn $name<
|
||||
$($($param,)*)?
|
||||
F: Fn($($arg_ty),*) -> $ret_ty + Copy
|
||||
>(f: F) -> $(extern $abi)? fn($($arg_ty),*) -> $ret_ty {
|
||||
// FIXME(eddyb) describe the `F` type (e.g. via `type_name::<F>`) once panic
|
||||
// formatting becomes possible in `const fn`.
|
||||
assert!(mem::size_of::<F>() == 0, "selfless_reify: closure must be zero-sized");
|
||||
|
||||
$(extern $abi)? fn wrapper<
|
||||
$($($param,)*)?
|
||||
F: Fn($($arg_ty),*) -> $ret_ty + Copy
|
||||
>($($arg: $arg_ty),*) -> $ret_ty {
|
||||
let f = unsafe {
|
||||
// SAFETY: `F` satisfies all criteria for "out of thin air"
|
||||
// reconstructability (see module-level doc comment).
|
||||
mem::MaybeUninit::<F>::uninit().assume_init()
|
||||
};
|
||||
f($($arg),*)
|
||||
}
|
||||
let _f_proof = f;
|
||||
wrapper::<
|
||||
$($($param,)*)?
|
||||
F
|
||||
>
|
||||
})+
|
||||
}
|
||||
}
|
||||
|
||||
define_reify_functions! {
|
||||
fn _reify_to_extern_c_fn_unary<A, R> for extern "C" fn(arg: A) -> R;
|
||||
|
||||
// HACK(eddyb) this abstraction is used with `for<'a> fn(BridgeConfig<'a>)
|
||||
// -> T` but that doesn't work with just `reify_to_extern_c_fn_unary`
|
||||
// because of the `fn` pointer type being "higher-ranked" (i.e. the
|
||||
// `for<'a>` binder).
|
||||
// FIXME(eddyb) try to remove the lifetime from `BridgeConfig`, that'd help.
|
||||
fn reify_to_extern_c_fn_hrt_bridge<R> for extern "C" fn(bridge: super::BridgeConfig<'_>) -> R;
|
||||
}
|
@ -1,339 +0,0 @@
|
||||
//! Server-side traits.
|
||||
|
||||
use super::*;
|
||||
|
||||
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
|
||||
use super::client::HandleStore;
|
||||
|
||||
pub trait Types {
|
||||
type FreeFunctions: 'static;
|
||||
type TokenStream: 'static + Clone;
|
||||
type Ident: 'static + Copy + Eq + Hash;
|
||||
type Literal: 'static + Clone;
|
||||
type SourceFile: 'static + Clone;
|
||||
type MultiSpan: 'static;
|
||||
type Diagnostic: 'static;
|
||||
type Span: 'static + Copy + Eq + Hash;
|
||||
}
|
||||
|
||||
/// Declare an associated fn of one of the traits below, adding necessary
|
||||
/// default bodies.
|
||||
macro_rules! associated_fn {
|
||||
(fn drop(&mut self, $arg:ident: $arg_ty:ty)) =>
|
||||
(fn drop(&mut self, $arg: $arg_ty) { mem::drop($arg) });
|
||||
|
||||
(fn clone(&mut self, $arg:ident: $arg_ty:ty) -> $ret_ty:ty) =>
|
||||
(fn clone(&mut self, $arg: $arg_ty) -> $ret_ty { $arg.clone() });
|
||||
|
||||
($($item:tt)*) => ($($item)*;)
|
||||
}
|
||||
|
||||
macro_rules! declare_server_traits {
|
||||
($($name:ident {
|
||||
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
|
||||
}),* $(,)?) => {
|
||||
$(pub trait $name: Types {
|
||||
$(associated_fn!(fn $method(&mut self, $($arg: $arg_ty),*) $(-> $ret_ty)?);)*
|
||||
})*
|
||||
|
||||
pub trait Server: Types $(+ $name)* {
|
||||
fn globals(&mut self) -> ExpnGlobals<Self::Span>;
|
||||
}
|
||||
}
|
||||
}
|
||||
with_api!(Self, self_, declare_server_traits);
|
||||
|
||||
pub(super) struct MarkedTypes<S: Types>(S);
|
||||
|
||||
impl<S: Server> Server for MarkedTypes<S> {
|
||||
fn globals(&mut self) -> ExpnGlobals<Self::Span> {
|
||||
<_>::mark(Server::globals(&mut self.0))
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! define_mark_types_impls {
|
||||
($($name:ident {
|
||||
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
|
||||
}),* $(,)?) => {
|
||||
impl<S: Types> Types for MarkedTypes<S> {
|
||||
$(type $name = Marked<S::$name, client::$name>;)*
|
||||
}
|
||||
|
||||
$(impl<S: $name> $name for MarkedTypes<S> {
|
||||
$(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<S: Types> {
|
||||
handle_store: HandleStore<S>,
|
||||
server: S,
|
||||
}
|
||||
|
||||
macro_rules! define_dispatcher_impl {
|
||||
($($name:ident {
|
||||
$(fn $method:ident($($arg:ident: $arg_ty:ty),* $(,)?) $(-> $ret_ty:ty)?;)*
|
||||
}),* $(,)?) => {
|
||||
// FIXME(eddyb) `pub` only for `ExecutionStrategy` below.
|
||||
pub trait DispatcherTrait {
|
||||
// HACK(eddyb) these are here to allow `Self::$name` to work below.
|
||||
$(type $name;)*
|
||||
fn dispatch(&mut self, buf: Buffer) -> Buffer;
|
||||
}
|
||||
|
||||
impl<S: Server> DispatcherTrait for Dispatcher<MarkedTypes<S>> {
|
||||
$(type $name = <MarkedTypes<S> as Types>::$name;)*
|
||||
fn dispatch(&mut self, mut buf: Buffer) -> Buffer {
|
||||
let Dispatcher { handle_store, server } = self;
|
||||
|
||||
let mut reader = &buf[..];
|
||||
match api_tags::Method::decode(&mut reader, &mut ()) {
|
||||
$(api_tags::Method::$name(m) => match m {
|
||||
$(api_tags::$name::$method => {
|
||||
let mut call_method = || {
|
||||
reverse_decode!(reader, handle_store; $($arg: $arg_ty),*);
|
||||
$name::$method(server, $($arg),*)
|
||||
};
|
||||
// HACK(eddyb) don't use `panic::catch_unwind` in a panic.
|
||||
// If client and server happen to use the same `libstd`,
|
||||
// `catch_unwind` asserts that the panic counter was 0,
|
||||
// even when the closure passed to it didn't panic.
|
||||
let r = if thread::panicking() {
|
||||
Ok(call_method())
|
||||
} else {
|
||||
panic::catch_unwind(panic::AssertUnwindSafe(call_method))
|
||||
.map_err(PanicMessage::from)
|
||||
};
|
||||
|
||||
buf.clear();
|
||||
r.encode(&mut buf, handle_store);
|
||||
})*
|
||||
}),*
|
||||
}
|
||||
buf
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
with_api!(Self, self_, define_dispatcher_impl);
|
||||
|
||||
pub trait ExecutionStrategy {
|
||||
fn run_bridge_and_client(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer,
|
||||
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer;
|
||||
}
|
||||
|
||||
pub struct SameThread;
|
||||
|
||||
impl ExecutionStrategy for SameThread {
|
||||
fn run_bridge_and_client(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer,
|
||||
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer {
|
||||
let mut dispatch = |buf| dispatcher.dispatch(buf);
|
||||
|
||||
run_client(BridgeConfig {
|
||||
input,
|
||||
dispatch: (&mut dispatch).into(),
|
||||
force_show_panics,
|
||||
_marker: marker::PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE(eddyb) Two implementations are provided, the second one is a bit
|
||||
// faster but neither is anywhere near as fast as same-thread execution.
|
||||
|
||||
pub struct CrossThread1;
|
||||
|
||||
impl ExecutionStrategy for CrossThread1 {
|
||||
fn run_bridge_and_client(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer,
|
||||
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer {
|
||||
use std::sync::mpsc::channel;
|
||||
|
||||
let (req_tx, req_rx) = channel();
|
||||
let (res_tx, res_rx) = channel();
|
||||
|
||||
let join_handle = thread::spawn(move || {
|
||||
let mut dispatch = |buf| {
|
||||
req_tx.send(buf).unwrap();
|
||||
res_rx.recv().unwrap()
|
||||
};
|
||||
|
||||
run_client(BridgeConfig {
|
||||
input,
|
||||
dispatch: (&mut dispatch).into(),
|
||||
force_show_panics,
|
||||
_marker: marker::PhantomData,
|
||||
})
|
||||
});
|
||||
|
||||
for b in req_rx {
|
||||
res_tx.send(dispatcher.dispatch(b)).unwrap();
|
||||
}
|
||||
|
||||
join_handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CrossThread2;
|
||||
|
||||
impl ExecutionStrategy for CrossThread2 {
|
||||
fn run_bridge_and_client(
|
||||
&self,
|
||||
dispatcher: &mut impl DispatcherTrait,
|
||||
input: Buffer,
|
||||
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
|
||||
force_show_panics: bool,
|
||||
) -> Buffer {
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
enum State<T> {
|
||||
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(BridgeConfig {
|
||||
input,
|
||||
dispatch: (&mut dispatch).into(),
|
||||
force_show_panics,
|
||||
_marker: marker::PhantomData,
|
||||
});
|
||||
|
||||
// Wake up the server so it can exit the dispatch loop.
|
||||
drop(state2);
|
||||
server_thread.unpark();
|
||||
|
||||
r
|
||||
});
|
||||
|
||||
// Check whether `state2` was dropped, to know when to stop.
|
||||
while Arc::get_mut(&mut state).is_none() {
|
||||
thread::park();
|
||||
let mut b = match &mut *state.lock().unwrap() {
|
||||
State::Req(b) => b.take(),
|
||||
_ => continue,
|
||||
};
|
||||
b = dispatcher.dispatch(b.take());
|
||||
*state.lock().unwrap() = State::Res(b);
|
||||
join_handle.thread().unpark();
|
||||
}
|
||||
|
||||
join_handle.join().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
fn run_server<
|
||||
S: Server,
|
||||
I: Encode<HandleStore<MarkedTypes<S>>>,
|
||||
O: for<'a, 's> DecodeMut<'a, 's, HandleStore<MarkedTypes<S>>>,
|
||||
>(
|
||||
strategy: &impl ExecutionStrategy,
|
||||
handle_counters: &'static client::HandleCounters,
|
||||
server: S,
|
||||
input: I,
|
||||
run_client: extern "C" fn(BridgeConfig<'_>) -> Buffer,
|
||||
force_show_panics: bool,
|
||||
) -> Result<O, PanicMessage> {
|
||||
let mut dispatcher =
|
||||
Dispatcher { handle_store: HandleStore::new(handle_counters), server: MarkedTypes(server) };
|
||||
|
||||
let globals = dispatcher.server.globals();
|
||||
|
||||
let mut buf = Buffer::new();
|
||||
(globals, input).encode(&mut buf, &mut dispatcher.handle_store);
|
||||
|
||||
buf = strategy.run_bridge_and_client(&mut dispatcher, buf, run_client, force_show_panics);
|
||||
|
||||
Result::decode(&mut &buf[..], &mut dispatcher.handle_store)
|
||||
}
|
||||
|
||||
impl client::Client<super::super::TokenStream, super::super::TokenStream> {
|
||||
pub fn run<S>(
|
||||
&self,
|
||||
strategy: &impl ExecutionStrategy,
|
||||
server: S,
|
||||
input: S::TokenStream,
|
||||
force_show_panics: bool,
|
||||
) -> Result<S::TokenStream, PanicMessage>
|
||||
where
|
||||
S: Server,
|
||||
S::TokenStream: Default,
|
||||
{
|
||||
let client::Client { get_handle_counters, run, _marker } = *self;
|
||||
run_server(
|
||||
strategy,
|
||||
get_handle_counters(),
|
||||
server,
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input),
|
||||
run,
|
||||
force_show_panics,
|
||||
)
|
||||
.map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
|
||||
}
|
||||
}
|
||||
|
||||
impl
|
||||
client::Client<
|
||||
(super::super::TokenStream, super::super::TokenStream),
|
||||
super::super::TokenStream,
|
||||
>
|
||||
{
|
||||
pub fn run<S>(
|
||||
&self,
|
||||
strategy: &impl ExecutionStrategy,
|
||||
server: S,
|
||||
input: S::TokenStream,
|
||||
input2: S::TokenStream,
|
||||
force_show_panics: bool,
|
||||
) -> Result<S::TokenStream, PanicMessage>
|
||||
where
|
||||
S: Server,
|
||||
S::TokenStream: Default,
|
||||
{
|
||||
let client::Client { get_handle_counters, run, _marker } = *self;
|
||||
run_server(
|
||||
strategy,
|
||||
get_handle_counters(),
|
||||
server,
|
||||
(
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input),
|
||||
<MarkedTypes<S> as Types>::TokenStream::mark(input2),
|
||||
),
|
||||
run,
|
||||
force_show_panics,
|
||||
)
|
||||
.map(|s| <Option<<MarkedTypes<S> as Types>::TokenStream>>::unmark(s).unwrap_or_default())
|
||||
}
|
||||
}
|
@ -1,166 +0,0 @@
|
||||
//! lib-proc-macro diagnostic
|
||||
//!
|
||||
//! Copy from <https://github.com/rust-lang/rust/blob/e45d9973b2665897a768312e971b82cc62633103/src/libproc_macro/diagnostic.rs>
|
||||
//! 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<Span>`.
|
||||
fn into_spans(self) -> Vec<Span>;
|
||||
}
|
||||
|
||||
impl MultiSpan for Span {
|
||||
fn into_spans(self) -> Vec<Span> {
|
||||
vec![self]
|
||||
}
|
||||
}
|
||||
|
||||
impl MultiSpan for Vec<Span> {
|
||||
fn into_spans(self) -> Vec<Span> {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> MultiSpan for &'a [Span] {
|
||||
fn into_spans(self) -> Vec<Span> {
|
||||
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<Span>,
|
||||
children: Vec<Diagnostic>,
|
||||
}
|
||||
|
||||
macro_rules! diagnostic_child_methods {
|
||||
($spanned:ident, $regular:ident, $level:expr) => {
|
||||
#[doc = concat!("Adds a new child diagnostics message to `self` with the [`",
|
||||
stringify!($level), "`] level, and the given `spans` and `message`.")]
|
||||
pub fn $spanned<S, T>(mut self, spans: S, message: T) -> Diagnostic
|
||||
where
|
||||
S: MultiSpan,
|
||||
T: Into<String>,
|
||||
{
|
||||
self.children.push(Diagnostic::spanned(spans, $level, message));
|
||||
self
|
||||
}
|
||||
|
||||
#[doc = concat!("Adds a new child diagnostic message to `self` with the [`",
|
||||
stringify!($level), "`] level, and the given `message`.")]
|
||||
pub fn $regular<T: Into<String>>(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::Item> {
|
||||
self.0.next()
|
||||
}
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Creates a new diagnostic with the given `level` and `message`.
|
||||
pub fn new<T: Into<String>>(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<S, T>(spans: S, level: Level, message: T) -> Diagnostic
|
||||
where
|
||||
S: MultiSpan,
|
||||
T: Into<String>,
|
||||
{
|
||||
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<T: Into<String>>(&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<S: MultiSpan>(&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<Span>) -> 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();
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,139 +0,0 @@
|
||||
//! # Quasiquoter
|
||||
//! This file contains the implementation internals of the quasiquoter provided by `quote!`.
|
||||
|
||||
//! This quasiquoter uses macros 2.0 hygiene to reliably access
|
||||
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
|
||||
|
||||
use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
|
||||
|
||||
macro_rules! quote_tt {
|
||||
(($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
|
||||
([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
|
||||
({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
|
||||
(,) => { Punct::new(',', Spacing::Alone) };
|
||||
(.) => { Punct::new('.', Spacing::Alone) };
|
||||
(;) => { Punct::new(';', Spacing::Alone) };
|
||||
(!) => { Punct::new('!', Spacing::Alone) };
|
||||
(<) => { Punct::new('<', Spacing::Alone) };
|
||||
(>) => { Punct::new('>', Spacing::Alone) };
|
||||
(&) => { Punct::new('&', Spacing::Alone) };
|
||||
(=) => { Punct::new('=', Spacing::Alone) };
|
||||
($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
|
||||
}
|
||||
|
||||
macro_rules! quote_ts {
|
||||
((@ $($t:tt)*)) => { $($t)* };
|
||||
(::) => {
|
||||
[
|
||||
TokenTree::from(Punct::new(':', Spacing::Joint)),
|
||||
TokenTree::from(Punct::new(':', Spacing::Alone)),
|
||||
].iter()
|
||||
.cloned()
|
||||
.map(|mut x| {
|
||||
x.set_span(Span::def_site());
|
||||
x
|
||||
})
|
||||
.collect::<TokenStream>()
|
||||
};
|
||||
($t:tt) => { TokenTree::from(quote_tt!($t)) };
|
||||
}
|
||||
|
||||
/// Simpler version of the real `quote!` macro, implemented solely
|
||||
/// through `macro_rules`, for bootstrapping the real implementation
|
||||
/// (see the `quote` function), which does not have access to the
|
||||
/// real `quote!` macro due to the `proc_macro` crate not being
|
||||
/// able to depend on itself.
|
||||
///
|
||||
/// Note: supported tokens are a subset of the real `quote!`, but
|
||||
/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
|
||||
macro_rules! quote {
|
||||
() => { TokenStream::new() };
|
||||
($($t:tt)*) => {
|
||||
[
|
||||
$(TokenStream::from(quote_ts!($t)),)*
|
||||
].iter().cloned().collect::<TokenStream>()
|
||||
};
|
||||
}
|
||||
|
||||
/// Quote a `TokenStream` into a `TokenStream`.
|
||||
/// This is the actual implementation of the `quote!()` proc macro.
|
||||
///
|
||||
/// It is loaded by the compiler in `register_builtin_macros`.
|
||||
pub fn quote(stream: TokenStream) -> TokenStream {
|
||||
if stream.is_empty() {
|
||||
return quote!(super::TokenStream::new());
|
||||
}
|
||||
let proc_macro_crate = quote!(crate);
|
||||
let mut after_dollar = false;
|
||||
let tokens = stream
|
||||
.into_iter()
|
||||
.filter_map(|tree| {
|
||||
if after_dollar {
|
||||
after_dollar = false;
|
||||
match tree {
|
||||
TokenTree::Ident(_) => {
|
||||
return Some(quote!(Into::<super::TokenStream>::into(
|
||||
Clone::clone(&(@ tree))),));
|
||||
}
|
||||
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
|
||||
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
|
||||
}
|
||||
} else if let TokenTree::Punct(ref tt) = tree {
|
||||
if tt.as_char() == '$' {
|
||||
after_dollar = true;
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
Some(quote!(super::TokenStream::from((@ match tree {
|
||||
TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new(
|
||||
(@ TokenTree::from(Literal::character(tt.as_char()))),
|
||||
(@ match tt.spacing() {
|
||||
Spacing::Alone => quote!(super::Spacing::Alone),
|
||||
Spacing::Joint => quote!(super::Spacing::Joint),
|
||||
}),
|
||||
))),
|
||||
TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new(
|
||||
(@ match tt.delimiter() {
|
||||
Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis),
|
||||
Delimiter::Brace => quote!(super::Delimiter::Brace),
|
||||
Delimiter::Bracket => quote!(super::Delimiter::Bracket),
|
||||
Delimiter::None => quote!(super::Delimiter::None),
|
||||
}),
|
||||
(@ quote(tt.stream())),
|
||||
))),
|
||||
TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new(
|
||||
(@ TokenTree::from(Literal::string(&tt.to_string()))),
|
||||
(@ quote_span(proc_macro_crate.clone(), tt.span())),
|
||||
))),
|
||||
TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({
|
||||
let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
|
||||
.parse::<super::TokenStream>()
|
||||
.unwrap()
|
||||
.into_iter();
|
||||
if let (Some(super::TokenTree::Literal(mut lit)), None) =
|
||||
(iter.next(), iter.next())
|
||||
{
|
||||
lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
|
||||
lit
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}))
|
||||
})),))
|
||||
})
|
||||
.collect::<TokenStream>();
|
||||
|
||||
if after_dollar {
|
||||
panic!("unexpected trailing `$` in `quote!`");
|
||||
}
|
||||
|
||||
quote!([(@ tokens)].iter().cloned().collect::<super::TokenStream>())
|
||||
}
|
||||
|
||||
/// Quote a `Span` into a `TokenStream`.
|
||||
/// This is needed to implement a custom quoter.
|
||||
pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
|
||||
let id = span.save_span();
|
||||
quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
|
||||
}
|
@ -1,791 +0,0 @@
|
||||
//! Rustc proc-macro server implementation with tt
|
||||
//!
|
||||
//! Based on idea from <https://github.com/fedochet/rust-proc-macro-expander>
|
||||
//! The lib-proc-macro server backend is `TokenStream`-agnostic, such that
|
||||
//! we could provide any TokenStream implementation.
|
||||
//! The original idea from fedochet is using proc-macro2 as backend,
|
||||
//! we use tt instead for better integration with RA.
|
||||
//!
|
||||
//! FIXME: No span and source file information is implemented yet
|
||||
|
||||
use super::proc_macro::bridge::{self, server};
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::hash::Hash;
|
||||
use std::ops::Bound;
|
||||
use std::{ascii, vec::IntoIter};
|
||||
|
||||
type Group = tt::Subtree;
|
||||
type TokenTree = tt::TokenTree;
|
||||
type Punct = tt::Punct;
|
||||
type Spacing = tt::Spacing;
|
||||
type Literal = tt::Literal;
|
||||
type Span = tt::TokenId;
|
||||
|
||||
#[derive(Debug, Default, Clone)]
|
||||
pub struct TokenStream {
|
||||
pub token_trees: Vec<TokenTree>,
|
||||
}
|
||||
|
||||
impl TokenStream {
|
||||
pub fn new() -> Self {
|
||||
TokenStream::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<TokenTree> for TokenStream {
|
||||
fn from(tree: TokenTree) -> TokenStream {
|
||||
TokenStream { token_trees: vec![tree] }
|
||||
}
|
||||
}
|
||||
|
||||
/// Collects a number of token trees into a single stream.
|
||||
impl FromIterator<TokenTree> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenTree>>(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<TokenStream> for TokenStream {
|
||||
fn from_iter<I: IntoIterator<Item = TokenStream>>(streams: I) -> Self {
|
||||
let mut builder = TokenStreamBuilder::new();
|
||||
streams.into_iter().for_each(|stream| builder.push(stream));
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<TokenTree> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenTree>>(&mut self, trees: I) {
|
||||
self.extend(trees.into_iter().map(TokenStream::from));
|
||||
}
|
||||
}
|
||||
|
||||
impl Extend<TokenStream> for TokenStream {
|
||||
fn extend<I: IntoIterator<Item = TokenStream>>(&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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SourceFile {
|
||||
// FIXME stub
|
||||
}
|
||||
|
||||
type Level = super::proc_macro::Level;
|
||||
type LineColumn = super::proc_macro::LineColumn;
|
||||
|
||||
/// A structure representing a diagnostic message and associated children
|
||||
/// messages.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Diagnostic {
|
||||
level: Level,
|
||||
message: String,
|
||||
spans: Vec<Span>,
|
||||
children: Vec<Diagnostic>,
|
||||
}
|
||||
|
||||
impl Diagnostic {
|
||||
/// Creates a new diagnostic with the given `level` and `message`.
|
||||
pub fn new<T: Into<String>>(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<IdentData, u32>,
|
||||
ident_data: Vec<IdentData>,
|
||||
}
|
||||
|
||||
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<TokenTree>;
|
||||
|
||||
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<TokenStream, LexError> {
|
||||
let (subtree, _token_map) =
|
||||
mbe::parse_to_token_tree(src).ok_or("Failed to parse from mbe")?;
|
||||
|
||||
let subtree = subtree_replace_token_ids_with_unspecified(subtree);
|
||||
Ok(TokenStream::with_subtree(subtree))
|
||||
}
|
||||
}
|
||||
|
||||
impl ToString for TokenStream {
|
||||
fn to_string(&self) -> String {
|
||||
tt::pretty(&self.token_trees)
|
||||
}
|
||||
}
|
||||
|
||||
fn subtree_replace_token_ids_with_unspecified(subtree: tt::Subtree) -> tt::Subtree {
|
||||
tt::Subtree {
|
||||
delimiter: 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<TokenTree>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct RustAnalyzer {
|
||||
ident_interner: IdentInterner,
|
||||
// FIXME: store span information here.
|
||||
}
|
||||
|
||||
impl server::Types for RustAnalyzer {
|
||||
type FreeFunctions = FreeFunctions;
|
||||
type TokenStream = TokenStream;
|
||||
type Ident = IdentId;
|
||||
type Literal = Literal;
|
||||
type SourceFile = SourceFile;
|
||||
type Diagnostic = Diagnostic;
|
||||
type Span = Span;
|
||||
type MultiSpan = Vec<Span>;
|
||||
}
|
||||
|
||||
impl server::FreeFunctions for RustAnalyzer {
|
||||
fn track_env_var(&mut self, _var: &str, _value: Option<&str>) {
|
||||
// FIXME: track env var accesses
|
||||
// https://github.com/rust-lang/rust/pull/71858
|
||||
}
|
||||
fn track_path(&mut self, _path: &str) {}
|
||||
}
|
||||
|
||||
impl server::TokenStream for RustAnalyzer {
|
||||
fn is_empty(&mut self, stream: &Self::TokenStream) -> bool {
|
||||
stream.is_empty()
|
||||
}
|
||||
fn from_str(&mut self, src: &str) -> Self::TokenStream {
|
||||
use std::str::FromStr;
|
||||
|
||||
Self::TokenStream::from_str(src).expect("cannot parse string")
|
||||
}
|
||||
fn to_string(&mut self, stream: &Self::TokenStream) -> String {
|
||||
stream.to_string()
|
||||
}
|
||||
fn from_token_tree(
|
||||
&mut self,
|
||||
tree: bridge::TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>,
|
||||
) -> Self::TokenStream {
|
||||
match tree {
|
||||
bridge::TokenTree::Group(group) => {
|
||||
let group = Group {
|
||||
delimiter: delim_to_internal(group.delimiter),
|
||||
token_trees: match group.stream {
|
||||
Some(stream) => stream.into_iter().collect(),
|
||||
None => Vec::new(),
|
||||
},
|
||||
};
|
||||
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 punct = tt::Punct {
|
||||
char: p.ch as char,
|
||||
spacing: if p.joint { Spacing::Joint } else { Spacing::Alone },
|
||||
id: p.span,
|
||||
};
|
||||
let leaf = tt::Leaf::from(punct);
|
||||
let tree = TokenTree::from(leaf);
|
||||
Self::TokenStream::from_iter(vec![tree])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_expr(&mut self, self_: &Self::TokenStream) -> Result<Self::TokenStream, ()> {
|
||||
Ok(self_.clone())
|
||||
}
|
||||
|
||||
fn concat_trees(
|
||||
&mut self,
|
||||
base: Option<Self::TokenStream>,
|
||||
trees: Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>>,
|
||||
) -> Self::TokenStream {
|
||||
let mut builder = TokenStreamBuilder::new();
|
||||
if let Some(base) = base {
|
||||
builder.push(base);
|
||||
}
|
||||
for tree in trees {
|
||||
builder.push(self.from_token_tree(tree));
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
|
||||
fn concat_streams(
|
||||
&mut self,
|
||||
base: Option<Self::TokenStream>,
|
||||
streams: Vec<Self::TokenStream>,
|
||||
) -> Self::TokenStream {
|
||||
let mut builder = TokenStreamBuilder::new();
|
||||
if let Some(base) = base {
|
||||
builder.push(base);
|
||||
}
|
||||
for stream in streams {
|
||||
builder.push(stream);
|
||||
}
|
||||
builder.build()
|
||||
}
|
||||
|
||||
fn into_trees(
|
||||
&mut self,
|
||||
stream: Self::TokenStream,
|
||||
) -> Vec<bridge::TokenTree<Self::TokenStream, Self::Span, Self::Ident, Self::Literal>> {
|
||||
stream
|
||||
.into_iter()
|
||||
.map(|tree| match tree {
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(ident)) => {
|
||||
bridge::TokenTree::Ident(IdentId(self.ident_interner.intern(&IdentData(ident))))
|
||||
}
|
||||
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => bridge::TokenTree::Literal(lit),
|
||||
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) => {
|
||||
bridge::TokenTree::Punct(bridge::Punct {
|
||||
ch: punct.char as u8,
|
||||
joint: punct.spacing == Spacing::Joint,
|
||||
span: punct.id,
|
||||
})
|
||||
}
|
||||
tt::TokenTree::Subtree(subtree) => bridge::TokenTree::Group(bridge::Group {
|
||||
delimiter: delim_to_external(subtree.delimiter),
|
||||
stream: if subtree.token_trees.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(subtree.token_trees.into_iter().collect())
|
||||
},
|
||||
span: bridge::DelimSpan::from_single(
|
||||
subtree.delimiter.map_or(Span::unspecified(), |del| del.id),
|
||||
),
|
||||
}),
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
|
||||
fn delim_to_internal(d: bridge::Delimiter) -> Option<tt::Delimiter> {
|
||||
let kind = match d {
|
||||
bridge::Delimiter::Parenthesis => tt::DelimiterKind::Parenthesis,
|
||||
bridge::Delimiter::Brace => tt::DelimiterKind::Brace,
|
||||
bridge::Delimiter::Bracket => tt::DelimiterKind::Bracket,
|
||||
bridge::Delimiter::None => return None,
|
||||
};
|
||||
Some(tt::Delimiter { id: tt::TokenId::unspecified(), kind })
|
||||
}
|
||||
|
||||
fn delim_to_external(d: Option<tt::Delimiter>) -> 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::Ident for RustAnalyzer {
|
||||
fn new(&mut self, string: &str, span: Self::Span, _is_raw: bool) -> Self::Ident {
|
||||
IdentId(self.ident_interner.intern(&IdentData(tt::Ident { text: string.into(), id: span })))
|
||||
}
|
||||
|
||||
fn span(&mut self, ident: Self::Ident) -> Self::Span {
|
||||
self.ident_interner.get(ident.0).0.id
|
||||
}
|
||||
fn with_span(&mut self, ident: Self::Ident, span: Self::Span) -> Self::Ident {
|
||||
let data = self.ident_interner.get(ident.0);
|
||||
let new = IdentData(tt::Ident { id: span, ..data.0.clone() });
|
||||
IdentId(self.ident_interner.intern(&new))
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Literal for RustAnalyzer {
|
||||
fn debug_kind(&mut self, _literal: &Self::Literal) -> String {
|
||||
// r-a: debug_kind and suffix are unsupported; corresponding client code has been changed to not call these.
|
||||
// They must still be present to be ABI-compatible and work with upstream proc_macro.
|
||||
"".to_owned()
|
||||
}
|
||||
fn from_str(&mut self, s: &str) -> Result<Self::Literal, ()> {
|
||||
Ok(Literal { text: s.into(), id: tt::TokenId::unspecified() })
|
||||
}
|
||||
fn symbol(&mut self, literal: &Self::Literal) -> String {
|
||||
literal.text.to_string()
|
||||
}
|
||||
fn suffix(&mut self, _literal: &Self::Literal) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn to_string(&mut self, literal: &Self::Literal) -> String {
|
||||
literal.to_string()
|
||||
}
|
||||
|
||||
fn integer(&mut self, n: &str) -> Self::Literal {
|
||||
let n = match n.parse::<i128>() {
|
||||
Ok(n) => n.to_string(),
|
||||
Err(_) => n.parse::<u128>().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::<char>::into)
|
||||
.collect::<String>();
|
||||
|
||||
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) {
|
||||
literal.id = span;
|
||||
}
|
||||
|
||||
fn subspan(
|
||||
&mut self,
|
||||
_literal: &Self::Literal,
|
||||
_start: Bound<usize>,
|
||||
_end: Bound<usize>,
|
||||
) -> Option<Self::Span> {
|
||||
// FIXME handle span
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl server::SourceFile for RustAnalyzer {
|
||||
// FIXME these are all stubs
|
||||
fn eq(&mut self, _file1: &Self::SourceFile, _file2: &Self::SourceFile) -> bool {
|
||||
true
|
||||
}
|
||||
fn path(&mut self, _file: &Self::SourceFile) -> String {
|
||||
String::new()
|
||||
}
|
||||
fn is_real(&mut self, _file: &Self::SourceFile) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Diagnostic for RustAnalyzer {
|
||||
fn new(&mut self, level: Level, msg: &str, spans: Self::MultiSpan) -> Self::Diagnostic {
|
||||
let mut diag = Diagnostic::new(level, msg);
|
||||
diag.spans = spans;
|
||||
diag
|
||||
}
|
||||
|
||||
fn sub(
|
||||
&mut self,
|
||||
_diag: &mut Self::Diagnostic,
|
||||
_level: Level,
|
||||
_msg: &str,
|
||||
_spans: Self::MultiSpan,
|
||||
) {
|
||||
// FIXME handle diagnostic
|
||||
//
|
||||
}
|
||||
|
||||
fn emit(&mut self, _diag: Self::Diagnostic) {
|
||||
// FIXME handle diagnostic
|
||||
// diag.emit()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Span for RustAnalyzer {
|
||||
fn debug(&mut self, span: Self::Span) -> String {
|
||||
format!("{:?}", span.0)
|
||||
}
|
||||
fn source_file(&mut self, _span: Self::Span) -> Self::SourceFile {
|
||||
SourceFile {}
|
||||
}
|
||||
fn save_span(&mut self, _span: Self::Span) -> usize {
|
||||
// FIXME stub
|
||||
0
|
||||
}
|
||||
fn recover_proc_macro_span(&mut self, _id: usize) -> Self::Span {
|
||||
// FIXME stub
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
/// Recent feature, not yet in the proc_macro
|
||||
///
|
||||
/// See PR:
|
||||
/// https://github.com/rust-lang/rust/pull/55780
|
||||
fn source_text(&mut self, _span: Self::Span) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn parent(&mut self, _span: Self::Span) -> Option<Self::Span> {
|
||||
// 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<Self::Span> {
|
||||
// Just return the first span again, because some macros will unwrap the result.
|
||||
Some(first)
|
||||
}
|
||||
fn resolved_at(&mut self, _span: Self::Span, _at: Self::Span) -> Self::Span {
|
||||
// FIXME handle span
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
|
||||
fn after(&mut self, _self_: Self::Span) -> Self::Span {
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
|
||||
fn before(&mut self, _self_: Self::Span) -> Self::Span {
|
||||
tt::TokenId::unspecified()
|
||||
}
|
||||
}
|
||||
|
||||
impl server::MultiSpan for RustAnalyzer {
|
||||
fn new(&mut self) -> Self::MultiSpan {
|
||||
// FIXME handle span
|
||||
vec![]
|
||||
}
|
||||
|
||||
fn push(&mut self, other: &mut Self::MultiSpan, span: Self::Span) {
|
||||
//TODP
|
||||
other.push(span)
|
||||
}
|
||||
}
|
||||
|
||||
impl server::Server for RustAnalyzer {
|
||||
fn globals(&mut self) -> bridge::ExpnGlobals<Self::Span> {
|
||||
bridge::ExpnGlobals {
|
||||
def_site: Span::unspecified(),
|
||||
call_site: Span::unspecified(),
|
||||
mixed_site: Span::unspecified(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::proc_macro::bridge::server::Literal;
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_ra_server_literals() {
|
||||
let mut srv = RustAnalyzer { ident_interner: IdentInterner::default() };
|
||||
assert_eq!(srv.integer("1234").text, "1234");
|
||||
|
||||
assert_eq!(srv.typed_integer("12", "u8").text, "12u8");
|
||||
assert_eq!(srv.typed_integer("255", "u16").text, "255u16");
|
||||
assert_eq!(srv.typed_integer("1234", "u32").text, "1234u32");
|
||||
assert_eq!(srv.typed_integer("15846685", "u64").text, "15846685u64");
|
||||
assert_eq!(srv.typed_integer("15846685258", "u128").text, "15846685258u128");
|
||||
assert_eq!(srv.typed_integer("156788984", "usize").text, "156788984usize");
|
||||
assert_eq!(srv.typed_integer("127", "i8").text, "127i8");
|
||||
assert_eq!(srv.typed_integer("255", "i16").text, "255i16");
|
||||
assert_eq!(srv.typed_integer("1234", "i32").text, "1234i32");
|
||||
assert_eq!(srv.typed_integer("15846685", "i64").text, "15846685i64");
|
||||
assert_eq!(srv.typed_integer("15846685258", "i128").text, "15846685258i128");
|
||||
assert_eq!(srv.float("0").text, "0.0");
|
||||
assert_eq!(srv.float("15684.5867").text, "15684.5867");
|
||||
assert_eq!(srv.f32("15684.58").text, "15684.58f32");
|
||||
assert_eq!(srv.f64("15684.58").text, "15684.58f64");
|
||||
|
||||
assert_eq!(srv.string("hello_world").text, "\"hello_world\"");
|
||||
assert_eq!(srv.character('c').text, "'c'");
|
||||
assert_eq!(srv.byte_string(b"1234586\x88").text, "b\"1234586\\x88\"");
|
||||
|
||||
// u128::max
|
||||
assert_eq!(
|
||||
srv.integer("340282366920938463463374607431768211455").text,
|
||||
"340282366920938463463374607431768211455"
|
||||
);
|
||||
// i128::min
|
||||
assert_eq!(
|
||||
srv.integer("-170141183460469231731687303715884105728").text,
|
||||
"-170141183460469231731687303715884105728"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ra_server_to_string() {
|
||||
let s = TokenStream {
|
||||
token_trees: vec![
|
||||
tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident {
|
||||
text: "struct".into(),
|
||||
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_ra_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(),
|
||||
}))
|
||||
);
|
||||
}
|
||||
}
|
@ -25,7 +25,6 @@
|
||||
|
||||
mod abi_1_58;
|
||||
mod abi_1_63;
|
||||
mod abi_1_64;
|
||||
#[cfg(feature = "sysroot-abi")]
|
||||
mod abi_sysroot;
|
||||
|
||||
@ -34,12 +33,11 @@ include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
|
||||
|
||||
// Used by `test/utils.rs`
|
||||
#[cfg(test)]
|
||||
pub(crate) use abi_1_64::TokenStream as TestTokenStream;
|
||||
pub(crate) use abi_1_63::TokenStream as TestTokenStream;
|
||||
|
||||
use super::dylib::LoadProcMacroDylibError;
|
||||
pub(crate) use abi_1_58::Abi as Abi_1_58;
|
||||
pub(crate) use abi_1_63::Abi as Abi_1_63;
|
||||
pub(crate) use abi_1_64::Abi as Abi_1_64;
|
||||
#[cfg(feature = "sysroot-abi")]
|
||||
pub(crate) use abi_sysroot::Abi as Abi_Sysroot;
|
||||
use libloading::Library;
|
||||
@ -58,7 +56,6 @@ impl PanicMessage {
|
||||
pub(crate) enum Abi {
|
||||
Abi1_58(Abi_1_58),
|
||||
Abi1_63(Abi_1_63),
|
||||
Abi1_64(Abi_1_64),
|
||||
#[cfg(feature = "sysroot-abi")]
|
||||
AbiSysroot(Abi_Sysroot),
|
||||
}
|
||||
@ -120,10 +117,6 @@ impl Abi {
|
||||
let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
|
||||
Ok(Abi::Abi1_63(inner))
|
||||
}
|
||||
(1, 64..) => {
|
||||
let inner = unsafe { Abi_1_64::from_lib(lib, symbol_name) }?;
|
||||
Ok(Abi::Abi1_64(inner))
|
||||
}
|
||||
_ => Err(LoadProcMacroDylibError::UnsupportedABI),
|
||||
}
|
||||
}
|
||||
@ -137,7 +130,6 @@ impl Abi {
|
||||
match self {
|
||||
Self::Abi1_58(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
Self::Abi1_64(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
#[cfg(feature = "sysroot-abi")]
|
||||
Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes),
|
||||
}
|
||||
@ -147,7 +139,6 @@ impl Abi {
|
||||
match self {
|
||||
Self::Abi1_58(abi) => abi.list_macros(),
|
||||
Self::Abi1_63(abi) => abi.list_macros(),
|
||||
Self::Abi1_64(abi) => abi.list_macros(),
|
||||
#[cfg(feature = "sysroot-abi")]
|
||||
Self::AbiSysroot(abi) => abi.list_macros(),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user