1pub(crate) mod flat;
3pub use self::flat::*;
4
5use std::io::{self, BufRead, Write};
6
7use paths::Utf8PathBuf;
8use serde::de::DeserializeOwned;
9use serde_derive::{Deserialize, Serialize};
10
11use crate::{ProcMacroKind, codec::Codec};
12
13#[derive(Debug, Serialize, Deserialize)]
15pub enum Request {
16 ApiVersionCheck {},
22
23 ListMacros { dylib_path: Utf8PathBuf },
26
27 ExpandMacro(Box<ExpandMacro>),
30
31 SetConfig(ServerConfig),
34}
35
36#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
38pub enum SpanMode {
39 #[default]
41 Id,
42
43 RustAnalyzer,
45}
46
47#[derive(Debug, Serialize, Deserialize)]
49pub enum Response {
50 ApiVersionCheck(u32),
56
57 ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
60
61 ExpandMacro(Result<FlatTree, PanicMessage>),
64
65 SetConfig(ServerConfig),
68
69 ExpandMacroExtended(Result<ExpandMacroExtended, PanicMessage>),
72}
73
74#[derive(Debug, Serialize, Deserialize, Default)]
76#[serde(default)]
77pub struct ServerConfig {
78 pub span_mode: SpanMode,
80}
81
82#[derive(Debug, Serialize, Deserialize)]
84pub struct ExpandMacroExtended {
85 pub tree: FlatTree,
87 pub span_data_table: Vec<u32>,
89}
90
91#[derive(Debug, Serialize, Deserialize)]
93pub struct PanicMessage(pub String);
94
95#[derive(Debug, Serialize, Deserialize)]
97pub struct ExpandMacro {
98 pub lib: Utf8PathBuf,
100 pub env: Vec<(String, String)>,
102 pub current_dir: Option<String>,
104 #[serde(flatten)]
106 pub data: ExpandMacroData,
107}
108
109#[derive(Debug, Serialize, Deserialize)]
111pub struct ExpandMacroData {
112 pub macro_body: FlatTree,
117
118 pub macro_name: String,
123
124 pub attributes: Option<FlatTree>,
126 #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
128 #[serde(default)]
129 pub has_global_spans: ExpnGlobals,
130 #[serde(skip_serializing_if = "Vec::is_empty")]
132 #[serde(default)]
133 pub span_data_table: Vec<u32>,
134}
135
136#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
138pub struct ExpnGlobals {
139 #[serde(skip_serializing)]
141 #[serde(default)]
142 pub serialize: bool,
143 pub def_site: usize,
145 pub call_site: usize,
147 pub mixed_site: usize,
149}
150
151impl ExpnGlobals {
152 fn skip_serializing_if(&self) -> bool {
153 !self.serialize
154 }
155}
156
157pub trait Message: serde::Serialize + DeserializeOwned {
158 fn read<R: BufRead, C: Codec>(inp: &mut R, buf: &mut C::Buf) -> io::Result<Option<Self>> {
159 Ok(match C::read(inp, buf)? {
160 None => None,
161 Some(buf) => Some(C::decode(buf)?),
162 })
163 }
164 fn write<W: Write, C: Codec>(self, out: &mut W) -> io::Result<()> {
165 let value = C::encode(&self)?;
166 C::write(out, &value)
167 }
168}
169
170impl Message for Request {}
171impl Message for Response {}
172
173#[cfg(test)]
174mod tests {
175 use intern::{Symbol, sym};
176 use span::{
177 Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize,
178 };
179 use tt::{
180 Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
181 TopSubtreeBuilder,
182 };
183
184 use crate::version;
185
186 use super::*;
187
188 fn fixture_token_tree_top_many_none() -> TopSubtree<Span> {
189 let anchor = SpanAnchor {
190 file_id: span::EditionedFileId::new(
191 span::FileId::from_raw(0xe4e4e),
192 span::Edition::CURRENT,
193 ),
194 ast_id: ROOT_ERASED_FILE_AST_ID,
195 };
196
197 let mut builder = TopSubtreeBuilder::new(Delimiter {
198 open: Span {
199 range: TextRange::empty(TextSize::new(0)),
200 anchor,
201 ctx: SyntaxContext::root(Edition::CURRENT),
202 },
203 close: Span {
204 range: TextRange::empty(TextSize::new(0)),
205 anchor,
206 ctx: SyntaxContext::root(Edition::CURRENT),
207 },
208 kind: DelimiterKind::Invisible,
209 });
210
211 builder.push(
212 Ident {
213 sym: Symbol::intern("struct"),
214 span: Span {
215 range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
216 anchor,
217 ctx: SyntaxContext::root(Edition::CURRENT),
218 },
219 is_raw: tt::IdentIsRaw::No,
220 }
221 .into(),
222 );
223 builder.push(
224 Ident {
225 sym: Symbol::intern("Foo"),
226 span: Span {
227 range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
228 anchor,
229 ctx: SyntaxContext::root(Edition::CURRENT),
230 },
231 is_raw: tt::IdentIsRaw::Yes,
232 }
233 .into(),
234 );
235 builder.push(Leaf::Literal(Literal {
236 symbol: Symbol::intern("Foo"),
237 span: Span {
238 range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
239 anchor,
240 ctx: SyntaxContext::root(Edition::CURRENT),
241 },
242 kind: tt::LitKind::Str,
243 suffix: None,
244 }));
245 builder.push(Leaf::Punct(Punct {
246 char: '@',
247 span: Span {
248 range: TextRange::at(TextSize::new(13), TextSize::of('@')),
249 anchor,
250 ctx: SyntaxContext::root(Edition::CURRENT),
251 },
252 spacing: Spacing::Joint,
253 }));
254 builder.open(
255 DelimiterKind::Brace,
256 Span {
257 range: TextRange::at(TextSize::new(14), TextSize::of('{')),
258 anchor,
259 ctx: SyntaxContext::root(Edition::CURRENT),
260 },
261 );
262 builder.open(
263 DelimiterKind::Bracket,
264 Span {
265 range: TextRange::at(TextSize::new(15), TextSize::of('[')),
266 anchor,
267 ctx: SyntaxContext::root(Edition::CURRENT),
268 },
269 );
270 builder.push(Leaf::Literal(Literal {
271 symbol: sym::INTEGER_0,
272 span: Span {
273 range: TextRange::at(TextSize::new(16), TextSize::of("0u32")),
274 anchor,
275 ctx: SyntaxContext::root(Edition::CURRENT),
276 },
277 kind: tt::LitKind::Integer,
278 suffix: Some(sym::u32),
279 }));
280 builder.close(Span {
281 range: TextRange::at(TextSize::new(20), TextSize::of(']')),
282 anchor,
283 ctx: SyntaxContext::root(Edition::CURRENT),
284 });
285
286 builder.close(Span {
287 range: TextRange::at(TextSize::new(21), TextSize::of('}')),
288 anchor,
289 ctx: SyntaxContext::root(Edition::CURRENT),
290 });
291
292 builder.build()
293 }
294
295 fn fixture_token_tree_top_empty_none() -> TopSubtree<Span> {
296 let anchor = SpanAnchor {
297 file_id: span::EditionedFileId::new(
298 span::FileId::from_raw(0xe4e4e),
299 span::Edition::CURRENT,
300 ),
301 ast_id: ROOT_ERASED_FILE_AST_ID,
302 };
303
304 let builder = TopSubtreeBuilder::new(Delimiter {
305 open: Span {
306 range: TextRange::empty(TextSize::new(0)),
307 anchor,
308 ctx: SyntaxContext::root(Edition::CURRENT),
309 },
310 close: Span {
311 range: TextRange::empty(TextSize::new(0)),
312 anchor,
313 ctx: SyntaxContext::root(Edition::CURRENT),
314 },
315 kind: DelimiterKind::Invisible,
316 });
317
318 builder.build()
319 }
320
321 fn fixture_token_tree_top_empty_brace() -> TopSubtree<Span> {
322 let anchor = SpanAnchor {
323 file_id: span::EditionedFileId::new(
324 span::FileId::from_raw(0xe4e4e),
325 span::Edition::CURRENT,
326 ),
327 ast_id: ROOT_ERASED_FILE_AST_ID,
328 };
329
330 let builder = TopSubtreeBuilder::new(Delimiter {
331 open: Span {
332 range: TextRange::empty(TextSize::new(0)),
333 anchor,
334 ctx: SyntaxContext::root(Edition::CURRENT),
335 },
336 close: Span {
337 range: TextRange::empty(TextSize::new(0)),
338 anchor,
339 ctx: SyntaxContext::root(Edition::CURRENT),
340 },
341 kind: DelimiterKind::Brace,
342 });
343
344 builder.build()
345 }
346
347 #[test]
348 fn test_proc_macro_rpc_works() {
349 for tt in [
350 fixture_token_tree_top_many_none,
351 fixture_token_tree_top_empty_none,
352 fixture_token_tree_top_empty_brace,
353 ] {
354 for v in version::RUST_ANALYZER_SPAN_SUPPORT..=version::CURRENT_API_VERSION {
355 let tt = tt();
356 let mut span_data_table = Default::default();
357 let task = ExpandMacro {
358 data: ExpandMacroData {
359 macro_body: FlatTree::from_subtree(tt.view(), v, &mut span_data_table),
360 macro_name: Default::default(),
361 attributes: None,
362 has_global_spans: ExpnGlobals {
363 serialize: true,
364 def_site: 0,
365 call_site: 0,
366 mixed_site: 0,
367 },
368 span_data_table: Vec::new(),
369 },
370 lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
371 env: Default::default(),
372 current_dir: Default::default(),
373 };
374
375 let json = serde_json::to_string(&task).unwrap();
376 let back: ExpandMacro = serde_json::from_str(&json).unwrap();
378
379 assert_eq!(
380 tt,
381 back.data.macro_body.to_subtree_resolved(v, &span_data_table),
382 "version: {v}"
383 );
384 }
385 }
386 }
387
388 #[test]
389 #[cfg(feature = "sysroot-abi")]
390 fn test_proc_macro_rpc_works_ts() {
391 for tt in [
392 fixture_token_tree_top_many_none,
393 fixture_token_tree_top_empty_none,
394 fixture_token_tree_top_empty_brace,
395 ] {
396 let tt = tt();
397 for v in version::RUST_ANALYZER_SPAN_SUPPORT..=version::CURRENT_API_VERSION {
398 let mut span_data_table = Default::default();
399 let flat_tree = FlatTree::from_subtree(tt.view(), v, &mut span_data_table);
400 assert_eq!(
401 tt,
402 flat_tree.clone().to_subtree_resolved(v, &span_data_table),
403 "version: {v}"
404 );
405 let ts = flat_tree.to_tokenstream_resolved(v, &span_data_table, |a, b| a.cover(b));
406 let call_site = *span_data_table.first().unwrap();
407 let mut span_data_table = Default::default();
408 assert_eq!(
409 tt,
410 FlatTree::from_tokenstream(ts.clone(), v, call_site, &mut span_data_table)
411 .to_subtree_resolved(v, &span_data_table),
412 "version: {v}, ts:\n{ts:#?}"
413 );
414 }
415 }
416 }
417}