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 needs_copy_as_bound_if_packed: false,
29 additional_bounds: Vec::new(),
30 supports_unions: false,
31 methods: vec![MethodDef {
33 generics: Bounds::empty(),
35 nonself_args: Vec::new(),
38 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
39 combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
41 StaticStruct(_, fields) => {
42 default_struct_substructure(cx, trait_span, substr, fields)
44 StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def),
45 _ => cx.span_bug(trait_span, "method in `derive(Default)`"),
49 associated_types: Vec::new(),
52 trait_def.expand(cx, mitem, item, push)
55 fn default_struct_substructure(
58 substr: &Substructure<'_>,
59 summary: &StaticFields,
61 // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
62 let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
63 let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
65 let expr = match summary {
66 Unnamed(_, false) => cx.expr_ident(trait_span, substr.type_ident),
67 Unnamed(fields, true) => {
68 let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
69 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
72 let default_fields = fields
74 .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
76 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
79 BlockOrExpr::new_expr(expr)
82 fn default_enum_substructure(
87 let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
88 && let Ok(_) = validate_default_attribute(cx, default_variant)
90 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
93 vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
96 DummyResult::raw_expr(trait_span, true)
98 BlockOrExpr::new_expr(expr)
101 fn extract_default_variant<'a>(
102 cx: &mut ExtCtxt<'_>,
103 enum_def: &'a EnumDef,
105 ) -> Result<&'a rustc_ast::Variant, ()> {
106 let default_variants: SmallVec<[_; 1]> = enum_def
109 .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
112 let variant = match default_variants.as_slice() {
113 [variant] => variant,
115 let possible_defaults = enum_def
118 .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
119 .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
121 let mut diag = cx.struct_span_err(trait_span, "no default declared");
122 diag.help("make a unit variant default by placing `#[default]` above it");
123 for variant in possible_defaults {
124 // Suggest making each unit variant default.
125 diag.tool_only_span_suggestion(
127 &format!("make `{}` default", variant.ident),
128 format!("#[default] {}", variant.ident),
129 Applicability::MaybeIncorrect,
136 [first, rest @ ..] => {
137 let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
138 diag.span_label(first.span, "first default");
139 diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
140 diag.note("only one variant can be default");
141 for variant in &default_variants {
142 // Suggest making each variant already tagged default.
143 let suggestion = default_variants
146 if v.span == variant.span {
149 Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
154 diag.tool_only_multipart_suggestion(
155 &format!("make `{}` default", variant.ident),
157 Applicability::MaybeIncorrect,
166 if !matches!(variant.data, VariantData::Unit(..)) {
169 "the `#[default]` attribute may only be used on unit enum variants",
171 .help("consider a manual implementation of `Default`")
177 if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
178 cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
179 .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
180 .help("consider a manual implementation of `Default`")
189 fn validate_default_attribute(
190 cx: &mut ExtCtxt<'_>,
191 default_variant: &rustc_ast::Variant,
192 ) -> Result<(), ()> {
193 let attrs: SmallVec<[_; 1]> =
194 cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
196 let attr = match attrs.as_slice() {
199 "this method must only be called with a variant that has a `#[default]` attribute",
201 [first, rest @ ..] => {
202 let suggestion_text =
203 if rest.len() == 1 { "try removing this" } else { "try removing these" };
205 cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
206 .note("only one `#[default]` attribute is needed")
207 .span_label(first.span, "`#[default]` used here")
208 .span_label(rest[0].span, "`#[default]` used again here")
209 .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
210 // This would otherwise display the empty replacement, hence the otherwise
211 // repetitive `.span_help` call above.
212 .tool_only_multipart_suggestion(
214 rest.iter().map(|attr| (attr.span, String::new())).collect(),
215 Applicability::MachineApplicable,
223 cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
224 .span_suggestion_hidden(
226 "try using `#[default]`",
228 Applicability::MaybeIncorrect,
237 struct DetectNonVariantDefaultAttr<'a, 'b> {
241 impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
242 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
243 if attr.has_name(kw::Default) {
247 "the `#[default]` attribute may only be used on unit enum variants",
252 rustc_ast::visit::walk_attribute(self, attr);
254 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
255 self.visit_ident(v.ident);
256 self.visit_vis(&v.vis);
257 self.visit_variant_data(&v.data);
258 walk_list!(self, visit_anon_const, &v.disr_expr);
259 for attr in &v.attrs {
260 rustc_ast::visit::walk_attribute(self, attr);
265 fn has_a_default_variant(item: &Annotatable) -> bool {
266 struct HasDefaultAttrOnVariant {
270 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
271 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) {
272 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
275 // no need to subrecurse.
279 let mut visitor = HasDefaultAttrOnVariant { found: false };
280 item.visit_with(&mut visitor);