rustc_builtin_macros/
derive.rs1use 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 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 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 report_unexpected_meta_item_lit(sess, lit);
67 None
68 }
69 })
70 .collect()
71 }
72 _ => ::alloc::vec::Vec::new()vec![],
73 };
74
75 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
94fn 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}