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