1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
4 use rustc_ast::{walk_list, EnumDef, VariantData};
5 use rustc_errors::Applicability;
6 use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
7 use rustc_span::symbol::Ident;
8 use rustc_span::symbol::{kw, sym};
10 use smallvec::SmallVec;
11 use thin_vec::thin_vec;
13 pub fn expand_deriving_default(
16 mitem: &ast::MetaItem,
18 push: &mut dyn FnMut(Annotatable),
21 item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
23 let attrs = thin_vec![cx.attr_word(sym::inline, span)];
24 let trait_def = TraitDef {
26 path: Path::new(vec![kw::Default, sym::Default]),
27 skip_path_as_bound: has_a_default_variant(item),
28 additional_bounds: Vec::new(),
29 supports_unions: false,
30 methods: vec![MethodDef {
32 generics: Bounds::empty(),
34 nonself_args: Vec::new(),
37 unify_fieldless_variants: false,
38 combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
40 StaticStruct(_, fields) => {
41 default_struct_substructure(cx, trait_span, substr, fields)
43 StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def),
44 _ => cx.span_bug(trait_span, "method in `derive(Default)`"),
48 associated_types: Vec::new(),
51 trait_def.expand(cx, mitem, item, push)
54 fn default_struct_substructure(
57 substr: &Substructure<'_>,
58 summary: &StaticFields,
60 // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
61 let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
62 let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
64 let expr = match summary {
65 Unnamed(_, false) => cx.expr_ident(trait_span, substr.type_ident),
66 Unnamed(fields, true) => {
67 let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
68 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
71 let default_fields = fields
73 .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
75 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
78 BlockOrExpr::new_expr(expr)
81 fn default_enum_substructure(
86 let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
87 && let Ok(_) = validate_default_attribute(cx, default_variant)
89 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
92 vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
95 DummyResult::raw_expr(trait_span, true)
97 BlockOrExpr::new_expr(expr)
100 fn extract_default_variant<'a>(
101 cx: &mut ExtCtxt<'_>,
102 enum_def: &'a EnumDef,
104 ) -> Result<&'a rustc_ast::Variant, ()> {
105 let default_variants: SmallVec<[_; 1]> = enum_def
108 .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
111 let variant = match default_variants.as_slice() {
112 [variant] => variant,
114 let possible_defaults = enum_def
117 .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
118 .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
120 let mut diag = cx.struct_span_err(trait_span, "no default declared");
121 diag.help("make a unit variant default by placing `#[default]` above it");
122 for variant in possible_defaults {
123 // Suggest making each unit variant default.
124 diag.tool_only_span_suggestion(
126 &format!("make `{}` default", variant.ident),
127 format!("#[default] {}", variant.ident),
128 Applicability::MaybeIncorrect,
135 [first, rest @ ..] => {
136 let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
137 diag.span_label(first.span, "first default");
138 diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
139 diag.note("only one variant can be default");
140 for variant in &default_variants {
141 // Suggest making each variant already tagged default.
142 let suggestion = default_variants
145 if v.span == variant.span {
148 Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
153 diag.tool_only_multipart_suggestion(
154 &format!("make `{}` default", variant.ident),
156 Applicability::MaybeIncorrect,
165 if !matches!(variant.data, VariantData::Unit(..)) {
168 "the `#[default]` attribute may only be used on unit enum variants",
170 .help("consider a manual implementation of `Default`")
176 if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
177 cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
178 .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
179 .help("consider a manual implementation of `Default`")
188 fn validate_default_attribute(
189 cx: &mut ExtCtxt<'_>,
190 default_variant: &rustc_ast::Variant,
191 ) -> Result<(), ()> {
192 let attrs: SmallVec<[_; 1]> =
193 cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
195 let attr = match attrs.as_slice() {
198 "this method must only be called with a variant that has a `#[default]` attribute",
200 [first, rest @ ..] => {
201 let suggestion_text =
202 if rest.len() == 1 { "try removing this" } else { "try removing these" };
204 cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
205 .note("only one `#[default]` attribute is needed")
206 .span_label(first.span, "`#[default]` used here")
207 .span_label(rest[0].span, "`#[default]` used again here")
208 .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
209 // This would otherwise display the empty replacement, hence the otherwise
210 // repetitive `.span_help` call above.
211 .tool_only_multipart_suggestion(
213 rest.iter().map(|attr| (attr.span, String::new())).collect(),
214 Applicability::MachineApplicable,
222 cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
223 .span_suggestion_hidden(
225 "try using `#[default]`",
227 Applicability::MaybeIncorrect,
236 struct DetectNonVariantDefaultAttr<'a, 'b> {
240 impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
241 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
242 if attr.has_name(kw::Default) {
246 "the `#[default]` attribute may only be used on unit enum variants",
251 rustc_ast::visit::walk_attribute(self, attr);
253 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
254 self.visit_ident(v.ident);
255 self.visit_vis(&v.vis);
256 self.visit_variant_data(&v.data);
257 walk_list!(self, visit_anon_const, &v.disr_expr);
258 for attr in &v.attrs {
259 rustc_ast::visit::walk_attribute(self, attr);
264 fn has_a_default_variant(item: &Annotatable) -> bool {
265 struct HasDefaultAttrOnVariant {
269 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
270 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) {
271 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
274 // no need to subrecurse.
278 let mut visitor = HasDefaultAttrOnVariant { found: false };
279 item.visit_with(&mut visitor);