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