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