Skip to main content

rustc_builtin_macros/
derive.rs

1use rustc_ast as ast;
2use rustc_ast::{GenericParamKind, ItemKind, MetaItemInner, MetaItemKind, StmtKind};
3use rustc_attr_parsing::{AttributeTemplate, validate_attr};
4use rustc_expand::base::{
5    Annotatable, DeriveResolution, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier,
6};
7use rustc_session::Session;
8use rustc_span::{ErrorGuaranteed, Ident, Span, sym};
9
10use crate::cfg_eval::cfg_eval;
11use crate::diagnostics;
12
13pub(crate) struct Expander {
14    pub is_const: bool,
15}
16
17impl MultiItemModifier for Expander {
18    fn expand(
19        &self,
20        ecx: &mut ExtCtxt<'_>,
21        span: Span,
22        meta_item: &ast::MetaItem,
23        item: Annotatable,
24        _: bool,
25    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
26        let sess = ecx.sess;
27        if report_bad_target(sess, &item, span).is_err() {
28            // We don't want to pass inappropriate targets to derive macros to avoid
29            // follow up errors, all other errors below are recoverable.
30            return ExpandResult::Ready(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [item]))vec![item]);
31        }
32
33        let (sess, features) = (ecx.sess, ecx.ecfg.features);
34        let result =
35            ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
36                let template = AttributeTemplate {
37                    list: Some(&["Trait1, Trait2, ..."]),
38                    ..Default::default()
39                };
40                validate_attr::check_builtin_meta_item(
41                    &sess.psess,
42                    meta_item,
43                    ast::AttrStyle::Outer,
44                    sym::derive,
45                    template,
46                    true,
47                );
48
49                let mut resolutions = match &meta_item.kind {
50                    MetaItemKind::List(list) => {
51                        list.iter()
52                            .filter_map(|meta_item_inner| match meta_item_inner {
53                                MetaItemInner::MetaItem(meta) => Some(meta),
54                                MetaItemInner::Lit(lit) => {
55                                    // Reject `#[derive("Debug")]`.
56                                    report_unexpected_meta_item_lit(sess, lit);
57                                    None
58                                }
59                            })
60                            .map(|meta| {
61                                // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
62                                // paths.
63                                report_path_args(sess, meta);
64                                meta.path.clone()
65                            })
66                            .map(|path| DeriveResolution {
67                                path,
68                                item: dummy_annotatable(),
69                                exts: None,
70                                is_const: self.is_const,
71                            })
72                            .collect()
73                    }
74                    _ => ::alloc::vec::Vec::new()vec![],
75                };
76
77                // Do not configure or clone items unless necessary.
78                match &mut resolutions[..] {
79                    [] => {}
80                    [first, others @ ..] => {
81                        first.item = cfg_eval(
82                            sess,
83                            features,
84                            item.clone(),
85                            ecx.current_expansion.lint_node_id,
86                        );
87                        for other in others {
88                            other.item = first.item.clone();
89                        }
90                    }
91                }
92
93                resolutions
94            });
95
96        match result {
97            Ok(()) => ExpandResult::Ready(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
        [item]))vec![item]),
98            Err(Indeterminate) => ExpandResult::Retry(item),
99        }
100    }
101}
102
103// The cheapest `Annotatable` to construct.
104fn dummy_annotatable() -> Annotatable {
105    Annotatable::GenericParam(ast::GenericParam {
106        id: ast::DUMMY_NODE_ID,
107        ident: Ident::dummy(),
108        attrs: Default::default(),
109        bounds: Default::default(),
110        is_placeholder: false,
111        kind: GenericParamKind::Lifetime,
112        colon_span: None,
113    })
114}
115
116fn report_bad_target(
117    sess: &Session,
118    item: &Annotatable,
119    span: Span,
120) -> Result<(), ErrorGuaranteed> {
121    let item_kind = match item {
122        Annotatable::Item(item) => Some(&item.kind),
123        Annotatable::Stmt(stmt) => match &stmt.kind {
124            StmtKind::Item(item) => Some(&item.kind),
125            _ => None,
126        },
127        _ => None,
128    };
129
130    let bad_target =
131        !#[allow(non_exhaustive_omitted_patterns)] match item_kind {
    Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)) =>
        true,
    _ => false,
}matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
132    if bad_target {
133        return Err(sess.dcx().emit_err(diagnostics::BadDeriveTarget { span, item: item.span() }));
134    }
135    Ok(())
136}
137
138fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
139    let help = match lit.kind {
140        ast::LitKind::Str(_, ast::StrStyle::Cooked)
141            if rustc_lexer::is_ident(lit.symbol.as_str()) =>
142        {
143            diagnostics::BadDeriveLitHelp::StrLit { sym: lit.symbol }
144        }
145        _ => diagnostics::BadDeriveLitHelp::Other,
146    };
147    sess.dcx().emit_err(diagnostics::BadDeriveLit { span: lit.span, help });
148}
149
150fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
151    let span = meta.span.with_lo(meta.path.span.hi());
152
153    match meta.kind {
154        MetaItemKind::Word => {}
155        MetaItemKind::List(..) => {
156            sess.dcx().emit_err(diagnostics::DerivePathArgsList { span });
157        }
158        MetaItemKind::NameValue(..) => {
159            sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span });
160        }
161    }
162}