]>
Commit | Line | Data |
---|---|---|
1 | use std::mem; | |
2 | ||
3 | use rustc_ast::attr; | |
4 | use rustc_ast::ptr::P; | |
5 | use rustc_ast::visit::{self, Visitor}; | |
6 | use rustc_ast::{self as ast, NodeId}; | |
7 | use rustc_ast_pretty::pprust; | |
8 | use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand}; | |
9 | use rustc_expand::expand::{AstFragment, ExpansionConfig}; | |
10 | use rustc_session::Session; | |
11 | use rustc_span::hygiene::AstPass; | |
12 | use rustc_span::source_map::SourceMap; | |
13 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; | |
14 | use rustc_span::{Span, DUMMY_SP}; | |
15 | use smallvec::smallvec; | |
16 | use std::cell::RefCell; | |
17 | ||
18 | struct ProcMacroDerive { | |
19 | id: NodeId, | |
20 | trait_name: Symbol, | |
21 | function_name: Ident, | |
22 | span: Span, | |
23 | attrs: Vec<Symbol>, | |
24 | } | |
25 | ||
26 | enum ProcMacroDefType { | |
27 | Attr, | |
28 | Bang, | |
29 | } | |
30 | ||
31 | struct ProcMacroDef { | |
32 | id: NodeId, | |
33 | function_name: Ident, | |
34 | span: Span, | |
35 | def_type: ProcMacroDefType, | |
36 | } | |
37 | ||
38 | enum ProcMacro { | |
39 | Derive(ProcMacroDerive), | |
40 | Def(ProcMacroDef), | |
41 | } | |
42 | ||
43 | struct CollectProcMacros<'a> { | |
44 | sess: &'a Session, | |
45 | macros: Vec<ProcMacro>, | |
46 | in_root: bool, | |
47 | handler: &'a rustc_errors::Handler, | |
48 | source_map: &'a SourceMap, | |
49 | is_proc_macro_crate: bool, | |
50 | is_test_crate: bool, | |
51 | } | |
52 | ||
53 | pub fn inject( | |
54 | sess: &Session, | |
55 | resolver: &mut dyn ResolverExpand, | |
56 | mut krate: ast::Crate, | |
57 | is_proc_macro_crate: bool, | |
58 | has_proc_macro_decls: bool, | |
59 | is_test_crate: bool, | |
60 | num_crate_types: usize, | |
61 | handler: &rustc_errors::Handler, | |
62 | ) -> ast::Crate { | |
63 | let ecfg = ExpansionConfig::default("proc_macro".to_string()); | |
64 | let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); | |
65 | ||
66 | let mut collect = CollectProcMacros { | |
67 | sess, | |
68 | macros: Vec::new(), | |
69 | in_root: true, | |
70 | handler, | |
71 | source_map: sess.source_map(), | |
72 | is_proc_macro_crate, | |
73 | is_test_crate, | |
74 | }; | |
75 | ||
76 | if has_proc_macro_decls || is_proc_macro_crate { | |
77 | visit::walk_crate(&mut collect, &krate); | |
78 | } | |
79 | let macros = collect.macros; | |
80 | ||
81 | if !is_proc_macro_crate { | |
82 | return krate; | |
83 | } | |
84 | ||
85 | if num_crate_types > 1 { | |
86 | handler.err("cannot mix `proc-macro` crate type with others"); | |
87 | } | |
88 | ||
89 | if is_test_crate { | |
90 | return krate; | |
91 | } | |
92 | ||
93 | let decls = mk_decls(&mut krate, &mut cx, ¯os); | |
94 | krate.items.push(decls); | |
95 | ||
96 | krate | |
97 | } | |
98 | ||
99 | impl<'a> CollectProcMacros<'a> { | |
100 | fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { | |
101 | if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { | |
102 | self.handler.span_err( | |
103 | sp, | |
104 | "`proc-macro` crate types currently cannot export any items other \ | |
105 | than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, \ | |
106 | or `#[proc_macro_attribute]`", | |
107 | ); | |
108 | } | |
109 | } | |
110 | ||
111 | fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { | |
112 | let (trait_name, proc_attrs) = | |
113 | match parse_macro_name_and_helper_attrs(self.handler, attr, "derive") { | |
114 | Some(name_and_attrs) => name_and_attrs, | |
115 | None => return, | |
116 | }; | |
117 | ||
118 | if self.in_root && item.vis.kind.is_pub() { | |
119 | self.macros.push(ProcMacro::Derive(ProcMacroDerive { | |
120 | id: item.id, | |
121 | span: item.span, | |
122 | trait_name, | |
123 | function_name: item.ident, | |
124 | attrs: proc_attrs, | |
125 | })); | |
126 | } else { | |
127 | let msg = if !self.in_root { | |
128 | "functions tagged with `#[proc_macro_derive]` must \ | |
129 | currently reside in the root of the crate" | |
130 | } else { | |
131 | "functions tagged with `#[proc_macro_derive]` must be `pub`" | |
132 | }; | |
133 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); | |
134 | } | |
135 | } | |
136 | ||
137 | fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { | |
138 | if self.in_root && item.vis.kind.is_pub() { | |
139 | self.macros.push(ProcMacro::Def(ProcMacroDef { | |
140 | id: item.id, | |
141 | span: item.span, | |
142 | function_name: item.ident, | |
143 | def_type: ProcMacroDefType::Attr, | |
144 | })); | |
145 | } else { | |
146 | let msg = if !self.in_root { | |
147 | "functions tagged with `#[proc_macro_attribute]` must \ | |
148 | currently reside in the root of the crate" | |
149 | } else { | |
150 | "functions tagged with `#[proc_macro_attribute]` must be `pub`" | |
151 | }; | |
152 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); | |
153 | } | |
154 | } | |
155 | ||
156 | fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { | |
157 | if self.in_root && item.vis.kind.is_pub() { | |
158 | self.macros.push(ProcMacro::Def(ProcMacroDef { | |
159 | id: item.id, | |
160 | span: item.span, | |
161 | function_name: item.ident, | |
162 | def_type: ProcMacroDefType::Bang, | |
163 | })); | |
164 | } else { | |
165 | let msg = if !self.in_root { | |
166 | "functions tagged with `#[proc_macro]` must \ | |
167 | currently reside in the root of the crate" | |
168 | } else { | |
169 | "functions tagged with `#[proc_macro]` must be `pub`" | |
170 | }; | |
171 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); | |
172 | } | |
173 | } | |
174 | } | |
175 | ||
176 | impl<'a> Visitor<'a> for CollectProcMacros<'a> { | |
177 | fn visit_item(&mut self, item: &'a ast::Item) { | |
178 | if let ast::ItemKind::MacroDef(..) = item.kind { | |
179 | if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) { | |
180 | let msg = | |
181 | "cannot export macro_rules! macros from a `proc-macro` crate type currently"; | |
182 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); | |
183 | } | |
184 | } | |
185 | ||
186 | // First up, make sure we're checking a bare function. If we're not then | |
187 | // we're just not interested in this item. | |
188 | // | |
189 | // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. | |
190 | let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); | |
191 | ||
192 | let mut found_attr: Option<&'a ast::Attribute> = None; | |
193 | ||
194 | for attr in &item.attrs { | |
195 | if self.sess.is_proc_macro_attr(&attr) { | |
196 | if let Some(prev_attr) = found_attr { | |
197 | let prev_item = prev_attr.get_normal_item(); | |
198 | let item = attr.get_normal_item(); | |
199 | let path_str = pprust::path_to_string(&item.path); | |
200 | let msg = if item.path.segments[0].ident.name | |
201 | == prev_item.path.segments[0].ident.name | |
202 | { | |
203 | format!( | |
204 | "only one `#[{}]` attribute is allowed on any given function", | |
205 | path_str, | |
206 | ) | |
207 | } else { | |
208 | format!( | |
209 | "`#[{}]` and `#[{}]` attributes cannot both be applied | |
210 | to the same function", | |
211 | path_str, | |
212 | pprust::path_to_string(&prev_item.path), | |
213 | ) | |
214 | }; | |
215 | ||
216 | self.handler | |
217 | .struct_span_err(attr.span, &msg) | |
218 | .span_label(prev_attr.span, "previous attribute here") | |
219 | .emit(); | |
220 | ||
221 | return; | |
222 | } | |
223 | ||
224 | found_attr = Some(attr); | |
225 | } | |
226 | } | |
227 | ||
228 | let attr = match found_attr { | |
229 | None => { | |
230 | self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span)); | |
231 | let prev_in_root = mem::replace(&mut self.in_root, false); | |
232 | visit::walk_item(self, item); | |
233 | self.in_root = prev_in_root; | |
234 | return; | |
235 | } | |
236 | Some(attr) => attr, | |
237 | }; | |
238 | ||
239 | if !is_fn { | |
240 | let msg = format!( | |
241 | "the `#[{}]` attribute may only be used on bare functions", | |
242 | pprust::path_to_string(&attr.get_normal_item().path), | |
243 | ); | |
244 | ||
245 | self.handler.span_err(attr.span, &msg); | |
246 | return; | |
247 | } | |
248 | ||
249 | if self.is_test_crate { | |
250 | return; | |
251 | } | |
252 | ||
253 | if !self.is_proc_macro_crate { | |
254 | let msg = format!( | |
255 | "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type", | |
256 | pprust::path_to_string(&attr.get_normal_item().path), | |
257 | ); | |
258 | ||
259 | self.handler.span_err(attr.span, &msg); | |
260 | return; | |
261 | } | |
262 | ||
263 | if self.sess.check_name(attr, sym::proc_macro_derive) { | |
264 | self.collect_custom_derive(item, attr); | |
265 | } else if self.sess.check_name(attr, sym::proc_macro_attribute) { | |
266 | self.collect_attr_proc_macro(item); | |
267 | } else if self.sess.check_name(attr, sym::proc_macro) { | |
268 | self.collect_bang_proc_macro(item); | |
269 | }; | |
270 | ||
271 | let prev_in_root = mem::replace(&mut self.in_root, false); | |
272 | visit::walk_item(self, item); | |
273 | self.in_root = prev_in_root; | |
274 | } | |
275 | } | |
276 | ||
277 | // Creates a new module which looks like: | |
278 | // | |
279 | // const _: () = { | |
280 | // extern crate proc_macro; | |
281 | // | |
282 | // use proc_macro::bridge::client::ProcMacro; | |
283 | // | |
284 | // #[rustc_proc_macro_decls] | |
285 | // #[allow(deprecated)] | |
286 | // static DECLS: &[ProcMacro] = &[ | |
287 | // ProcMacro::custom_derive($name_trait1, &[], ::$name1); | |
288 | // ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); | |
289 | // // ... | |
290 | // ]; | |
291 | // } | |
292 | fn mk_decls( | |
293 | ast_krate: &mut ast::Crate, | |
294 | cx: &mut ExtCtxt<'_>, | |
295 | macros: &[ProcMacro], | |
296 | ) -> P<ast::Item> { | |
297 | // We're the ones filling in this Vec, | |
298 | // so it should be empty to start with | |
299 | assert!(ast_krate.proc_macros.is_empty()); | |
300 | ||
301 | let expn_id = cx.resolver.expansion_for_ast_pass( | |
302 | DUMMY_SP, | |
303 | AstPass::ProcMacroHarness, | |
304 | &[sym::rustc_attrs, sym::proc_macro_internals], | |
305 | None, | |
306 | ); | |
307 | let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); | |
308 | ||
309 | let proc_macro = Ident::new(sym::proc_macro, span); | |
310 | let krate = cx.item(span, proc_macro, Vec::new(), ast::ItemKind::ExternCrate(None)); | |
311 | ||
312 | let bridge = Ident::new(sym::bridge, span); | |
313 | let client = Ident::new(sym::client, span); | |
314 | let proc_macro_ty = Ident::new(sym::ProcMacro, span); | |
315 | let custom_derive = Ident::new(sym::custom_derive, span); | |
316 | let attr = Ident::new(sym::attr, span); | |
317 | let bang = Ident::new(sym::bang, span); | |
318 | ||
319 | let krate_ref = RefCell::new(ast_krate); | |
320 | ||
321 | // We add NodeIds to 'krate.proc_macros' in the order | |
322 | // that we generate expressions. The position of each NodeId | |
323 | // in the 'proc_macros' Vec corresponds to its position | |
324 | // in the static array that will be generated | |
325 | let decls = { | |
326 | let local_path = | |
327 | |sp: Span, name| cx.expr_path(cx.path(sp.with_ctxt(span.ctxt()), vec![name])); | |
328 | let proc_macro_ty_method_path = |method| { | |
329 | cx.expr_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty, method])) | |
330 | }; | |
331 | macros | |
332 | .iter() | |
333 | .map(|m| match m { | |
334 | ProcMacro::Derive(cd) => { | |
335 | krate_ref.borrow_mut().proc_macros.push(cd.id); | |
336 | cx.expr_call( | |
337 | span, | |
338 | proc_macro_ty_method_path(custom_derive), | |
339 | vec![ | |
340 | cx.expr_str(cd.span, cd.trait_name), | |
341 | cx.expr_vec_slice( | |
342 | span, | |
343 | cd.attrs | |
344 | .iter() | |
345 | .map(|&s| cx.expr_str(cd.span, s)) | |
346 | .collect::<Vec<_>>(), | |
347 | ), | |
348 | local_path(cd.span, cd.function_name), | |
349 | ], | |
350 | ) | |
351 | } | |
352 | ProcMacro::Def(ca) => { | |
353 | krate_ref.borrow_mut().proc_macros.push(ca.id); | |
354 | let ident = match ca.def_type { | |
355 | ProcMacroDefType::Attr => attr, | |
356 | ProcMacroDefType::Bang => bang, | |
357 | }; | |
358 | ||
359 | cx.expr_call( | |
360 | span, | |
361 | proc_macro_ty_method_path(ident), | |
362 | vec![ | |
363 | cx.expr_str(ca.span, ca.function_name.name), | |
364 | local_path(ca.span, ca.function_name), | |
365 | ], | |
366 | ) | |
367 | } | |
368 | }) | |
369 | .collect() | |
370 | }; | |
371 | ||
372 | let decls_static = cx | |
373 | .item_static( | |
374 | span, | |
375 | Ident::new(sym::_DECLS, span), | |
376 | cx.ty_rptr( | |
377 | span, | |
378 | cx.ty( | |
379 | span, | |
380 | ast::TyKind::Slice( | |
381 | cx.ty_path(cx.path(span, vec![proc_macro, bridge, client, proc_macro_ty])), | |
382 | ), | |
383 | ), | |
384 | None, | |
385 | ast::Mutability::Not, | |
386 | ), | |
387 | ast::Mutability::Not, | |
388 | cx.expr_vec_slice(span, decls), | |
389 | ) | |
390 | .map(|mut i| { | |
391 | let attr = cx.meta_word(span, sym::rustc_proc_macro_decls); | |
392 | i.attrs.push(cx.attribute(attr)); | |
393 | ||
394 | let deprecated_attr = attr::mk_nested_word_item(Ident::new(sym::deprecated, span)); | |
395 | let allow_deprecated_attr = | |
396 | attr::mk_list_item(Ident::new(sym::allow, span), vec![deprecated_attr]); | |
397 | i.attrs.push(cx.attribute(allow_deprecated_attr)); | |
398 | ||
399 | i | |
400 | }); | |
401 | ||
402 | let block = cx.expr_block( | |
403 | cx.block(span, vec![cx.stmt_item(span, krate), cx.stmt_item(span, decls_static)]), | |
404 | ); | |
405 | ||
406 | let anon_constant = cx.item_const( | |
407 | span, | |
408 | Ident::new(kw::Underscore, span), | |
409 | cx.ty(span, ast::TyKind::Tup(Vec::new())), | |
410 | block, | |
411 | ); | |
412 | ||
413 | // Integrate the new item into existing module structures. | |
414 | let items = AstFragment::Items(smallvec![anon_constant]); | |
415 | cx.monotonic_expander().fully_expand_fragment(items).make_items().pop().unwrap() | |
416 | } |