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) => Some(meta),
54 MetaItemInner::Lit(lit) => {
55 report_unexpected_meta_item_lit(sess, lit);
57 None
58 }
59 })
60 .map(|meta| {
61 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 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
103fn 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}