]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/proc_macro_harness.rs
New upstream version 1.55.0+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / proc_macro_harness.rs
CommitLineData
9e0c209e
SL
1use std::mem;
2
74b04a01 3use rustc_ast::attr;
74b04a01
XL
4use rustc_ast::ptr::P;
5use rustc_ast::visit::{self, Visitor};
3dfed10e 6use rustc_ast::{self as ast, NodeId};
74b04a01 7use rustc_ast_pretty::pprust;
136023e0 8use rustc_expand::base::{parse_macro_name_and_helper_attrs, ExtCtxt, ResolverExpand};
dfeec247 9use rustc_expand::expand::{AstFragment, ExpansionConfig};
3dfed10e 10use rustc_session::Session;
dfeec247 11use rustc_span::hygiene::AstPass;
ba9703b0 12use rustc_span::source_map::SourceMap;
f9f354fc 13use rustc_span::symbol::{kw, sym, Ident, Symbol};
dfeec247 14use rustc_span::{Span, DUMMY_SP};
e1599b0c 15use smallvec::smallvec;
74b04a01 16use std::cell::RefCell;
32a655c1 17
8bb4bdeb 18struct 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
26enum ProcMacroDefType {
27 Attr,
dfeec247 28 Bang,
e1599b0c
XL
29}
30
8bb4bdeb 31struct ProcMacroDef {
74b04a01 32 id: NodeId,
32a655c1
SL
33 function_name: Ident,
34 span: Span,
dfeec247 35 def_type: ProcMacroDefType,
e1599b0c
XL
36}
37
38enum ProcMacro {
39 Derive(ProcMacroDerive),
dfeec247 40 Def(ProcMacroDef),
32a655c1
SL
41}
42
43struct 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 53pub 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, &macros);
6a06907d 94 krate.items.push(decls);
9e0c209e 95
8bb4bdeb
XL
96 krate
97}
9e0c209e 98
32a655c1 99impl<'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
176impl<'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
292fn 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}