@@ -10,7 +10,7 @@ use rustc_hir::{
10
10
} ;
11
11
use rustc_lint:: { LateContext , LateLintPass } ;
12
12
use rustc_middle:: ty:: adjustment:: { Adjust , PointerCoercion } ;
13
- use rustc_middle:: ty:: { self , AdtDef , GenericArgsRef , Ty , TypeckResults } ;
13
+ use rustc_middle:: ty:: { self , AdtDef , GenericArgsRef , Ty , TypeckResults , VariantDef } ;
14
14
use rustc_session:: impl_lint_pass;
15
15
use rustc_span:: sym;
16
16
@@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool {
85
85
}
86
86
}
87
87
88
+ fn determine_derive_macro ( cx : & LateContext < ' _ > , is_const : bool ) -> Option < & ' static str > {
89
+ ( !is_const)
90
+ . then_some ( "derive" )
91
+ . or_else ( || cx. tcx . features ( ) . enabled ( sym:: derive_const) . then_some ( "derive_const" ) )
92
+ }
93
+
94
+ #[ expect( clippy:: too_many_arguments) ]
88
95
fn check_struct < ' tcx > (
89
96
cx : & LateContext < ' tcx > ,
90
97
item : & ' tcx Item < ' _ > ,
@@ -93,6 +100,7 @@ fn check_struct<'tcx>(
93
100
adt_def : AdtDef < ' _ > ,
94
101
ty_args : GenericArgsRef < ' _ > ,
95
102
typeck_results : & ' tcx TypeckResults < ' tcx > ,
103
+ is_const : bool ,
96
104
) {
97
105
if let TyKind :: Path ( QPath :: Resolved ( _, p) ) = self_ty. kind
98
106
&& let Some ( PathSegment { args, .. } ) = p. segments . last ( )
@@ -125,14 +133,18 @@ fn check_struct<'tcx>(
125
133
ExprKind :: Tup ( fields) => fields. iter ( ) . all ( is_default_without_adjusts) ,
126
134
ExprKind :: Call ( callee, args) if is_path_self ( callee) => args. iter ( ) . all ( is_default_without_adjusts) ,
127
135
ExprKind :: Struct ( _, fields, _) => fields. iter ( ) . all ( |ef| is_default_without_adjusts ( ef. expr ) ) ,
128
- _ => false ,
136
+ _ => return ,
129
137
} ;
130
138
131
- if should_emit {
139
+ if should_emit && let Some ( derive_snippet ) = determine_derive_macro ( cx , is_const ) {
132
140
let struct_span = cx. tcx . def_span ( adt_def. did ( ) ) ;
141
+ let indent_enum = indent_of ( cx, struct_span) . unwrap_or ( 0 ) ;
133
142
let suggestions = vec ! [
134
143
( item. span, String :: new( ) ) , // Remove the manual implementation
135
- ( struct_span. shrink_to_lo( ) , "#[derive(Default)]\n " . to_string( ) ) , // Add the derive attribute
144
+ (
145
+ struct_span. shrink_to_lo( ) ,
146
+ format!( "#[{derive_snippet}(Default)]\n {}" , " " . repeat( indent_enum) ) ,
147
+ ) , // Add the derive attribute
136
148
] ;
137
149
138
150
span_lint_and_then ( cx, DERIVABLE_IMPLS , item. span , "this `impl` can be derived" , |diag| {
@@ -145,11 +157,41 @@ fn check_struct<'tcx>(
145
157
}
146
158
}
147
159
148
- fn check_enum < ' tcx > ( cx : & LateContext < ' tcx > , item : & ' tcx Item < ' _ > , func_expr : & Expr < ' _ > , adt_def : AdtDef < ' _ > ) {
149
- if let ExprKind :: Path ( QPath :: Resolved ( None , p) ) = & peel_blocks ( func_expr) . kind
150
- && let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , id) = p. res
151
- && let variant_id = cx. tcx . parent ( id)
152
- && let Some ( variant_def) = adt_def. variants ( ) . iter ( ) . find ( |v| v. def_id == variant_id)
160
+ fn extract_enum_variant < ' tcx > (
161
+ cx : & LateContext < ' tcx > ,
162
+ func_expr : & ' tcx Expr < ' tcx > ,
163
+ adt_def : AdtDef < ' tcx > ,
164
+ ) -> Option < & ' tcx VariantDef > {
165
+ match & peel_blocks ( func_expr) . kind {
166
+ ExprKind :: Path ( QPath :: Resolved ( None , p) )
167
+ if let Res :: Def ( DefKind :: Ctor ( CtorOf :: Variant , CtorKind :: Const ) , id) = p. res
168
+ && let variant_id = cx. tcx . parent ( id)
169
+ && let Some ( variant_def) = adt_def. variants ( ) . iter ( ) . find ( |v| v. def_id == variant_id) =>
170
+ {
171
+ Some ( variant_def)
172
+ } ,
173
+ ExprKind :: Path ( QPath :: TypeRelative ( ty, segment) )
174
+ if let TyKind :: Path ( QPath :: Resolved ( None , p) ) = & ty. kind
175
+ && let Res :: SelfTyAlias {
176
+ is_trait_impl : true , ..
177
+ } = p. res
178
+ && let variant_ident = segment. ident
179
+ && let Some ( variant_def) = adt_def. variants ( ) . iter ( ) . find ( |v| v. ident ( cx. tcx ) == variant_ident) =>
180
+ {
181
+ Some ( variant_def)
182
+ } ,
183
+ _ => None ,
184
+ }
185
+ }
186
+
187
+ fn check_enum < ' tcx > (
188
+ cx : & LateContext < ' tcx > ,
189
+ item : & ' tcx Item < ' tcx > ,
190
+ func_expr : & ' tcx Expr < ' tcx > ,
191
+ adt_def : AdtDef < ' tcx > ,
192
+ is_const : bool ,
193
+ ) {
194
+ if let Some ( variant_def) = extract_enum_variant ( cx, func_expr, adt_def)
153
195
&& variant_def. fields . is_empty ( )
154
196
&& !variant_def. is_field_list_non_exhaustive ( )
155
197
{
@@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
158
200
let variant_span = cx. tcx . def_span ( variant_def. def_id ) ;
159
201
let indent_variant = indent_of ( cx, variant_span) . unwrap_or ( 0 ) ;
160
202
203
+ let Some ( derive_snippet) = determine_derive_macro ( cx, is_const) else {
204
+ return ;
205
+ } ;
206
+
161
207
let suggestions = vec ! [
162
208
( item. span, String :: new( ) ) , // Remove the manual implementation
163
209
(
164
210
enum_span. shrink_to_lo( ) ,
165
- format!( "#[derive (Default)]\n {}" , " " . repeat( indent_enum) ) ,
211
+ format!( "#[{derive_snippet} (Default)]\n {}" , " " . repeat( indent_enum) ) ,
166
212
) , // Add the derive attribute
167
213
(
168
214
variant_span. shrink_to_lo( ) ,
@@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
201
247
&& !attrs. iter ( ) . any ( |attr| attr. doc_str ( ) . is_some ( ) )
202
248
&& cx. tcx . hir_attrs ( impl_item_hir) . is_empty ( )
203
249
{
250
+ let is_const = of_trait. constness == hir:: Constness :: Const ;
204
251
if adt_def. is_struct ( ) {
205
- check_struct ( cx, item, self_ty, func_expr, adt_def, args, cx. tcx . typeck_body ( * b) ) ;
252
+ check_struct (
253
+ cx,
254
+ item,
255
+ self_ty,
256
+ func_expr,
257
+ adt_def,
258
+ args,
259
+ cx. tcx . typeck_body ( * b) ,
260
+ is_const,
261
+ ) ;
206
262
} else if adt_def. is_enum ( ) && self . msrv . meets ( cx, msrvs:: DEFAULT_ENUM_ATTRIBUTE ) {
207
- check_enum ( cx, item, func_expr, adt_def) ;
263
+ check_enum ( cx, item, func_expr, adt_def, is_const ) ;
208
264
}
209
265
}
210
266
}
0 commit comments