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