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