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

View file

@ -13,7 +13,7 @@
#![deny(unsafe_code)] #![deny(unsafe_code)]
pub use crate::proc_macro::{Delimiter, Level, LineColumn, Spacing}; pub use super::{Delimiter, Level, LineColumn, Spacing};
use std::fmt; use std::fmt;
use std::hash::Hash; use std::hash::Hash;
use std::marker; 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> //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features //! augmented with removing unstable features
use super::super::TokenStream as ProcMacroTokenStream;
use super::*; use super::*;
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. // 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) 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>( pub fn run<S: Server>(
&self, &self,
strategy: &impl ExecutionStrategy, 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>( pub fn run<S: Server>(
&self, &self,
strategy: &impl ExecutionStrategy, strategy: &impl ExecutionStrategy,

View file

@ -133,7 +133,7 @@ impl Extend<TokenStream> for TokenStream {
/// Public implementation details for the `TokenStream` type, such as iterators. /// Public implementation details for the `TokenStream` type, such as iterators.
pub mod token_stream { 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. /// An iterator over `TokenStream`'s `TokenTree`s.
/// The iteration is "shallow", e.g., the iterator doesn't recurse into delimited groups, /// 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 //! 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::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;
@ -97,9 +97,9 @@ impl Extend<TokenStream> for TokenStream {
} }
} }
type Level = crate::proc_macro::Level; type Level = super::proc_macro::Level;
type LineColumn = crate::proc_macro::LineColumn; type LineColumn = super::proc_macro::LineColumn;
type SourceFile = crate::proc_macro::SourceFile; type SourceFile = super::proc_macro::SourceFile;
/// A structure representing a diagnostic message and associated children /// A structure representing a diagnostic message and associated children
/// messages. /// messages.
@ -734,8 +734,8 @@ impl server::MultiSpan for Rustc {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::proc_macro::bridge::server::Literal;
use super::*; use super::*;
use crate::proc_macro::bridge::server::Literal;
#[test] #[test]
fn test_rustc_server_literals() { 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> //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/client.rs>
//! augmented with removing unstable features //! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*; use super::*;
macro_rules! define_handles { macro_rules! define_handles {
@ -401,26 +402,26 @@ fn run_client<A: for<'a, 's> DecodeMut<'a, 's, ()>, R: Encode<()>>(
b b
} }
impl Client<fn(crate::TokenStream) -> crate::TokenStream> { impl Client<fn(CrateTokenStream) -> CrateTokenStream> {
pub fn expand1(f: fn(crate::TokenStream) -> crate::TokenStream) -> Self { pub fn expand1(f: fn(CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run( extern "C" fn run(
bridge: Bridge<'_>, bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream) -> crate::TokenStream, f: impl FnOnce(CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> { ) -> 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 } Client { get_handle_counters: HandleCounters::get, run, f }
} }
} }
impl Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream> { impl Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream> {
pub fn expand2(f: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream) -> Self { pub fn expand2(f: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream) -> Self {
extern "C" fn run( extern "C" fn run(
bridge: Bridge<'_>, bridge: Bridge<'_>,
f: impl FnOnce(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, f: impl FnOnce(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Buffer<u8> { ) -> Buffer<u8> {
run_client(bridge, |(input, input2)| { 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 } Client { get_handle_counters: HandleCounters::get, run, f }
@ -433,17 +434,17 @@ pub enum ProcMacro {
CustomDerive { CustomDerive {
trait_name: &'static str, trait_name: &'static str,
attributes: &'static [&'static str], attributes: &'static [&'static str],
client: Client<fn(crate::TokenStream) -> crate::TokenStream>, client: Client<fn(CrateTokenStream) -> CrateTokenStream>,
}, },
Attr { Attr {
name: &'static str, name: &'static str,
client: Client<fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream>, client: Client<fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream>,
}, },
Bang { Bang {
name: &'static str, 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( pub fn custom_derive(
trait_name: &'static str, trait_name: &'static str,
attributes: &'static [&'static str], attributes: &'static [&'static str],
expand: fn(crate::TokenStream) -> crate::TokenStream, expand: fn(CrateTokenStream) -> CrateTokenStream,
) -> Self { ) -> Self {
ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) } ProcMacro::CustomDerive { trait_name, attributes, client: Client::expand1(expand) }
} }
pub fn attr( pub fn attr(
name: &'static str, name: &'static str,
expand: fn(crate::TokenStream, crate::TokenStream) -> crate::TokenStream, expand: fn(CrateTokenStream, CrateTokenStream) -> CrateTokenStream,
) -> Self { ) -> Self {
ProcMacro::Attr { name, client: Client::expand2(expand) } 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) } 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> //! Copy from <https://github.com/rust-lang/rust/blob/6050e523bae6de61de4e060facc43dc512adaccd/src/libproc_macro/bridge/server.rs>
//! augmented with removing unstable features //! augmented with removing unstable features
use super::super::TokenStream as CrateTokenStream;
use super::*; use super::*;
// FIXME(eddyb) generate the definition of `HandleStore` in `server.rs`. // 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) 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>( pub fn run<S: Server>(
&self, &self,
strategy: &impl ExecutionStrategy, 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>( pub fn run<S: Server>(
&self, &self,
strategy: &impl ExecutionStrategy, strategy: &impl ExecutionStrategy,

View file

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

View file

@ -8,7 +8,7 @@
//! //!
//! FIXME: No span and source file information is implemented yet //! 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::collections::HashMap;
use std::hash::Hash; use std::hash::Hash;
@ -97,9 +97,9 @@ impl Extend<TokenStream> for TokenStream {
} }
} }
type Level = crate::proc_macro_nightly::Level; type Level = super::proc_macro::Level;
type LineColumn = crate::proc_macro_nightly::LineColumn; type LineColumn = super::proc_macro::LineColumn;
type SourceFile = crate::proc_macro_nightly::SourceFile; type SourceFile = super::proc_macro::SourceFile;
/// A structure representing a diagnostic message and associated children /// A structure representing a diagnostic message and associated children
/// messages. /// messages.
@ -737,8 +737,8 @@ impl server::MultiSpan for Rustc {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::super::proc_macro::bridge::server::Literal;
use super::*; use super::*;
use crate::proc_macro_nightly::bridge::server::Literal;
#[test] #[test]
fn test_rustc_server_literals() { 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 //! Handles dynamic library loading for proc macro
use std::{ use std::{
convert::{TryFrom, TryInto},
fmt, fmt,
fs::File, fs::File,
io, io,
@ -11,16 +10,9 @@ use std::{
use libloading::Library; use libloading::Library;
use memmap2::Mmap; use memmap2::Mmap;
use object::Object; use object::Object;
use proc_macro_api::{read_dylib_info, ProcMacroKind, RustCInfo}; use proc_macro_api::{read_dylib_info, ProcMacroKind};
use crate::{ use super::abis::Abi;
proc_macro::bridge::{self as stable_bridge, client::ProcMacro as StableProcMacro},
rustc_server::TokenStream as StableTokenStream,
};
use crate::{
proc_macro_nightly::bridge::{self as nightly_bridge, client::ProcMacro as NightlyProcMacro},
rustc_server_nightly::TokenStream as NightlyTokenStream,
};
const NEW_REGISTRAR_SYMBOL: &str = "_rustc_proc_macro_decls_"; 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()) } 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)] #[derive(Debug)]
pub enum LoadProcMacroDylibError { pub enum LoadProcMacroDylibError {
Io(io::Error), Io(io::Error),
LibLoading(libloading::Error),
UnsupportedABI, UnsupportedABI,
} }
@ -115,6 +87,7 @@ impl fmt::Display for LoadProcMacroDylibError {
match self { match self {
Self::Io(e) => e.fmt(f), Self::Io(e) => e.fmt(f),
Self::UnsupportedABI => write!(f, "unsupported ABI version"), 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 { impl From<libloading::Error> for LoadProcMacroDylibError {
StableProcMacroLibrary { fn from(e: libloading::Error) -> Self {
_lib: Library, LoadProcMacroDylibError::LibLoading(e)
exported_macros: Vec<crate::proc_macro::bridge::client::ProcMacro>, }
}, }
NightlyProcMacroLibrary {
_lib: Library, struct ProcMacroLibraryLibloading {
exported_macros: Vec<crate::proc_macro_nightly::bridge::client::ProcMacro>, // Hold on to the library so it doesn't unload
}, _lib: Library,
abi: Abi,
} }
impl ProcMacroLibraryLibloading { impl ProcMacroLibraryLibloading {
@ -143,57 +117,10 @@ impl ProcMacroLibraryLibloading {
})?; })?;
let version_info = read_dylib_info(file)?; let version_info = read_dylib_info(file)?;
let macro_abi: ProcMacroABI = version_info.try_into()?;
let lib = load_library(file).map_err(invalid_data_err)?; let lib = load_library(file).map_err(invalid_data_err)?;
match macro_abi { let abi = Abi::from_lib(&lib, symbol_name, version_info)?;
ProcMacroABI::Stable => { Ok(ProcMacroLibraryLibloading { _lib: lib, abi })
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(),
}
} }
} }
@ -219,28 +146,13 @@ impl Expander {
macro_name: &str, macro_name: &str,
macro_body: &tt::Subtree, macro_body: &tt::Subtree,
attributes: Option<&tt::Subtree>, attributes: Option<&tt::Subtree>,
) -> Result<tt::Subtree, PanicMessage> { ) -> Result<tt::Subtree, String> {
match &self.inner { let result = self.inner.abi.expand(macro_name, macro_body, attributes);
ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => { result.map_err(|e| e.as_str().unwrap_or_else(|| "<unknown error>".to_string()))
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)
}
}
} }
pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
match &self.inner { self.inner.abi.list_macros()
ProcMacroLibraryLibloading::StableProcMacroLibrary { exported_macros, .. } => {
list_macros_stable(&exported_macros[..])
}
ProcMacroLibraryLibloading::NightlyProcMacroLibrary { exported_macros, .. } => {
list_macros_nightly(&exported_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> { fn ensure_file_with_lock_free_access(path: &Path) -> io::Result<PathBuf> {
Ok(path.to_path_buf()) 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)… //! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
#![allow(unreachable_pub)] #![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; mod dylib;
use proc_macro::bridge::client::TokenStream; mod abis;
use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask}; use proc_macro_api::{ExpansionResult, ExpansionTask, ListMacrosResult, ListMacrosTask};
use std::{ use std::{
collections::{hash_map::Entry, HashMap}, collections::{hash_map::Entry, HashMap},
@ -62,10 +49,7 @@ impl ProcMacroSrv {
match result { match result {
Ok(expansion) => Ok(ExpansionResult { expansion }), Ok(expansion) => Ok(ExpansionResult { expansion }),
Err(msg) => { Err(msg) => Err(format!("proc-macro panicked: {}", msg)),
let msg = msg.as_str().unwrap_or("<unknown error>");
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> { fn parse_string(code: &str) -> Option<crate::abis::abi_1_47::TokenStream> {
Some(crate::rustc_server::TokenStream::from_str(code).unwrap()) // 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) { pub fn assert_expand(macro_name: &str, ra_fixture: &str, expect: Expect) {