]> git.proxmox.com Git - rustc.git/blob - src/tools/rust-analyzer/crates/proc-macro-api/src/msg.rs
New upstream version 1.69.0+dfsg1
[rustc.git] / src / tools / rust-analyzer / crates / proc-macro-api / src / msg.rs
1 //! Defines messages for cross-process message passing based on `ndjson` wire protocol
2 pub(crate) mod flat;
3
4 use std::{
5 io::{self, BufRead, Write},
6 path::PathBuf,
7 };
8
9 use serde::{de::DeserializeOwned, Deserialize, Serialize};
10
11 use crate::ProcMacroKind;
12
13 pub use crate::msg::flat::FlatTree;
14
15 pub const NO_VERSION_CHECK_VERSION: u32 = 0;
16 pub const CURRENT_API_VERSION: u32 = 1;
17
18 #[derive(Debug, Serialize, Deserialize)]
19 pub enum Request {
20 ListMacros { dylib_path: PathBuf },
21 ExpandMacro(ExpandMacro),
22 ApiVersionCheck {},
23 }
24
25 #[derive(Debug, Serialize, Deserialize)]
26 pub enum Response {
27 ListMacros(Result<Vec<(String, ProcMacroKind)>, String>),
28 ExpandMacro(Result<FlatTree, PanicMessage>),
29 ApiVersionCheck(u32),
30 }
31
32 #[derive(Debug, Serialize, Deserialize)]
33 pub struct PanicMessage(pub String);
34
35 #[derive(Debug, Serialize, Deserialize)]
36 pub struct ExpandMacro {
37 /// Argument of macro call.
38 ///
39 /// In custom derive this will be a struct or enum; in attribute-like macro - underlying
40 /// item; in function-like macro - the macro body.
41 pub macro_body: FlatTree,
42
43 /// Name of macro to expand.
44 ///
45 /// In custom derive this is the name of the derived trait (`Serialize`, `Getters`, etc.).
46 /// In attribute-like and function-like macros - single name of macro itself (`show_streams`).
47 pub macro_name: String,
48
49 /// Possible attributes for the attribute-like macros.
50 pub attributes: Option<FlatTree>,
51
52 pub lib: PathBuf,
53
54 /// Environment variables to set during macro expansion.
55 pub env: Vec<(String, String)>,
56
57 pub current_dir: Option<String>,
58 }
59
60 pub trait Message: Serialize + DeserializeOwned {
61 fn read(inp: &mut impl BufRead, buf: &mut String) -> io::Result<Option<Self>> {
62 Ok(match read_json(inp, buf)? {
63 None => None,
64 Some(text) => {
65 let mut deserializer = serde_json::Deserializer::from_str(text);
66 // Note that some proc-macro generate very deep syntax tree
67 // We have to disable the current limit of serde here
68 deserializer.disable_recursion_limit();
69 Some(Self::deserialize(&mut deserializer)?)
70 }
71 })
72 }
73 fn write(self, out: &mut impl Write) -> io::Result<()> {
74 let text = serde_json::to_string(&self)?;
75 write_json(out, &text)
76 }
77 }
78
79 impl Message for Request {}
80 impl Message for Response {}
81
82 fn read_json<'a>(inp: &mut impl BufRead, buf: &'a mut String) -> io::Result<Option<&'a String>> {
83 loop {
84 buf.clear();
85
86 inp.read_line(buf)?;
87 buf.pop(); // Remove trailing '\n'
88
89 if buf.is_empty() {
90 return Ok(None);
91 }
92
93 // Some ill behaved macro try to use stdout for debugging
94 // We ignore it here
95 if !buf.starts_with('{') {
96 tracing::error!("proc-macro tried to print : {}", buf);
97 continue;
98 }
99
100 return Ok(Some(buf));
101 }
102 }
103
104 fn write_json(out: &mut impl Write, msg: &str) -> io::Result<()> {
105 tracing::debug!("> {}", msg);
106 out.write_all(msg.as_bytes())?;
107 out.write_all(b"\n")?;
108 out.flush()?;
109 Ok(())
110 }
111
112 #[cfg(test)]
113 mod tests {
114 use super::*;
115 use crate::tt::*;
116
117 fn fixture_token_tree() -> Subtree {
118 let mut subtree = Subtree { delimiter: Delimiter::unspecified(), token_trees: Vec::new() };
119 subtree
120 .token_trees
121 .push(TokenTree::Leaf(Ident { text: "struct".into(), span: TokenId(0) }.into()));
122 subtree
123 .token_trees
124 .push(TokenTree::Leaf(Ident { text: "Foo".into(), span: TokenId(1) }.into()));
125 subtree.token_trees.push(TokenTree::Leaf(Leaf::Literal(Literal {
126 text: "Foo".into(),
127 span: TokenId::unspecified(),
128 })));
129 subtree.token_trees.push(TokenTree::Leaf(Leaf::Punct(Punct {
130 char: '@',
131 span: TokenId::unspecified(),
132 spacing: Spacing::Joint,
133 })));
134 subtree.token_trees.push(TokenTree::Subtree(Subtree {
135 delimiter: Delimiter {
136 open: TokenId(2),
137 close: TokenId::UNSPECIFIED,
138 kind: DelimiterKind::Brace,
139 },
140 token_trees: vec![],
141 }));
142 subtree
143 }
144
145 #[test]
146 fn test_proc_macro_rpc_works() {
147 let tt = fixture_token_tree();
148 let task = ExpandMacro {
149 macro_body: FlatTree::new(&tt),
150 macro_name: Default::default(),
151 attributes: None,
152 lib: std::env::current_dir().unwrap(),
153 env: Default::default(),
154 current_dir: Default::default(),
155 };
156
157 let json = serde_json::to_string(&task).unwrap();
158 // println!("{}", json);
159 let back: ExpandMacro = serde_json::from_str(&json).unwrap();
160
161 assert_eq!(tt, back.macro_body.to_subtree());
162 }
163 }