Introduce proc_macro_srv::abis, impl 1.47 and 1.55

Rather than a "Stable" and "Nightly" ABI we instead name ABIs based on
the version of the rust compiler in which they were introduced. We place
these ABIs in a new module - `proc_macro_srv::abis` - where we also add
some mchinery to abstract over ABIs. This should make it easy to add new
ABIs at a later date as the rust compiler evolves.
This commit is contained in:
Alex Good 2021-07-12 15:47:47 +01:00
parent 14a51d28b5
commit e240eb67a8
28 changed files with 390 additions and 327 deletions

View file

@ -0,0 +1,106 @@
//! Macro ABI for version 1.47 of rustc
#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;
#[allow(dead_code)]
#[doc(hidden)]
mod rustc_server;
use libloading::Library;
use proc_macro_api::ProcMacroKind;
use super::PanicMessage;
pub use rustc_server::TokenStream;
pub(crate) struct Abi {
exported_macros: Vec<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 = rustc_server::TokenStream::with_subtree(macro_body.clone());
let parsed_attributes = attributes.map_or(rustc_server::TokenStream::new(), |attr| {
rustc_server::TokenStream::with_subtree(attr.clone())
});
for proc_macro in &self.exported_macros {
match proc_macro {
proc_macro::bridge::client::ProcMacro::CustomDerive {
trait_name, client, ..
} if *trait_name == macro_name => {
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_attributes,
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
_ => continue,
}
}
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
self.exported_macros
.iter()
.map(|proc_macro| match proc_macro {
proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
(trait_name.to_string(), ProcMacroKind::CustomDerive)
}
proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
(name.to_string(), ProcMacroKind::FuncLike)
}
proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
(name.to_string(), ProcMacroKind::Attr)
}
})
.collect()
}
}

View file

@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
//! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*;
macro_rules! define_handles {
@ -401,26 +402,26 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
b
}
impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream) -> CrateTokenStream> {
pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |input| f(crate::TokenStream(input)).0)
run_client(bridge, |input| f(CrateTokenStream(input)).0)
}
Client { get_handle_counters: HandleCounters::get, run, f }
}
}
impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |(input, input2)| {
f(crate::TokenStream(input), crate::TokenStream(input2)).0
f(CrateTokenStream(input), CrateTokenStream(input2)).0
})
}
Client { get_handle_counters: HandleCounters::get, run, f }
@ -433,17 +434,17 @@ pub enum ProcMacro {
CustomDerive {
trait_name: &'static str,
attributes: &'static [&'static str],
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},
Attr {
name: &'static str,
client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream>,
},
Bang {
name: &'static str,
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},
}
@ -465,19 +466,19 @@ impl ProcMacro {
pub fn custom_derive(
trait_name: &'static str,
attributes: &'static [&'static str],
expand: fn(crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
}
pub fn attr(
name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::Attr { name, client: Client::expand2(expand) }
}
pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
pub fn bang(name: &'static str, expand: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
ProcMacro::Bang { name, client: Client::expand1(expand) }
}
}

View file

@ -13,7 +13,7 @@
#![deny(unsafe_code)]
pub use crate::proc_macro::{Delimiter, Level, LineColumn, Spacing};
pub use super::{Delimiter, Level, LineColumn, Spacing};
use std::fmt;
use std::hash::Hash;
use std::marker;

View file

@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features
use super::super::TokenStream as ProcMacroTokenStream;
use super::*;
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
@ -308,7 +309,7 @@ fn run_server<
Result::decode(&mut &b[..], &mut dispatcher.handle_store)
}
impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(ProcMacroTokenStream) -> ProcMacroTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,
@ -330,7 +331,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
}
}
impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(ProcMacroTokenStream, ProcMacroTokenStream) -> ProcMacroTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,

View file

