]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_expand/src/proc_macro.rs
Rollup merge of #93347 - WaffleLapkin:better_char_decode_utf16_size_hint, r=dtolnay
[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::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
8 use rustc_data_structures::sync::Lrc;
9 use rustc_errors::ErrorReported;
10 use rustc_parse::nt_to_tokenstream;
11 use rustc_parse::parser::ForceCollect;
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<fn(pm::TokenStream) -> pm::TokenStream>,
18 }
19
20 impl base::ProcMacro for BangProcMacro {
21     fn expand<'cx>(
22         &self,
23         ecx: &'cx mut ExtCtxt<'_>,
24         span: Span,
25         input: TokenStream,
26     ) -> Result<TokenStream, ErrorReported> {
27         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
28         let server = proc_macro_server::Rustc::new(ecx);
29         self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
30             let mut err = ecx.struct_span_err(span, "proc macro panicked");
31             if let Some(s) = e.as_str() {
32                 err.help(&format!("message: {}", s));
33             }
34             err.emit();
35             ErrorReported
36         })
37     }
38 }
39
40 pub struct AttrProcMacro {
41     pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
42 }
43
44 impl base::AttrProcMacro for AttrProcMacro {
45     fn expand<'cx>(
46         &self,
47         ecx: &'cx mut ExtCtxt<'_>,
48         span: Span,
49         annotation: TokenStream,
50         annotated: TokenStream,
51     ) -> Result<TokenStream, ErrorReported> {
52         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
53         let server = proc_macro_server::Rustc::new(ecx);
54         self.client
55             .run(&EXEC_STRATEGY, server, annotation, annotated, proc_macro_backtrace)
56             .map_err(|e| {
57                 let mut err = ecx.struct_span_err(span, "custom attribute panicked");
58                 if let Some(s) = e.as_str() {
59                     err.help(&format!("message: {}", s));
60                 }
61                 err.emit();
62                 ErrorReported
63             })
64     }
65 }
66
67 pub struct ProcMacroDerive {
68     pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
69 }
70
71 impl MultiItemModifier for ProcMacroDerive {
72     fn expand(
73         &self,
74         ecx: &mut ExtCtxt<'_>,
75         span: Span,
76         _meta_item: &ast::MetaItem,
77         item: Annotatable,
78     ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
79         // We need special handling for statement items
80         // (e.g. `fn foo() { #[derive(Debug)] struct Bar; }`)
81         let mut is_stmt = false;
82         let item = match item {
83             Annotatable::Item(item) => token::NtItem(item),
84             Annotatable::Stmt(stmt) => {
85                 is_stmt = true;
86                 assert!(stmt.is_item());
87
88                 // A proc macro can't observe the fact that we're passing
89                 // them an `NtStmt` - it can only see the underlying tokens
90                 // of the wrapped item
91                 token::NtStmt(stmt.into_inner())
92             }
93             _ => unreachable!(),
94         };
95         let input = if crate::base::pretty_printing_compatibility_hack(&item, &ecx.sess.parse_sess)
96         {
97             TokenTree::token(token::Interpolated(Lrc::new(item)), DUMMY_SP).into()
98         } else {
99             nt_to_tokenstream(&item, &ecx.sess.parse_sess, CanSynthesizeMissingTokens::No)
100         };
101
102         let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
103         let server = proc_macro_server::Rustc::new(ecx);
104         let stream = match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
105             Ok(stream) => stream,
106             Err(e) => {
107                 let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
108                 if let Some(s) = e.as_str() {
109                     err.help(&format!("message: {}", s));
110                 }
111                 err.emit();
112                 return ExpandResult::Ready(vec![]);
113             }
114         };
115
116         let error_count_before = ecx.sess.parse_sess.span_diagnostic.err_count();
117         let mut parser =
118             rustc_parse::stream_to_parser(&ecx.sess.parse_sess, stream, Some("proc-macro derive"));
119         let mut items = vec![];
120
121         loop {
122             match parser.parse_item(ForceCollect::No) {
123                 Ok(None) => break,
124                 Ok(Some(item)) => {
125                     if is_stmt {
126                         items.push(Annotatable::Stmt(P(ecx.stmt_item(span, item))));
127                     } else {
128                         items.push(Annotatable::Item(item));
129                     }
130                 }
131                 Err(mut err) => {
132                     err.emit();
133                     break;
134                 }
135             }
136         }
137
138         // fail if there have been errors emitted
139         if ecx.sess.parse_sess.span_diagnostic.err_count() > error_count_before {
140             ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
141         }
142
143         ExpandResult::Ready(items)
144     }
145 }