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