1#![cfg_attr(not(feature = "sysroot-abi"), allow(unused_crate_dependencies))]
9#![cfg_attr(
10 feature = "sysroot-abi",
11 feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)
12)]
13#![allow(internal_features, unused_features)]
14#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
15
16#[cfg(feature = "in-rust-tree")]
17extern crate rustc_driver as _;
18
19pub mod bidirectional_protocol;
20pub mod legacy_protocol;
21pub mod pool;
22pub mod process;
23pub mod transport;
24
25use paths::{AbsPath, AbsPathBuf};
26use semver::Version;
27use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
28use std::{fmt, io, sync::Arc, time::SystemTime};
29
30use crate::{
31 bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess,
32};
33
34pub mod version {
36 pub const NO_VERSION_CHECK_VERSION: u32 = 0;
37 pub const VERSION_CHECK_VERSION: u32 = 1;
38 pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
39 pub const HAS_GLOBAL_SPANS: u32 = 3;
40 pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
41 pub const EXTENDED_LEAF_DATA: u32 = 5;
43 pub const HASHED_AST_ID: u32 = 6;
44
45 pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID;
47}
48
49#[derive(Copy, Clone, Debug, PartialEq, Eq)]
51pub enum ProtocolFormat {
52 JsonLegacy,
54 BidirectionalPostcardPrototype,
56}
57
58impl fmt::Display for ProtocolFormat {
59 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
60 match self {
61 ProtocolFormat::JsonLegacy => write!(f, "json-legacy"),
62 ProtocolFormat::BidirectionalPostcardPrototype => {
63 write!(f, "bidirectional-postcard-prototype")
64 }
65 }
66 }
67}
68
69#[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)]
71pub enum ProcMacroKind {
72 CustomDerive,
74 Attr,
76 #[serde(alias = "Bang")]
78 #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))]
79 Bang,
80}
81
82#[derive(Debug)]
85pub struct ProcMacroClient {
86 pool: Arc<ProcMacroServerPool>,
91 path: AbsPathBuf,
92}
93
94pub struct MacroDylib {
96 path: AbsPathBuf,
97}
98
99impl MacroDylib {
100 pub fn new(path: AbsPathBuf) -> MacroDylib {
102 MacroDylib { path }
103 }
104}
105
106#[derive(Debug, Clone)]
111pub struct ProcMacro {
112 pool: ProcMacroServerPool,
113 dylib_path: Arc<AbsPathBuf>,
114 name: Box<str>,
115 kind: ProcMacroKind,
116 dylib_last_modified: Option<SystemTime>,
117}
118
119impl Eq for ProcMacro {}
120impl PartialEq for ProcMacro {
121 fn eq(&self, other: &Self) -> bool {
122 self.name == other.name
123 && self.kind == other.kind
124 && self.dylib_path == other.dylib_path
125 && self.dylib_last_modified == other.dylib_last_modified
126 }
127}
128
129#[derive(Clone, Debug)]
131pub struct ServerError {
132 pub message: String,
133 pub io: Option<Arc<io::Error>>,
134}
135
136impl fmt::Display for ServerError {
137 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138 self.message.fmt(f)?;
139 if let Some(io) = &self.io {
140 f.write_str(": ")?;
141 io.fmt(f)?;
142 }
143 Ok(())
144 }
145}
146
147impl ProcMacroClient {
148 pub fn spawn<'a>(
150 process_path: &AbsPath,
151 env: impl IntoIterator<
152 Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
153 > + Clone,
154 version: Option<&Version>,
155 num_process: usize,
156 ) -> io::Result<ProcMacroClient> {
157 let pool_size = num_process;
158 let mut workers = Vec::with_capacity(pool_size);
159 for _ in 0..pool_size {
160 let worker = ProcMacroServerProcess::spawn(process_path, env.clone(), version)?;
161 workers.push(worker);
162 }
163
164 let pool = ProcMacroServerPool::new(workers);
165 Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
166 }
167
168 pub fn with_io_channels(
172 process_path: &AbsPath,
173 spawn: impl Fn(
174 Option<ProtocolFormat>,
175 ) -> io::Result<(
176 Box<dyn process::ProcessExit>,
177 Box<dyn io::Write + Send + Sync>,
178 Box<dyn io::BufRead + Send + Sync>,
179 )> + Clone,
180 version: Option<&Version>,
181 num_process: usize,
182 ) -> io::Result<ProcMacroClient> {
183 let pool_size = num_process;
184 let mut workers = Vec::with_capacity(pool_size);
185 for _ in 0..pool_size {
186 let worker =
187 ProcMacroServerProcess::run(spawn.clone(), version, || "<unknown>".to_owned())?;
188 workers.push(worker);
189 }
190
191 let pool = ProcMacroServerPool::new(workers);
192 Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
193 }
194
195 pub fn server_path(&self) -> &AbsPath {
197 &self.path
198 }
199
200 pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> {
202 self.pool.load_dylib(&dylib)
203 }
204
205 pub fn exited(&self) -> Option<&ServerError> {
207 self.pool.exited()
208 }
209}
210
211impl ProcMacro {
212 pub fn name(&self) -> &str {
214 &self.name
215 }
216
217 pub fn kind(&self) -> ProcMacroKind {
219 self.kind
220 }
221
222 fn needs_fixup_change(&self) -> bool {
223 let version = self.pool.version();
224 (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
225 }
226
227 fn change_fixup_to_match_old_server(&self, tt: &mut tt::TopSubtree) {
229 const OLD_FIXUP_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(!0 - 1);
230 tt.change_every_ast_id(|ast_id| {
231 if *ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
232 *ast_id = OLD_FIXUP_AST_ID;
233 } else if *ast_id == OLD_FIXUP_AST_ID {
234 *ast_id = FIXUP_ERASED_FILE_AST_ID_MARKER;
236 }
237 });
238 }
239
240 pub fn expand(
243 &self,
244 subtree: tt::SubtreeView<'_>,
245 attr: Option<tt::SubtreeView<'_>>,
246 env: Vec<(String, String)>,
247 def_site: Span,
248 call_site: Span,
249 mixed_site: Span,
250 current_dir: String,
251 callback: Option<SubCallback<'_>>,
252 ) -> Result<Result<tt::TopSubtree, String>, ServerError> {
253 let (mut subtree, mut attr) = (subtree, attr);
254 let (mut subtree_changed, mut attr_changed);
255 if self.needs_fixup_change() {
256 subtree_changed = tt::TopSubtree::from_subtree(subtree);
257 self.change_fixup_to_match_old_server(&mut subtree_changed);
258 subtree = subtree_changed.view();
259
260 if let Some(attr) = &mut attr {
261 attr_changed = tt::TopSubtree::from_subtree(*attr);
262 self.change_fixup_to_match_old_server(&mut attr_changed);
263 *attr = attr_changed.view();
264 }
265 }
266
267 self.pool.pick_process()?.expand(
268 self,
269 subtree,
270 attr,
271 env,
272 def_site,
273 call_site,
274 mixed_site,
275 current_dir,
276 callback,
277 )
278 }
279}