]>
Commit | Line | Data |
---|---|---|
9e0c209e SL |
1 | use std::mem; |
2 | ||
74b04a01 | 3 | use rustc_ast::attr; |
74b04a01 XL |
4 | use rustc_ast::ptr::P; |
5 | use rustc_ast::visit::{self, Visitor}; | |
3dfed10e | 6 | use rustc_ast::{self as ast, NodeId}; |
74b04a01 | 7 | use rustc_ast_pretty::pprust; |
136023e0 | 8 | use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand}; |
dfeec247 | 9 | use rustc_expand::expand::{AstFragment, ExpansionConfig}; |
3dfed10e | 10 | use rustc_session::Session; |
dfeec247 | 11 | use rustc_span::hygiene::AstPass; |
ba9703b0 | 12 | use rustc_span::source_map::SourceMap; |
f9f354fc | 13 | use rustc_span::symbol::{kw, sym, Ident, Symbol}; |
dfeec247 | 14 | use rustc_span::{Span, DUMMY_SP}; |
e1599b0c | 15 | use smallvec::smallvec; |
74b04a01 | 16 | use std::cell::RefCell; |
32a655c1 | 17 | |
8bb4bdeb | 18 | struct ProcMacroDerive { |
74b04a01 | 19 | id: NodeId, |
f9f354fc | 20 | trait_name: Symbol, |
9e0c209e SL |
21 | function_name: Ident, |
22 | span: Span, | |
f9f354fc | 23 | attrs: Vec<Symbol>, |
9e0c209e SL |
24 | } |
25 | ||
e1599b0c XL |
26 | enum ProcMacroDefType { |
27 | Attr, | |
dfeec247 | 28 | Bang, |
e1599b0c XL |
29 | } |
30 | ||
8bb4bdeb | 31 | struct ProcMacroDef { |
74b04a01 | 32 | id: NodeId, |
32a655c1 SL |
33 | function_name: Ident, |
34 | span: Span, | |
dfeec247 | 35 | def_type: ProcMacroDefType, |
e1599b0c XL |
36 | } |
37 | ||
38 | enum ProcMacro { | |
39 | Derive(ProcMacroDerive), | |
dfeec247 | 40 | Def(ProcMacroDef), |
32a655c1 SL |
41 | } |
42 | ||
43 | struct CollectProcMacros<'a> { | |
3dfed10e | 44 | sess: &'a Session, |
e1599b0c | 45 | macros: Vec<ProcMacro>, |
9e0c209e | 46 | in_root: bool, |
dfeec247 | 47 | handler: &'a rustc_errors::Handler, |
ba9703b0 | 48 | source_map: &'a SourceMap, |
c30ab7b3 | 49 | is_proc_macro_crate: bool, |
476ff2be | 50 | is_test_crate: bool, |
9e0c209e SL |
51 | } |
52 | ||
dfeec247 | 53 | pub fn inject( |
3dfed10e | 54 | sess: &Session, |
f035d41b | 55 | resolver: &mut dyn ResolverExpand, |
dfeec247 XL |
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 { | |
c30ab7b3 | 63 | let ecfg = ExpansionConfig::default("proc_macro".to_string()); |
ba9703b0 | 64 | let mut cx = ExtCtxt::new(sess, ecfg, resolver, None); |
9e0c209e | 65 | |
e1599b0c | 66 | let mut collect = CollectProcMacros { |
3dfed10e | 67 | sess, |
e1599b0c XL |
68 | macros: Vec::new(), |
69 | in_root: true, | |
70 | handler, | |
ba9703b0 | 71 | source_map: sess.source_map(), |
e1599b0c XL |
72 | is_proc_macro_crate, |
73 | is_test_crate, | |
9e0c209e | 74 | }; |
9e0c209e | 75 | |
e1599b0c XL |
76 | if has_proc_macro_decls || is_proc_macro_crate { |
77 | visit::walk_crate(&mut collect, &krate); | |
78 | } | |
e1599b0c XL |
79 | let macros = collect.macros; |
80 | ||
c30ab7b3 | 81 | if !is_proc_macro_crate { |
dfeec247 | 82 | return krate; |
9e0c209e SL |
83 | } |
84 | ||
85 | if num_crate_types > 1 { | |
c30ab7b3 | 86 | handler.err("cannot mix `proc-macro` crate type with others"); |
9e0c209e SL |
87 | } |
88 | ||
476ff2be SL |
89 | if is_test_crate { |
90 | return krate; | |
91 | } | |
92 | ||
74b04a01 | 93 | let decls = mk_decls(&mut krate, &mut cx, ¯os); |
6a06907d | 94 | krate.items.push(decls); |
9e0c209e | 95 | |
8bb4bdeb XL |
96 | krate |
97 | } | |
9e0c209e | 98 | |
32a655c1 | 99 | impl<'a> CollectProcMacros<'a> { |
9e0c209e | 100 | fn check_not_pub_in_root(&self, vis: &ast::Visibility, sp: Span) { |
1b1a35ee | 101 | if self.is_proc_macro_crate && self.in_root && vis.kind.is_pub() { |
60c5eb7d XL |
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 | ); | |
9e0c209e SL |
108 | } |
109 | } | |
9e0c209e | 110 | |
32a655c1 | 111 | fn collect_custom_derive(&mut self, item: &'a ast::Item, attr: &'a ast::Attribute) { |
136023e0 XL |
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 | }; | |
476ff2be | 117 | |
1b1a35ee | 118 | if self.in_root && item.vis.kind.is_pub() { |
e1599b0c | 119 | self.macros.push(ProcMacro::Derive(ProcMacroDerive { |
74b04a01 | 120 | id: item.id, |
9e0c209e | 121 | span: item.span, |
136023e0 | 122 | trait_name, |
9e0c209e | 123 | function_name: item.ident, |
476ff2be | 124 | attrs: proc_attrs, |
e1599b0c | 125 | })); |
9e0c209e | 126 | } else { |
476ff2be SL |
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 | }; | |
ba9703b0 | 133 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); |
9e0c209e | 134 | } |
32a655c1 SL |
135 | } |
136 | ||
9fa01778 | 137 | fn collect_attr_proc_macro(&mut self, item: &'a ast::Item) { |
1b1a35ee | 138 | if self.in_root && item.vis.kind.is_pub() { |
e1599b0c | 139 | self.macros.push(ProcMacro::Def(ProcMacroDef { |
74b04a01 | 140 | id: item.id, |
32a655c1 SL |
141 | span: item.span, |
142 | function_name: item.ident, | |
dfeec247 | 143 | def_type: ProcMacroDefType::Attr, |
e1599b0c | 144 | })); |
32a655c1 SL |
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 | }; | |
ba9703b0 | 152 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); |
32a655c1 SL |
153 | } |
154 | } | |
8bb4bdeb | 155 | |
9fa01778 | 156 | fn collect_bang_proc_macro(&mut self, item: &'a ast::Item) { |
1b1a35ee | 157 | if self.in_root && item.vis.kind.is_pub() { |
e1599b0c | 158 | self.macros.push(ProcMacro::Def(ProcMacroDef { |
74b04a01 | 159 | id: item.id, |
8bb4bdeb XL |
160 | span: item.span, |
161 | function_name: item.ident, | |
dfeec247 | 162 | def_type: ProcMacroDefType::Bang, |
e1599b0c | 163 | })); |
8bb4bdeb XL |
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 | }; | |
ba9703b0 | 171 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); |
8bb4bdeb XL |
172 | } |
173 | } | |
32a655c1 SL |
174 | } |
175 | ||
176 | impl<'a> Visitor<'a> for CollectProcMacros<'a> { | |
177 | fn visit_item(&mut self, item: &'a ast::Item) { | |
e74abb32 | 178 | if let ast::ItemKind::MacroDef(..) = item.kind { |
3dfed10e | 179 | if self.is_proc_macro_crate && self.sess.contains_name(&item.attrs, sym::macro_export) { |
8bb4bdeb XL |
180 | let msg = |
181 | "cannot export macro_rules! macros from a `proc-macro` crate type currently"; | |
ba9703b0 | 182 | self.handler.span_err(self.source_map.guess_head_span(item.span), msg); |
8bb4bdeb XL |
183 | } |
184 | } | |
185 | ||
32a655c1 SL |
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 | // | |
dc9dc135 | 189 | // If we find one, try to locate a `#[proc_macro_derive]` attribute on it. |
5869c6ff | 190 | let is_fn = matches!(item.kind, ast::ItemKind::Fn(..)); |
32a655c1 SL |
191 | |
192 | let mut found_attr: Option<&'a ast::Attribute> = None; | |
193 | ||
194 | for attr in &item.attrs { | |
3dfed10e | 195 | if self.sess.is_proc_macro_attr(&attr) { |
32a655c1 | 196 | if let Some(prev_attr) = found_attr { |
60c5eb7d XL |
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); | |
dfeec247 XL |
200 | let msg = if item.path.segments[0].ident.name |
201 | == prev_item.path.segments[0].ident.name | |
202 | { | |
e74abb32 XL |
203 | format!( |
204 | "only one `#[{}]` attribute is allowed on any given function", | |
205 | path_str, | |
206 | ) | |
32a655c1 | 207 | } else { |
e74abb32 XL |
208 | format!( |
209 | "`#[{}]` and `#[{}]` attributes cannot both be applied | |
210 | to the same function", | |
211 | path_str, | |
60c5eb7d | 212 | pprust::path_to_string(&prev_item.path), |
e74abb32 | 213 | ) |
32a655c1 SL |
214 | }; |
215 | ||
dfeec247 XL |
216 | self.handler |
217 | .struct_span_err(attr.span, &msg) | |
60c5eb7d | 218 | .span_label(prev_attr.span, "previous attribute here") |
32a655c1 SL |
219 | .emit(); |
220 | ||
221 | return; | |
222 | } | |
223 | ||
224 | found_attr = Some(attr); | |
225 | } | |
226 | } | |
227 | ||
228 | let attr = match found_attr { | |
229 | None => { | |
ba9703b0 | 230 | self.check_not_pub_in_root(&item.vis, self.source_map.guess_head_span(item.span)); |
8faf50e0 XL |
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; | |
dfeec247 | 235 | } |
32a655c1 SL |
236 | Some(attr) => attr, |
237 | }; | |
238 | ||
239 | if !is_fn { | |
e74abb32 XL |
240 | let msg = format!( |
241 | "the `#[{}]` attribute may only be used on bare functions", | |
60c5eb7d | 242 | pprust::path_to_string(&attr.get_normal_item().path), |
e74abb32 | 243 | ); |
32a655c1 | 244 | |
532ac7d7 | 245 | self.handler.span_err(attr.span, &msg); |
32a655c1 SL |
246 | return; |
247 | } | |
248 | ||
249 | if self.is_test_crate { | |
250 | return; | |
251 | } | |
252 | ||
253 | if !self.is_proc_macro_crate { | |
e74abb32 XL |
254 | let msg = format!( |
255 | "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type", | |
60c5eb7d | 256 | pprust::path_to_string(&attr.get_normal_item().path), |
e74abb32 | 257 | ); |
32a655c1 | 258 | |
532ac7d7 | 259 | self.handler.span_err(attr.span, &msg); |
32a655c1 SL |
260 | return; |
261 | } | |
262 | ||
3dfed10e | 263 | if self.sess.check_name(attr, sym::proc_macro_derive) { |
32a655c1 | 264 | self.collect_custom_derive(item, attr); |
3dfed10e | 265 | } else if self.sess.check_name(attr, sym::proc_macro_attribute) { |
9fa01778 | 266 | self.collect_attr_proc_macro(item); |
3dfed10e | 267 | } else if self.sess.check_name(attr, sym::proc_macro) { |
9fa01778 | 268 | self.collect_bang_proc_macro(item); |
32a655c1 | 269 | }; |
9e0c209e | 270 | |
8faf50e0 | 271 | let prev_in_root = mem::replace(&mut self.in_root, false); |
9e0c209e | 272 | visit::walk_item(self, item); |
9e0c209e SL |
273 | self.in_root = prev_in_root; |
274 | } | |
9e0c209e SL |
275 | } |
276 | ||
277 | // Creates a new module which looks like: | |
278 | // | |
e1599b0c | 279 | // const _: () = { |
c30ab7b3 | 280 | // extern crate proc_macro; |
9e0c209e | 281 | // |
a1dfa0c6 | 282 | // use proc_macro::bridge::client::ProcMacro; |
9e0c209e | 283 | // |
a1dfa0c6 | 284 | // #[rustc_proc_macro_decls] |
e74abb32 | 285 | // #[allow(deprecated)] |
a1dfa0c6 XL |
286 | // static DECLS: &[ProcMacro] = &[ |
287 | // ProcMacro::custom_derive($name_trait1, &[], ::$name1); | |
288 | // ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2); | |
9e0c209e | 289 | // // ... |
a1dfa0c6 | 290 | // ]; |
9e0c209e | 291 | // } |
74b04a01 XL |
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 | ||
e1599b0c XL |
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 | ); | |
136023e0 | 307 | let span = DUMMY_SP.with_def_site_ctxt(expn_id.to_expn_id()); |
e1599b0c XL |
308 | |
309 | let proc_macro = Ident::new(sym::proc_macro, span); | |
dfeec247 | 310 | let krate = cx.item(span, proc_macro, Vec::new(), ast::ItemKind::ExternCrate(None)); |
9e0c209e | 311 | |
3dfed10e XL |
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); | |
a1dfa0c6 | 318 | |
74b04a01 XL |
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 | |
a1dfa0c6 | 325 | let decls = { |
dfeec247 XL |
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])) | |
a1dfa0c6 | 330 | }; |
dfeec247 XL |
331 | macros |
332 | .iter() | |
333 | .map(|m| match m { | |
74b04a01 XL |
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 | } | |
e1599b0c | 352 | ProcMacro::Def(ca) => { |
74b04a01 | 353 | krate_ref.borrow_mut().proc_macros.push(ca.id); |
e1599b0c XL |
354 | let ident = match ca.def_type { |
355 | ProcMacroDefType::Attr => attr, | |
dfeec247 | 356 | ProcMacroDefType::Bang => bang, |
e1599b0c XL |
357 | }; |
358 | ||
dfeec247 XL |
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 | ) | |
e1599b0c | 367 | } |
dfeec247 XL |
368 | }) |
369 | .collect() | |
8faf50e0 | 370 | }; |
32a655c1 | 371 | |
dfeec247 XL |
372 | let decls_static = cx |
373 | .item_static( | |
374 | span, | |
3dfed10e | 375 | Ident::new(sym::_DECLS, span), |
dfeec247 XL |
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 | ); | |
9e0c209e | 405 | |
e1599b0c XL |
406 | let anon_constant = cx.item_const( |
407 | span, | |
f9f354fc | 408 | Ident::new(kw::Underscore, span), |
e1599b0c XL |
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() | |
9e0c209e | 416 | } |