@ -133,7 +133,7 @@ impl Extend<TokenStream> for TokenStream {
/// Public implementation details for the `TokenStream` type, such as iterators.
pub mod token_stream {
use crate::proc_macro::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
use super::{bridge, Group, Ident, Literal, Punct, TokenStream, TokenTree};
/// An iterator over `TokenStream`'s `TokenTree`s.
/// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups,

View file

@ -8,7 +8,7 @@
//!
//! FIXME: No span and source file information is implemented yet
use crate::proc_macro::bridge::{self, server};
use super::proc_macro::bridge::{self, server};
use std::collections::HashMap;
use std::hash::Hash;
@ -97,9 +97,9 @@ impl Extend<TokenStream> for TokenStream {
}
}
type Level = crate::proc_macro::Level;
type LineColumn = crate::proc_macro::LineColumn;
type SourceFile = crate::proc_macro::SourceFile;
type Level = super::proc_macro::Level;
type LineColumn = super::proc_macro::LineColumn;
type SourceFile = super::proc_macro::SourceFile;
/// A structure representing a diagnostic message and associated children
/// messages.
@ -734,8 +734,8 @@ impl server::MultiSpan for Rustc {
#[cfg(test)]
mod tests {
use super::super::proc_macro::bridge::server::Literal;
use super::*;
use crate::proc_macro::bridge::server::Literal;
#[test]
fn test_rustc_server_literals() {

View file

@ -0,0 +1,104 @@
//! Macro ABI for version 1.55 of rustc
#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;
#[allow(dead_code)]
#[doc(hidden)]
mod rustc_server;
use libloading::Library;
use proc_macro_api::ProcMacroKind;
use super::PanicMessage;
pub(crate) struct Abi {
exported_macros: Vec<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 = rustc_server::TokenStream::with_subtree(macro_body.clone());
let parsed_attributes = attributes.map_or(rustc_server::TokenStream::new(), |attr| {
rustc_server::TokenStream::with_subtree(attr.clone())
});
for proc_macro in &self.exported_macros {
match proc_macro {
proc_macro::bridge::client::ProcMacro::CustomDerive {
trait_name, client, ..
} if *trait_name == macro_name => {
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
proc_macro::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&proc_macro::bridge::server::SameThread,
rustc_server::Rustc::default(),
parsed_attributes,
parsed_body,
false,
);
return res.map(|it| it.into_subtree()).map_err(PanicMessage::from);
}
_ => continue,
}
}
Err(proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()).into())
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
self.exported_macros
.iter()
.map(|proc_macro| match proc_macro {
proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
(trait_name.to_string(), ProcMacroKind::CustomDerive)
}
proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
(name.to_string(), ProcMacroKind::FuncLike)
}
proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
(name.to_string(), ProcMacroKind::Attr)
}
})
.collect()
}
}

View file

