]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/proc-macro-srv/src/abis/mod.rs
New upstream version 1.68.2+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / proc-macro-srv / src / abis / mod.rs
1 //! Procedural macros are implemented by compiling the macro providing crate
2 //! to a dynamic library with a particular ABI which the compiler uses to expand
3 //! macros. Unfortunately this ABI is not specified and can change from version
4 //! to version of the compiler. To support this we copy the ABI from the rust
5 //! compiler into submodules of this module (e.g proc_macro_srv::abis::abi_1_47).
6 //!
7 //! All of these ABIs are subsumed in the `Abi` enum, which exposes a simple
8 //! interface the rest of rust-analyzer can use to talk to the macro
9 //! provider.
10 //!
11 //! # Adding a new ABI
12 //!
13 //! To add a new ABI you'll need to copy the source of the target proc_macro
14 //! crate from the source tree of the Rust compiler into this directory tree.
15 //! Then you'll need to modify it
16 //! - Remove any feature! or other things which won't compile on stable
17 //! - change any absolute imports to relative imports within the ABI tree
18 //!
19 //! Then you'll need to add a branch to the `Abi` enum and an implementation of
20 //! `Abi::expand`, `Abi::list_macros` and `Abi::from_lib` for the new ABI. See
21 //! `proc_macro_srv/src/abis/abi_1_47/mod.rs` for an example. Finally you'll
22 //! need to update the conditionals in `Abi::from_lib` to return your new ABI
23 //! for the relevant versions of the rust compiler
24 //!
25
26 mod abi_1_58;
27 mod abi_1_63;
28 #[cfg(feature = "sysroot-abi")]
29 mod abi_sysroot;
30
31 // see `build.rs`
32 include!(concat!(env!("OUT_DIR"), "/rustc_version.rs"));
33
34 // Used by `test/utils.rs`
35 #[cfg(all(test, feature = "sysroot-abi"))]
36 pub(crate) use abi_sysroot::TokenStream as TestTokenStream;
37
38 use super::dylib::LoadProcMacroDylibError;
39 pub(crate) use abi_1_58::Abi as Abi_1_58;
40 pub(crate) use abi_1_63::Abi as Abi_1_63;
41 #[cfg(feature = "sysroot-abi")]
42 pub(crate) use abi_sysroot::Abi as Abi_Sysroot;
43 use libloading::Library;
44 use proc_macro_api::{ProcMacroKind, RustCInfo};
45
46 pub struct PanicMessage {
47 message: Option<String>,
48 }
49
50 impl PanicMessage {
51 pub fn as_str(&self) -> Option<String> {
52 self.message.clone()
53 }
54 }
55
56 pub(crate) enum Abi {
57 Abi1_58(Abi_1_58),
58 Abi1_63(Abi_1_63),
59 #[cfg(feature = "sysroot-abi")]
60 AbiSysroot(Abi_Sysroot),
61 }
62
63 impl Abi {
64 /// Load a new ABI.
65 ///
66 /// # Arguments
67 ///
68 /// *`lib` - The dynamic library containing the macro implementations
69 /// *`symbol_name` - The symbol name the macros can be found attributes
70 /// *`info` - RustCInfo about the compiler that was used to compile the
71 /// macro crate. This is the information we use to figure out
72 /// which ABI to return
73 pub fn from_lib(
74 lib: &Library,
75 symbol_name: String,
76 info: RustCInfo,
77 ) -> Result<Abi, LoadProcMacroDylibError> {
78 // the sysroot ABI relies on `extern proc_macro` with unstable features,
79 // instead of a snapshot of the proc macro bridge's source code. it's only
80 // enabled if we have an exact version match.
81 #[cfg(feature = "sysroot-abi")]
82 {
83 if info.version_string == RUSTC_VERSION_STRING {
84 let inner = unsafe { Abi_Sysroot::from_lib(lib, symbol_name) }?;
85 return Ok(Abi::AbiSysroot(inner));
86 }
87
88 // if we reached this point, versions didn't match. in testing, we
89 // want that to panic - this could mean that the format of `rustc
90 // --version` no longer matches the format of the version string
91 // stored in the `.rustc` section, and we want to catch that in-tree
92 // with `x.py test`
93 #[cfg(test)]
94 {
95 let allow_mismatch = std::env::var("PROC_MACRO_SRV_ALLOW_SYSROOT_MISMATCH");
96 if let Ok("1") = allow_mismatch.as_deref() {
97 // only used by rust-analyzer developers, when working on the
98 // sysroot ABI from the rust-analyzer repository - which should
99 // only happen pre-subtree. this can be removed later.
100 } else {
101 panic!(
102 "sysroot ABI mismatch: dylib rustc version (read from .rustc section): {:?} != proc-macro-srv version (read from 'rustc --version'): {:?}",
103 info.version_string, RUSTC_VERSION_STRING
104 );
105 }
106 }
107 }
108
109 // FIXME: this should use exclusive ranges when they're stable
110 // https://github.com/rust-lang/rust/issues/37854
111 match (info.version.0, info.version.1) {
112 (1, 58..=62) => {
113 let inner = unsafe { Abi_1_58::from_lib(lib, symbol_name) }?;
114 Ok(Abi::Abi1_58(inner))
115 }
116 (1, 63) => {
117 let inner = unsafe { Abi_1_63::from_lib(lib, symbol_name) }?;
118 Ok(Abi::Abi1_63(inner))
119 }
120 _ => Err(LoadProcMacroDylibError::UnsupportedABI(info.version_string)),
121 }
122 }
123
124 pub fn expand(
125 &self,
126 macro_name: &str,
127 macro_body: &tt::Subtree,
128 attributes: Option<&tt::Subtree>,
129 ) -> Result<tt::Subtree, PanicMessage> {
130 match self {
131 Self::Abi1_58(abi) => abi.expand(macro_name, macro_body, attributes),
132 Self::Abi1_63(abi) => abi.expand(macro_name, macro_body, attributes),
133 #[cfg(feature = "sysroot-abi")]
134 Self::AbiSysroot(abi) => abi.expand(macro_name, macro_body, attributes),
135 }
136 }
137
138 pub fn list_macros(&self) -> Vec<(String, ProcMacroKind)> {
139 match self {
140 Self::Abi1_58(abi) => abi.list_macros(),
141 Self::Abi1_63(abi) => abi.list_macros(),
142 #[cfg(feature = "sysroot-abi")]
143 Self::AbiSysroot(abi) => abi.list_macros(),
144 }
145 }
146 }
147
148 #[test]
149 fn test_version_check() {
150 let path = paths::AbsPathBuf::assert(crate::proc_macro_test_dylib_path());
151 let info = proc_macro_api::read_dylib_info(&path).unwrap();
152 assert!(info.version.1 >= 50);
153 }