1 use crate::deriving
::generic
::ty
::*;
2 use crate::deriving
::generic
::*;
5 use rustc_ast
::walk_list
;
6 use rustc_ast
::EnumDef
;
7 use rustc_ast
::VariantData
;
8 use rustc_ast
::{Expr, MetaItem}
;
9 use rustc_errors
::Applicability
;
10 use rustc_expand
::base
::{Annotatable, DummyResult, ExtCtxt}
;
11 use rustc_span
::symbol
::Ident
;
12 use rustc_span
::symbol
::{kw, sym}
;
14 use smallvec
::SmallVec
;
16 pub fn expand_deriving_default(
21 push
: &mut dyn FnMut(Annotatable
),
23 item
.visit_with(&mut DetectNonVariantDefaultAttr { cx }
);
25 let inline
= cx
.meta_word(span
, sym
::inline
);
26 let attrs
= vec
![cx
.attribute(inline
)];
27 let trait_def
= TraitDef
{
29 attributes
: Vec
::new(),
30 path
: Path
::new(vec
![kw
::Default
, sym
::Default
]),
31 additional_bounds
: Vec
::new(),
32 generics
: Bounds
::empty(),
34 supports_unions
: false,
35 methods
: vec
![MethodDef
{
37 generics
: Bounds
::empty(),
43 unify_fieldless_variants
: false,
44 combine_substructure
: combine_substructure(Box
::new(|cx
, trait_span
, substr
| {
46 StaticStruct(_
, fields
) => {
47 default_struct_substructure(cx
, trait_span
, substr
, fields
)
49 StaticEnum(enum_def
, _
) => default_enum_substructure(cx
, trait_span
, enum_def
),
50 _
=> cx
.span_bug(trait_span
, "method in `derive(Default)`"),
54 associated_types
: Vec
::new(),
56 trait_def
.expand(cx
, mitem
, item
, push
)
59 fn default_struct_substructure(
62 substr
: &Substructure
<'_
>,
63 summary
: &StaticFields
,
65 // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
66 let default_ident
= cx
.std_path(&[kw
::Default
, sym
::Default
, kw
::Default
]);
67 let default_call
= |span
| cx
.expr_call_global(span
, default_ident
.clone(), Vec
::new());
70 Unnamed(ref fields
, is_tuple
) => {
72 cx
.expr_ident(trait_span
, substr
.type_ident
)
74 let exprs
= fields
.iter().map(|sp
| default_call(*sp
)).collect();
75 cx
.expr_call_ident(trait_span
, substr
.type_ident
, exprs
)
78 Named(ref fields
) => {
79 let default_fields
= fields
81 .map(|&(ident
, span
)| cx
.field_imm(span
, ident
, default_call(span
)))
83 cx
.expr_struct_ident(trait_span
, substr
.type_ident
, default_fields
)
88 fn default_enum_substructure(
93 let Ok(default_variant
) = extract_default_variant(cx
, enum_def
, trait_span
) else {
94 return DummyResult
::raw_expr(trait_span
, true);
97 // At this point, we know that there is exactly one variant with a `#[default]` attribute. The
98 // attribute hasn't yet been validated.
100 if let Err(()) = validate_default_attribute(cx
, default_variant
) {
101 return DummyResult
::raw_expr(trait_span
, true);
104 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
106 cx
.expr_path(cx
.path(
107 default_variant
.span
,
108 vec
![Ident
::new(kw
::SelfUpper
, default_variant
.span
), default_variant
.ident
],
112 fn extract_default_variant
<'a
>(
113 cx
: &mut ExtCtxt
<'_
>,
114 enum_def
: &'a EnumDef
,
116 ) -> Result
<&'a rustc_ast
::Variant
, ()> {
117 let default_variants
: SmallVec
<[_
; 1]> = enum_def
120 .filter(|variant
| cx
.sess
.contains_name(&variant
.attrs
, kw
::Default
))
123 let variant
= match default_variants
.as_slice() {
124 [variant
] => variant
,
126 let possible_defaults
= enum_def
129 .filter(|variant
| matches
!(variant
.data
, VariantData
::Unit(..)))
130 .filter(|variant
| !cx
.sess
.contains_name(&variant
.attrs
, sym
::non_exhaustive
));
132 let mut diag
= cx
.struct_span_err(trait_span
, "no default declared");
133 diag
.help("make a unit variant default by placing `#[default]` above it");
134 for variant
in possible_defaults
{
135 // Suggest making each unit variant default.
136 diag
.tool_only_span_suggestion(
138 &format
!("make `{}` default", variant
.ident
),
139 format
!("#[default] {}", variant
.ident
),
140 Applicability
::MaybeIncorrect
,
147 [first
, rest @
..] => {
148 let mut diag
= cx
.struct_span_err(trait_span
, "multiple declared defaults");
149 diag
.span_label(first
.span
, "first default");
150 diag
.span_labels(rest
.iter().map(|variant
| variant
.span
), "additional default");
151 diag
.note("only one variant can be default");
152 for variant
in &default_variants
{
153 // Suggest making each variant already tagged default.
154 let suggestion
= default_variants
157 if v
.ident
== variant
.ident
{
160 Some((cx
.sess
.find_by_name(&v
.attrs
, kw
::Default
)?
.span
, String
::new()))
165 diag
.tool_only_multipart_suggestion(
166 &format
!("make `{}` default", variant
.ident
),
168 Applicability
::MaybeIncorrect
,
177 if !matches
!(variant
.data
, VariantData
::Unit(..)) {
180 "the `#[default]` attribute may only be used on unit enum variants",
182 .help("consider a manual implementation of `Default`")
188 if let Some(non_exhaustive_attr
) = cx
.sess
.find_by_name(&variant
.attrs
, sym
::non_exhaustive
) {
189 cx
.struct_span_err(variant
.ident
.span
, "default variant must be exhaustive")
190 .span_label(non_exhaustive_attr
.span
, "declared `#[non_exhaustive]` here")
191 .help("consider a manual implementation of `Default`")
200 fn validate_default_attribute(
201 cx
: &mut ExtCtxt
<'_
>,
202 default_variant
: &rustc_ast
::Variant
,
203 ) -> Result
<(), ()> {
204 let attrs
: SmallVec
<[_
; 1]> =
205 cx
.sess
.filter_by_name(&default_variant
.attrs
, kw
::Default
).collect();
207 let attr
= match attrs
.as_slice() {
210 "this method must only be called with a variant that has a `#[default]` attribute",
212 [first
, rest @
..] => {
213 let suggestion_text
=
214 if rest
.len() == 1 { "try removing this" }
else { "try removing these" }
;
216 cx
.struct_span_err(default_variant
.ident
.span
, "multiple `#[default]` attributes")
217 .note("only one `#[default]` attribute is needed")
218 .span_label(first
.span
, "`#[default]` used here")
219 .span_label(rest
[0].span
, "`#[default]` used again here")
220 .span_help(rest
.iter().map(|attr
| attr
.span
).collect
::<Vec
<_
>>(), suggestion_text
)
221 // This would otherwise display the empty replacement, hence the otherwise
222 // repetitive `.span_help` call above.
223 .tool_only_multipart_suggestion(
225 rest
.iter().map(|attr
| (attr
.span
, String
::new())).collect(),
226 Applicability
::MachineApplicable
,
234 cx
.struct_span_err(attr
.span
, "`#[default]` attribute does not accept a value")
235 .span_suggestion_hidden(
237 "try using `#[default]`",
239 Applicability
::MaybeIncorrect
,
248 struct DetectNonVariantDefaultAttr
<'a
, 'b
> {
252 impl<'a
, 'b
> rustc_ast
::visit
::Visitor
<'a
> for DetectNonVariantDefaultAttr
<'a
, 'b
> {
253 fn visit_attribute(&mut self, attr
: &'a rustc_ast
::Attribute
) {
254 if attr
.has_name(kw
::Default
) {
258 "the `#[default]` attribute may only be used on unit enum variants",
263 rustc_ast
::visit
::walk_attribute(self, attr
);
265 fn visit_variant(&mut self, v
: &'a rustc_ast
::Variant
) {
266 self.visit_ident(v
.ident
);
267 self.visit_vis(&v
.vis
);
268 self.visit_variant_data(&v
.data
);
269 walk_list
!(self, visit_anon_const
, &v
.disr_expr
);
270 for attr
in &v
.attrs
{
271 rustc_ast
::visit
::walk_attribute(self, attr
);