]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/proc_macro.rs
Rollup merge of #63055 - Mark-Simulacrum:save-analysis-clean-2, r=Xanewok
[rust.git] / src / libsyntax / ext / proc_macro.rs
1 use crate::ast::{self, ItemKind, Attribute, Mac};
2 use crate::attr::{mark_used, mark_known, HasAttrs};
3 use crate::errors::{Applicability, FatalError};
4 use crate::ext::base::{self, *};
5 use crate::ext::build::AstBuilder;
6 use crate::ext::proc_macro_server;
7 use crate::parse::{self, token};
8 use crate::parse::parser::PathStyle;
9 use crate::symbol::{sym, Symbol};
10 use crate::tokenstream::{self, TokenStream};
11 use crate::visit::Visitor;
12
13 use rustc_data_structures::fx::FxHashSet;
14 use rustc_data_structures::sync::Lrc;
15 use syntax_pos::hygiene::{ExpnInfo, ExpnKind};
16 use syntax_pos::{Span, DUMMY_SP};
17
18 const EXEC_STRATEGY: proc_macro::bridge::server::SameThread =
19     proc_macro::bridge::server::SameThread;
20
21 pub struct BangProcMacro {
22     pub client: proc_macro::bridge::client::Client<
23         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
24     >,
25 }
26
27 impl base::ProcMacro for BangProcMacro {
28     fn expand<'cx>(&self,
29                    ecx: &'cx mut ExtCtxt<'_>,
30                    span: Span,
31                    input: TokenStream)
32                    -> TokenStream {
33         let server = proc_macro_server::Rustc::new(ecx);
34         match self.client.run(&EXEC_STRATEGY, server, input) {
35             Ok(stream) => stream,
36             Err(e) => {
37                 let msg = "proc macro panicked";
38                 let mut err = ecx.struct_span_fatal(span, msg);
39                 if let Some(s) = e.as_str() {
40                     err.help(&format!("message: {}", s));
41                 }
42
43                 err.emit();
44                 FatalError.raise();
45             }
46         }
47     }
48 }
49
50 pub struct AttrProcMacro {
51     pub client: proc_macro::bridge::client::Client<
52         fn(proc_macro::TokenStream, proc_macro::TokenStream) -> proc_macro::TokenStream,
53     >,
54 }
55
56 impl base::AttrProcMacro for AttrProcMacro {
57     fn expand<'cx>(&self,
58                    ecx: &'cx mut ExtCtxt<'_>,
59                    span: Span,
60                    annotation: TokenStream,
61                    annotated: TokenStream)
62                    -> TokenStream {
63         let server = proc_macro_server::Rustc::new(ecx);
64         match self.client.run(&EXEC_STRATEGY, server, annotation, annotated) {
65             Ok(stream) => stream,
66             Err(e) => {
67                 let msg = "custom attribute panicked";
68                 let mut err = ecx.struct_span_fatal(span, msg);
69                 if let Some(s) = e.as_str() {
70                     err.help(&format!("message: {}", s));
71                 }
72
73                 err.emit();
74                 FatalError.raise();
75             }
76         }
77     }
78 }
79
80 pub struct ProcMacroDerive {
81     pub client: proc_macro::bridge::client::Client<
82         fn(proc_macro::TokenStream) -> proc_macro::TokenStream,
83     >,
84     pub attrs: Vec<ast::Name>,
85 }
86
87 impl MultiItemModifier for ProcMacroDerive {
88     fn expand(&self,
89               ecx: &mut ExtCtxt<'_>,
90               span: Span,
91               _meta_item: &ast::MetaItem,
92               item: Annotatable)
93               -> Vec<Annotatable> {
94         let item = match item {
95             Annotatable::Item(item) => item,
96             Annotatable::ImplItem(_) |
97             Annotatable::TraitItem(_) |
98             Annotatable::ForeignItem(_) |
99             Annotatable::Stmt(_) |
100             Annotatable::Expr(_) => {
101                 ecx.span_err(span, "proc-macro derives may only be \
102                                     applied to a struct, enum, or union");
103                 return Vec::new()
104             }
105         };
106         match item.node {
107             ItemKind::Struct(..) |
108             ItemKind::Enum(..) |
109             ItemKind::Union(..) => {},
110             _ => {
111                 ecx.span_err(span, "proc-macro derives may only be \
112                                     applied to a struct, enum, or union");
113                 return Vec::new()
114             }
115         }
116
117         // Mark attributes as known, and used.
118         MarkAttrs(&self.attrs).visit_item(&item);
119
120         let token = token::Interpolated(Lrc::new(token::NtItem(item)));
121         let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();
122
123         let server = proc_macro_server::Rustc::new(ecx);
124         let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
125             Ok(stream) => stream,
126             Err(e) => {
127                 let msg = "proc-macro derive panicked";
128                 let mut err = ecx.struct_span_fatal(span, msg);
129                 if let Some(s) = e.as_str() {
130                     err.help(&format!("message: {}", s));
131                 }
132
133                 err.emit();
134                 FatalError.raise();
135             }
136         };
137
138         let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
139         let msg = "proc-macro derive produced unparseable tokens";
140
141         let mut parser = parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
142         let mut items = vec![];
143
144         loop {
145             match parser.parse_item() {
146                 Ok(None) => break,
147                 Ok(Some(item)) => {
148                     items.push(Annotatable::Item(item))
149                 }
150                 Err(mut err) => {
151                     // FIXME: handle this better
152                     err.cancel();
153                     ecx.struct_span_fatal(span, msg).emit();
154                     FatalError.raise();
155                 }
156             }
157         }
158
159
160         // fail if there have been errors emitted
161         if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
162             ecx.struct_span_fatal(span, msg).emit();
163             FatalError.raise();
164         }
165
166         items
167     }
168 }
169
170 struct MarkAttrs<'a>(&'a [ast::Name]);
171
172 impl<'a> Visitor<'a> for MarkAttrs<'a> {
173     fn visit_attribute(&mut self, attr: &Attribute) {
174         if let Some(ident) = attr.ident() {
175             if self.0.contains(&ident.name) {
176                 mark_used(attr);
177                 mark_known(attr);
178             }
179         }
180     }
181
182     fn visit_mac(&mut self, _mac: &Mac) {}
183 }
184
185 pub fn is_proc_macro_attr(attr: &Attribute) -> bool {
186     [sym::proc_macro, sym::proc_macro_attribute, sym::proc_macro_derive]
187         .iter().any(|kind| attr.check_name(*kind))
188 }
189
190 crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
191     let mut result = Vec::new();
192     attrs.retain(|attr| {
193         if attr.path != sym::derive {
194             return true;
195         }
196         if !attr.is_meta_item_list() {
197             cx.struct_span_err(attr.span, "malformed `derive` attribute input")
198                 .span_suggestion(
199                     attr.span,
200                     "missing traits to be derived",
201                     "#[derive(Trait1, Trait2, ...)]".to_owned(),
202                     Applicability::HasPlaceholders,
203                 ).emit();
204             return false;
205         }
206
207         match attr.parse_list(cx.parse_sess,
208                               |parser| parser.parse_path_allowing_meta(PathStyle::Mod)) {
209             Ok(traits) => {
210                 result.extend(traits);
211                 true
212             }
213             Err(mut e) => {
214                 e.emit();
215                 false
216             }
217         }
218     });
219     result
220 }
221
222 crate fn add_derived_markers<T: HasAttrs>(
223     cx: &mut ExtCtxt<'_>, span: Span, traits: &[ast::Path], item: &mut T
224 ) {
225     let (mut names, mut pretty_name) = (FxHashSet::default(), String::new());
226     for (i, path) in traits.iter().enumerate() {
227         if i > 0 {
228             pretty_name.push_str(", ");
229         }
230         pretty_name.push_str(&path.to_string());
231         names.insert(unwrap_or!(path.segments.get(0), continue).ident.name);
232     }
233
234     let span = span.fresh_expansion(cx.current_expansion.id, ExpnInfo::allow_unstable(
235         ExpnKind::Macro(MacroKind::Derive, Symbol::intern(&pretty_name)), span,
236         cx.parse_sess.edition, cx.allow_derive_markers.clone(),
237     ));
238
239     item.visit_attrs(|attrs| {
240         if names.contains(&sym::Eq) && names.contains(&sym::PartialEq) {
241             let meta = cx.meta_word(span, sym::structural_match);
242             attrs.push(cx.attribute(span, meta));
243         }
244         if names.contains(&sym::Copy) {
245             let meta = cx.meta_word(span, sym::rustc_copy_clone_marker);
246             attrs.push(cx.attribute(span, meta));
247         }
248     });
249 }