1use rustc_ast as ast;
2use rustc_ast::tokenstream::TokenStream;
3use rustc_data_structures::AtomicRef;
4use rustc_data_structures::profiling::TimingGuard;
5use rustc_errors::ErrorGuaranteed;
6use rustc_parse::parser::{AllowConstBlockItems, ForceCollect, Parser};
7use rustc_proc_macro as pm;
8use rustc_session::Session;
9use rustc_session::config::ProcMacroExecutionStrategy;
10use rustc_span::profiling::SpannedEventArgRecorder;
11use rustc_span::{LocalExpnId, Span};
12
13use crate::base::{self, *};
14use crate::{diagnostics, proc_macro_server};
15
16fn exec_strategy(sess: &Session) -> impl pm::bridge::server::ExecutionStrategy + 'static {
17 pm::bridge::server::MaybeCrossThread {
18 cross_thread: sess.opts.unstable_opts.proc_macro_execution_strategy
19 == ProcMacroExecutionStrategy::CrossThread,
20 }
21}
22
23fn record_expand_proc_macro<'a>(
24 ecx: &ExtCtxt<'a>,
25 name: &'static str,
26 span: Span,
27) -> TimingGuard<'a> {
28 ecx.sess.prof.generic_activity_with_arg_recorder(name, |recorder| {
29 recorder.record_arg_with_span(ecx.sess.source_map(), ecx.expansion_descr(), span);
30 })
31}
32
33pub struct BangProcMacro {
34 pub client: pm::bridge::client::Client,
35}
36
37impl base::BangProcMacro for BangProcMacro {
38 fn expand(
39 &self,
40 ecx: &mut ExtCtxt<'_>,
41 span: Span,
42 input: TokenStream,
43 ) -> Result<TokenStream, ErrorGuaranteed> {
44 let _timer = record_expand_proc_macro(ecx, "expand_proc_macro", span);
45
46 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
47 let strategy = exec_strategy(ecx.sess);
48 let server = proc_macro_server::Rustc::new(ecx);
49 self.client.run1(&strategy, server, input, proc_macro_backtrace).map_err(|e| {
50 ecx.dcx().emit_err(diagnostics::ProcMacroPanicked {
51 span,
52 message: e
53 .into_string()
54 .map(|message| diagnostics::ProcMacroPanickedHelp { message }),
55 })
56 })
57 }
58}
59
60pub struct AttrProcMacro {
61 pub client: pm::bridge::client::Client,
62}
63
64impl base::AttrProcMacro for AttrProcMacro {
65 fn expand(
66 &self,
67 ecx: &mut ExtCtxt<'_>,
68 span: Span,
69 annotation: TokenStream,
70 annotated: TokenStream,
71 ) -> Result<TokenStream, ErrorGuaranteed> {
72 let _timer = record_expand_proc_macro(ecx, "expand_proc_macro", span);
73
74 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
75 let strategy = exec_strategy(ecx.sess);
76 let server = proc_macro_server::Rustc::new(ecx);
77 self.client.run2(&strategy, server, annotation, annotated, proc_macro_backtrace).map_err(
78 |e| {
79 ecx.dcx().emit_err(diagnostics::CustomAttributePanicked {
80 span,
81 message: e
82 .into_string()
83 .map(|message| diagnostics::CustomAttributePanickedHelp { message }),
84 })
85 },
86 )
87 }
88}
89
90pub struct DeriveProcMacro {
91 pub client: DeriveClient,
92}
93
94impl MultiItemModifier for DeriveProcMacro {
95 fn expand(
96 &self,
97 ecx: &mut ExtCtxt<'_>,
98 span: Span,
99 _meta_item: &ast::MetaItem,
100 item: Annotatable,
101 _is_derive_const: bool,
102 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
103 let _timer = record_expand_proc_macro(ecx, "expand_derive_proc_macro_outer", span);
104
105 let is_stmt = #[allow(non_exhaustive_omitted_patterns)] match item {
Annotatable::Stmt(..) => true,
_ => false,
}matches!(item, Annotatable::Stmt(..));
108
109 let input = item.to_tokens();
110
111 let invoc_id = ecx.current_expansion.id;
112
113 let res = if ecx.sess.opts.incremental.is_some()
114 && ecx.sess.opts.unstable_opts.cache_proc_macros
115 {
116 (*EXPAND_DERIVE_MACRO_CACHED)(invoc_id, input, ecx, self.client)
117 } else {
118 expand_derive_macro(invoc_id, input, ecx, self.client)
119 };
120
121 let Ok(output) = res else {
122 return ExpandResult::Ready(::alloc::vec::Vec::new()vec![]);
124 };
125
126 let error_count_before = ecx.dcx().err_count();
127 let mut parser = Parser::new(&ecx.sess.psess, output, Some("proc-macro derive"));
128 let mut items = ::alloc::vec::Vec::new()vec![];
129
130 loop {
131 match parser.parse_item(
132 ForceCollect::No,
133 if is_stmt { AllowConstBlockItems::No } else { AllowConstBlockItems::Yes },
134 ) {
135 Ok(None) => break,
136 Ok(Some(item)) => {
137 if is_stmt {
138 items.push(Annotatable::Stmt(Box::new(ecx.stmt_item(span, item))));
139 } else {
140 items.push(Annotatable::Item(item));
141 }
142 }
143 Err(err) => {
144 err.emit();
145 break;
146 }
147 }
148 }
149
150 if ecx.dcx().err_count() > error_count_before {
152 ecx.dcx().emit_err(diagnostics::ProcMacroDeriveTokens { span });
153 }
154
155 ExpandResult::Ready(items)
156 }
157}
158
159type DeriveClient = pm::bridge::client::Client;
160
161pub fn expand_derive_macro(
162 invoc_id: LocalExpnId,
163 input: TokenStream,
164 ecx: &mut ExtCtxt<'_>,
165 client: DeriveClient,
166) -> Result<TokenStream, ()> {
167 let _timer =
168 ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
169 let invoc_expn_data = invoc_id.expn_data();
170 let span = invoc_expn_data.call_site;
171 let event_arg = invoc_expn_data.kind.descr();
172 recorder.record_arg_with_span(ecx.sess.source_map(), event_arg, span);
173 });
174
175 let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
176 let strategy = exec_strategy(ecx.sess);
177 let server = proc_macro_server::Rustc::new(ecx);
178
179 match client.run1(&strategy, server, input, proc_macro_backtrace) {
180 Ok(stream) => Ok(stream),
181 Err(e) => {
182 let invoc_expn_data = invoc_id.expn_data();
183 let span = invoc_expn_data.call_site;
184 ecx.dcx().emit_err({
185 diagnostics::ProcMacroDerivePanicked {
186 span,
187 message: e
188 .into_string()
189 .map(|message| diagnostics::ProcMacroDerivePanickedHelp { message }),
190 }
191 });
192 Err(())
193 }
194 }
195}
196
197pub static EXPAND_DERIVE_MACRO_CACHED: AtomicRef<
198 fn(LocalExpnId, TokenStream, &mut ExtCtxt<'_>, DeriveClient) -> Result<TokenStream, ()>,
199> = AtomicRef::new(
200 &(|_, _, _: &mut ExtCtxt<'_>, _| -> Result<_, _> {
201 {
::core::panicking::panic_fmt(format_args!("`EXPAND_DERIVE_MACRO_CACHED` callback was not setup; it must be set in `rustc_interface::callbacks`"));
}panic!(
202 "`EXPAND_DERIVE_MACRO_CACHED` callback was not setup; it must be set in `rustc_interface::callbacks`"
203 )
204 } as _),
205);