ra_ap_proc_macro_api/
lib.rs1pub mod legacy_protocol;
9mod process;
10
11use paths::{AbsPath, AbsPathBuf};
12use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
13use std::{fmt, io, sync::Arc, time::SystemTime};
14
15use crate::process::ProcMacroServerProcess;
16
17pub mod version {
19 pub const NO_VERSION_CHECK_VERSION: u32 = 0;
20 pub const VERSION_CHECK_VERSION: u32 = 1;
21 pub const ENCODE_CLOSE_SPAN_VERSION: u32 = 2;
22 pub const HAS_GLOBAL_SPANS: u32 = 3;
23 pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
24 pub const EXTENDED_LEAF_DATA: u32 = 5;
26 pub const HASHED_AST_ID: u32 = 6;
27
28 pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID;
30}
31
32#[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)]
34pub enum ProcMacroKind {
35 CustomDerive,
37 Attr,
39 #[serde(alias = "Bang")]
41 #[serde(rename(serialize = "FuncLike", deserialize = "FuncLike"))]
42 Bang,
43}
44
45#[derive(Debug)]
48pub struct ProcMacroClient {
49 process: Arc<ProcMacroServerProcess>,
54 path: AbsPathBuf,
55}
56
57pub struct MacroDylib {
59 path: AbsPathBuf,
60}
61
62impl MacroDylib {
63 pub fn new(path: AbsPathBuf) -> MacroDylib {
65 MacroDylib { path }
66 }
67}
68
69#[derive(Debug, Clone)]
74pub struct ProcMacro {
75 process: Arc<ProcMacroServerProcess>,
76 dylib_path: Arc<AbsPathBuf>,
77 name: Box<str>,
78 kind: ProcMacroKind,
79 dylib_last_modified: Option<SystemTime>,
80}
81
82impl Eq for ProcMacro {}
83impl PartialEq for ProcMacro {
84 fn eq(&self, other: &Self) -> bool {
85 self.name == other.name
86 && self.kind == other.kind
87 && self.dylib_path == other.dylib_path
88 && self.dylib_last_modified == other.dylib_last_modified
89 && Arc::ptr_eq(&self.process, &other.process)
90 }
91}
92
93#[derive(Clone, Debug)]
95pub struct ServerError {
96 pub message: String,
97 pub io: Option<Arc<io::Error>>,
98}
99
100impl fmt::Display for ServerError {
101 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
102 self.message.fmt(f)?;
103 if let Some(io) = &self.io {
104 f.write_str(": ")?;
105 io.fmt(f)?;
106 }
107 Ok(())
108 }
109}
110
111impl ProcMacroClient {
112 pub fn spawn<'a>(
114 process_path: &AbsPath,
115 env: impl IntoIterator<
116 Item = (impl AsRef<std::ffi::OsStr>, &'a Option<impl 'a + AsRef<std::ffi::OsStr>>),
117 > + Clone,
118 ) -> io::Result<ProcMacroClient> {
119 let process = ProcMacroServerProcess::run(process_path, env)?;
120 Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
121 }
122
123 pub fn server_path(&self) -> &AbsPath {
125 &self.path
126 }
127
128 pub fn load_dylib(&self, dylib: MacroDylib) -> Result<Vec<ProcMacro>, ServerError> {
130 let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
131 let macros = self.process.find_proc_macros(&dylib.path)?;
132
133 let dylib_path = Arc::new(dylib.path);
134 let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
135 .ok()
136 .and_then(|metadata| metadata.modified().ok());
137 match macros {
138 Ok(macros) => Ok(macros
139 .into_iter()
140 .map(|(name, kind)| ProcMacro {
141 process: self.process.clone(),
142 name: name.into(),
143 kind,
144 dylib_path: dylib_path.clone(),
145 dylib_last_modified,
146 })
147 .collect()),
148 Err(message) => Err(ServerError { message, io: None }),
149 }
150 }
151
152 pub fn exited(&self) -> Option<&ServerError> {
154 self.process.exited()
155 }
156}
157
158impl ProcMacro {
159 pub fn name(&self) -> &str {
161 &self.name
162 }
163
164 pub fn kind(&self) -> ProcMacroKind {
166 self.kind
167 }
168
169 fn needs_fixup_change(&self) -> bool {
170 let version = self.process.version();
171 (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
172 }
173
174 fn change_fixup_to_match_old_server(&self, tt: &mut tt::TopSubtree<Span>) {
176 const OLD_FIXUP_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(!0 - 1);
177 let change_ast_id = |ast_id: &mut ErasedFileAstId| {
178 if *ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
179 *ast_id = OLD_FIXUP_AST_ID;
180 } else if *ast_id == OLD_FIXUP_AST_ID {
181 *ast_id = FIXUP_ERASED_FILE_AST_ID_MARKER;
183 }
184 };
185
186 for tt in &mut tt.0 {
187 match tt {
188 tt::TokenTree::Leaf(tt::Leaf::Ident(tt::Ident { span, .. }))
189 | tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { span, .. }))
190 | tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { span, .. })) => {
191 change_ast_id(&mut span.anchor.ast_id);
192 }
193 tt::TokenTree::Subtree(subtree) => {
194 change_ast_id(&mut subtree.delimiter.open.anchor.ast_id);
195 change_ast_id(&mut subtree.delimiter.close.anchor.ast_id);
196 }
197 }
198 }
199 }
200
201 pub fn expand(
204 &self,
205 subtree: tt::SubtreeView<'_, Span>,
206 attr: Option<tt::SubtreeView<'_, Span>>,
207 env: Vec<(String, String)>,
208 def_site: Span,
209 call_site: Span,
210 mixed_site: Span,
211 current_dir: String,
212 ) -> Result<Result<tt::TopSubtree<Span>, String>, ServerError> {
213 let (mut subtree, mut attr) = (subtree, attr);
214 let (mut subtree_changed, mut attr_changed);
215 if self.needs_fixup_change() {
216 subtree_changed = tt::TopSubtree::from_subtree(subtree);
217 self.change_fixup_to_match_old_server(&mut subtree_changed);
218 subtree = subtree_changed.view();
219
220 if let Some(attr) = &mut attr {
221 attr_changed = tt::TopSubtree::from_subtree(*attr);
222 self.change_fixup_to_match_old_server(&mut attr_changed);
223 *attr = attr_changed.view();
224 }
225 }
226
227 legacy_protocol::expand(
228 self,
229 subtree,
230 attr,
231 env,
232 def_site,
233 call_site,
234 mixed_site,
235 current_dir,
236 )
237 }
238}