rustc_builtin_macros/deriving/
default.rs1use core::ops::ControlFlow;
2
3use rustc_ast::visit::{Visitor, visit_opt};
4use rustc_ast::{self as ast, EnumDef, Safety, VariantData, attr};
5use rustc_expand::base::{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::diagnostics;
13
14pub(crate) fn expand_deriving_default(
15 cx: &ExtCtxt<'_>,
16 span: Span,
17 item: &ast::Item,
18 push: &mut dyn FnMut(Box<ast::Item>),
19 is_const: bool,
20) {
21 DetectNonVariantDefaultAttr { cx }.visit_item(item);
22
23 let trait_def = TraitDef {
24 span,
25 path: new_path(cx, span, &[kw::Default, sym::Default], &[]),
26 skip_path_as_bound: has_a_default_variant(item),
27 needs_copy_as_bound_if_packed: false,
28 additional_bounds: SmallVec::new(),
29 supports_unions: false,
30 methods: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(MethodDef {
name: kw::Default,
generics: cx.empty_generics(span),
explicit_self: false,
nonself_args: SmallVec::new(),
ret_ty: Self_,
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::inline, span));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(|cx, trait_span,
substr|
{
match substr {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, variant_data)
}
StaticEnum(enum_def) => {
default_enum_substructure(cx, trait_span, enum_def,
item.span)
}
_ =>
cx.dcx().span_bug(trait_span,
"unexpected substructure in `derive(Default)`"),
}
}),
});
vec
} else {
::smallvec::SmallVec::from_vec(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[MethodDef {
name: kw::Default,
generics: cx.empty_generics(span),
explicit_self: false,
nonself_args: SmallVec::new(),
ret_ty: Self_,
attributes: {
let len = [()].len();
let mut vec = ::thin_vec::ThinVec::with_capacity(len);
vec.push(cx.attr_word(sym::inline, span));
vec
},
fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
combine_substructure: combine_substructure(|cx, trait_span,
substr|
{
match substr {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, variant_data)
}
StaticEnum(enum_def) => {
default_enum_substructure(cx, trait_span, enum_def,
item.span)
}
_ =>
cx.dcx().span_bug(trait_span,
"unexpected substructure in `derive(Default)`"),
}
}),
}])))
}
}smallvec![MethodDef {
31 name: kw::Default,
32 generics: cx.empty_generics(span),
33 explicit_self: false,
34 nonself_args: SmallVec::new(),
35 ret_ty: Self_,
36 attributes: thin_vec![cx.attr_word(sym::inline, span)],
37 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
38 combine_substructure: combine_substructure(|cx, trait_span, substr| {
39 match substr {
40 StaticStruct(variant_data) => {
41 default_struct_substructure(cx, trait_span, variant_data)
42 }
43 StaticEnum(enum_def) => {
44 default_enum_substructure(cx, trait_span, enum_def, item.span)
45 }
46 _ => cx
47 .dcx()
48 .span_bug(trait_span, "unexpected substructure in `derive(Default)`"),
49 }
50 }),
51 }],
52 associated_types: SmallVec::new(),
53 is_const,
54 safety: Safety::Default,
55 document: true,
56 };
57 trait_def.expand(cx, 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 variant_data: &VariantData,
70) -> BlockOrExpr {
71 let expr = match variant_data {
72 VariantData::Unit(_) => cx.expr_ident(trait_span, Ident::new(kw::SelfUpper, trait_span)),
73 VariantData::Tuple(fields, _) => {
74 let exprs = fields
75 .iter()
76 .map(|field| default_call(cx, field.span.with_ctxt(trait_span.ctxt())))
77 .collect();
78 cx.expr_call_ident(trait_span, Ident::new(kw::SelfUpper, trait_span), exprs)
79 }
80 VariantData::Struct { fields, .. } => {
81 let default_fields = fields
82 .iter()
83 .map(|field| {
84 let span = field.span.with_ctxt(trait_span.ctxt());
85 let value = if let Some(default_val) = field.default_value() {
86 cx.expr(
88 default_val.value.span,
89 ast::ExprKind::ConstBlock(default_val.clone()),
90 )
91 } else {
92 default_call(cx, span)
94 };
95 cx.field_imm(span, field.ident.unwrap(), value)
96 })
97 .collect();
98 cx.expr_struct_ident(trait_span, Ident::new(kw::SelfUpper, trait_span), default_fields)
99 }
100 };
101 BlockOrExpr::new_expr(expr)
102}
103
104fn default_enum_substructure(
105 cx: &ExtCtxt<'_>,
106 trait_span: Span,
107 enum_def: &EnumDef,
108 item_span: Span,
109) -> BlockOrExpr {
110 let expr = match try {
111 let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
112 validate_default_attribute(cx, default_variant)?;
113 default_variant
114 } {
115 Ok(default_variant) => {
116 match &default_variant.data {
118 VariantData::Unit(_) => cx.expr_path(cx.path(
119 default_variant.span,
120 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident]))vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
121 )),
122 VariantData::Struct { fields, .. } => {
123 let default_fields = fields
126 .iter()
127 .map(|field| {
128 cx.field_imm(
129 field.span,
130 field.ident.unwrap(),
131 match field.default_value() {
132 None => default_call(cx, field.span),
134 Some(val) => cx.expr(
136 val.value.span,
137 ast::ExprKind::ConstBlock(val.clone()),
138 ),
139 },
140 )
141 })
142 .collect();
143 let path = cx.path(
144 default_variant.span,
145 ::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[Ident::new(kw::SelfUpper, default_variant.span),
default_variant.ident]))vec![
146 Ident::new(kw::SelfUpper, default_variant.span),
147 default_variant.ident,
148 ],
149 );
150 cx.expr_struct(default_variant.span, path, default_fields)
151 }
152 VariantData::Tuple(..) => {
154 cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
155 }
156 }
157 }
158 Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
159 };
160 BlockOrExpr::new_expr(expr)
161}
162
163fn extract_default_variant<'a>(
164 cx: &ExtCtxt<'_>,
165 enum_def: &'a EnumDef,
166 trait_span: Span,
167 item_span: Span,
168) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
169 let default_variants: SmallVec<[_; 1]> = enum_def
170 .variants
171 .iter()
172 .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
173 .collect();
174
175 let variant = match default_variants.as_slice() {
176 [variant] => variant,
177 [] => {
178 let possible_defaults = enum_def
179 .variants
180 .iter()
181 .filter(|variant| #[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)))
182 .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
183
184 let suggs = possible_defaults
185 .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
186 .collect();
187 let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant {
188 span: trait_span,
189 item_span,
190 suggs,
191 });
192
193 return Err(guar);
194 }
195 [first, rest @ ..] => {
196 let suggs = default_variants
197 .iter()
198 .filter_map(|variant| {
199 let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
200 let spans: Vec<Span> = default_variants
201 .iter()
202 .flat_map(|v| {
203 attr::filter_by_name(&v.attrs, kw::Default)
204 .filter_map(|attr| (attr.span != keep).then_some(attr.span))
205 })
206 .collect();
207 (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg {
208 spans,
209 ident: variant.ident,
210 })
211 })
212 .collect();
213 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults {
214 span: trait_span,
215 first: first.span,
216 additional: rest.iter().map(|v| v.span).collect(),
217 suggs,
218 });
219 return Err(guar);
220 }
221 };
222
223 if cx.ecfg.features.default_field_values()
224 && let VariantData::Struct { fields, .. } = &variant.data
225 && fields.iter().all(|f| f.default_value().is_some())
226 && !fields.is_empty()
228 {
229 } else if !#[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)) {
231 let post = if cx.ecfg.features.default_field_values() {
232 " or variants where every field has a default value"
233 } else {
234 ""
235 };
236 let guar =
237 cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post });
238 return Err(guar);
239 }
240
241 if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
242 let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault {
243 span: variant.ident.span,
244 non_exhaustive: non_exhaustive_attr.span,
245 });
246
247 return Err(guar);
248 }
249
250 Ok(variant)
251}
252
253fn validate_default_attribute(
254 cx: &ExtCtxt<'_>,
255 default_variant: &rustc_ast::Variant,
256) -> Result<(), ErrorGuaranteed> {
257 let attrs: SmallVec<[_; 1]> =
258 attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
259
260 let attr = match attrs.as_slice() {
261 [attr] => attr,
262 [] => cx.dcx().bug(
263 "this method must only be called with a variant that has a `#[default]` attribute",
264 ),
265 [first, rest @ ..] => {
266 let sugg = diagnostics::MultipleDefaultAttrsSugg {
267 spans: rest.iter().map(|attr| attr.span).collect(),
268 };
269 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs {
270 span: default_variant.ident.span,
271 first: first.span,
272 first_rest: rest[0].span,
273 rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
274 only_one: rest.len() == 1,
275 sugg,
276 });
277
278 return Err(guar);
279 }
280 };
281 if !attr.is_word() {
282 let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span });
283
284 return Err(guar);
285 }
286 Ok(())
287}
288
289struct DetectNonVariantDefaultAttr<'a, 'b> {
290 cx: &'a ExtCtxt<'b>,
291}
292
293impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
294 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
295 if attr.has_name(kw::Default) {
296 let post = if self.cx.ecfg.features.default_field_values() {
297 " or variants where every field has a default value"
298 } else {
299 ""
300 };
301 self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post });
302 }
303
304 rustc_ast::visit::walk_attribute(self, attr);
305 }
306 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
307 self.visit_ident(&v.ident);
308 self.visit_vis(&v.vis);
309 self.visit_variant_data(&v.data);
310 if let Some(x) = &v.disr_expr {
match ::rustc_ast_ir::visit::VisitorResult::branch(self.visit_anon_const(x))
{
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};
};visit_opt!(self, visit_anon_const, &v.disr_expr);
311 for attr in &v.attrs {
312 rustc_ast::visit::walk_attribute(self, attr);
313 }
314 }
315}
316
317fn has_a_default_variant(item: &ast::Item) -> bool {
318 struct HasDefaultAttrOnVariant;
319
320 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
321 type Result = ControlFlow<()>;
322 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
323 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
324 ControlFlow::Break(())
325 } else {
326 ControlFlow::Continue(())
328 }
329 }
330 }
331
332 HasDefaultAttrOnVariant.visit_item(item).is_break()
333}