@@ -2,11 +2,15 @@ use crate::deriving::generic::ty::*;
2
2
use crate :: deriving:: generic:: * ;
3
3
4
4
use rustc_ast:: ptr:: P ;
5
+ use rustc_ast:: EnumDef ;
6
+ use rustc_ast:: VariantData ;
5
7
use rustc_ast:: { Expr , MetaItem } ;
6
- use rustc_errors:: struct_span_err ;
8
+ use rustc_errors:: Applicability ;
7
9
use rustc_expand:: base:: { Annotatable , DummyResult , ExtCtxt } ;
10
+ use rustc_span:: symbol:: Ident ;
8
11
use rustc_span:: symbol:: { kw, sym} ;
9
12
use rustc_span:: Span ;
13
+ use smallvec:: SmallVec ;
10
14
11
15
pub fn expand_deriving_default (
12
16
cx : & mut ExtCtxt < ' _ > ,
@@ -34,53 +38,184 @@ pub fn expand_deriving_default(
34
38
attributes: attrs,
35
39
is_unsafe: false ,
36
40
unify_fieldless_variants: false ,
37
- combine_substructure: combine_substructure( Box :: new( |a, b, c| {
38
- default_substructure( a, b, c)
41
+ combine_substructure: combine_substructure( Box :: new( |cx, trait_span, substr| {
42
+ match substr. fields {
43
+ StaticStruct ( _, fields) => {
44
+ default_struct_substructure( cx, trait_span, substr, fields)
45
+ }
46
+ StaticEnum ( enum_def, _) => {
47
+ if !cx. sess. features_untracked( ) . derive_default_enum {
48
+ rustc_session:: parse:: feature_err(
49
+ cx. parse_sess( ) ,
50
+ sym:: derive_default_enum,
51
+ span,
52
+ "deriving `Default` on enums is experimental" ,
53
+ )
54
+ . emit( ) ;
55
+ }
56
+ default_enum_substructure( cx, trait_span, enum_def)
57
+ }
58
+ _ => cx. span_bug( trait_span, "method in `derive(Default)`" ) ,
59
+ }
39
60
} ) ) ,
40
61
} ] ,
41
62
associated_types : Vec :: new ( ) ,
42
63
} ;
43
64
trait_def. expand ( cx, mitem, item, push)
44
65
}
45
66
46
- fn default_substructure (
67
+ fn default_struct_substructure (
47
68
cx : & mut ExtCtxt < ' _ > ,
48
69
trait_span : Span ,
49
70
substr : & Substructure < ' _ > ,
71
+ summary : & StaticFields ,
50
72
) -> P < Expr > {
51
73
// Note that `kw::Default` is "default" and `sym::Default` is "Default"!
52
74
let default_ident = cx. std_path ( & [ kw:: Default , sym:: Default , kw:: Default ] ) ;
53
75
let default_call = |span| cx. expr_call_global ( span, default_ident. clone ( ) , Vec :: new ( ) ) ;
54
76
55
- match * substr. fields {
56
- StaticStruct ( _, ref summary) => match * summary {
57
- Unnamed ( ref fields, is_tuple) => {
58
- if !is_tuple {
59
- cx. expr_ident ( trait_span, substr. type_ident )
60
- } else {
61
- let exprs = fields. iter ( ) . map ( |sp| default_call ( * sp) ) . collect ( ) ;
62
- cx. expr_call_ident ( trait_span, substr. type_ident , exprs)
63
- }
64
- }
65
- Named ( ref fields) => {
66
- let default_fields = fields
67
- . iter ( )
68
- . map ( |& ( ident, span) | cx. field_imm ( span, ident, default_call ( span) ) )
69
- . collect ( ) ;
70
- cx. expr_struct_ident ( trait_span, substr. type_ident , default_fields)
77
+ match summary {
78
+ Unnamed ( ref fields, is_tuple) => {
79
+ if !is_tuple {
80
+ cx. expr_ident ( trait_span, substr. type_ident )
81
+ } else {
82
+ let exprs = fields. iter ( ) . map ( |sp| default_call ( * sp) ) . collect ( ) ;
83
+ cx. expr_call_ident ( trait_span, substr. type_ident , exprs)
71
84
}
72
- } ,
73
- StaticEnum ( ..) => {
74
- struct_span_err ! (
75
- & cx. sess. parse_sess. span_diagnostic,
76
- trait_span,
77
- E0665 ,
78
- "`Default` cannot be derived for enums, only structs"
79
- )
85
+ }
86
+ Named ( ref fields) => {
87
+ let default_fields = fields
88
+ . iter ( )
89
+ . map ( |& ( ident, span) | cx. field_imm ( span, ident, default_call ( span) ) )
90
+ . collect ( ) ;
91
+ cx. expr_struct_ident ( trait_span, substr. type_ident , default_fields)
92
+ }
93
+ }
94
+ }
95
+
96
+ fn default_enum_substructure (
97
+ cx : & mut ExtCtxt < ' _ > ,
98
+ trait_span : Span ,
99
+ enum_def : & EnumDef ,
100
+ ) -> P < Expr > {
101
+ let default_variant = match extract_default_variant ( cx, enum_def, trait_span) {
102
+ Ok ( value) => value,
103
+ Err ( ( ) ) => return DummyResult :: raw_expr ( trait_span, true ) ,
104
+ } ;
105
+
106
+ // At this point, we know that there is exactly one variant with a `#[default]` attribute. The
107
+ // attribute hasn't yet been validated.
108
+
109
+ if let Err ( ( ) ) = validate_default_attribute ( cx, default_variant) {
110
+ return DummyResult :: raw_expr ( trait_span, true ) ;
111
+ }
112
+
113
+ // We now know there is exactly one unit variant with exactly one `#[default]` attribute.
114
+
115
+ cx. expr_path ( cx. path (
116
+ default_variant. span ,
117
+ vec ! [ Ident :: new( kw:: SelfUpper , default_variant. span) , default_variant. ident] ,
118
+ ) )
119
+ }
120
+
121
+ fn extract_default_variant < ' a > (
122
+ cx : & mut ExtCtxt < ' _ > ,
123
+ enum_def : & ' a EnumDef ,
124
+ trait_span : Span ,
125
+ ) -> Result < & ' a rustc_ast:: Variant , ( ) > {
126
+ let default_variants: SmallVec < [ _ ; 1 ] > = enum_def
127
+ . variants
128
+ . iter ( )
129
+ . filter ( |variant| cx. sess . contains_name ( & variant. attrs , kw:: Default ) )
130
+ . collect ( ) ;
131
+
132
+ let variant = match default_variants. as_slice ( ) {
133
+ [ variant] => variant,
134
+ [ ] => {
135
+ cx. struct_span_err ( trait_span, "no default declared" )
136
+ . help ( "make a unit variant default by placing `#[default]` above it" )
137
+ . emit ( ) ;
138
+
139
+ return Err ( ( ) ) ;
140
+ }
141
+ [ first, rest @ ..] => {
142
+ cx. struct_span_err ( trait_span, "multiple declared defaults" )
143
+ . span_label ( first. span , "first default" )
144
+ . span_labels ( rest. iter ( ) . map ( |variant| variant. span ) , "additional default" )
145
+ . note ( "only one variant can be default" )
146
+ . emit ( ) ;
147
+
148
+ return Err ( ( ) ) ;
149
+ }
150
+ } ;
151
+
152
+ if !matches ! ( variant. data, VariantData :: Unit ( ..) ) {
153
+ cx. struct_span_err ( variant. ident . span , "`#[default]` may only be used on unit variants" )
154
+ . help ( "consider a manual implementation of `Default`" )
155
+ . emit ( ) ;
156
+
157
+ return Err ( ( ) ) ;
158
+ }
159
+
160
+ if let Some ( non_exhaustive_attr) = cx. sess . find_by_name ( & variant. attrs , sym:: non_exhaustive) {
161
+ cx. struct_span_err ( variant. ident . span , "default variant must be exhaustive" )
162
+ . span_label ( non_exhaustive_attr. span , "declared `#[non_exhaustive]` here" )
163
+ . help ( "consider a manual implementation of `Default`" )
80
164
. emit ( ) ;
81
- // let compilation continue
82
- DummyResult :: raw_expr ( trait_span, true )
165
+
166
+ return Err ( ( ) ) ;
167
+ }
168
+
169
+ Ok ( variant)
170
+ }
171
+
172
+ fn validate_default_attribute (
173
+ cx : & mut ExtCtxt < ' _ > ,
174
+ default_variant : & rustc_ast:: Variant ,
175
+ ) -> Result < ( ) , ( ) > {
176
+ let attrs: SmallVec < [ _ ; 1 ] > =
177
+ cx. sess . filter_by_name ( & default_variant. attrs , kw:: Default ) . collect ( ) ;
178
+
179
+ let attr = match attrs. as_slice ( ) {
180
+ [ attr] => attr,
181
+ [ ] => cx. bug (
182
+ "this method must only be called with a variant that has a `#[default]` attribute" ,
183
+ ) ,
184
+ [ first, rest @ ..] => {
185
+ // FIXME(jhpratt) Do we want to perform this check? It doesn't exist
186
+ // for `#[inline]`, `#[non_exhaustive]`, and presumably others.
187
+
188
+ let suggestion_text =
189
+ if rest. len ( ) == 1 { "try removing this" } else { "try removing these" } ;
190
+
191
+ cx. struct_span_err ( default_variant. ident . span , "multiple `#[default]` attributes" )
192
+ . note ( "only one `#[default]` attribute is needed" )
193
+ . span_label ( first. span , "`#[default]` used here" )
194
+ . span_label ( rest[ 0 ] . span , "`#[default]` used again here" )
195
+ . span_help ( rest. iter ( ) . map ( |attr| attr. span ) . collect :: < Vec < _ > > ( ) , suggestion_text)
196
+ // This would otherwise display the empty replacement, hence the otherwise
197
+ // repetitive `.span_help` call above.
198
+ . tool_only_multipart_suggestion (
199
+ suggestion_text,
200
+ rest. iter ( ) . map ( |attr| ( attr. span , String :: new ( ) ) ) . collect ( ) ,
201
+ Applicability :: MachineApplicable ,
202
+ )
203
+ . emit ( ) ;
204
+
205
+ return Err ( ( ) ) ;
83
206
}
84
- _ => cx. span_bug ( trait_span, "method in `derive(Default)`" ) ,
207
+ } ;
208
+ if !attr. is_word ( ) {
209
+ cx. struct_span_err ( attr. span , "`#[default]` attribute does not accept a value" )
210
+ . span_suggestion_hidden (
211
+ attr. span ,
212
+ "try using `#[default]`" ,
213
+ "#[default]" . into ( ) ,
214
+ Applicability :: MaybeIncorrect ,
215
+ )
216
+ . emit ( ) ;
217
+
218
+ return Err ( ( ) ) ;
85
219
}
220
+ Ok ( ( ) )
86
221
}
0 commit comments