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 inline = cx.meta_word(span, sym::inline);
24 let attrs = thin_vec![cx.attribute(inline)];
25 let trait_def = TraitDef {
27 path: Path::new(vec![kw::Default, sym::Default]),
28 skip_path_as_bound: has_a_default_variant(item),
29 additional_bounds: Vec::new(),
30 supports_unions: false,
31 methods: vec![MethodDef {
33 generics: Bounds::empty(),
35 nonself_args: Vec::new(),
38 unify_fieldless_variants: false,
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(ref fields, is_tuple) => {
68 cx.expr_ident(trait_span, substr.type_ident)
70 let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
71 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
74 Named(ref fields) => {
75 let default_fields = fields
77 .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
79 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
82 BlockOrExpr::new_expr(expr)
85 fn default_enum_substructure(
90 let expr = if let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span)
91 && let Ok(_) = validate_default_attribute(cx, default_variant)
93 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
96 vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
99 DummyResult::raw_expr(trait_span, true)
101 BlockOrExpr::new_expr(expr)
104 fn extract_default_variant<'a>(
105 cx: &mut ExtCtxt<'_>,
106 enum_def: &'a EnumDef,
108 ) -> Result<&'a rustc_ast::Variant, ()> {
109 let default_variants: SmallVec<[_; 1]> = enum_def
112 .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
115 let variant = match default_variants.as_slice() {
116 [variant] => variant,
118 let possible_defaults = enum_def
121 .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
122 .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
124 let mut diag = cx.struct_span_err(trait_span, "no default declared");
125 diag.help("make a unit variant default by placing `#[default]` above it");
126 for variant in possible_defaults {
127 // Suggest making each unit variant default.
128 diag.tool_only_span_suggestion(
130 &format!("make `{}` default", variant.ident),
131 format!("#[default] {}", variant.ident),
132 Applicability::MaybeIncorrect,
139 [first, rest @ ..] => {
140 let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
141 diag.span_label(first.span, "first default");
142 diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
143 diag.note("only one variant can be default");
144 for variant in &default_variants {
145 // Suggest making each variant already tagged default.
146 let suggestion = default_variants
149 if v.ident == variant.ident {
152 Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
157 diag.tool_only_multipart_suggestion(
158 &format!("make `{}` default", variant.ident),
160 Applicability::MaybeIncorrect,
169 if !matches!(variant.data, VariantData::Unit(..)) {
172 "the `#[default]` attribute may only be used on unit enum variants",
174 .help("consider a manual implementation of `Default`")
180 if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
181 cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
182 .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
183 .help("consider a manual implementation of `Default`")
192 fn validate_default_attribute(
193 cx: &mut ExtCtxt<'_>,
194 default_variant: &rustc_ast::Variant,
195 ) -> Result<(), ()> {
196 let attrs: SmallVec<[_; 1]> =
197 cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
199 let attr = match attrs.as_slice() {
202 "this method must only be called with a variant that has a `#[default]` attribute",
204 [first, rest @ ..] => {
205 let suggestion_text =
206 if rest.len() == 1 { "try removing this" } else { "try removing these" };
208 cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
209 .note("only one `#[default]` attribute is needed")
210 .span_label(first.span, "`#[default]` used here")
211 .span_label(rest[0].span, "`#[default]` used again here")
212 .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
213 // This would otherwise display the empty replacement, hence the otherwise
214 // repetitive `.span_help` call above.
215 .tool_only_multipart_suggestion(
217 rest.iter().map(|attr| (attr.span, String::new())).collect(),
218 Applicability::MachineApplicable,
226 cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
227 .span_suggestion_hidden(
229 "try using `#[default]`",
231 Applicability::MaybeIncorrect,
240 struct DetectNonVariantDefaultAttr<'a, 'b> {
244 impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
245 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
246 if attr.has_name(kw::Default) {
250 "the `#[default]` attribute may only be used on unit enum variants",
255 rustc_ast::visit::walk_attribute(self, attr);
257 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
258 self.visit_ident(v.ident);
259 self.visit_vis(&v.vis);
260 self.visit_variant_data(&v.data);
261 walk_list!(self, visit_anon_const, &v.disr_expr);
262 for attr in &v.attrs {
263 rustc_ast::visit::walk_attribute(self, attr);
268 fn has_a_default_variant(item: &Annotatable) -> bool {
269 struct HasDefaultAttrOnVariant {
273 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
274 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) {
275 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
278 // no need to subrecurse.
282 let mut visitor = HasDefaultAttrOnVariant { found: false };
283 item.visit_with(&mut visitor);