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