1 use crate::cfg_eval::cfg_eval;
4 use rustc_ast::{attr, 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};
13 pub(crate) struct Expander;
15 impl MultiItemModifier for Expander {
18 ecx: &mut ExtCtxt<'_>,
20 meta_item: &ast::MetaItem,
22 ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
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]);
30 let (sess, features) = (ecx.sess, ecx.ecfg.features);
32 ecx.resolver.resolve_derives(ecx.current_expansion.id, ecx.force_mode, &|| {
34 AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() };
36 attr::mk_attr_outer(&sess.parse_sess.attr_id_generator, meta_item.clone());
37 validate_attr::check_builtin_attribute(
44 let mut resolutions: Vec<_> = attr
48 .filter_map(|nested_meta| match nested_meta {
49 NestedMetaItem::MetaItem(meta) => Some(meta),
50 NestedMetaItem::Literal(lit) => {
51 // Reject `#[derive("Debug")]`.
52 report_unexpected_literal(sess, &lit);
57 // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the paths.
58 report_path_args(sess, &meta);
61 .map(|path| (path, dummy_annotatable(), None))
64 // Do not configure or clone items unless necessary.
65 match &mut resolutions[..] {
67 [(_, first_item, _), others @ ..] => {
68 *first_item = cfg_eval(
72 ecx.current_expansion.lint_node_id,
74 for (_, item, _) in others {
75 *item = first_item.clone();
84 Ok(()) => ExpandResult::Ready(vec![item]),
85 Err(Indeterminate) => ExpandResult::Retry(item),
90 // The cheapest `Annotatable` to construct.
91 fn dummy_annotatable() -> Annotatable {
92 Annotatable::GenericParam(ast::GenericParam {
93 id: ast::DUMMY_NODE_ID,
94 ident: Ident::empty(),
95 attrs: Default::default(),
96 bounds: Default::default(),
97 is_placeholder: false,
98 kind: GenericParamKind::Lifetime,
103 fn report_bad_target(sess: &Session, item: &Annotatable, span: Span) -> bool {
104 let item_kind = match item {
105 Annotatable::Item(item) => Some(&item.kind),
106 Annotatable::Stmt(stmt) => match &stmt.kind {
107 StmtKind::Item(item) => Some(&item.kind),
114 !matches!(item_kind, Some(ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..)));
120 "`derive` may only be applied to `struct`s, `enum`s and `union`s",
122 .span_label(span, "not applicable here")
123 .span_label(item.span(), "not a `struct`, `enum` or `union`")
129 fn report_unexpected_literal(sess: &Session, lit: &ast::Lit) {
130 let help_msg = match lit.token_lit.kind {
131 token::Str if rustc_lexer::is_ident(lit.token_lit.symbol.as_str()) => {
132 format!("try using `#[derive({})]`", lit.token_lit.symbol)
134 _ => "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
136 struct_span_err!(sess, lit.span, E0777, "expected path to a trait, found literal",)
137 .span_label(lit.span, "not a trait")
142 fn report_path_args(sess: &Session, meta: &ast::MetaItem) {
143 let report_error = |title, action| {
144 let span = meta.span.with_lo(meta.path.span.hi());
145 sess.struct_span_err(span, title)
146 .span_suggestion(span, action, "", Applicability::MachineApplicable)
150 MetaItemKind::Word => {}
151 MetaItemKind::List(..) => report_error(
152 "traits in `#[derive(...)]` don't accept arguments",
153 "remove the arguments",
155 MetaItemKind::NameValue(..) => {
156 report_error("traits in `#[derive(...)]` don't accept values", "remove the value")