rustc_expand/
proc_macro.rs

1use 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        // We need special handling for statement items
117        // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
118        let is_stmt = matches!(item, Annotatable::Stmt(..));
119
120        // We used to have an alternative behaviour for crates that needed it.
121        // We had a lint for a long time, but now we just emit a hard error.
122        // Eventually we might remove the special case hard error check
123        // altogether. See #73345.
124        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        // fail if there have been errors emitted
176        if ecx.dcx().err_count() > error_count_before {
177            ecx.dcx().emit_err(errors::ProcMacroDeriveTokens { span });
178        }
179
180        ExpandResult::Ready(items)
181    }
182}