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