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