]> git.proxmox.com Git - rustc.git/blame - compiler/rustc_builtin_macros/src/derive.rs
New upstream version 1.67.1+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / derive.rs
CommitLineData
6a06907d
XL
1use crate::cfg_eval::cfg_eval;
2
c295e0f8 3use rustc_ast as ast;
487cf647 4use rustc_ast::{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
487cf647 13pub(crate) struct Expander(pub bool);
6a06907d
XL
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,
487cf647 22 _: bool,
6a06907d
XL
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
c295e0f8 31 let (sess, features) = (ecx.sess, ecx.ecfg.features);
cdc7bbd5
XL
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() };
487cf647 36 validate_attr::check_builtin_meta_item(
cdc7bbd5 37 &sess.parse_sess,
487cf647
FG
38 &meta_item,
39 ast::AttrStyle::Outer,
cdc7bbd5
XL
40 sym::derive,
41 template,
42 );
6a06907d 43
487cf647
FG
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 };
c295e0f8
XL
66
67 // Do not configure or clone items unless necessary.
68 match &mut resolutions[..] {
69 [] => {}
487cf647 70 [(_, first_item, ..), others @ ..] => {
5e7ed085
FG
71 *first_item = cfg_eval(
72 sess,
73 features,
74 item.clone(),
75 ecx.current_expansion.lint_node_id,
76 );
487cf647 77 for (_, item, _, _) in others {
c295e0f8
XL
78 *item = first_item.clone();
79 }
80 }
81 }
82
83 resolutions
cdc7bbd5 84 });
6a06907d 85
cdc7bbd5 86 match result {
136023e0 87 Ok(()) => ExpandResult::Ready(vec![item]),
6a06907d
XL
88 Err(Indeterminate) => ExpandResult::Retry(item),
89 }
90 }
91}
92
c295e0f8
XL
93// The cheapest `Annotatable` to construct.
94fn dummy_annotatable() -> Annotatable {
95 Annotatable::GenericParam(ast::GenericParam {
96 id: ast::DUMMY_NODE_ID,
3c0e092e 97 ident: Ident::empty(),
c295e0f8
XL
98 attrs: Default::default(),
99 bounds: Default::default(),
100 is_placeholder: false,
101 kind: GenericParamKind::Lifetime,
04454e1e 102 colon_span: None,
c295e0f8
XL
103 })
104}
105
6a06907d
XL
106fn 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,
136023e0 123 "`derive` may only be applied to `struct`s, `enum`s and `union`s",
6a06907d 124 )
136023e0
XL
125 .span_label(span, "not applicable here")
126 .span_label(item.span(), "not a `struct`, `enum` or `union`")
6a06907d
XL
127 .emit();
128 }
129 bad_target
130}
131
487cf647 132fn report_unexpected_meta_item_lit(sess: &Session, lit: &ast::MetaItemLit) {
f2b60f7d
FG
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)
6a06907d
XL
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",)
136023e0 140 .span_label(lit.span, "not a trait")
6a06907d
XL
141 .help(&help_msg)
142 .emit();
143}
144
145fn 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)
923072b8 149 .span_suggestion(span, action, "", Applicability::MachineApplicable)
6a06907d
XL
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}