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