rustc_expand/
proc_macro.rs1use rustc_ast::tokenstream::TokenStream;
2use rustc_errors::ErrorGuaranteed;
3use rustc_parse::parser::{ForceCollect, Parser};
4use rustc_session::config::ProcMacroExecutionStrategy;
5use rustc_span::Span;
6use rustc_span::profiling::SpannedEventArgRecorder;
7use {rustc_ast as ast, rustc_proc_macro as pm};
8
9use crate::base::{self, *};
10use crate::{errors, proc_macro_server};
11
12struct MessagePipe<T> {
13 tx: std::sync::mpsc::SyncSender<T>,
14 rx: std::sync::mpsc::Receiver<T>,
15}
16
17impl<T> pm::bridge::server::MessagePipe<T> for MessagePipe<T> {
18 fn new() -> (Self, Self) {
19 let (tx1, rx1) = std::sync::mpsc::sync_channel(1);
20 let (tx2, rx2) = std::sync::mpsc::sync_channel(1);
21 (MessagePipe { tx: tx1, rx: rx2 }, MessagePipe { tx: tx2, rx: rx1 })
22 }
23
24 fn send(&mut self, value: T) {
25 self.tx.send(value).unwrap();
26 }
27
28 fn recv(&mut self) -> Option<T> {
29 self.rx.recv().ok()
30 }
31}
32
33fn exec_strategy(ecx: &ExtCtxt<'_>) -> impl pm::bridge::server::ExecutionStrategy + 'static {
34 pm::bridge::server::MaybeCrossThread::<MessagePipe<_>>::new(
35 ecx.sess.opts.unstable_opts.proc_macro_execution_strategy
36 == ProcMacroExecutionStrategy::CrossThread,
37 )
38}
39
40pub struct BangProcMacro {
41 pub client: pm::bridge::client::Client<pm::TokenStream, pm::TokenStream>,
42}
43
44impl base::BangProcMacro for BangProcMacro {
45 fn expand(
46 &self,
47 ecx: &mut ExtCtxt<'_>,
48 span: Span,
49 input: TokenStream,
50 ) -> Result<TokenStream, ErrorGuaranteed> {
51 let _timer =
52 ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
53 recorder.record_arg_with_span(ecx.sess.source_map(), ecx.expansion_descr(), span);
54 });
55
56 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
57 let strategy = exec_strategy(ecx);
58 let server = proc_macro_server::Rustc::new(ecx);
59 self.client.run(&strategy, server, input, proc_macro_backtrace).map_err(|e| {
60 ecx.dcx().emit_err(errors::ProcMacroPanicked {
61 span,
62 message: e
63 .as_str()
64 .map(|message| errors::ProcMacroPanickedHelp { message: message.into() }),
65 })
66 })
67 }
68}
69
70pub struct AttrProcMacro {
71 pub client: pm::bridge::client::Client<(pm::TokenStream, pm::TokenStream), pm::TokenStream>,
72}
73
74impl base::AttrProcMacro for AttrProcMacro {
75 fn expand(
76 &self,
77 ecx: &mut ExtCtxt<'_>,
78 span: Span,
79 annotation: TokenStream,
80 annotated: TokenStream,
81 ) -> Result<TokenStream, ErrorGuaranteed> {
82 let _timer =
83 ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
84 recorder.record_arg_with_span(ecx.sess.source_map(), ecx.expansion_descr(), span);
85 });
86
87 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
88 let strategy = exec_strategy(ecx);
89 let server = proc_macro_server::Rustc::new(ecx);
90 self.client.run(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err(
91 |e| {
92 ecx.dcx().emit_err(errors::CustomAttributePanicked {
93 span,
94 message: e.as_str().map(|message| errors::CustomAttributePanickedHelp {
95 message: message.into(),
96 }),
97 })
98 },
99 )
100 }
101}
102
103pub struct DeriveProcMacro {
104 pub client: pm::bridge::client::Client<pm::TokenStream, pm::TokenStream>,
105}
106
107impl MultiItemModifier for DeriveProcMacro {
108 fn expand(
109 &self,
110 ecx: &mut ExtCtxt<'_>,
111 span: Span,
112 _meta_item: &ast::MetaItem,
113 item: Annotatable,
114 _is_derive_const: bool,
115 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
116 let is_stmt = matches!(item, Annotatable::Stmt(..));
119
120 crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.psess);
125 let input = item.to_tokens();
126 let stream = {
127 let _timer =
128 ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
129 recorder.record_arg_with_span(
130 ecx.sess.source_map(),
131 ecx.expansion_descr(),
132 span,
133 );
134 });
135 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
136 let strategy = exec_strategy(ecx);
137 let server = proc_macro_server::Rustc::new(ecx);
138 match self.client.run(&strategy, server, input, proc_macro_backtrace) {
139 Ok(stream) => stream,
140 Err(e) => {
141 ecx.dcx().emit_err({
142 errors::ProcMacroDerivePanicked {
143 span,
144 message: e.as_str().map(|message| {
145 errors::ProcMacroDerivePanickedHelp { message: message.into() }
146 }),
147 }
148 });
149 return ExpandResult::Ready(vec![]);
150 }
151 }
152 };
153
154 let error_count_before = ecx.dcx().err_count();
155 let mut parser = Parser::new(&ecx.sess.psess, stream, Some("proc-macro derive"));
156 let mut items = vec![];
157
158 loop {
159 match parser.parse_item(ForceCollect::No) {
160 Ok(None) => break,
161 Ok(Some(item)) => {
162 if is_stmt {
163 items.push(Annotatable::Stmt(Box::new(ecx.stmt_item(span, item))));
164 } else {
165 items.push(Annotatable::Item(item));
166 }
167 }
168 Err(err) => {
169 err.emit();
170 break;
171 }
172 }
173 }
174
175 if ecx.dcx().err_count() > error_count_before {
177 ecx.dcx().emit_err(errors::ProcMacroDeriveTokens { span });
178 }
179
180 ExpandResult::Ready(items)
181 }
182}