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