rustc_builtin_macros/deriving/
default.rs

1use core::ops::ControlFlow;
2
3use rustc_ast::visit::visit_opt;
4use rustc_ast::{self as ast, EnumDef, Safety, VariantData, attr};
5use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
6use rustc_span::{ErrorGuaranteed, Ident, Span, kw, sym};
7use smallvec::SmallVec;
8use thin_vec::{ThinVec, thin_vec};
9
10use crate::deriving::generic::ty::*;
11use crate::deriving::generic::*;
12use crate::errors;
13
14pub(crate) fn expand_deriving_default(
15    cx: &ExtCtxt<'_>,
16    span: Span,
17    mitem: &ast::MetaItem,
18    item: &Annotatable,
19    push: &mut dyn FnMut(Annotatable),
20    is_const: bool,
21) {
22    item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
23
24    let trait_def = TraitDef {
25        span,
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 {
32            name: kw::Default,
33            generics: Bounds::empty(),
34            explicit_self: false,
35            nonself_args: Vec::new(),
36            ret_ty: Self_,
37            attributes: thin_vec![cx.attr_word(sym::inline, span)],
38            fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
39            combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
40                match substr.fields {
41                    StaticStruct(_, fields) => {
42                        default_struct_substructure(cx, trait_span, substr, fields)
43                    }
44                    StaticEnum(enum_def) => {
45                        default_enum_substructure(cx, trait_span, enum_def, item.span())
46                    }
47                    _ => cx.dcx().span_bug(trait_span, "method in `derive(Default)`"),
48                }
49            })),
50        }],
51        associated_types: Vec::new(),
52        is_const,
53        is_staged_api_crate: cx.ecfg.features.staged_api(),
54        safety: Safety::Default,
55        document: true,
56    };
57    trait_def.expand(cx, mitem, item, push)
58}
59
60fn default_call(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
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    cx.expr_call_global(span, default_ident, ThinVec::new())
64}
65
66fn default_struct_substructure(
67    cx: &ExtCtxt<'_>,
68    trait_span: Span,
69    substr: &Substructure<'_>,
70    summary: &StaticFields,
71) -> BlockOrExpr {
72    let expr = match summary {
73        Unnamed(_, IsTuple::No) => cx.expr_ident(trait_span, substr.type_ident),
74        Unnamed(fields, IsTuple::Yes) => {
75            let exprs = fields.iter().map(|sp| default_call(cx, *sp)).collect();
76            cx.expr_call_ident(trait_span, substr.type_ident, exprs)
77        }
78        Named(fields) => {
79            let default_fields = fields
80                .iter()
81                .map(|(ident, span, default_val)| {
82                    let value = match default_val {
83                        // We use `Default::default()`.
84                        None => default_call(cx, *span),
85                        // We use the field default const expression.
86                        Some(val) => {
87                            cx.expr(val.value.span, ast::ExprKind::ConstBlock(val.clone()))
88                        }
89                    };
90                    cx.field_imm(*span, *ident, value)
91                })
92                .collect();
93            cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
94        }
95    };
96    BlockOrExpr::new_expr(expr)
97}
98
99fn default_enum_substructure(
100    cx: &ExtCtxt<'_>,
101    trait_span: Span,
102    enum_def: &EnumDef,
103    item_span: Span,
104) -> BlockOrExpr {
105    let expr = match try {
106        let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
107        validate_default_attribute(cx, default_variant)?;
108        default_variant
109    } {
110        Ok(default_variant) => {
111            // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
112            match &default_variant.data {
113                VariantData::Unit(_) => cx.expr_path(cx.path(
114                    default_variant.span,
115                    vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
116                )),
117                VariantData::Struct { fields, .. } => {
118                    // This only happens if `#![feature(default_field_values)]`. We have validated
119                    // all fields have default values in the definition.
120                    let default_fields = fields
121                        .iter()
122                        .map(|field| {
123                            cx.field_imm(
124                                field.span,
125                                field.ident.unwrap(),
126                                match &field.default {
127                                    // We use `Default::default()`.
128                                    None => default_call(cx, field.span),
129                                    // We use the field default const expression.
130                                    Some(val) => cx.expr(
131                                        val.value.span,
132                                        ast::ExprKind::ConstBlock(val.clone()),
133                                    ),
134                                },
135                            )
136                        })
137                        .collect();
138                    let path = cx.path(
139                        default_variant.span,
140                        vec![
141                            Ident::new(kw::SelfUpper, default_variant.span),
142                            default_variant.ident,
143                        ],
144                    );
145                    cx.expr_struct(default_variant.span, path, default_fields)
146                }
147                // Logic error in `extract_default_variant`.
148                VariantData::Tuple(..) => {
149                    cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
150                }
151            }
152        }
153        Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
154    };
155    BlockOrExpr::new_expr(expr)
156}
157
158fn extract_default_variant<'a>(
159    cx: &ExtCtxt<'_>,
160    enum_def: &'a EnumDef,
161    trait_span: Span,
162    item_span: Span,
163) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
164    let default_variants: SmallVec<[_; 1]> = enum_def
165        .variants
166        .iter()
167        .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
168        .collect();
169
170    let variant = match default_variants.as_slice() {
171        [variant] => variant,
172        [] => {
173            let possible_defaults = enum_def
174                .variants
175                .iter()
176                .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
177                .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
178
179            let suggs = possible_defaults
180                .map(|v| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
181                .collect();
182            let guar =
183                cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs });
184
185            return Err(guar);
186        }
187        [first, rest @ ..] => {
188            let suggs = default_variants
189                .iter()
190                .filter_map(|variant| {
191                    let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
192                    let spans: Vec<Span> = default_variants
193                        .iter()
194                        .flat_map(|v| {
195                            attr::filter_by_name(&v.attrs, kw::Default)
196                                .filter_map(|attr| (attr.span != keep).then_some(attr.span))
197                        })
198                        .collect();
199                    (!spans.is_empty())
200                        .then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident })
201                })
202                .collect();
203            let guar = cx.dcx().emit_err(errors::MultipleDefaults {
204                span: trait_span,
205                first: first.span,
206                additional: rest.iter().map(|v| v.span).collect(),
207                suggs,
208            });
209            return Err(guar);
210        }
211    };
212
213    if cx.ecfg.features.default_field_values()
214        && let VariantData::Struct { fields, .. } = &variant.data
215        && fields.iter().all(|f| f.default.is_some())
216        // Disallow `#[default] Variant {}`
217        && !fields.is_empty()
218    {
219        // Allowed
220    } else if !matches!(variant.data, VariantData::Unit(..)) {
221        let post = if cx.ecfg.features.default_field_values() {
222            " or variants where every field has a default value"
223        } else {
224            ""
225        };
226        let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post });
227        return Err(guar);
228    }
229
230    if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
231        let guar = cx.dcx().emit_err(errors::NonExhaustiveDefault {
232            span: variant.ident.span,
233            non_exhaustive: non_exhaustive_attr.span,
234        });
235
236        return Err(guar);
237    }
238
239    Ok(variant)
240}
241
242fn validate_default_attribute(
243    cx: &ExtCtxt<'_>,
244    default_variant: &rustc_ast::Variant,
245) -> Result<(), ErrorGuaranteed> {
246    let attrs: SmallVec<[_; 1]> =
247        attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
248
249    let attr = match attrs.as_slice() {
250        [attr] => attr,
251        [] => cx.dcx().bug(
252            "this method must only be called with a variant that has a `#[default]` attribute",
253        ),
254        [first, rest @ ..] => {
255            let sugg = errors::MultipleDefaultAttrsSugg {
256                spans: rest.iter().map(|attr| attr.span).collect(),
257            };
258            let guar = cx.dcx().emit_err(errors::MultipleDefaultAttrs {
259                span: default_variant.ident.span,
260                first: first.span,
261                first_rest: rest[0].span,
262                rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
263                only_one: rest.len() == 1,
264                sugg,
265            });
266
267            return Err(guar);
268        }
269    };
270    if !attr.is_word() {
271        let guar = cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span });
272
273        return Err(guar);
274    }
275    Ok(())
276}
277
278struct DetectNonVariantDefaultAttr<'a, 'b> {
279    cx: &'a ExtCtxt<'b>,
280}
281
282impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
283    fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
284        if attr.has_name(kw::Default) {
285            let post = if self.cx.ecfg.features.default_field_values() {
286                " or variants where every field has a default value"
287            } else {
288                ""
289            };
290            self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post });
291        }
292
293        rustc_ast::visit::walk_attribute(self, attr);
294    }
295    fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
296        self.visit_ident(&v.ident);
297        self.visit_vis(&v.vis);
298        self.visit_variant_data(&v.data);
299        visit_opt!(self, visit_anon_const, &v.disr_expr);
300        for attr in &v.attrs {
301            rustc_ast::visit::walk_attribute(self, attr);
302        }
303    }
304}
305
306fn has_a_default_variant(item: &Annotatable) -> bool {
307    struct HasDefaultAttrOnVariant;
308
309    impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
310        type Result = ControlFlow<()>;
311        fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
312            if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
313                ControlFlow::Break(())
314            } else {
315                // no need to walk the variant, we are only looking for top level variants
316                ControlFlow::Continue(())
317            }
318        }
319    }
320
321    item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
322}