]>
Commit | Line | Data |
---|---|---|
6a06907d XL |
1 | use crate::cfg_eval::cfg_eval; |
2 | ||
c295e0f8 | 3 | use rustc_ast as ast; |
487cf647 | 4 | use rustc_ast::{token, GenericParamKind, ItemKind, MetaItemKind, NestedMetaItem, StmtKind}; |
6a06907d XL |
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; | |
c295e0f8 | 10 | use rustc_span::symbol::{sym, Ident}; |
6a06907d XL |
11 | use rustc_span::Span; |
12 | ||
487cf647 | 13 | pub(crate) struct Expander(pub bool); |
6a06907d XL |
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, | |
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. |
94 | fn 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 |
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, | |
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 | 132 | fn 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 | ||
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) | |
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 | } |