proc_macro_api/legacy_protocol/
msg.rs

1//! Defines messages for cross-process message passing based on `ndjson` wire protocol
2pub(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/// Represents requests sent from the client to the proc-macro-srv.
14#[derive(Debug, Serialize, Deserialize)]
15pub enum Request {
16    // IMPORTANT: Keep his first, otherwise postcard will break as its not a self describing format
17    // As such, this is the only request that needs to be supported across all protocol versions
18    // and by keeping it first, we ensure it always has the same discriminant encoding in postcard
19    /// Performs an API version check between the client and the server.
20    /// Since [`crate::version::VERSION_CHECK_VERSION`]
21    ApiVersionCheck {},
22
23    /// Retrieves a list of macros from a given dynamic library.
24    /// Since [`crate::version::NO_VERSION_CHECK_VERSION`]
25    ListMacros { dylib_path: Utf8PathBuf },
26
27    /// Expands a procedural macro.
28    /// Since [`crate::version::NO_VERSION_CHECK_VERSION`]
29    ExpandMacro(Box<ExpandMacro>),
30
31    /// Sets server-specific configurations.
32    /// Since [`crate::version::RUST_ANALYZER_SPAN_SUPPORT`]
33    SetConfig(ServerConfig),
34}
35
36/// Defines the mode used for handling span data.
37#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
38pub enum SpanMode {
39    /// Default mode, where spans are identified by an ID.
40    #[default]
41    Id,
42
43    /// Rust Analyzer-specific span handling mode.
44    RustAnalyzer,
45}
46
47/// Represents responses sent from the proc-macro-srv to the client.
48#[derive(Debug, Serialize, Deserialize)]
49pub enum Response {
50    // IMPORTANT: Keep his first, otherwise postcard will break as its not a self describing format
51    // As such, this is the only request that needs to be supported across all protocol versions
52    // and by keeping it first, we ensure it always has the same discriminant encoding in postcard
53    /// Returns the API version supported by the server.
54    /// Since [`crate::version::NO_VERSION_CHECK_VERSION`]
55    ApiVersionCheck(u32),
56
57    /// Returns a list of available macros in a dynamic library.
58    /// Since [`crate::version::NO_VERSION_CHECK_VERSION`]
59    ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
60
61    /// Returns result of a macro expansion.
62    /// Since [`crate::version::NO_VERSION_CHECK_VERSION`]
63    ExpandMacro(Result<FlatTree, PanicMessage>),
64
65    /// Confirms the application of a configuration update.
66    /// Since [`crate::version::RUST_ANALYZER_SPAN_SUPPORT`]
67    SetConfig(ServerConfig),
68
69    /// Returns the result of a macro expansion, including extended span data.
70    /// Since [`crate::version::RUST_ANALYZER_SPAN_SUPPORT`]
71    ExpandMacroExtended(Result<ExpandMacroExtended, PanicMessage>),
72}
73
74/// Configuration settings for the proc-macro-srv.
75#[derive(Debug, Serialize, Deserialize, Default)]
76#[serde(default)]
77pub struct ServerConfig {
78    /// Defines how span data should be handled.
79    pub span_mode: SpanMode,
80}
81
82/// Represents an extended macro expansion response, including span data mappings.
83#[derive(Debug, Serialize, Deserialize)]
84pub struct ExpandMacroExtended {
85    /// The expanded syntax tree.
86    pub tree: FlatTree,
87    /// Additional span data mappings.
88    pub span_data_table: Vec<u32>,
89}
90
91/// Represents an error message when a macro expansion results in a panic.
92#[derive(Debug, Serialize, Deserialize)]
93pub struct PanicMessage(pub String);
94
95/// Represents a macro expansion request sent from the client.
96#[derive(Debug, Serialize, Deserialize)]
97pub struct ExpandMacro {
98    /// The path to the dynamic library containing the macro.
99    pub lib: Utf8PathBuf,
100    /// Environment variables to set during macro expansion.
101    pub env: Vec<(String, String)>,
102    /// The current working directory for the macro expansion.
103    pub current_dir: Option<String>,
104    /// Macro expansion data, including the macro body, name and attributes.
105    #[serde(flatten)]
106    pub data: ExpandMacroData,
107}
108
109/// Represents the input data required for expanding a macro.
110#[derive(Debug, Serialize, Deserialize)]
111pub struct ExpandMacroData {
112    /// Argument of macro call.
113    ///
114    /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
115    /// item; in function-like macro - the macro body.
116    pub macro_body: FlatTree,
117
118    /// Name of macro to expand.
119    ///
120    /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
121    /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
122    pub macro_name: String,
123
124    /// Possible attributes for the attribute-like macros.
125    pub attributes: Option<FlatTree>,
126    /// marker for serde skip stuff
127    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
128    #[serde(default)]
129    pub has_global_spans: ExpnGlobals,
130    /// Table of additional span data.
131    #[serde(skip_serializing_if = "Vec::is_empty")]
132    #[serde(default)]
133    pub span_data_table: Vec<u32>,
134}
135
136/// Represents global expansion settings, including span resolution.
137#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
138pub struct ExpnGlobals {
139    /// Determines whether to serialize the expansion settings.
140    #[serde(skip_serializing)]
141    #[serde(default)]
142    pub serialize: bool,
143    /// Defines the `def_site` span location.
144    pub def_site: usize,
145    /// Defines the `call_site` span location.
146    pub call_site: usize,
147    /// Defines the `mixed_site` span location.
148    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                // println!("{}", json);
377                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}