@@ -2,22 +2,22 @@ use std::ops::ControlFlow;
2
2
3
3
use clippy_utils:: {
4
4
diagnostics:: span_lint_and_then,
5
- paths,
5
+ is_path_lang_item , paths,
6
6
ty:: match_type,
7
7
visitors:: { for_each_expr, Visitable } ,
8
8
} ;
9
9
use rustc_ast:: LitKind ;
10
10
use rustc_data_structures:: fx:: FxHashSet ;
11
+ use rustc_hir:: Block ;
11
12
use rustc_hir:: {
12
13
def:: { DefKind , Res } ,
13
- Expr , ImplItemKind , MatchSource , Node ,
14
+ Expr , ImplItemKind , LangItem , Node ,
14
15
} ;
15
- use rustc_hir:: { Block , PatKind } ;
16
16
use rustc_hir:: { ExprKind , Impl , ItemKind , QPath , TyKind } ;
17
17
use rustc_hir:: { ImplItem , Item , VariantData } ;
18
18
use rustc_lint:: { LateContext , LateLintPass } ;
19
+ use rustc_middle:: ty:: Ty ;
19
20
use rustc_middle:: ty:: TypeckResults ;
20
- use rustc_middle:: ty:: { EarlyBinder , Ty } ;
21
21
use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
22
22
use rustc_span:: { sym, Span , Symbol } ;
23
23
@@ -38,11 +38,12 @@ declare_clippy_lint! {
38
38
/// Oftentimes there is more logic to a `Debug` impl if it uses `write!` macro, so it tries
39
39
/// to be on the conservative side and not lint in those cases in an attempt to prevent false positives.
40
40
///
41
- /// This lint also does not look through function calls, so calling `.field(self.as_slice())` for example
42
- /// does not consider fields used inside of `as_slice()` as used by the `Debug` impl.
41
+ /// This lint also does not look through function calls, so calling a function does not consider fields
42
+ /// used inside of that function as used by the `Debug` impl.
43
43
///
44
44
/// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive`
45
- /// method.
45
+ /// method, as well as enums because their exhaustiveness is already checked by the compiler when matching on the enum,
46
+ /// making it much less likely to accidentally forget to update the `Debug` impl when adding a new variant.
46
47
///
47
48
/// ### Example
48
49
/// ```rust
@@ -185,8 +186,7 @@ fn check_struct<'tcx>(
185
186
. fields ( )
186
187
. iter ( )
187
188
. filter_map ( |field| {
188
- let EarlyBinder ( field_ty) = cx. tcx . type_of ( field. def_id ) ;
189
- if field_accesses. contains ( & field. ident . name ) || field_ty. is_phantom_data ( ) {
189
+ if field_accesses. contains ( & field. ident . name ) || is_path_lang_item ( cx, field. ty , LangItem :: PhantomData ) {
190
190
None
191
191
} else {
192
192
Some ( ( field. span , "this field is unused" ) )
@@ -201,82 +201,6 @@ fn check_struct<'tcx>(
201
201
}
202
202
}
203
203
204
- /// Attempts to find unused fields in variants assuming that
205
- /// the item is an enum.
206
- ///
207
- /// Currently, only simple cases are detected where the user
208
- /// matches on `self` and calls `debug_struct` inside of the arms
209
- fn check_enum < ' tcx > (
210
- cx : & LateContext < ' tcx > ,
211
- typeck_results : & TypeckResults < ' tcx > ,
212
- block : & ' tcx Block < ' tcx > ,
213
- self_ty : Ty < ' tcx > ,
214
- item : & ' tcx Item < ' tcx > ,
215
- ) {
216
- let Some ( arms) = for_each_expr ( block, |expr| {
217
- if let ExprKind :: Match ( val, arms, MatchSource :: Normal ) = expr. kind
218
- && let match_ty = typeck_results. expr_ty_adjusted ( val) . peel_refs ( )
219
- && match_ty == self_ty
220
- {
221
- ControlFlow :: Break ( arms)
222
- } else {
223
- ControlFlow :: Continue ( ( ) )
224
- }
225
- } ) else {
226
- return ;
227
- } ;
228
-
229
- let mut span_notes = Vec :: new ( ) ;
230
-
231
- for arm in arms {
232
- if !should_lint ( cx, typeck_results, arm. body ) {
233
- continue ;
234
- }
235
-
236
- arm. pat . walk_always ( |pat| match pat. kind {
237
- PatKind :: Wild => span_notes. push ( ( pat. span , "unused field here due to wildcard `_`" ) ) ,
238
- PatKind :: Tuple ( _, rest) | PatKind :: TupleStruct ( .., rest) if rest. as_opt_usize ( ) . is_some ( ) => {
239
- span_notes. push ( ( pat. span , "more unused fields here due to rest pattern `..`" ) ) ;
240
- } ,
241
- PatKind :: Struct ( .., true ) => {
242
- span_notes. push ( ( pat. span , "more unused fields here due to rest pattern `..`" ) ) ;
243
- } ,
244
- _ => { } ,
245
- } ) ;
246
-
247
- let mut field_accesses = FxHashSet :: default ( ) ;
248
- let mut check_field_access = |sym, expr| {
249
- if !typeck_results. expr_ty ( expr) . is_phantom_data ( ) {
250
- arm. pat . each_binding ( |_, _, _, pat_ident| {
251
- if sym == pat_ident. name {
252
- field_accesses. insert ( pat_ident) ;
253
- }
254
- } ) ;
255
- }
256
- } ;
257
-
258
- for_each_expr ( arm. body , |expr| {
259
- if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = expr. kind && let Some ( segment) = path. segments . first ( )
260
- {
261
- check_field_access ( segment. ident . name , expr) ;
262
- } else if let Some ( sym) = as_field_call ( cx, typeck_results, expr) {
263
- check_field_access ( sym, expr) ;
264
- }
265
- ControlFlow :: < !, _ > :: Continue ( ( ) )
266
- } ) ;
267
-
268
- arm. pat . each_binding ( |_, _, span, pat_ident| {
269
- if !field_accesses. contains ( & pat_ident) {
270
- span_notes. push ( ( span, "the field referenced by this binding is unused" ) ) ;
271
- }
272
- } ) ;
273
- }
274
-
275
- if !span_notes. is_empty ( ) {
276
- report_lints ( cx, item. span , span_notes) ;
277
- }
278
- }
279
-
280
204
impl < ' tcx > LateLintPass < ' tcx > for MissingFieldsInDebug {
281
205
fn check_item ( & mut self , cx : & LateContext < ' tcx > , item : & ' tcx rustc_hir:: Item < ' tcx > ) {
282
206
// is this an `impl Debug for X` block?
@@ -301,10 +225,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
301
225
&& let typeck_results = cx. tcx . typeck_body ( * body_id)
302
226
&& should_lint ( cx, typeck_results, block)
303
227
{
304
- match & self_item. kind {
305
- ItemKind :: Struct ( data, _) => check_struct ( cx, typeck_results, block, self_ty, item, data) ,
306
- ItemKind :: Enum ( ..) => check_enum ( cx, typeck_results, block, self_ty, item) ,
307
- _ => { }
228
+ // we intentionally only lint structs, see lint description
229
+ if let ItemKind :: Struct ( data, _) = & self_item. kind {
230
+ check_struct ( cx, typeck_results, block, self_ty, item, data) ;
308
231
}
309
232
}
310
233
}
0 commit comments