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