]> git.proxmox.com Git - rustc.git/blob - compiler/rustc_builtin_macros/src/deriving/default.rs
New upstream version 1.62.1+dfsg1
[rustc.git] / compiler / rustc_builtin_macros / src / deriving / default.rs
1 use crate::deriving::generic::ty::*;
2 use crate::deriving::generic::*;
3
4 use rustc_ast::ptr::P;
5 use rustc_ast::walk_list;
6 use rustc_ast::EnumDef;
7 use rustc_ast::VariantData;
8 use rustc_ast::{Expr, MetaItem};
9 use rustc_errors::Applicability;
10 use rustc_expand::base::{Annotatable, DummyResult, ExtCtxt};
11 use rustc_span::symbol::Ident;
12 use rustc_span::symbol::{kw, sym};
13 use rustc_span::Span;
14 use smallvec::SmallVec;
15
16 pub fn expand_deriving_default(
17 cx: &mut ExtCtxt<'_>,
18 span: Span,
19 mitem: &MetaItem,
20 item: &Annotatable,
21 push: &mut dyn FnMut(Annotatable),
22 ) {
23 item.visit_with(&mut DetectNonVariantDefaultAttr { cx });
24
25 let inline = cx.meta_word(span, sym::inline);
26 let attrs = vec![cx.attribute(inline)];
27 let trait_def = TraitDef {
28 span,
29 attributes: Vec::new(),
30 path: Path::new(vec![kw::Default, sym::Default]),
31 additional_bounds: Vec::new(),
32 generics: Bounds::empty(),
33 is_unsafe: false,
34 supports_unions: false,
35 methods: vec![MethodDef {
36 name: kw::Default,
37 generics: Bounds::empty(),
38 explicit_self: None,
39 args: Vec::new(),
40 ret_ty: Self_,
41 attributes: attrs,
42 is_unsafe: false,
43 unify_fieldless_variants: false,
44 combine_substructure: combine_substructure(Box::new(|cx, trait_span, substr| {
45 match substr.fields {
46 StaticStruct(_, fields) => {
47 default_struct_substructure(cx, trait_span, substr, fields)
48 }
49 StaticEnum(enum_def, _) => default_enum_substructure(cx, trait_span, enum_def),
50 _ => cx.span_bug(trait_span, "method in `derive(Default)`"),
51 }
52 })),
53 }],
54 associated_types: Vec::new(),
55 };
56 trait_def.expand(cx, mitem, item, push)
57 }
58
59 fn default_struct_substructure(
60 cx: &mut ExtCtxt<'_>,
61 trait_span: Span,
62 substr: &Substructure<'_>,
63 summary: &StaticFields,
64 ) -> P<Expr> {
65 // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
66 let default_ident = cx.std_path(&[kw::Default, sym::Default, kw::Default]);
67 let default_call = |span| cx.expr_call_global(span, default_ident.clone(), Vec::new());
68
69 match summary {
70 Unnamed(ref fields, is_tuple) => {
71 if !is_tuple {
72 cx.expr_ident(trait_span, substr.type_ident)
73 } else {
74 let exprs = fields.iter().map(|sp| default_call(*sp)).collect();
75 cx.expr_call_ident(trait_span, substr.type_ident, exprs)
76 }
77 }
78 Named(ref fields) => {
79 let default_fields = fields
80 .iter()
81 .map(|&(ident, span)| cx.field_imm(span, ident, default_call(span)))
82 .collect();
83 cx.expr_struct_ident(trait_span, substr.type_ident, default_fields)
84 }
85 }
86 }
87
88 fn default_enum_substructure(
89 cx: &mut ExtCtxt<'_>,
90 trait_span: Span,
91 enum_def: &EnumDef,
92 ) -> P<Expr> {
93 let Ok(default_variant) = extract_default_variant(cx, enum_def, trait_span) else {
94 return DummyResult::raw_expr(trait_span, true);
95 };
96
97 // At this point, we know that there is exactly one variant with a `#[default]` attribute. The
98 // attribute hasn't yet been validated.
99
100 if let Err(()) = validate_default_attribute(cx, default_variant) {
101 return DummyResult::raw_expr(trait_span, true);
102 }
103
104 // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
105
106 cx.expr_path(cx.path(
107 default_variant.span,
108 vec![Ident::new(kw::SelfUpper, default_variant.span), default_variant.ident],
109 ))
110 }
111
112 fn extract_default_variant<'a>(
113 cx: &mut ExtCtxt<'_>,
114 enum_def: &'a EnumDef,
115 trait_span: Span,
116 ) -> Result<&'a rustc_ast::Variant, ()> {
117 let default_variants: SmallVec<[_; 1]> = enum_def
118 .variants
119 .iter()
120 .filter(|variant| cx.sess.contains_name(&variant.attrs, kw::Default))
121 .collect();
122
123 let variant = match default_variants.as_slice() {
124 [variant] => variant,
125 [] => {
126 let possible_defaults = enum_def
127 .variants
128 .iter()
129 .filter(|variant| matches!(variant.data, VariantData::Unit(..)))
130 .filter(|variant| !cx.sess.contains_name(&variant.attrs, sym::non_exhaustive));
131
132 let mut diag = cx.struct_span_err(trait_span, "no default declared");
133 diag.help("make a unit variant default by placing `#[default]` above it");
134 for variant in possible_defaults {
135 // Suggest making each unit variant default.
136 diag.tool_only_span_suggestion(
137 variant.span,
138 &format!("make `{}` default", variant.ident),
139 format!("#[default] {}", variant.ident),
140 Applicability::MaybeIncorrect,
141 );
142 }
143 diag.emit();
144
145 return Err(());
146 }
147 [first, rest @ ..] => {
148 let mut diag = cx.struct_span_err(trait_span, "multiple declared defaults");
149 diag.span_label(first.span, "first default");
150 diag.span_labels(rest.iter().map(|variant| variant.span), "additional default");
151 diag.note("only one variant can be default");
152 for variant in &default_variants {
153 // Suggest making each variant already tagged default.
154 let suggestion = default_variants
155 .iter()
156 .filter_map(|v| {
157 if v.ident == variant.ident {
158 None
159 } else {
160 Some((cx.sess.find_by_name(&v.attrs, kw::Default)?.span, String::new()))
161 }
162 })
163 .collect();
164
165 diag.tool_only_multipart_suggestion(
166 &format!("make `{}` default", variant.ident),
167 suggestion,
168 Applicability::MaybeIncorrect,
169 );
170 }
171 diag.emit();
172
173 return Err(());
174 }
175 };
176
177 if !matches!(variant.data, VariantData::Unit(..)) {
178 cx.struct_span_err(
179 variant.ident.span,
180 "the `#[default]` attribute may only be used on unit enum variants",
181 )
182 .help("consider a manual implementation of `Default`")
183 .emit();
184
185 return Err(());
186 }
187
188 if let Some(non_exhaustive_attr) = cx.sess.find_by_name(&variant.attrs, sym::non_exhaustive) {
189 cx.struct_span_err(variant.ident.span, "default variant must be exhaustive")
190 .span_label(non_exhaustive_attr.span, "declared `#[non_exhaustive]` here")
191 .help("consider a manual implementation of `Default`")
192 .emit();
193
194 return Err(());
195 }
196
197 Ok(variant)
198 }
199
200 fn validate_default_attribute(
201 cx: &mut ExtCtxt<'_>,
202 default_variant: &rustc_ast::Variant,
203 ) -> Result<(), ()> {
204 let attrs: SmallVec<[_; 1]> =
205 cx.sess.filter_by_name(&default_variant.attrs, kw::Default).collect();
206
207 let attr = match attrs.as_slice() {
208 [attr] => attr,
209 [] => cx.bug(
210 "this method must only be called with a variant that has a `#[default]` attribute",
211 ),
212 [first, rest @ ..] => {
213 let suggestion_text =
214 if rest.len() == 1 { "try removing this" } else { "try removing these" };
215
216 cx.struct_span_err(default_variant.ident.span, "multiple `#[default]` attributes")
217 .note("only one `#[default]` attribute is needed")
218 .span_label(first.span, "`#[default]` used here")
219 .span_label(rest[0].span, "`#[default]` used again here")
220 .span_help(rest.iter().map(|attr| attr.span).collect::<Vec<_>>(), suggestion_text)
221 // This would otherwise display the empty replacement, hence the otherwise
222 // repetitive `.span_help` call above.
223 .tool_only_multipart_suggestion(
224 suggestion_text,
225 rest.iter().map(|attr| (attr.span, String::new())).collect(),
226 Applicability::MachineApplicable,
227 )
228 .emit();
229
230 return Err(());
231 }
232 };
233 if !attr.is_word() {
234 cx.struct_span_err(attr.span, "`#[default]` attribute does not accept a value")
235 .span_suggestion_hidden(
236 attr.span,
237 "try using `#[default]`",
238 "#[default]",
239 Applicability::MaybeIncorrect,
240 )
241 .emit();
242
243 return Err(());
244 }
245 Ok(())
246 }
247
248 struct DetectNonVariantDefaultAttr<'a, 'b> {
249 cx: &'a ExtCtxt<'b>,
250 }
251
252 impl<'a, 'b> rustc_ast::visit::Visitor<'a> for DetectNonVariantDefaultAttr<'a, 'b> {
253 fn visit_attribute(&mut self, attr: &'a rustc_ast::Attribute) {
254 if attr.has_name(kw::Default) {
255 self.cx
256 .struct_span_err(
257 attr.span,
258 "the `#[default]` attribute may only be used on unit enum variants",
259 )
260 .emit();
261 }
262
263 rustc_ast::visit::walk_attribute(self, attr);
264 }
265 fn visit_variant(&mut self, v: &'a rustc_ast::Variant) {
266 self.visit_ident(v.ident);
267 self.visit_vis(&v.vis);
268 self.visit_variant_data(&v.data);
269 walk_list!(self, visit_anon_const, &v.disr_expr);
270 for attr in &v.attrs {
271 rustc_ast::visit::walk_attribute(self, attr);
272 }
273 }
274 }