]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_builtin_macros/src/derive.rs
New upstream version 1.75.0+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / derive.rs
1 use crate::cfg_eval::cfg_eval;
2 use crate::errors;
3
4 use rustc_ast as ast;
5 use rustc_ast::{GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
6 use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
7 use rustc_feature::AttributeTemplate;
8 use rustc_parse::validate_attr;
9 use rustc_session::Session;
10 use rustc_span::symbol::{sym, Ident};
11 use rustc_span::{ErrorGuaranteed, Span};
12
13 pub(crate) struct Expander(pub bool);
14
15 impl MultiItemModifier for Expander {
16 fn expand(
17 &self,
18 ecx: &mut ExtCtxt<'_>,
19 span: Span,
20 meta_item: &ast::MetaItem,
21 item: Annotatable,
22 _: bool,
23 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
24 let sess = ecx.sess;
25 if report_bad_target(sess, &item, span).is_err() {
26 // We don't want to pass inappropriate targets to derive macros to avoid
27 // follow up errors, all other errors below are recoverable.
28 return ExpandResult::Ready(vec![item]);
29 }
30
31 let (sess, features) = (ecx.sess, ecx.ecfg.features);
32 let result =
33 ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
34 let template =
35 AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
36 validate_attr::check_builtin_meta_item(
37 &sess.parse_sess,
38 &meta_item,
39 ast::AttrStyle::Outer,
40 sym::derive,
41 template,
42 );
43
44 let mut resolutions = match &meta_item.kind {
45 MetaItemKind::List(list) => {
46 list.iter()
47 .filter_map(|nested_meta| match nested_meta {
48 NestedMetaItem::MetaItem(meta) => Some(meta),
49 NestedMetaItem::Lit(lit) => {
50 // Reject `#[derive("Debug")]`.
51 report_unexpected_meta_item_lit(sess, &lit);
52 None
53 }
54 })
55 .map(|meta| {
56 // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
57 // paths.
58 report_path_args(sess, &meta);
59 meta.path.clone()
60 })
61 .map(|path| (path, dummy_annotatable(), None, self.0))
62 .collect()
63 }
64 _ => vec![],
65 };
66
67 // Do not configure or clone items unless necessary.
68 match &mut resolutions[..] {
69 [] => {}
70 [(_, first_item, ..), others @ ..] => {
71 *first_item = cfg_eval(
72 sess,
73 features,
74 item.clone(),
75 ecx.current_expansion.lint_node_id,
76 );
77 for (_, item, _, _) in others {
78 *item = first_item.clone();
79 }
80 }
81 }
82
83 resolutions
84 });
85
86 match result {
87 Ok(()) => ExpandResult::Ready(vec![item]),
88 Err(Indeterminate) => ExpandResult::Retry(item),
89 }
90 }
91 }
92
93 // The cheapest `Annotatable` to construct.
94 fn dummy_annotatable() -> Annotatable {
95 Annotatable::GenericParam(ast::GenericParam {
96 id: ast::DUMMY_NODE_ID,
97 ident: Ident::empty(),
98 attrs: Default::default(),
99 bounds: Default::default(),
100 is_placeholder: false,
101 kind: GenericParamKind::Lifetime,
102 colon_span: None,
103 })
104 }
105
106 fn report_bad_target(
107 sess: &Session,
108 item: &Annotatable,
109 span: Span,
110 ) -> Result<(), ErrorGuaranteed> {
111 let item_kind = match item {
112 Annotatable::Item(item) => Some(&item.kind),
113 Annotatable::Stmt(stmt) => match &stmt.kind {
114 StmtKind::Item(item) => Some(&item.kind),
115 _ => None,
116 },
117 _ => None,
118 };
119
120 let bad_target =
121 !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
122 if bad_target {
123 return Err(sess.emit_err(errors::BadDeriveTarget { span, item: item.span() }));
124 }
125 Ok(())
126 }
127
128 fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
129 let help = match lit.kind {
130 ast::LitKind::Str(_, ast::StrStyle::Cooked)
131 if rustc_lexer::is_ident(lit.symbol.as_str()) =>
132 {
133 errors::BadDeriveLitHelp::StrLit { sym: lit.symbol }
134 }
135 _ => errors::BadDeriveLitHelp::Other,
136 };
137 sess.emit_err(errors::BadDeriveLit { span: lit.span, help });
138 }
139
140 fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
141 let span = meta.span.with_lo(meta.path.span.hi());
142
143 match meta.kind {
144 MetaItemKind::Word => {}
145 MetaItemKind::List(..) => {
146 sess.emit_err(errors::DerivePathArgsList { span });
147 }
148 MetaItemKind::NameValue(..) => {
149 sess.emit_err(errors::DerivePathArgsValue { span });
150 }
151 }
152 }