diff options
Diffstat (limited to 'src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs')
-rw-r--r-- | src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs new file mode 100644 index 000000000..bcf3f1184 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs @@ -0,0 +1,155 @@ +//! Procedural macros are implemented by compiling the macro providing crate +//! to a dynamic library with a particular ABI which the compiler uses to expand +//! macros. Unfortunately this ABI is not specified and can change from version +//! to version of the compiler. To support this we copy the ABI from the rust +//! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47). +//! +//! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple +//! interface the rest of rust analyzer can use to talk to the macro +//! provider. +//! +//! # Adding a new ABI +//! +//! To add a new ABI you'll need to copy the source of the target proc_macro +//! crate from the source tree of the Rust compiler into this directory tree. +//! Then you'll need to modify it +//! - Remove any feature! or other things which won't compile on stable +//! - change any absolute imports to relative imports within the ABI tree +//! +//! Then you'll need to add a branch to the `Abi` enum and an implementation of +//! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See +//! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll +//! need to update the conditionals in `Abi::from_lib` to return your new ABI +//! for the relevant versions of the rust compiler +//! + +mod abi_1_58; +mod abi_1_63; +mod abi_1_64; +#[cfg(feature = "sysroot-abi")] +mod abi_sysroot; + +// see `build.rs` +include!(concat!(env!("OUT_DIR"), "/rustc_version.rs")); + +// Used by `test/utils.rs` +#[cfg(test)] +pub(crate) use abi_1_64::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; +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_58(Abi_1_58), + Abi1_63(Abi_1_63), + Abi1_64(Abi_1_64), + #[cfg(feature = "sysroot-abi")] + AbiSysroot(Abi_Sysroot), +} + +impl Abi { + /// Load a new ABI. + /// + /// # Arguments + /// + /// *`lib` - The dynamic library containing the macro implementations + /// *`symbol_name` - The symbol name the macros can be found attributes + /// *`info` - RustCInfo about the compiler that was used to compile the + /// macro crate. This is the information we use to figure out + /// which ABI to return + pub fn from_lib( + lib: &Library, + symbol_name: String, + info: RustCInfo, + ) -> Result<Abi, LoadProcMacroDylibError> { + // the sysroot ABI relies on `extern proc_macro` with unstable features, + // instead of a snapshot of the proc macro bridge's source code. it's only + // enabled if we have an exact version match. + #[cfg(feature = "sysroot-abi")] + { + if info.version_string == RUSTC_VERSION_STRING { + let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?; + return Ok(Abi::AbiSysroot(inner)); + } + + // if we reached this point, versions didn't match. in testing, we + // want that to panic - this could mean that the format of `rustc + // --version` no longer matches the format of the version string + // stored in the `.rustc` section, and we want to catch that in-tree + // with `x.py test` + #[cfg(test)] + { + let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH"); + if let Ok("1") = allow_mismatch.as_deref() { + // only used by rust-analyzer developers, when working on the + // sysroot ABI from the rust-analyzer repository - which should + // only happen pre-subtree. this can be removed later. + } else { + panic!( + "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}", + info.version_string, RUSTC_VERSION_STRING + ); + } + } + } + + // FIXME: this should use exclusive ranges when they're stable + // https://github.com/rust-lang/rust/issues/37854 + match (info.version.0, info.version.1) { + (1, 58..=62) => { + let inner = unsafe { Abi_1_58::from_lib(lib, symbol_name) }?; + Ok(Abi::Abi1_58(inner)) + } + (1, 63) => { + 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), + } + } + + pub fn expand( + &self, + macro_name: &str, + macro_body: &tt::Subtree, + attributes: Option<&tt::Subtree>, + ) -> Result<tt::Subtree, PanicMessage> { + 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), + } + } + + pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> { + 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(), + } + } +} |