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;
12
13/// Represents requests sent from the client to the proc-macro-srv.
14#[derive(Debug, Serialize, Deserialize)]
15pub enum Request {
16    /// Retrieves a list of macros from a given dynamic library.
17    /// Since [`NO_VERSION_CHECK_VERSION`]
18    ListMacros { dylib_path: Utf8PathBuf },
19
20    /// Expands a procedural macro.
21    /// Since [`NO_VERSION_CHECK_VERSION`]
22    ExpandMacro(Box<ExpandMacro>),
23
24    /// Performs an API version check between the client and the server.
25    /// Since [`VERSION_CHECK_VERSION`]
26    ApiVersionCheck {},
27
28    /// Sets server-specific configurations.
29    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
30    SetConfig(ServerConfig),
31}
32
33/// Defines the mode used for handling span data.
34#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize, PartialEq, Eq)]
35pub enum SpanMode {
36    /// Default mode, where spans are identified by an ID.
37    #[default]
38    Id,
39
40    /// Rust Analyzer-specific span handling mode.
41    RustAnalyzer,
42}
43
44/// Represents responses sent from the proc-macro-srv to the client.
45#[derive(Debug, Serialize, Deserialize)]
46pub enum Response {
47    /// Returns a list of available macros in a dynamic library.
48    /// Since [`NO_VERSION_CHECK_VERSION`]
49    ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
50
51    /// Returns result of a macro expansion.
52    /// Since [`NO_VERSION_CHECK_VERSION`]
53    ExpandMacro(Result<FlatTree, PanicMessage>),
54
55    /// Returns the API version supported by the server.
56    /// Since [`NO_VERSION_CHECK_VERSION`]
57    ApiVersionCheck(u32),
58
59    /// Confirms the application of a configuration update.
60    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
61    SetConfig(ServerConfig),
62
63    /// Returns the result of a macro expansion, including extended span data.
64    /// Since [`RUST_ANALYZER_SPAN_SUPPORT`]
65    ExpandMacroExtended(Result<ExpandMacroExtended, PanicMessage>),
66}
67
68/// Configuration settings for the proc-macro-srv.
69#[derive(Debug, Serialize, Deserialize, Default)]
70#[serde(default)]
71pub struct ServerConfig {
72    /// Defines how span data should be handled.
73    pub span_mode: SpanMode,
74}
75
76/// Represents an extended macro expansion response, including span data mappings.
77#[derive(Debug, Serialize, Deserialize)]
78pub struct ExpandMacroExtended {
79    /// The expanded syntax tree.
80    pub tree: FlatTree,
81    /// Additional span data mappings.
82    pub span_data_table: Vec<u32>,
83}
84
85/// Represents an error message when a macro expansion results in a panic.
86#[derive(Debug, Serialize, Deserialize)]
87pub struct PanicMessage(pub String);
88
89/// Represents a macro expansion request sent from the client.
90#[derive(Debug, Serialize, Deserialize)]
91pub struct ExpandMacro {
92    /// The path to the dynamic library containing the macro.
93    pub lib: Utf8PathBuf,
94    /// Environment variables to set during macro expansion.
95    pub env: Vec<(String, String)>,
96    /// The current working directory for the macro expansion.
97    pub current_dir: Option<String>,
98    /// Macro expansion data, including the macro body, name and attributes.
99    #[serde(flatten)]
100    pub data: ExpandMacroData,
101}
102
103/// Represents the input data required for expanding a macro.
104#[derive(Debug, Serialize, Deserialize)]
105pub struct ExpandMacroData {
106    /// Argument of macro call.
107    ///
108    /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
109    /// item; in function-like macro - the macro body.
110    pub macro_body: FlatTree,
111
112    /// Name of macro to expand.
113    ///
114    /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
115    /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
116    pub macro_name: String,
117
118    /// Possible attributes for the attribute-like macros.
119    pub attributes: Option<FlatTree>,
120    /// marker for serde skip stuff
121    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
122    #[serde(default)]
123    pub has_global_spans: ExpnGlobals,
124    /// Table of additional span data.
125    #[serde(skip_serializing_if = "Vec::is_empty")]
126    #[serde(default)]
127    pub span_data_table: Vec<u32>,
128}
129
130/// Represents global expansion settings, including span resolution.
131#[derive(Copy, Clone, Default, Debug, Serialize, Deserialize)]
132pub struct ExpnGlobals {
133    /// Determines whether to serialize the expansion settings.
134    #[serde(skip_serializing)]
135    #[serde(default)]
136    pub serialize: bool,
137    /// Defines the `def_site` span location.
138    pub def_site: usize,
139    /// Defines the `call_site` span location.
140    pub call_site: usize,
141    /// Defines the `mixed_site` span location.
142    pub mixed_site: usize,
143}
144
145impl ExpnGlobals {
146    fn skip_serializing_if(&self) -> bool {
147        !self.serialize
148    }
149}
150
151pub trait Message: serde::Serialize + DeserializeOwned {
152    fn read<R: BufRead>(
153        from_proto: ProtocolRead<R>,
154        inp: &mut R,
155        buf: &mut String,
156    ) -> io::Result<Option<Self>> {
157        Ok(match from_proto(inp, buf)? {
158            None => None,
159            Some(text) => {
160                let mut deserializer = serde_json::Deserializer::from_str(text);
161                // Note that some proc-macro generate very deep syntax tree
162                // We have to disable the current limit of serde here
163                deserializer.disable_recursion_limit();
164                Some(Self::deserialize(&mut deserializer)?)
165            }
166        })
167    }
168    fn write<W: Write>(self, to_proto: ProtocolWrite<W>, out: &mut W) -> io::Result<()> {
169        let text = serde_json::to_string(&self)?;
170        to_proto(out, &text)
171    }
172}
173
174impl Message for Request {}
175impl Message for Response {}
176
177/// Type alias for a function that reads protocol messages from a buffered input stream.
178#[allow(type_alias_bounds)]
179type ProtocolRead<R: BufRead> =
180    for<'i, 'buf> fn(inp: &'i mut R, buf: &'buf mut String) -> io::Result<Option<&'buf String>>;
181/// Type alias for a function that writes protocol messages to an output stream.
182#[allow(type_alias_bounds)]
183type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str) -> io::Result<()>;
184
185#[cfg(test)]
186mod tests {
187    use intern::{Symbol, sym};
188    use span::{
189        Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize,
190    };
191    use tt::{
192        Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
193        TopSubtreeBuilder,
194    };
195
196    use crate::version;
197
198    use super::*;
199
200    fn fixture_token_tree() -> TopSubtree<Span> {
201        let anchor = SpanAnchor {
202            file_id: span::EditionedFileId::new(
203                span::FileId::from_raw(0xe4e4e),
204                span::Edition::CURRENT,
205            ),
206            ast_id: ROOT_ERASED_FILE_AST_ID,
207        };
208
209        let mut builder = TopSubtreeBuilder::new(Delimiter {
210            open: Span {
211                range: TextRange::empty(TextSize::new(0)),
212                anchor,
213                ctx: SyntaxContext::root(Edition::CURRENT),
214            },
215            close: Span {
216                range: TextRange::empty(TextSize::new(19)),
217                anchor,
218                ctx: SyntaxContext::root(Edition::CURRENT),
219            },
220            kind: DelimiterKind::Invisible,
221        });
222
223        builder.push(
224            Ident {
225                sym: Symbol::intern("struct"),
226                span: Span {
227                    range: TextRange::at(TextSize::new(0), TextSize::of("struct")),
228                    anchor,
229                    ctx: SyntaxContext::root(Edition::CURRENT),
230                },
231                is_raw: tt::IdentIsRaw::No,
232            }
233            .into(),
234        );
235        builder.push(
236            Ident {
237                sym: Symbol::intern("Foo"),
238                span: Span {
239                    range: TextRange::at(TextSize::new(5), TextSize::of("r#Foo")),
240                    anchor,
241                    ctx: SyntaxContext::root(Edition::CURRENT),
242                },
243                is_raw: tt::IdentIsRaw::Yes,
244            }
245            .into(),
246        );
247        builder.push(Leaf::Literal(Literal {
248            symbol: Symbol::intern("Foo"),
249            span: Span {
250                range: TextRange::at(TextSize::new(10), TextSize::of("\"Foo\"")),
251                anchor,
252                ctx: SyntaxContext::root(Edition::CURRENT),
253            },
254            kind: tt::LitKind::Str,
255            suffix: None,
256        }));
257        builder.push(Leaf::Punct(Punct {
258            char: '@',
259            span: Span {
260                range: TextRange::at(TextSize::new(13), TextSize::of('@')),
261                anchor,
262                ctx: SyntaxContext::root(Edition::CURRENT),
263            },
264            spacing: Spacing::Joint,
265        }));
266        builder.open(
267            DelimiterKind::Brace,
268            Span {
269                range: TextRange::at(TextSize::new(14), TextSize::of('{')),
270                anchor,
271                ctx: SyntaxContext::root(Edition::CURRENT),
272            },
273        );
274        builder.push(Leaf::Literal(Literal {
275            symbol: sym::INTEGER_0,
276            span: Span {
277                range: TextRange::at(TextSize::new(15), TextSize::of("0u32")),
278                anchor,
279                ctx: SyntaxContext::root(Edition::CURRENT),
280            },
281            kind: tt::LitKind::Integer,
282            suffix: Some(sym::u32),
283        }));
284        builder.close(Span {
285            range: TextRange::at(TextSize::new(19), TextSize::of('}')),
286            anchor,
287            ctx: SyntaxContext::root(Edition::CURRENT),
288        });
289
290        builder.build()
291    }
292
293    #[test]
294    fn test_proc_macro_rpc_works() {
295        let tt = fixture_token_tree();
296        for v in version::RUST_ANALYZER_SPAN_SUPPORT..=version::CURRENT_API_VERSION {
297            let mut span_data_table = Default::default();
298            let task = ExpandMacro {
299                data: ExpandMacroData {
300                    macro_body: FlatTree::new(tt.view(), v, &mut span_data_table),
301                    macro_name: Default::default(),
302                    attributes: None,
303                    has_global_spans: ExpnGlobals {
304                        serialize: true,
305                        def_site: 0,
306                        call_site: 0,
307                        mixed_site: 0,
308                    },
309                    span_data_table: Vec::new(),
310                },
311                lib: Utf8PathBuf::from_path_buf(std::env::current_dir().unwrap()).unwrap(),
312                env: Default::default(),
313                current_dir: Default::default(),
314            };
315
316            let json = serde_json::to_string(&task).unwrap();
317            // println!("{}", json);
318            let back: ExpandMacro = serde_json::from_str(&json).unwrap();
319
320            assert!(
321                tt == back.data.macro_body.to_subtree_resolved(v, &span_data_table),
322                "version: {v}"
323            );
324        }
325    }
326}