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