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