rustc_builtin_macros/deriving/
default.rs1use 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 is_staged_api_crate: cx.ecfg.features.staged_api(),
55 };
56 trait_def.expand(cx, mitem, item, push)
57}
58
59fn default_call(cx: &ExtCtxt<'_>, span: Span) -> ast::ptr::P<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 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 {
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 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| 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| errors::NoDefaultVariantSugg { span: v.span.shrink_to_lo() })
180 .collect();
181 let guar =
182 cx.dcx().emit_err(errors::NoDefaultVariant { span: trait_span, item_span, suggs });
183
184 return Err(guar);
185 }
186 [first, rest @ ..] => {
187 let suggs = default_variants
188 .iter()
189 .filter_map(|variant| {
190 let keep = attr::find_by_name(&variant.attrs, kw::Default)?.span;
191 let spans: Vec<Span> = default_variants
192 .iter()
193 .flat_map(|v| {
194 attr::filter_by_name(&v.attrs, kw::Default)
195 .filter_map(|attr| (attr.span != keep).then_some(attr.span))
196 })
197 .collect();
198 (!spans.is_empty())
199 .then_some(errors::MultipleDefaultsSugg { spans, ident: variant.ident })
200 })
201 .collect();
202 let guar = cx.dcx().emit_err(errors::MultipleDefaults {
203 span: trait_span,
204 first: first.span,
205 additional: rest.iter().map(|v| v.span).collect(),
206 suggs,
207 });
208 return Err(guar);
209 }
210 };
211
212 if cx.ecfg.features.default_field_values()
213 && let VariantData::Struct { fields, .. } = &variant.data
214 && fields.iter().all(|f| f.default.is_some())
215 && !fields.is_empty()
217 {
218 } else if !matches!(variant.data, VariantData::Unit(..)) {
220 let post = if cx.ecfg.features.default_field_values() {
221 " or variants where every field has a default value"
222 } else {
223 ""
224 };
225 let guar = cx.dcx().emit_err(errors::NonUnitDefault { span: variant.ident.span, post });
226 return Err(guar);
227 }
228
229 if let Some(non_exhaustive_attr) = attr::find_by_name(&variant.attrs, sym::non_exhaustive) {
230 let guar = cx.dcx().emit_err(errors::NonExhaustiveDefault {
231 span: variant.ident.span,
232 non_exhaustive: non_exhaustive_attr.span,
233 });
234
235 return Err(guar);
236 }
237
238 Ok(variant)
239}
240
241fn validate_default_attribute(
242 cx: &ExtCtxt<'_>,
243 default_variant: &rustc_ast::Variant,
244) -> Result<(), ErrorGuaranteed> {
245 let attrs: SmallVec<[_; 1]> =
246 attr::filter_by_name(&default_variant.attrs, kw::Default).collect();
247
248 let attr = match attrs.as_slice() {
249 [attr] => attr,
250 [] => cx.dcx().bug(
251 "this method must only be called with a variant that has a `#[default]` attribute",
252 ),
253 [first, rest @ ..] => {
254 let sugg = errors::MultipleDefaultAttrsSugg {
255 spans: rest.iter().map(|attr| attr.span).collect(),
256 };
257 let guar = cx.dcx().emit_err(errors::MultipleDefaultAttrs {
258 span: default_variant.ident.span,
259 first: first.span,
260 first_rest: rest[0].span,
261 rest: rest.iter().map(|attr| attr.span).collect::<Vec<_>>().into(),
262 only_one: rest.len() == 1,
263 sugg,
264 });
265
266 return Err(guar);
267 }
268 };
269 if !attr.is_word() {
270 let guar = cx.dcx().emit_err(errors::DefaultHasArg { span: attr.span });
271
272 return Err(guar);
273 }
274 Ok(())
275}
276
277struct DetectNonVariantDefaultAttr<'a, 'b> {
278 cx: &'a ExtCtxt<'b>,
279}
280
281impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
282 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
283 if attr.has_name(kw::Default) {
284 let post = if self.cx.ecfg.features.default_field_values() {
285 " or variants where every field has a default value"
286 } else {
287 ""
288 };
289 self.cx.dcx().emit_err(errors::NonUnitDefault { span: attr.span, post });
290 }
291
292 rustc_ast::visit::walk_attribute(self, attr);
293 }
294 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
295 self.visit_ident(&v.ident);
296 self.visit_vis(&v.vis);
297 self.visit_variant_data(&v.data);
298 visit_opt!(self, visit_anon_const, &v.disr_expr);
299 for attr in &v.attrs {
300 rustc_ast::visit::walk_attribute(self, attr);
301 }
302 }
303}
304
305fn has_a_default_variant(item: &Annotatable) -> bool {
306 struct HasDefaultAttrOnVariant;
307
308 impl<'ast> rustc_ast::visit::Visitor<'ast> for HasDefaultAttrOnVariant {
309 type Result = ControlFlow<()>;
310 fn visit_variant(&mut self, v: &'ast rustc_ast::Variant) -> ControlFlow<()> {
311 if v.attrs.iter().any(|attr| attr.has_name(kw::Default)) {
312 ControlFlow::Break(())
313 } else {
314 ControlFlow::Continue(())
316 }
317 }
318 }
319
320 item.visit_with(&mut HasDefaultAttrOnVariant).is_break()
321}