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(ref fields, is_tuple) => {
67 cx.expr_ident(trait_span, substr.type_ident)
69 let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
70 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
73 Named(ref fields) => {
74 let default_fields = fields
76 .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
78 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
81 BlockOrExpr::new_expr(expr)
84 fn default_enum_substructure(
89 let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
90 && let Ok(_) = validate_default_attribute(cx, default_variant)
92 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
95 vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
98 DummyResult::raw_expr(trait_span, true)
100 BlockOrExpr::new_expr(expr)
103 fn extract_default_variant<'a>(
104 cx: &mut ExtCtxt<'_>,
105 enum_def: &'a EnumDef,
107 ) -> Result<&'a rustc_ast::Variant, ()> {
108 let default_variants: SmallVec<[_; 1]> = enum_def
111 .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
114 let variant = match default_variants.as_slice() {
115 [variant] => variant,
117 let possible_defaults = enum_def
120 .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
121 .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
123 let mut diag = cx.struct_span_err(trait_span, "no default declared");
124 diag.help("make a unit variant default by placing `#[default]` above it");
125 for variant in possible_defaults {
126 // Suggest making each unit variant default.
127 diag.tool_only_span_suggestion(
129 &format!("make `{}` default", variant.ident),
130 format!("#[default] {}", variant.ident),
131 Applicability::MaybeIncorrect,
138 [first, rest @ ..] => {
139 let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
140 diag.span_label(first.span, "first default");
141 diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
142 diag.note("only one variant can be default");
143 for variant in &default_variants {
144 // Suggest making each variant already tagged default.
145 let suggestion = default_variants
148 if v.span == variant.span {
151 Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
156 diag.tool_only_multipart_suggestion(
157 &format!("make `{}` default", variant.ident),
159 Applicability::MaybeIncorrect,
168 if !matches!(variant.data, VariantData::Unit(..)) {
171 "the `#[default]` attribute may only be used on unit enum variants",
173 .help("consider a manual implementation of `Default`")
179 if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
180 cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
181 .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
182 .help("consider a manual implementation of `Default`")
191 fn validate_default_attribute(
192 cx: &mut ExtCtxt<'_>,
193 default_variant: &rustc_ast::Variant,
194 ) -> Result<(), ()> {
195 let attrs: SmallVec<[_; 1]> =
196 cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
198 let attr = match attrs.as_slice() {
201 "this method must only be called with a variant that has a `#[default]` attribute",
203 [first, rest @ ..] => {
204 let suggestion_text =
205 if rest.len() == 1 { "try removing this" } else { "try removing these" };
207 cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
208 .note("only one `#[default]` attribute is needed")
209 .span_label(first.span, "`#[default]` used here")
210 .span_label(rest[0].span, "`#[default]` used again here")
211 .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
212 // This would otherwise display the empty replacement, hence the otherwise
213 // repetitive `.span_help` call above.
214 .tool_only_multipart_suggestion(
216 rest.iter().map(|attr| (attr.span, String::new())).collect(),
217 Applicability::MachineApplicable,
225 cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
226 .span_suggestion_hidden(
228 "try using `#[default]`",
230 Applicability::MaybeIncorrect,
239 struct DetectNonVariantDefaultAttr<'a, 'b> {
243 impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
244 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
245 if attr.has_name(kw::Default) {
249 "the `#[default]` attribute may only be used on unit enum variants",
254 rustc_ast::visit::walk_attribute(self, attr);
256 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
257 self.visit_ident(v.ident);
258 self.visit_vis(&v.vis);
259 self.visit_variant_data(&v.data);
260 walk_list!(self, visit_anon_const, &v.disr_expr);
261 for attr in &v.attrs {
262 rustc_ast::visit::walk_attribute(self, attr);
267 fn has_a_default_variant(item: &Annotatable) -> bool {
268 struct HasDefaultAttrOnVariant {
272 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
273 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) {
274 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
277 // no need to subrecurse.
281 let mut visitor = HasDefaultAttrOnVariant { found: false };
282 item.visit_with(&mut visitor);