rustc_builtin_macros/deriving/
default.rs1use 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 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 None => default_call(cx, *span),
85 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 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 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 None => default_call(cx, field.span),
129 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 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 && !fields.is_empty()
218 {
219 } 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 ControlFlow::Continue(())
317 }
318 }
319 }
320
321 item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
322}