]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/derive.rs
Merge tag 'debian/1.52.1+dfsg1-1_exp2' into proxmox/buster
[rustc.git] / compiler / rustc_builtin_macros / src / derive.rs
CommitLineData
6a06907d
XL
1use crate::cfg_eval::cfg_eval;
2
3use rustc_ast::{self as ast, token, ItemKind, MetaItemKind, NestedMetaItem, StmtKind};
4use rustc_errors::{struct_span_err, Applicability};
5use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt, Indeterminate, MultiItemModifier};
6use rustc_feature::AttributeTemplate;
7use rustc_parse::validate_attr;
8use rustc_session::Session;
9use rustc_span::symbol::sym;
10use rustc_span::Span;
11
12crate struct Expander;
13
14impl MultiItemModifier for Expander {
15 fn expand(
16 &self,
17 ecx: &mut ExtCtxt<'_>,
18 span: Span,
19 meta_item: &ast::MetaItem,
20 item: Annotatable,
21 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
22 let sess = ecx.sess;
23 if report_bad_target(sess, &item, span) {
24 // We don't want to pass inappropriate targets to derive macros to avoid
25 // follow up errors, all other errors below are recoverable.
26 return ExpandResult::Ready(vec![item]);
27 }
28
29 let template =
30 AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
31 let attr = ecx.attribute(meta_item.clone());
32 validate_attr::check_builtin_attribute(&sess.parse_sess, &attr, sym::derive, template);
33
34 let derives: Vec<_> = attr
35 .meta_item_list()
36 .unwrap_or_default()
37 .into_iter()
38 .filter_map(|nested_meta| match nested_meta {
39 NestedMetaItem::MetaItem(meta) => Some(meta),
40 NestedMetaItem::Literal(lit) => {
41 // Reject `#[derive("Debug")]`.
42 report_unexpected_literal(sess, &lit);
43 None
44 }
45 })
46 .map(|meta| {
47 // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
48 report_path_args(sess, &meta);
49 meta.path
50 })
51 .collect();
52
53 // FIXME: Try to cache intermediate results to avoid collecting same paths multiple times.
54 match ecx.resolver.resolve_derives(ecx.current_expansion.id, derives, ecx.force_mode) {
55 Ok(()) => ExpandResult::Ready(cfg_eval(ecx, item)),
56 Err(Indeterminate) => ExpandResult::Retry(item),
57 }
58 }
59}
60
61fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
62 let item_kind = match item {
63 Annotatable::Item(item) => Some(&item.kind),
64 Annotatable::Stmt(stmt) => match &stmt.kind {
65 StmtKind::Item(item) => Some(&item.kind),
66 _ => None,
67 },
68 _ => None,
69 };
70
71 let bad_target =
72 !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
73 if bad_target {
74 struct_span_err!(
75 sess,
76 span,
77 E0774,
78 "`derive` may only be applied to structs, enums and unions",
79 )
80 .emit();
81 }
82 bad_target
83}
84
85fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) {
86 let help_msg = match lit.token.kind {
87 token::Str if rustc_lexer::is_ident(&lit.token.symbol.as_str()) => {
88 format!("try using `#[derive({})]`", lit.token.symbol)
89 }
90 _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
91 };
92 struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",)
93 .help(&help_msg)
94 .emit();
95}
96
97fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
98 let report_error = |title, action| {
99 let span = meta.span.with_lo(meta.path.span.hi());
100 sess.struct_span_err(span, title)
101 .span_suggestion(span, action, String::new(), Applicability::MachineApplicable)
102 .emit();
103 };
104 match meta.kind {
105 MetaItemKind::Word => {}
106 MetaItemKind::List(..) => report_error(
107 "traits in `#[derive(...)]` don't accept arguments",
108 "remove the arguments",
109 ),
110 MetaItemKind::NameValue(..) => {
111 report_error("traits in `#[derive(...)]` don't accept values", "remove the value")
112 }
113 }
114}