]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_expand/src/proc_macro.rs
Auto merge of #93700 - rossmacarthur:ft/iter-next-chunk, r=m-ou-se
[rust.git] / compiler / rustc_expand / src / proc_macro.rs
1 use crate::base::{self, *};
2 use crate::proc_macro_server;
3
4 use rustc_ast as ast;
5 use rustc_ast::ptr::P;
6 use rustc_ast::token;
7 use rustc_ast::tokenstream::{TokenStream, TokenTree};
8 use rustc_data_structures::sync::Lrc;
9 use rustc_errors::ErrorGuaranteed;
10 use rustc_parse::parser::ForceCollect;
11 use rustc_span::profiling::SpannedEventArgRecorder;
12 use rustc_span::{Span, DUMMY_SP};
13
14 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
15
16 pub struct BangProcMacro {
17     pub client: pm::bridge::client::Client<pm::TokenStream, pm::TokenStream>,
18 }
19
20 impl base::BangProcMacro for BangProcMacro {
21     fn expand<'cx>(
22         &self,
23         ecx: &'cx mut ExtCtxt<'_>,
24         span: Span,
25         input: TokenStream,
26     ) -> Result<TokenStream, ErrorGuaranteed> {
27         let _timer =
28             ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
29                 recorder.record_arg_with_span(ecx.expansion_descr(), span);
30             });
31
32         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
33         let server = proc_macro_server::Rustc::new(ecx);
34         self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
35             let mut err = ecx.struct_span_err(span, "proc macro panicked");
36             if let Some(s) = e.as_str() {
37                 err.help(&format!("message: {}", s));
38             }
39             err.emit()
40         })
41     }
42 }
43
44 pub struct AttrProcMacro {
45     pub client: pm::bridge::client::Client<(pm::TokenStream, pm::TokenStream), pm::TokenStream>,
46 }
47
48 impl base::AttrProcMacro for AttrProcMacro {
49     fn expand<'cx>(
50         &self,
51         ecx: &'cx mut ExtCtxt<'_>,
52         span: Span,
53         annotation: TokenStream,
54         annotated: TokenStream,
55     ) -> Result<TokenStream, ErrorGuaranteed> {
56         let _timer =
57             ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
58                 recorder.record_arg_with_span(ecx.expansion_descr(), span);
59             });
60
61         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
62         let server = proc_macro_server::Rustc::new(ecx);
63         self.client
64             .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace)
65             .map_err(|e| {
66                 let mut err = ecx.struct_span_err(span, "custom attribute panicked");
67                 if let Some(s) = e.as_str() {
68                     err.help(&format!("message: {}", s));
69                 }
70                 err.emit()
71             })
72     }
73 }
74
75 pub struct DeriveProcMacro {
76     pub client: pm::bridge::client::Client<pm::TokenStream, pm::TokenStream>,
77 }
78
79 impl MultiItemModifier for DeriveProcMacro {
80     fn expand(
81         &self,
82         ecx: &mut ExtCtxt<'_>,
83         span: Span,
84         _meta_item: &ast::MetaItem,
85         item: Annotatable,
86     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
87         // We need special handling for statement items
88         // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
89         let is_stmt = matches!(item, Annotatable::Stmt(..));
90         let hack = crate::base::ann_pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess);
91         let input = if hack {
92             let nt = match item {
93                 Annotatable::Item(item) => token::NtItem(item),
94                 Annotatable::Stmt(stmt) => token::NtStmt(stmt),
95                 _ => unreachable!(),
96             };
97             TokenTree::token(token::Interpolated(Lrc::new(nt)), DUMMY_SP).into()
98         } else {
99             item.to_tokens()
100         };
101
102         let stream = {
103             let _timer =
104                 ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
105                     recorder.record_arg_with_span(ecx.expansion_descr(), span);
106                 });
107             let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
108             let server = proc_macro_server::Rustc::new(ecx);
109             match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
110                 Ok(stream) => stream,
111                 Err(e) => {
112                     let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
113                     if let Some(s) = e.as_str() {
114                         err.help(&format!("message: {}", s));
115                     }
116                     err.emit();
117                     return ExpandResult::Ready(vec![]);
118                 }
119             }
120         };
121
122         let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
123         let mut parser =
124             rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
125         let mut items = vec![];
126
127         loop {
128             match parser.parse_item(ForceCollect::No) {
129                 Ok(None) => break,
130                 Ok(Some(item)) => {
131                     if is_stmt {
132                         items.push(Annotatable::Stmt(P(ecx.stmt_item(span, item))));
133                     } else {
134                         items.push(Annotatable::Item(item));
135                     }
136                 }
137                 Err(mut err) => {
138                     err.emit();
139                     break;
140                 }
141             }
142         }
143
144         // fail if there have been errors emitted
145         if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
146             ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
147         }
148
149         ExpandResult::Ready(items)
150     }
151 }