rustc_builtin_macros/deriving/
default.rs

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