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