1
1
use clippy_utils:: diagnostics:: span_lint_hir_and_then;
2
- use clippy_utils:: return_ty;
3
2
use clippy_utils:: source:: snippet;
4
3
use clippy_utils:: sugg:: DiagExt ;
4
+ use clippy_utils:: { is_default_equivalent_call, return_ty} ;
5
5
use rustc_errors:: Applicability ;
6
6
use rustc_hir as hir;
7
7
use rustc_hir:: HirIdMap ;
@@ -134,7 +134,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
134
134
if impl_item. span . in_external_macro ( cx. sess ( ) . source_map ( ) ) {
135
135
return ;
136
136
}
137
- if let hir:: ImplItemKind :: Fn ( ref sig, _ ) = impl_item. kind {
137
+ if let hir:: ImplItemKind :: Fn ( ref sig, body_id ) = impl_item. kind {
138
138
let name = impl_item. ident . name ;
139
139
let id = impl_item. owner_id ;
140
140
if sig. header . is_unsafe ( ) {
@@ -199,9 +199,31 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
199
199
return ;
200
200
}
201
201
suggest_new_without_default ( cx, item, impl_item, id, self_ty, generics, impl_self_ty) ;
202
- } else {
202
+ } else if let hir :: ExprKind :: Block ( block , _ ) = cx . tcx . hir ( ) . body ( body_id ) . value . kind {
203
203
// this type has an automatically derived `Default` implementation
204
204
// check if `new` and `default` are equivalent
205
+ if !check_block_calls_default ( cx, block) {
206
+ let self_ty_fmt = self_ty. to_string ( ) ;
207
+ let self_type_snip = snippet ( cx, impl_self_ty. span , & self_ty_fmt) ;
208
+ span_lint_hir_and_then (
209
+ cx,
210
+ DEFAULT_MISMATCHES_NEW ,
211
+ id. into ( ) ,
212
+ impl_item. span ,
213
+ format ! (
214
+ "you should consider delegating to the auto-derived `Default` for `{self_type_snip}`"
215
+ ) ,
216
+ |diag| {
217
+ diag. suggest_prepend_item (
218
+ cx,
219
+ block. span ,
220
+ "try using this" ,
221
+ & "Self::default()" ,
222
+ Applicability :: MaybeIncorrect ,
223
+ ) ;
224
+ } ,
225
+ ) ;
226
+ }
205
227
}
206
228
}
207
229
}
@@ -211,14 +233,55 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
211
233
}
212
234
}
213
235
236
+ /// Check if a block contains one of these:
237
+ /// - Empty block with an expr (e.g., `{ Self::default() }`)
238
+ /// - One statement (e.g., `{ return Self::default(); }`)
239
+ fn check_block_calls_default ( cx : & LateContext < ' _ > , block : & hir:: Block < ' _ > ) -> bool {
240
+ if let Some ( expr) = block. expr
241
+ && block. stmts . is_empty ( )
242
+ {
243
+ // Check the block's trailing expression (e.g., `Self::default()`)
244
+ check_expr_call_default ( cx, expr)
245
+ } else if let [ hir:: Stmt { kind, .. } ] = block. stmts
246
+ && let hir:: StmtKind :: Expr ( expr) | hir:: StmtKind :: Semi ( expr) = kind
247
+ {
248
+ // Check if the single statement is a return or expr
249
+ match expr. kind {
250
+ // Case 1: `return Self::default();`
251
+ hir:: ExprKind :: Ret ( Some ( ret_expr) ) => check_expr_call_default ( cx, ret_expr) ,
252
+ // Case 2: `Self::default()` (possibly inside a block)
253
+ hir:: ExprKind :: Block ( block, _) => check_block_calls_default ( cx, block) ,
254
+ // Case 3: Direct call (e.g., `Self::default()` as the entire body)
255
+ _ => check_expr_call_default ( cx, expr) ,
256
+ }
257
+ } else {
258
+ false
259
+ }
260
+ }
261
+
262
+ /// Check for `Self::default()` call syntax or equivalent
263
+ fn check_expr_call_default ( cx : & LateContext < ' _ > , expr : & hir:: Expr < ' _ > ) -> bool {
264
+ if let hir:: ExprKind :: Call ( callee, args) = expr. kind
265
+ && args. is_empty ( )
266
+ // FIXME: does this include `Self { }` style calls, which is equivalent,
267
+ // but not the same as `Self::default()`?
268
+ // FIXME: what should the whole_call_expr (3rd arg) be?
269
+ && is_default_equivalent_call ( cx, callee, None )
270
+ {
271
+ true
272
+ } else {
273
+ false
274
+ }
275
+ }
276
+
214
277
fn suggest_new_without_default < ' tcx > (
215
278
cx : & LateContext < ' tcx > ,
216
- item : & rustc_hir :: Item < ' _ > ,
217
- impl_item : & rustc_hir :: ImplItem < ' _ > ,
218
- id : rustc_hir :: OwnerId ,
279
+ item : & hir :: Item < ' _ > ,
280
+ impl_item : & hir :: ImplItem < ' _ > ,
281
+ id : hir :: OwnerId ,
219
282
self_ty : Ty < ' tcx > ,
220
- generics : & rustc_hir :: Generics < ' _ > ,
221
- impl_self_ty : & rustc_hir :: Ty < ' _ > ,
283
+ generics : & hir :: Generics < ' _ > ,
284
+ impl_self_ty : & hir :: Ty < ' _ > ,
222
285
) {
223
286
let generics_sugg = snippet ( cx, generics. span , "" ) ;
224
287
let where_clause_sugg = if generics. has_where_clause_predicates {
@@ -239,23 +302,15 @@ fn suggest_new_without_default<'tcx>(
239
302
cx,
240
303
item. span ,
241
304
"try adding this" ,
242
- & create_new_without_default_suggest_msg ( & self_type_snip, & generics_sugg, & where_clause_sugg) ,
305
+ & format ! (
306
+ "impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{
307
+ fn default() -> Self {{
308
+ Self::new()
309
+ }}
310
+ }}"
311
+ ) ,
243
312
Applicability :: MachineApplicable ,
244
313
) ;
245
314
} ,
246
315
) ;
247
316
}
248
-
249
- fn create_new_without_default_suggest_msg (
250
- self_type_snip : & str ,
251
- generics_sugg : & str ,
252
- where_clause_sugg : & str ,
253
- ) -> String {
254
- #[ rustfmt:: skip]
255
- format ! (
256
- "impl{generics_sugg} Default for {self_type_snip}{where_clause_sugg} {{
257
- fn default() -> Self {{
258
- Self::new()
259
- }}
260
- }}" )
261
- }
0 commit comments