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