rustc_expand/
proc_macro.rs

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