]> git.lizzy.rs Git - rust.git/blob - src/librustc_expand/proc_macro.rs
Auto merge of #69474 - Dylan-DPC:rollup-ciotplu, r=Dylan-DPC
[rust.git] / src / librustc_expand / proc_macro.rs
1 use crate::base::{self, *};
2 use crate::proc_macro_server;
3
4 use rustc_data_structures::sync::Lrc;
5 use rustc_errors::{Applicability, FatalError};
6 use rustc_span::symbol::sym;
7 use rustc_span::{Span, DUMMY_SP};
8 use syntax::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
9 use syntax::token;
10 use syntax::tokenstream::{self, TokenStream};
11
12 const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
13
14 pub struct BangProcMacro {
15     pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
16 }
17
18 impl base::ProcMacro for BangProcMacro {
19     fn expand<'cx>(
20         &self,
21         ecx: &'cx mut ExtCtxt<'_>,
22         span: Span,
23         input: TokenStream,
24     ) -> TokenStream {
25         let server = proc_macro_server::Rustc::new(ecx);
26         match self.client.run(&EXEC_STRATEGY, server, input) {
27             Ok(stream) => stream,
28             Err(e) => {
29                 let msg = "proc macro panicked";
30                 let mut err = ecx.struct_span_fatal(span, msg);
31                 if let Some(s) = e.as_str() {
32                     err.help(&format!("message: {}", s));
33                 }
34
35                 err.emit();
36                 FatalError.raise();
37             }
38         }
39     }
40 }
41
42 pub struct AttrProcMacro {
43     pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
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     ) -> TokenStream {
54         let server = proc_macro_server::Rustc::new(ecx);
55         match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
56             Ok(stream) => stream,
57             Err(e) => {
58                 let msg = "custom attribute panicked";
59                 let mut err = ecx.struct_span_fatal(span, msg);
60                 if let Some(s) = e.as_str() {
61                     err.help(&format!("message: {}", s));
62                 }
63
64                 err.emit();
65                 FatalError.raise();
66             }
67         }
68     }
69 }
70
71 pub struct ProcMacroDerive {
72     pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
73 }
74
75 impl MultiItemModifier for ProcMacroDerive {
76     fn expand(
77         &self,
78         ecx: &mut ExtCtxt<'_>,
79         span: Span,
80         _meta_item: &ast::MetaItem,
81         item: Annotatable,
82     ) -> Vec<Annotatable> {
83         let item = match item {
84             Annotatable::Arm(..)
85             | Annotatable::Field(..)
86             | Annotatable::FieldPat(..)
87             | Annotatable::GenericParam(..)
88             | Annotatable::Param(..)
89             | Annotatable::StructField(..)
90             | Annotatable::Variant(..) => panic!("unexpected annotatable"),
91             Annotatable::Item(item) => item,
92             Annotatable::ImplItem(_)
93             | Annotatable::TraitItem(_)
94             | Annotatable::ForeignItem(_)
95             | Annotatable::Stmt(_)
96             | Annotatable::Expr(_) => {
97                 ecx.span_err(
98                     span,
99                     "proc-macro derives may only be \
100                                     applied to a struct, enum, or union",
101                 );
102                 return Vec::new();
103             }
104         };
105         match item.kind {
106             ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}
107             _ => {
108                 ecx.span_err(
109                     span,
110                     "proc-macro derives may only be \
111                                     applied to a struct, enum, or union",
112                 );
113                 return Vec::new();
114             }
115         }
116
117         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
118         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
119
120         let server = proc_macro_server::Rustc::new(ecx);
121         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
122             Ok(stream) => stream,
123             Err(e) => {
124                 let msg = "proc-macro derive panicked";
125                 let mut err = ecx.struct_span_fatal(span, msg);
126                 if let Some(s) = e.as_str() {
127                     err.help(&format!("message: {}", s));
128                 }
129
130                 err.emit();
131                 FatalError.raise();
132             }
133         };
134
135         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
136         let msg = "proc-macro derive produced unparseable tokens";
137
138         let mut parser =
139             rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
140         let mut items = vec![];
141
142         loop {
143             match parser.parse_item() {
144                 Ok(None) => break,
145                 Ok(Some(item)) => items.push(Annotatable::Item(item)),
146                 Err(mut err) => {
147                     // FIXME: handle this better
148                     err.cancel();
149                     ecx.struct_span_fatal(span, msg).emit();
150                     FatalError.raise();
151                 }
152             }
153         }
154
155         // fail if there have been errors emitted
156         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
157             ecx.struct_span_fatal(span, msg).emit();
158             FatalError.raise();
159         }
160
161         items
162     }
163 }
164
165 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
166     let mut result = Vec::new();
167     attrs.retain(|attr| {
168         if !attr.has_name(sym::derive) {
169             return true;
170         }
171
172         // 1) First let's ensure that it's a meta item.
173         let nmis = match attr.meta_item_list() {
174             None => {
175                 cx.struct_span_err(attr.span, "malformed `derive` attribute input")
176                     .span_suggestion(
177                         attr.span,
178                         "missing traits to be derived",
179                         "#[derive(Trait1, Trait2, ...)]".to_owned(),
180                         Applicability::HasPlaceholders,
181                     )
182                     .emit();
183                 return false;
184             }
185             Some(x) => x,
186         };
187
188         let mut error_reported_filter_map = false;
189         let mut error_reported_map = false;
190         let traits = nmis
191             .into_iter()
192             // 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
193             .filter_map(|nmi| match nmi {
194                 NestedMetaItem::Literal(lit) => {
195                     error_reported_filter_map = true;
196                     cx.struct_span_err(lit.span, "expected path to a trait, found literal")
197                         .help("for example, write `#[derive(Debug)]` for `Debug`")
198                         .emit();
199                     None
200                 }
201                 NestedMetaItem::MetaItem(mi) => Some(mi),
202             })
203             // 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
204             // but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
205             // In this case we can still at least determine that the user
206             // wanted this trait to be derived, so let's keep it.
207             .map(|mi| {
208                 let mut traits_dont_accept = |title, action| {
209                     error_reported_map = true;
210                     let sp = mi.span.with_lo(mi.path.span.hi());
211                     cx.struct_span_err(sp, title)
212                         .span_suggestion(
213                             sp,
214                             action,
215                             String::new(),
216                             Applicability::MachineApplicable,
217                         )
218                         .emit();
219                 };
220                 match &mi.kind {
221                     MetaItemKind::List(..) => traits_dont_accept(
222                         "traits in `#[derive(...)]` don't accept arguments",
223                         "remove the arguments",
224                     ),
225                     MetaItemKind::NameValue(..) => traits_dont_accept(
226                         "traits in `#[derive(...)]` don't accept values",
227                         "remove the value",
228                     ),
229                     MetaItemKind::Word => {}
230                 }
231                 mi.path
232             });
233
234         result.extend(traits);
235         !error_reported_filter_map && !error_reported_map
236     });
237     result
238 }