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