1 use crate::cfg_eval
::cfg_eval
;
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}
;
13 pub(crate) struct Expander(pub bool
);
15 impl MultiItemModifier
for Expander
{
18 ecx
: &mut ExtCtxt
<'_
>,
20 meta_item
: &ast
::MetaItem
,
23 ) -> ExpandResult
<Vec
<Annotatable
>, Annotatable
> {
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
]);
31 let (sess
, features
) = (ecx
.sess
, ecx
.ecfg
.features
);
33 ecx
.resolver
.resolve_derives(ecx
.current_expansion
.id
, ecx
.force_mode
, &|| {
35 AttributeTemplate { list: Some("Trait1, Trait2, ..."), ..Default::default() }
;
36 validate_attr
::check_builtin_meta_item(
39 ast
::AttrStyle
::Outer
,
44 let mut resolutions
= match &meta_item
.kind
{
45 MetaItemKind
::List(list
) => {
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
);
56 // Reject `#[derive(Debug = "value", Debug(abc))]`, but recover the
58 report_path_args(sess
, &meta
);
61 .map(|path
| (path
, dummy_annotatable(), None
, self.0))
67 // Do not configure or clone items unless necessary.
68 match &mut resolutions
[..] {
70 [(_
, first_item
, ..), others @
..] => {
71 *first_item
= cfg_eval(
75 ecx
.current_expansion
.lint_node_id
,
77 for (_
, item
, _
, _
) in others
{
78 *item
= first_item
.clone();
87 Ok(()) => ExpandResult
::Ready(vec
![item
]),
88 Err(Indeterminate
) => ExpandResult
::Retry(item
),
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
,
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
),
117 !matches
!(item_kind
, Some(ItemKind
::Struct(..) | ItemKind
::Enum(..) | ItemKind
::Union(..)));
123 "`derive` may only be applied to `struct`s, `enum`s and `union`s",
125 .span_label(span
, "not applicable here")
126 .span_label(item
.span(), "not a `struct`, `enum` or `union`")
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
)
137 _
=> "for example, write `#[derive(Debug)]` for `Debug`".to_string(),
139 struct_span_err
!(sess
, lit
.span
, E0777
, "expected path to a trait, found literal",)
140 .span_label(lit
.span
, "not a trait")
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
)
153 MetaItemKind
::Word
=> {}
154 MetaItemKind
::List(..) => report_error(
155 "traits in `#[derive(...)]` don't accept arguments",
156 "remove the arguments",
158 MetaItemKind
::NameValue(..) => {
159 report_error("traits in `#[derive(...)]` don't accept values", "remove the value")