]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_builtin_macros/src/derive.rs
Auto merge of #104824 - klensy:bump-some, r=Mark-Simulacrum
[rust.git] / compiler / rustc_builtin_macros / src / derive.rs
1 use crate::cfg_eval::cfg_eval;
2
3 use rustc_ast as ast;
4 use rustc_ast::{token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
5 use rustc_errors::{struct_span_err, Applicability};
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::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) {
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(sess: &Session, item: &Annotatable, span: Span) -> bool {
107     let item_kind = match item {
108         Annotatable::Item(item) => Some(&item.kind),
109         Annotatable::Stmt(stmt) => match &stmt.kind {
110             StmtKind::Item(item) => Some(&item.kind),
111             _ => None,
112         },
113         _ => None,
114     };
115
116     let bad_target =
117         !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
118     if bad_target {
119         struct_span_err!(
120             sess,
121             span,
122             E0774,
123             "`derive` may only be applied to `struct`s, `enum`s and `union`s",
124         )
125         .span_label(span, "not applicable here")
126         .span_label(item.span(), "not a `struct`, `enum` or `union`")
127         .emit();
128     }
129     bad_target
130 }
131
132 fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
133     let help_msg = match lit.token_lit.kind {
134         token::Str if rustc_lexer::is_ident(lit.token_lit.symbol.as_str()) => {
135             format!("try using `#[derive({})]`", lit.token_lit.symbol)
136         }
137         _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
138     };
139     struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",)
140         .span_label(lit.span, "not a trait")
141         .help(&help_msg)
142         .emit();
143 }
144
145 fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
146     let report_error = |title, action| {
147         let span = meta.span.with_lo(meta.path.span.hi());
148         sess.struct_span_err(span, title)
149             .span_suggestion(span, action, "", Applicability::MachineApplicable)
150             .emit();
151     };
152     match meta.kind {
153         MetaItemKind::Word => {}
154         MetaItemKind::List(..) => report_error(
155             "traits in `#[derive(...)]` don't accept arguments",
156             "remove the arguments",
157         ),
158         MetaItemKind::NameValue(..) => {
159             report_error("traits in `#[derive(...)]` don't accept values", "remove the value")
160         }
161     }
162 }