1use 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::diagnostics;
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(::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[kw::Default, sym::Default]))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: SmallVec::new(),
30 supports_unions: false,
31 methods: {
let count = 0usize + 1usize;
let mut vec = ::smallvec::SmallVec::new();
if count <= vec.inline_size() {
vec.push(MethodDef {
name: kw::Default,
generics: Bounds::empty(),
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.fields {
StaticStruct(_, fields) => {
default_struct_substructure(cx, trait_span, substr, fields)
}
StaticEnum(enum_def) => {
default_enum_substructure(cx, trait_span, enum_def,
item.span())
}
_ =>
cx.dcx().span_bug(trait_span,
"method 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: Bounds::empty(),
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.fields {
StaticStruct(_, fields) => {
default_struct_substructure(cx, trait_span, substr, fields)
}
StaticEnum(enum_def) => {
default_enum_substructure(cx, trait_span, enum_def,
item.span())
}
_ =>
cx.dcx().span_bug(trait_span,
"method in `derive(Default)`"),
}
}),
}])))
}
}smallvec![MethodDef {
32 name: kw::Default,
33 generics: Bounds::empty(),
34 explicit_self: false,
35 nonself_args: SmallVec::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(|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: SmallVec::new(),
52 is_const,
53 safety: Safety::Default,
54 document: true,
55 };
56 trait_def.expand(cx, mitem, item, push)
57}
58
59fn default_call(cx: &ExtCtxt<'_>, span: Span) -> Box<ast::Expr> {
60 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 None => default_call(cx, span),
84 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 match &default_variant.data {
112 VariantData::Unit(_) => cx.expr_path(cx.path(
113 default_variant.span,
114 ::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],
115 )),
116 VariantData::Struct { fields, .. } => {
117 let default_fields = fields
120 .iter()
121 .map(|field| {
122 cx.field_imm(
123 field.span,
124 field.ident.unwrap(),
125 match field.default_value() {
126 None => default_call(cx, field.span),
128 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 ::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![
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 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| #[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}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| diagnostics::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
180 .collect();
181 let guar = cx.dcx().emit_err(diagnostics::NoDefaultVariant {
182 span: trait_span,
183 item_span,
184 suggs,
185 });
186
187 return Err(guar);
188 }
189 [first, rest @ ..] => {
190 let suggs = default_variants
191 .iter()
192 .filter_map(|variant| {
193 let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
194 let spans: Vec<Span> = default_variants
195 .iter()
196 .flat_map(|v| {
197 attr::filter_by_name(&v.attrs, kw::Default)
198 .filter_map(|attr| (attr.span != keep).then_some(attr.span))
199 })
200 .collect();
201 (!spans.is_empty()).then_some(diagnostics::MultipleDefaultsSugg {
202 spans,
203 ident: variant.ident,
204 })
205 })
206 .collect();
207 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaults {
208 span: trait_span,
209 first: first.span,
210 additional: rest.iter().map(|v| v.span).collect(),
211 suggs,
212 });
213 return Err(guar);
214 }
215 };
216
217 if cx.ecfg.features.default_field_values()
218 && let VariantData::Struct { fields, .. } = &variant.data
219 && fields.iter().all(|f| f.default_value().is_some())
220 && !fields.is_empty()
222 {
223 } else if !#[allow(non_exhaustive_omitted_patterns)] match variant.data {
VariantData::Unit(..) => true,
_ => false,
}matches!(variant.data, VariantData::Unit(..)) {
225 let post = if cx.ecfg.features.default_field_values() {
226 " or variants where every field has a default value"
227 } else {
228 ""
229 };
230 let guar =
231 cx.dcx().emit_err(diagnostics::NonUnitDefault { span: variant.ident.span, post });
232 return Err(guar);
233 }
234
235 if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
236 let guar = cx.dcx().emit_err(diagnostics::NonExhaustiveDefault {
237 span: variant.ident.span,
238 non_exhaustive: non_exhaustive_attr.span,
239 });
240
241 return Err(guar);
242 }
243
244 Ok(variant)
245}
246
247fn validate_default_attribute(
248 cx: &ExtCtxt<'_>,
249 default_variant: &rustc_ast::Variant,
250) -> Result<(), ErrorGuaranteed> {
251 let attrs: SmallVec<[_; 1]> =
252 attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
253
254 let attr = match attrs.as_slice() {
255 [attr] => attr,
256 [] => cx.dcx().bug(
257 "this method must only be called with a variant that has a `#[default]` attribute",
258 ),
259 [first, rest @ ..] => {
260 let sugg = diagnostics::MultipleDefaultAttrsSugg {
261 spans: rest.iter().map(|attr| attr.span).collect(),
262 };
263 let guar = cx.dcx().emit_err(diagnostics::MultipleDefaultAttrs {
264 span: default_variant.ident.span,
265 first: first.span,
266 first_rest: rest[0].span,
267 rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
268 only_one: rest.len() == 1,
269 sugg,
270 });
271
272 return Err(guar);
273 }
274 };
275 if !attr.is_word() {
276 let guar = cx.dcx().emit_err(diagnostics::DefaultHasArg { span: attr.span });
277
278 return Err(guar);
279 }
280 Ok(())
281}
282
283struct DetectNonVariantDefaultAttr<'a, 'b> {
284 cx: &'a ExtCtxt<'b>,
285}
286
287impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
288 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
289 if attr.has_name(kw::Default) {
290 let post = if self.cx.ecfg.features.default_field_values() {
291 " or variants where every field has a default value"
292 } else {
293 ""
294 };
295 self.cx.dcx().emit_err(diagnostics::NonUnitDefault { span: attr.span, post });
296 }
297
298 rustc_ast::visit::walk_attribute(self, attr);
299 }
300 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
301 self.visit_ident(&v.ident);
302 self.visit_vis(&v.vis);
303 self.visit_variant_data(&v.data);
304 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);
305 for attr in &v.attrs {
306 rustc_ast::visit::walk_attribute(self, attr);
307 }
308 }
309}
310
311fn has_a_default_variant(item: &Annotatable) -> bool {
312 struct HasDefaultAttrOnVariant;
313
314 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
315 type Result = ControlFlow<()>;
316 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
317 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
318 ControlFlow::Break(())
319 } else {
320 ControlFlow::Continue(())
322 }
323 }
324 }
325
326 item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
327}