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::*;
11use crate::deriving::new_path;
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], ::alloc::vec::Vec::new()vec![]),
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(),
has_other_selflike_arg: false,
ret_ty: cx.ty_self(span),
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.fields {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, variant_data,
substr.type_ident)
}
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(),
has_other_selflike_arg: false,
ret_ty: cx.ty_self(span),
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.fields {
StaticStruct(variant_data) => {
default_struct_substructure(cx, trait_span, variant_data,
substr.type_ident)
}
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 has_other_selflike_arg: false,
36 ret_ty: cx.ty_self(span),
37 attributes: thin_vec![cx.attr_word(sym::inline, span)],
38 fieldless_variants_strategy: FieldlessVariantsStrategy::Default,
39 combine_substructure: combine_substructure(|cx, trait_span, substr| {
40 match substr.fields {
41 StaticStruct(variant_data) => {
42 default_struct_substructure(cx, trait_span, variant_data, substr.type_ident)
43 }
44 StaticEnum(enum_def) => {
45 default_enum_substructure(cx, trait_span, enum_def, item.span)
46 }
47 _ => cx
48 .dcx()
49 .span_bug(trait_span, "unexpected substructure in `derive(Default)`"),
50 }
51 }),
52 }],
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 type_ident: Ident,
71) -> BlockOrExpr {
72 if variant_data.fields().is_empty() {
76 return BlockOrExpr::new_expr(cx.expr_struct_ident(trait_span, type_ident, ThinVec::new()));
77 }
78
79 let expr = match variant_data {
80 VariantData::Unit(_) => cx.expr_ident(trait_span, type_ident),
82 VariantData::Tuple(fields, _) => {
83 let exprs = fields
84 .iter()
85 .map(|field| default_call(cx, field.span.with_ctxt(trait_span.ctxt())))
86 .collect();
87 cx.expr_call_ident(trait_span, type_ident, exprs)
88 }
89 VariantData::Struct { fields, .. } => {
90 let default_fields = fields
91 .iter()
92 .map(|field| {
93 let span = field.span.with_ctxt(trait_span.ctxt());
94 let value = if let Some(default_val) = field.default_value() {
95 cx.expr(
97 default_val.value.span,
98 ast::ExprKind::ConstBlock(default_val.clone()),
99 )
100 } else {
101 default_call(cx, span)
103 };
104 cx.field_imm(span, field.ident.unwrap(), value)
105 })
106 .collect();
107 cx.expr_struct_ident(trait_span, type_ident, default_fields)
108 }
109 };
110 BlockOrExpr::new_expr(expr)
111}
112
113fn default_enum_substructure(
114 cx: &ExtCtxt<'_>,
115 trait_span: Span,
116 enum_def: &EnumDef,
117 item_span: Span,
118) -> BlockOrExpr {
119 let expr = match try {
120 let default_variant = extract_default_variant(cx, enum_def, trait_span, item_span)?;
121 validate_default_attribute(cx, default_variant)?;
122 default_variant
123 } {
124 Ok(default_variant) => {
125 match &default_variant.data {
127 VariantData::Unit(_) => cx.expr_path(cx.path(
128 default_variant.span,
129 ::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],
130 )),
131 VariantData::Struct { fields, .. } => {
132 let default_fields = fields
135 .iter()
136 .map(|field| {
137 cx.field_imm(
138 field.span,
139 field.ident.unwrap(),
140 match field.default_value() {
141 None => default_call(cx, field.span),
143 Some(val) => cx.expr(
145 val.value.span,
146 ast::ExprKind::ConstBlock(val.clone()),
147 ),
148 },
149 )
150 })
151 .collect();
152 let path = cx.path(
153 default_variant.span,
154 ::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![
155 Ident::new(kw::SelfUpper, default_variant.span),
156 default_variant.ident,
157 ],
158 );
159 cx.expr_struct(default_variant.span, path, default_fields)
160 }
161 VariantData::Tuple(..) => {
163 cx.dcx().bug("encountered tuple variant annotated with `#[default]`")
164 }
165 }
166 }
167 Err(guar) => DummyResult::raw_expr(trait_span, Some(guar)),
168 };
169 BlockOrExpr::new_expr(expr)
170}
171
172fn extract_default_variant<'a>(
173 cx: &ExtCtxt<'_>,
174 enum_def: &'a EnumDef,
175 trait_span: Span,
176 item_span: Span,
177) -> Result<&'a rustc_ast::Variant, ErrorGuaranteed> {
178 let default_variants: SmallVec<[_; 1]> = enum_def
179 .variants
180 .iter()
181 .filter(|variant| attr::contains_name(&variant.attrs, kw::Default))
182 .collect();
183
184 let variant = match default_variants.as_slice() {
185 [variant] => variant,
186 [] => {
187 let possible_defaults = enum_def
188 .variants
189 .iter()
190 .filter(|variant| #[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)))
191 .filter(|variant| !attr::contains_name(&variant.attrs, sym::non_exhaustive));
192
193 let suggs = possible_defaults
194 .map(|v| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
195 .collect();
196 let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant {
197 span: trait_span,
198 item_span,
199 suggs,
200 });
201
202 return Err(guar);
203 }
204 [first, rest @ ..] => {
205 let suggs = default_variants
206 .iter()
207 .filter_map(|variant| {
208 let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
209 let spans: Vec<Span> = default_variants
210 .iter()
211 .flat_map(|v| {
212 attr::filter_by_name(&v.attrs, kw::Default)
213 .filter_map(|attr| (attr.span != keep).then_some(attr.span))
214 })
215 .collect();
216 (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg {
217 spans,
218 ident: variant.ident,
219 })
220 })
221 .collect();
222 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults {
223 span: trait_span,
224 first: first.span,
225 additional: rest.iter().map(|v| v.span).collect(),
226 suggs,
227 });
228 return Err(guar);
229 }
230 };
231
232 if cx.ecfg.features.default_field_values()
233 && let VariantData::Struct { fields, .. } = &variant.data
234 && fields.iter().all(|f| f.default_value().is_some())
235 && !fields.is_empty()
237 {
238 } else if !#[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)) {
240 let post = if cx.ecfg.features.default_field_values() {
241 " or variants where every field has a default value"
242 } else {
243 ""
244 };
245 let guar =
246 cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post });
247 return Err(guar);
248 }
249
250 if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
251 let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault {
252 span: variant.ident.span,
253 non_exhaustive: non_exhaustive_attr.span,
254 });
255
256 return Err(guar);
257 }
258
259 Ok(variant)
260}
261
262fn validate_default_attribute(
263 cx: &ExtCtxt<'_>,
264 default_variant: &rustc_ast::Variant,
265) -> Result<(), ErrorGuaranteed> {
266 let attrs: SmallVec<[_; 1]> =
267 attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
268
269 let attr = match attrs.as_slice() {
270 [attr] => attr,
271 [] => cx.dcx().bug(
272 "this method must only be called with a variant that has a `#[default]` attribute",
273 ),
274 [first, rest @ ..] => {
275 let sugg = diagnostics::MultipleDefaultAttrsSugg {
276 spans: rest.iter().map(|attr| attr.span).collect(),
277 };
278 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs {
279 span: default_variant.ident.span,
280 first: first.span,
281 first_rest: rest[0].span,
282 rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
283 only_one: rest.len() == 1,
284 sugg,
285 });
286
287 return Err(guar);
288 }
289 };
290 if !attr.is_word() {
291 let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span });
292
293 return Err(guar);
294 }
295 Ok(())
296}
297
298struct DetectNonVariantDefaultAttr<'a, 'b> {
299 cx: &'a ExtCtxt<'b>,
300}
301
302impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
303 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
304 if attr.has_name(kw::Default) {
305 let post = if self.cx.ecfg.features.default_field_values() {
306 " or variants where every field has a default value"
307 } else {
308 ""
309 };
310 self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post });
311 }
312
313 rustc_ast::visit::walk_attribute(self, attr);
314 }
315 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
316 self.visit_ident(&v.ident);
317 self.visit_vis(&v.vis);
318 self.visit_variant_data(&v.data);
319 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);
320 for attr in &v.attrs {
321 rustc_ast::visit::walk_attribute(self, attr);
322 }
323 }
324}
325
326fn has_a_default_variant(item: &ast::Item) -> bool {
327 struct HasDefaultAttrOnVariant;
328
329 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
330 type Result = ControlFlow<()>;
331 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
332 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
333 ControlFlow::Break(())
334 } else {
335 ControlFlow::Continue(())
337 }
338 }
339 }
340
341 HasDefaultAttrOnVariant.visit_item(item).is_break()
342}