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) => {
54                                    // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
55                                    // paths.
56                                    report_path_args(sess, meta);
57                                    Some(DeriveResolution {
58                                        path: meta.path.clone(),
59                                        item: dummy_annotatable(),
60                                        exts: None,
61                                        is_const: self.is_const,
62                                    })
63                                }
64                                MetaItemInner::Lit(lit) => {
65                                    // Reject `#[derive("Debug")]`.
66                                    report_unexpected_meta_item_lit(sess, lit);
67                                    None
68                                }
69                            })
70                            .collect()
71                    }
72                    _ => ::alloc::vec::Vec::new()vec![],
73                };
74
75                // Do not configure or clone items unless necessary.
76                if let [first, others @ ..] = &mut resolutions {
77                    first.item =
78                        cfg_eval(sess, features, item.clone(), ecx.current_expansion.lint_node_id);
79                    for other in others {
80                        other.item = first.item.clone();
81                    }
82                }
83
84                resolutions
85            });
86
87        match result {
88            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]),
89            Err(Indeterminate) => ExpandResult::Retry(item),
90        }
91    }
92}
93
94// The cheapest `Annotatable` to construct.
95fn dummy_annotatable() -> Annotatable {
96    Annotatable::GenericParam(ast::GenericParam {
97        id: ast::DUMMY_NODE_ID,
98        ident: Ident::dummy(),
99        attrs: Default::default(),
100        bounds: Default::default(),
101        is_placeholder: false,
102        kind: GenericParamKind::Lifetime,
103        colon_span: None,
104    })
105}
106
107fn report_bad_target(
108    sess: &Session,
109    item: &Annotatable,
110    span: Span,
111) -> Result<(), ErrorGuaranteed> {
112    let item_kind = match item {
113        Annotatable::Item(item) => Some(&item.kind),
114        Annotatable::Stmt(stmt) => match &stmt.kind {
115            StmtKind::Item(item) => Some(&item.kind),
116            _ => None,
117        },
118        _ => None,
119    };
120
121    let bad_target =
122        !#[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(..)));
123    if bad_target {
124        return Err(sess.dcx().emit_err(diagnostics::BadDeriveTarget { span, item: item.span() }));
125    }
126    Ok(())
127}
128
129fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
130    let help = match lit.kind {
131        ast::LitKind::Str(_, ast::StrStyle::Cooked)
132            if rustc_lexer::is_ident(lit.symbol.as_str()) =>
133        {
134            diagnostics::BadDeriveLitHelp::StrLit { sym: lit.symbol }
135        }
136        _ => diagnostics::BadDeriveLitHelp::Other,
137    };
138    sess.dcx().emit_err(diagnostics::BadDeriveLit { span: lit.span, help });
139}
140
141fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
142    let span = meta.span.with_lo(meta.path.span.hi());
143
144    match meta.kind {
145        MetaItemKind::Word => {}
146        MetaItemKind::List(..) => {
147            sess.dcx().emit_err(diagnostics::DerivePathArgsList { span });
148        }
149        MetaItemKind::NameValue(..) => {
150            sess.dcx().emit_err(diagnostics::DerivePathArgsValue { span });
151        }
152    }
153}