@@ -2,11 +2,15 @@ use crate::deriving::generic::ty::*;
22use crate :: deriving:: generic:: * ;
33
44use rustc_ast:: ptr:: P ;
5+ use rustc_ast:: EnumDef ;
6+ use rustc_ast:: VariantData ;
57use rustc_ast:: { Expr , MetaItem } ;
6- use rustc_errors:: struct_span_err ;
8+ use rustc_errors:: Applicability ;
79use rustc_expand:: base:: { Annotatable , DummyResult , ExtCtxt } ;
10+ use rustc_span:: symbol:: Ident ;
811use rustc_span:: symbol:: { kw, sym} ;
912use rustc_span:: Span ;
13+ use smallvec:: SmallVec ;
1014
1115pub fn expand_deriving_default (
1216 cx : & mut ExtCtxt < ' _ > ,
@@ -34,53 +38,184 @@ pub fn expand_deriving_default(
3438 attributes: attrs,
3539 is_unsafe: false ,
3640 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+ }
3960 } ) ) ,
4061 } ] ,
4162 associated_types : Vec :: new ( ) ,
4263 } ;
4364 trait_def. expand ( cx, mitem, item, push)
4465}
4566
46- fn default_substructure (
67+ fn default_struct_substructure (
4768 cx : & mut ExtCtxt < ' _ > ,
4869 trait_span : Span ,
4970 substr : & Substructure < ' _ > ,
71+ summary : & StaticFields ,
5072) -> P < Expr > {
5173 // Note that `kw::Default` is "default" and `sym::Default` is "Default"!
5274 let default_ident = cx. std_path ( & [ kw:: Default , sym:: Default , kw:: Default ] ) ;
5375 let default_call = |span| cx. expr_call_global ( span, default_ident. clone ( ) , Vec :: new ( ) ) ;
5476
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)
7184 }
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`" )
80164 . 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 ( ( ) ) ;
83206 }
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 ( ( ) ) ;
85219 }
220+ Ok ( ( ) )
86221}
0 commit comments