@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
//! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*;
macro_rules! define_handles {
@ -401,26 +402,26 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
b
}
impl Client<fn(crate::TokenStream) -> crate::TokenStream> {
pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream) -> CrateTokenStream> {
pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |input| f(crate::TokenStream(input)).0)
run_client(bridge, |input| f(CrateTokenStream(input)).0)
}
Client { get_handle_counters: HandleCounters::get, run, f }
}
}
impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self {
impl Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run(
bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> {
run_client(bridge, |(input, input2)| {
f(crate::TokenStream(input), crate::TokenStream(input2)).0
f(CrateTokenStream(input), CrateTokenStream(input2)).0
})
}
Client { get_handle_counters: HandleCounters::get, run, f }
@ -433,17 +434,17 @@ pub enum ProcMacro {
CustomDerive {
trait_name: &'static str,
attributes: &'static [&'static str],
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},
Attr {
name: &'static str,
client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream>,
},
Bang {
name: &'static str,
client: Client<fn(crate::TokenStream) -> crate::TokenStream>,
client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
},
}
@ -465,19 +466,19 @@ impl ProcMacro {
pub fn custom_derive(
trait_name: &'static str,
attributes: &'static [&'static str],
expand: fn(crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
}
pub fn attr(
name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream,
expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Self {
ProcMacro::Attr { name, client: Client::expand2(expand) }
}
pub fn bang(name: &'static str, expand: fn(crate::TokenStream) -> crate::TokenStream) -> Self {
pub fn bang(name: &'static str, expand: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
ProcMacro::Bang { name, client: Client::expand1(expand) }
}
}

View file

@ -3,6 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*;
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`.
@ -302,7 +303,7 @@ fn run_server<
Result::decode(&mut &b[..], &mut dispatcher.handle_store)
}
impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(CrateTokenStream) -> CrateTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,
@ -324,7 +325,7 @@ impl client::Client<fn(crate::TokenStream) -> crate::TokenStream> {
}
}
impl client::Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> {
impl client::Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
pub fn run<S: Server>(
&self,
strategy: &impl ExecutionStrategy,

View file

@ -3,7 +3,7 @@
//! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/diagnostic.rs>
//! augmented with removing unstable features
use crate::proc_macro::Span;
use super::Span;
/// An enum representing a diagnostic level.
#[derive(Copy, Clone, Debug)]
@ -146,15 +146,15 @@ impl Diagnostic {
/// Emit the diagnostic.
pub fn emit(self) {
fn to_internal(spans: Vec<Span>) -> crate::proc_macro::bridge::client::MultiSpan {
let mut multi_span = crate::proc_macro::bridge::client::MultiSpan::new();
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 = crate::proc_macro::bridge::client::Diagnostic::new(
let mut diag = super::bridge::client::Diagnostic::new(
self.level,
&self.message[..],
to_internal(self.spans),

View file

@ -8,7 +8,7 @@
//!
//! FIXME: No span and source file information is implemented yet
use crate::proc_macro_nightly::bridge::{self, server};
use super::proc_macro::bridge::{self, server};
use std::collections::HashMap;
use std::hash::Hash;
@ -97,9 +97,9 @@ impl Extend<TokenStream> for TokenStream {
}
}
type Level = crate::proc_macro_nightly::Level;
type LineColumn = crate::proc_macro_nightly::LineColumn;
type SourceFile = crate::proc_macro_nightly::SourceFile;
type Level = super::proc_macro::Level;
type LineColumn = super::proc_macro::LineColumn;
type SourceFile = super::proc_macro::SourceFile;
/// A structure representing a diagnostic message and associated children
/// messages.
@ -737,8 +737,8 @@ impl server::MultiSpan for Rustc {
#[cfg(test)]
mod tests {
use super::super::proc_macro::bridge::server::Literal;
use super::*;
use crate::proc_macro_nightly::bridge::server::Literal;
#[test]
fn test_rustc_server_literals() {

View file

@ -0,0 +1,97 @@
//! Procedural macros are implemented by compiling the macro providing crate
//! to a dynamic library with a particular ABI which the compiler uses to expand
//! macros. Unfortunately this ABI is not specified and can change from version
//! to version of the compiler. To support this we copy the ABI from the rust
//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
//!
//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
//! interface the rest of rust analyzer can use to talk to the macro
//! provider.
//!
//! # Adding a new ABI
//!
//! To add a new ABI you'll need to copy the source of the target proc_macro
//! crate from the source tree of the Rust compiler into this directory tree.
//! Then you'll need to modify it
//! - Remove any feature! or other things which won't compile on stable
//! - change any absolute imports to relative imports within the ABI tree
//!
//! Then you'll need to add a branch to the `Abi` enum and an implementation of
//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
//! need to update the conditionals in `Abi::from_lib` to return your new ABI
//! for the relevant versions of the rust compiler
//!
// pub(crate) so tests can use the TokenStream, more notes in test/utils.rs
pub(crate) mod abi_1_47;
mod abi_1_55;
use super::dylib::LoadProcMacroDylibError;
pub(crate) use abi_1_47::Abi as Abi_1_47;
pub(crate) use abi_1_55::Abi as Abi_1_55;
use libloading::Library;
use proc_macro_api::{ProcMacroKind, RustCInfo};
pub struct PanicMessage {
message: Option<String>,
}
impl PanicMessage {
pub fn as_str(&self) -> Option<String> {
self.message.clone()
}
}
pub(crate) enum Abi {
Abi1_47(Abi_1_47),
Abi1_55(Abi_1_55),
}
impl Abi {
/// Load a new ABI.
///
/// # Arguments
///
/// *`lib` - The dynamic library containing the macro implementations
/// *`symbol_name` - The symbol name the macros can be found attributes
/// *`info` - RustCInfo about the compiler that was used to compile the
/// macro crate. This is the information we use to figure out
/// which ABI to return
pub fn from_lib(
lib: &Library,
symbol_name: String,
info: RustCInfo,
) -> Result<Abi, LoadProcMacroDylibError> {
if info.version.0 != 1 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 47 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 54 {
let inner = unsafe { Abi_1_47::from_lib(lib, symbol_name) }?;
Ok(Abi::Abi1_47(inner))
} else {
let inner = unsafe { Abi_1_55::from_lib(lib, symbol_name) }?;
Ok(Abi::Abi1_55(inner))
}
}
pub fn expand(
&self,
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, PanicMessage> {
match self {
Self::Abi1_55(abi) => abi.expand(macro_name, macro_body, attributes),
Self::Abi1_47(abi) => abi.expand(macro_name, macro_body, attributes),
}
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
match self {
Self::Abi1_47(abi) => abi.list_macros(),
Self::Abi1_55(abi) => abi.list_macros(),
}
}
}

View file

@ -1,7 +1,6 @@
//! Handles dynamic library loading for proc macro
use std::{
convert::{TryFrom, TryInto},
fmt,
fs::File,
io,
@ -11,16 +10,9 @@ use std::{
use libloading::Library;
use memmap2::Mmap;
use object::Object;
use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo};
use proc_macro_api::{read_dylib_info, ProcMacroKind};
use crate::{
proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro},
rustc_server::TokenStream as StableTokenStream,
};
use crate::{
proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro},
rustc_server_nightly::TokenStream as NightlyTokenStream,
};
use super::abis::Abi;
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_";
@ -83,30 +75,10 @@ fn load_library(file: &Path) -> Result<Library, libloading::Error> {
unsafe { UnixLibrary::open(Some(file), RTLD_NOW | RTLD_DEEPBIND).map(|lib| lib.into()) }
}
enum ProcMacroABI {
Stable,
Nightly,
}
impl TryFrom<RustCInfo> for ProcMacroABI {
type Error = LoadProcMacroDylibError;
fn try_from(info: RustCInfo) -> Result<Self, Self::Error> {
if info.version.0 != 1 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 47 {
Err(LoadProcMacroDylibError::UnsupportedABI)
} else if info.version.1 < 54 {
Ok(ProcMacroABI::Stable)
} else {
Ok(ProcMacroABI::Nightly)
}
}
}
#[derive(Debug)]
pub enum LoadProcMacroDylibError {
Io(io::Error),
LibLoading(libloading::Error),
UnsupportedABI,
}
@ -115,6 +87,7 @@ impl fmt::Display for LoadProcMacroDylibError {
match self {
Self::Io(e) => e.fmt(f),
Self::UnsupportedABI => write!(f, "unsupported ABI version"),
Self::LibLoading(e) => e.fmt(f),
}
}
}
@ -125,15 +98,16 @@ impl From<io::Error> for LoadProcMacroDylibError {
}
}
enum ProcMacroLibraryLibloading {
StableProcMacroLibrary {
_lib: Library,
exported_macros: Vec<crate::proc_macro::bridge::client::ProcMacro>,
},
NightlyProcMacroLibrary {
_lib: Library,
exported_macros: Vec<crate::proc_macro_nightly::bridge::client::ProcMacro>,
},
impl From<libloading::Error> for LoadProcMacroDylibError {
fn from(e: libloading::Error) -> Self {
LoadProcMacroDylibError::LibLoading(e)
}
}
struct ProcMacroLibraryLibloading {
// Hold on to the library so it doesn't unload
_lib: Library,
abi: Abi,
}
impl ProcMacroLibraryLibloading {
@ -143,57 +117,10 @@ impl ProcMacroLibraryLibloading {
})?;
let version_info = read_dylib_info(file)?;
let macro_abi: ProcMacroABI = version_info.try_into()?;
let lib = load_library(file).map_err(invalid_data_err)?;
match macro_abi {
ProcMacroABI::Stable => {
let macros: libloading::Symbol<&&[crate::proc_macro::bridge::client::ProcMacro]> =
unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
let macros_vec = macros.to_vec();
Ok(ProcMacroLibraryLibloading::StableProcMacroLibrary {
_lib: lib,
exported_macros: macros_vec,
})
}
ProcMacroABI::Nightly => {
let macros: libloading::Symbol<
&&[crate::proc_macro_nightly::bridge::client::ProcMacro],
> = unsafe { lib.get(symbol_name.as_bytes()) }.map_err(invalid_data_err)?;
let macros_vec = macros.to_vec();
Ok(ProcMacroLibraryLibloading::NightlyProcMacroLibrary {
_lib: lib,
exported_macros: macros_vec,
})
}
}
}
}
#[derive(Debug)]
pub enum PanicMessage {
Stable(stable_bridge::PanicMessage),
Nightly(nightly_bridge::PanicMessage),
}
impl From<stable_bridge::PanicMessage> for PanicMessage {
fn from(p: stable_bridge::PanicMessage) -> Self {
PanicMessage::Stable(p)
}
}
impl From<nightly_bridge::PanicMessage> for PanicMessage {
fn from(p: nightly_bridge::PanicMessage) -> Self {
PanicMessage::Nightly(p)
}
}
impl PanicMessage {
pub fn as_str(&self) -> Option<&str> {
match self {
Self::Stable(p) => p.as_str(),
Self::Nightly(p) => p.as_str(),
}
let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
}
}
@ -219,28 +146,13 @@ impl Expander {
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, PanicMessage> {
match &self.inner {
ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
expand_stable(macro_name, macro_body, attributes, &exported_macros[..])
.map_err(PanicMessage::from)
}
ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
expand_nightly(macro_name, macro_body, attributes, &exported_macros[..])
.map_err(PanicMessage::from)
}
}
) -> Result<tt::Subtree, String> {
let result = self.inner.abi.expand(macro_name, macro_body, attributes);
result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
}
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
match &self.inner {
ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
list_macros_stable(&exported_macros[..])
}
ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
list_macros_nightly(&exported_macros[..])
}
}
self.inner.abi.list_macros()
}
}
@ -276,153 +188,3 @@ fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
Ok(path.to_path_buf())
}
fn expand_nightly(
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
macros: &[NightlyProcMacro],
) -> Result<tt::Subtree, crate::proc_macro_nightly::bridge::PanicMessage> {
let parsed_body = NightlyTokenStream::with_subtree(macro_body.clone());
let parsed_attributes = attributes
.map_or(crate::rustc_server_nightly::TokenStream::new(), |attr| {
NightlyTokenStream::with_subtree(attr.clone())
});
for proc_macro in macros {
match proc_macro {
crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
trait_name,
client,
..
} if *trait_name == macro_name => {
let res = client.run(
&crate::proc_macro_nightly::bridge::server::SameThread,
crate::rustc_server_nightly::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&crate::proc_macro_nightly::bridge::server::SameThread,
crate::rustc_server_nightly::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&crate::proc_macro_nightly::bridge::server::SameThread,
crate::rustc_server_nightly::Rustc::default(),
parsed_attributes,
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
_ => continue,
}
}
Err(crate::proc_macro_nightly::bridge::PanicMessage::String("Nothing to expand".to_string()))
}
fn expand_stable(
macro_name: &str,
macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>,
macros: &[StableProcMacro],
) -> Result<tt::Subtree, crate::proc_macro::bridge::PanicMessage> {
let parsed_body = StableTokenStream::with_subtree(macro_body.clone());
let parsed_attributes = attributes.map_or(crate::rustc_server::TokenStream::new(), |attr| {
StableTokenStream::with_subtree(attr.clone())
});
for proc_macro in macros {
match proc_macro {
crate::proc_macro::bridge::client::ProcMacro::CustomDerive {
trait_name,
client,
..
} if *trait_name == macro_name => {
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
crate::proc_macro::bridge::client::ProcMacro::Bang { name, client }
if *name == macro_name =>
{
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
crate::proc_macro::bridge::client::ProcMacro::Attr { name, client }
if *name == macro_name =>
{
let res = client.run(
&crate::proc_macro::bridge::server::SameThread,
crate::rustc_server::Rustc::default(),
parsed_attributes,
parsed_body,
false,
);
return res.map(|it| it.into_subtree());
}
_ => continue,
}
}
Err(crate::proc_macro::bridge::PanicMessage::String("Nothing to expand".to_string()))
}
pub fn list_macros_stable(macros: &[StableProcMacro]) -> Vec<(String, ProcMacroKind)> {
macros
.iter()
.map(|proc_macro| match proc_macro {
crate::proc_macro::bridge::client::ProcMacro::CustomDerive { trait_name, .. } => {
(trait_name.to_string(), ProcMacroKind::CustomDerive)
}
crate::proc_macro::bridge::client::ProcMacro::Bang { name, .. } => {
(name.to_string(), ProcMacroKind::FuncLike)
}
crate::proc_macro::bridge::client::ProcMacro::Attr { name, .. } => {
(name.to_string(), ProcMacroKind::Attr)
}
})
.collect()
}
pub fn list_macros_nightly(macros: &[NightlyProcMacro]) -> Vec<(String, ProcMacroKind)> {
macros
.iter()
.map(|proc_macro| match proc_macro {
crate::proc_macro_nightly::bridge::client::ProcMacro::CustomDerive {
trait_name,
..
} => (trait_name.to_string(), ProcMacroKind::CustomDerive),
crate::proc_macro_nightly::bridge::client::ProcMacro::Bang { name, .. } => {
(name.to_string(), ProcMacroKind::FuncLike)
}
crate::proc_macro_nightly::bridge::client::ProcMacro::Attr { name, .. } => {
(name.to_string(), ProcMacroKind::Attr)
}
})
.collect()
}

View file

@ -11,23 +11,10 @@
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
#![allow(unreachable_pub)]
#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro;
#[allow(dead_code)]
#[doc(hidden)]
mod proc_macro_nightly;
#[doc(hidden)]
mod rustc_server;
#[doc(hidden)]
mod rustc_server_nightly;
mod dylib;
use proc_macro::bridge::client::TokenStream;
mod abis;
use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::{
collections::{hash_map::Entry, HashMap},
@ -62,10 +49,7 @@ impl ProcMacroSrv {
match result {
Ok(expansion) => Ok(ExpansionResult { expansion }),
Err(msg) => {
let msg = msg.as_str().unwrap_or("<unknown error>");
Err(format!("proc-macro panicked: {}", msg))
}
Err(msg) => Err(format!("proc-macro panicked: {}", msg)),
}
}

View file

@ -12,8 +12,14 @@ pub mod fixtures {
}
}
fn parse_string(code: &str) -> Option<crate::rustc_server::TokenStream> {
Some(crate::rustc_server::TokenStream::from_str(code).unwrap())
fn parse_string(code: &str) -> Option<crate::abis::abi_1_47::TokenStream> {
// This is a bit strange. We need to parse a string into a token stream into
// order to create a tt::SubTree from it in fixtures. `into_subtree` is
// implemented by all the ABIs we have so we arbitrarily choose one ABI to
// write a `parse_string` function for and use that. The tests don't really
// care which ABI we're using as the `into_subtree` function isn't part of
// the ABI and shouldn't change between ABI versions.
crate::abis::abi_1_47::TokenStream::from_str(code).ok()
}
pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {