3 use rustc_ast
::ast
::{self, Ident, NodeId}
;
5 use rustc_ast
::expand
::is_proc_macro_attr
;
7 use rustc_ast
::visit
::{self, Visitor}
;
8 use rustc_ast_pretty
::pprust
;
9 use rustc_expand
::base
::{ExtCtxt, Resolver}
;
10 use rustc_expand
::expand
::{AstFragment, ExpansionConfig}
;
11 use rustc_session
::parse
::ParseSess
;
12 use rustc_span
::hygiene
::AstPass
;
13 use rustc_span
::source_map
::SourceMap
;
14 use rustc_span
::symbol
::{kw, sym}
;
15 use rustc_span
::{Span, DUMMY_SP}
;
16 use smallvec
::smallvec
;
17 use std
::cell
::RefCell
;
19 struct ProcMacroDerive
{
21 trait_name
: ast
::Name
,
24 attrs
: Vec
<ast
::Name
>,
27 enum ProcMacroDefType
{
36 def_type
: ProcMacroDefType
,
40 Derive(ProcMacroDerive
),
44 struct CollectProcMacros
<'a
> {
45 macros
: Vec
<ProcMacro
>,
47 handler
: &'a rustc_errors
::Handler
,
48 source_map
: &'a SourceMap
,
49 is_proc_macro_crate
: bool
,
55 resolver
: &mut dyn Resolver
,
56 mut krate
: ast
::Crate
,
57 is_proc_macro_crate
: bool
,
58 has_proc_macro_decls
: bool
,
60 num_crate_types
: usize,
61 handler
: &rustc_errors
::Handler
,
63 let ecfg
= ExpansionConfig
::default("proc_macro".to_string());
64 let mut cx
= ExtCtxt
::new(sess
, ecfg
, resolver
, None
);
66 let mut collect
= CollectProcMacros
{
70 source_map
: sess
.source_map(),
75 if has_proc_macro_decls
|| is_proc_macro_crate
{
76 visit
::walk_crate(&mut collect
, &krate
);
78 let macros
= collect
.macros
;
80 if !is_proc_macro_crate
{
84 if num_crate_types
> 1 {
85 handler
.err("cannot mix `proc-macro` crate type with others");
92 let decls
= mk_decls(&mut krate
, &mut cx
, ¯os
);
93 krate
.module
.items
.push(decls
);
98 impl<'a
> CollectProcMacros
<'a
> {
99 fn check_not_pub_in_root(&self, vis
: &ast
::Visibility
, sp
: Span
) {
100 if self.is_proc_macro_crate
&& self.in_root
&& vis
.node
.is_pub() {
101 self.handler
.span_err(
103 "`proc-macro` crate types currently cannot export any items other \
104 than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, \
105 or `#[proc_macro_attribute]`",
110 fn collect_custom_derive(&mut self, item
: &'a ast
::Item
, attr
: &'a ast
::Attribute
) {
111 // Once we've located the `#[proc_macro_derive]` attribute, verify
112 // that it's of the form `#[proc_macro_derive(Foo)]` or
113 // `#[proc_macro_derive(Foo, attributes(A, ..))]`
114 let list
= match attr
.meta_item_list() {
118 if list
.len() != 1 && list
.len() != 2 {
119 self.handler
.span_err(attr
.span
, "attribute must have either one or two arguments");
122 let trait_attr
= match list
[0].meta_item() {
123 Some(meta_item
) => meta_item
,
125 self.handler
.span_err(list
[0].span(), "not a meta item");
129 let trait_ident
= match trait_attr
.ident() {
130 Some(trait_ident
) if trait_attr
.is_word() => trait_ident
,
132 self.handler
.span_err(trait_attr
.span
, "must only be one word");
137 if !trait_ident
.name
.can_be_raw() {
138 self.handler
.span_err(
140 &format
!("`{}` cannot be a name of derive macro", trait_ident
),
144 let attributes_attr
= list
.get(1);
145 let proc_attrs
: Vec
<_
> = if let Some(attr
) = attributes_attr
{
146 if !attr
.check_name(sym
::attributes
) {
147 self.handler
.span_err(attr
.span(), "second argument must be `attributes`")
149 attr
.meta_item_list()
152 .span_err(attr
.span(), "attribute must be of form: `attributes(foo, bar)`");
157 let attr
= match attr
.meta_item() {
158 Some(meta_item
) => meta_item
,
160 self.handler
.span_err(attr
.span(), "not a meta item");
165 let ident
= match attr
.ident() {
166 Some(ident
) if attr
.is_word() => ident
,
168 self.handler
.span_err(attr
.span
, "must only be one word");
172 if !ident
.name
.can_be_raw() {
173 self.handler
.span_err(
175 &format
!("`{}` cannot be a name of derive helper attribute", ident
),
186 if self.in_root
&& item
.vis
.node
.is_pub() {
187 self.macros
.push(ProcMacro
::Derive(ProcMacroDerive
{
190 trait_name
: trait_ident
.name
,
191 function_name
: item
.ident
,
195 let msg
= if !self.in_root
{
196 "functions tagged with `#[proc_macro_derive]` must \
197 currently reside in the root of the crate"
199 "functions tagged with `#[proc_macro_derive]` must be `pub`"
201 self.handler
.span_err(self.source_map
.guess_head_span(item
.span
), msg
);
205 fn collect_attr_proc_macro(&mut self, item
: &'a ast
::Item
) {
206 if self.in_root
&& item
.vis
.node
.is_pub() {
207 self.macros
.push(ProcMacro
::Def(ProcMacroDef
{
210 function_name
: item
.ident
,
211 def_type
: ProcMacroDefType
::Attr
,
214 let msg
= if !self.in_root
{
215 "functions tagged with `#[proc_macro_attribute]` must \
216 currently reside in the root of the crate"
218 "functions tagged with `#[proc_macro_attribute]` must be `pub`"
220 self.handler
.span_err(self.source_map
.guess_head_span(item
.span
), msg
);
224 fn collect_bang_proc_macro(&mut self, item
: &'a ast
::Item
) {
225 if self.in_root
&& item
.vis
.node
.is_pub() {
226 self.macros
.push(ProcMacro
::Def(ProcMacroDef
{
229 function_name
: item
.ident
,
230 def_type
: ProcMacroDefType
::Bang
,
233 let msg
= if !self.in_root
{
234 "functions tagged with `#[proc_macro]` must \
235 currently reside in the root of the crate"
237 "functions tagged with `#[proc_macro]` must be `pub`"
239 self.handler
.span_err(self.source_map
.guess_head_span(item
.span
), msg
);
244 impl<'a
> Visitor
<'a
> for CollectProcMacros
<'a
> {
245 fn visit_item(&mut self, item
: &'a ast
::Item
) {
246 if let ast
::ItemKind
::MacroDef(..) = item
.kind
{
247 if self.is_proc_macro_crate
&& attr
::contains_name(&item
.attrs
, sym
::macro_export
) {
249 "cannot export macro_rules! macros from a `proc-macro` crate type currently";
250 self.handler
.span_err(self.source_map
.guess_head_span(item
.span
), msg
);
254 // First up, make sure we're checking a bare function. If we're not then
255 // we're just not interested in this item.
257 // If we find one, try to locate a `#[proc_macro_derive]` attribute on it.
258 let is_fn
= match item
.kind
{
259 ast
::ItemKind
::Fn(..) => true,
263 let mut found_attr
: Option
<&'a ast
::Attribute
> = None
;
265 for attr
in &item
.attrs
{
266 if is_proc_macro_attr(&attr
) {
267 if let Some(prev_attr
) = found_attr
{
268 let prev_item
= prev_attr
.get_normal_item();
269 let item
= attr
.get_normal_item();
270 let path_str
= pprust
::path_to_string(&item
.path
);
271 let msg
= if item
.path
.segments
[0].ident
.name
272 == prev_item
.path
.segments
[0].ident
.name
275 "only one `#[{}]` attribute is allowed on any given function",
280 "`#[{}]` and `#[{}]` attributes cannot both be applied
281 to the same function",
283 pprust
::path_to_string(&prev_item
.path
),
288 .struct_span_err(attr
.span
, &msg
)
289 .span_label(prev_attr
.span
, "previous attribute here")
295 found_attr
= Some(attr
);
299 let attr
= match found_attr
{
301 self.check_not_pub_in_root(&item
.vis
, self.source_map
.guess_head_span(item
.span
));
302 let prev_in_root
= mem
::replace(&mut self.in_root
, false);
303 visit
::walk_item(self, item
);
304 self.in_root
= prev_in_root
;
312 "the `#[{}]` attribute may only be used on bare functions",
313 pprust
::path_to_string(&attr
.get_normal_item().path
),
316 self.handler
.span_err(attr
.span
, &msg
);
320 if self.is_test_crate
{
324 if !self.is_proc_macro_crate
{
326 "the `#[{}]` attribute is only usable with crates of the `proc-macro` crate type",
327 pprust
::path_to_string(&attr
.get_normal_item().path
),
330 self.handler
.span_err(attr
.span
, &msg
);
334 if attr
.check_name(sym
::proc_macro_derive
) {
335 self.collect_custom_derive(item
, attr
);
336 } else if attr
.check_name(sym
::proc_macro_attribute
) {
337 self.collect_attr_proc_macro(item
);
338 } else if attr
.check_name(sym
::proc_macro
) {
339 self.collect_bang_proc_macro(item
);
342 let prev_in_root
= mem
::replace(&mut self.in_root
, false);
343 visit
::walk_item(self, item
);
344 self.in_root
= prev_in_root
;
347 fn visit_mac(&mut self, mac
: &'a ast
::MacCall
) {
348 visit
::walk_mac(self, mac
)
352 // Creates a new module which looks like:
355 // extern crate proc_macro;
357 // use proc_macro::bridge::client::ProcMacro;
359 // #[rustc_proc_macro_decls]
360 // #[allow(deprecated)]
361 // static DECLS: &[ProcMacro] = &[
362 // ProcMacro::custom_derive($name_trait1, &[], ::$name1);
363 // ProcMacro::custom_derive($name_trait2, &["attribute_name"], ::$name2);
368 ast_krate
: &mut ast
::Crate
,
369 cx
: &mut ExtCtxt
<'_
>,
370 macros
: &[ProcMacro
],
372 // We're the ones filling in this Vec,
373 // so it should be empty to start with
374 assert
!(ast_krate
.proc_macros
.is_empty());
376 let expn_id
= cx
.resolver
.expansion_for_ast_pass(
378 AstPass
::ProcMacroHarness
,
379 &[sym
::rustc_attrs
, sym
::proc_macro_internals
],
382 let span
= DUMMY_SP
.with_def_site_ctxt(expn_id
);
384 let proc_macro
= Ident
::new(sym
::proc_macro
, span
);
385 let krate
= cx
.item(span
, proc_macro
, Vec
::new(), ast
::ItemKind
::ExternCrate(None
));
387 let bridge
= cx
.ident_of("bridge", span
);
388 let client
= cx
.ident_of("client", span
);
389 let proc_macro_ty
= cx
.ident_of("ProcMacro", span
);
390 let custom_derive
= cx
.ident_of("custom_derive", span
);
391 let attr
= cx
.ident_of("attr", span
);
392 let bang
= cx
.ident_of("bang", span
);
394 let krate_ref
= RefCell
::new(ast_krate
);
396 // We add NodeIds to 'krate.proc_macros' in the order
397 // that we generate expressions. The position of each NodeId
398 // in the 'proc_macros' Vec corresponds to its position
399 // in the static array that will be generated
402 |sp
: Span
, name
| cx
.expr_path(cx
.path(sp
.with_ctxt(span
.ctxt()), vec
![name
]));
403 let proc_macro_ty_method_path
= |method
| {
404 cx
.expr_path(cx
.path(span
, vec
![proc_macro
, bridge
, client
, proc_macro_ty
, method
]))
409 ProcMacro
::Derive(cd
) => {
410 krate_ref
.borrow_mut().proc_macros
.push(cd
.id
);
413 proc_macro_ty_method_path(custom_derive
),
415 cx
.expr_str(cd
.span
, cd
.trait_name
),
420 .map(|&s
| cx
.expr_str(cd
.span
, s
))
421 .collect
::<Vec
<_
>>(),
423 local_path(cd
.span
, cd
.function_name
),
427 ProcMacro
::Def(ca
) => {
428 krate_ref
.borrow_mut().proc_macros
.push(ca
.id
);
429 let ident
= match ca
.def_type
{
430 ProcMacroDefType
::Attr
=> attr
,
431 ProcMacroDefType
::Bang
=> bang
,
436 proc_macro_ty_method_path(ident
),
438 cx
.expr_str(ca
.span
, ca
.function_name
.name
),
439 local_path(ca
.span
, ca
.function_name
),
447 let decls_static
= cx
450 cx
.ident_of("_DECLS", span
),
456 cx
.ty_path(cx
.path(span
, vec
![proc_macro
, bridge
, client
, proc_macro_ty
])),
460 ast
::Mutability
::Not
,
462 ast
::Mutability
::Not
,
463 cx
.expr_vec_slice(span
, decls
),
466 let attr
= cx
.meta_word(span
, sym
::rustc_proc_macro_decls
);
467 i
.attrs
.push(cx
.attribute(attr
));
469 let deprecated_attr
= attr
::mk_nested_word_item(Ident
::new(sym
::deprecated
, span
));
470 let allow_deprecated_attr
=
471 attr
::mk_list_item(Ident
::new(sym
::allow
, span
), vec
![deprecated_attr
]);
472 i
.attrs
.push(cx
.attribute(allow_deprecated_attr
));
477 let block
= cx
.expr_block(
478 cx
.block(span
, vec
![cx
.stmt_item(span
, krate
), cx
.stmt_item(span
, decls_static
)]),
481 let anon_constant
= cx
.item_const(
483 ast
::Ident
::new(kw
::Underscore
, span
),
484 cx
.ty(span
, ast
::TyKind
::Tup(Vec
::new())),
488 // Integrate the new item into existing module structures.
489 let items
= AstFragment
::Items(smallvec
![anon_constant
]);
490 cx
.monotonic_expander().fully_expand_fragment(items
).make_items().pop().unwrap()