@@ -16,8 +16,8 @@ use rustc_hir::{Block, PatKind};
1616use rustc_hir:: { ExprKind , Impl , ItemKind , QPath , TyKind } ;
1717use rustc_hir:: { ImplItem , Item , VariantData } ;
1818use rustc_lint:: { LateContext , LateLintPass } ;
19- use rustc_middle:: ty:: Ty ;
2019use rustc_middle:: ty:: TypeckResults ;
20+ use rustc_middle:: ty:: { EarlyBinder , Ty } ;
2121use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
2222use rustc_span:: { sym, Span , Symbol } ;
2323
@@ -41,6 +41,9 @@ declare_clippy_lint! {
4141 /// This lint also does not look through function calls, so calling `.field(self.as_slice())` for example
4242 /// does not consider fields used inside of `as_slice()` as used by the `Debug` impl.
4343 ///
44+ /// Lastly, it also ignores tuple structs as their `DebugTuple` formatter does not have a `finish_non_exhaustive`
45+ /// method.
46+ ///
4447 /// ### Example
4548 /// ```rust
4649 /// use std::fmt;
@@ -107,7 +110,9 @@ fn should_lint<'tcx>(
107110 typeck_results : & TypeckResults < ' tcx > ,
108111 block : impl Visitable < ' tcx > ,
109112) -> bool {
113+ // Is there a call to `DebugStruct::finish_non_exhaustive`? Don't lint if there is.
110114 let mut has_finish_non_exhaustive = false ;
115+ // Is there a call to `DebugStruct::debug_struct`? Do lint if there is.
111116 let mut has_debug_struct = false ;
112117
113118 for_each_expr ( block, |expr| {
@@ -128,6 +133,8 @@ fn should_lint<'tcx>(
128133
129134/// Checks if the given expression is a call to `DebugStruct::field`
130135/// and the first argument to it is a string literal and if so, returns it
136+ ///
137+ /// Example: `.field("foo", ....)` returns `Some("foo")`
131138fn as_field_call < ' tcx > (
132139 cx : & LateContext < ' tcx > ,
133140 typeck_results : & TypeckResults < ' tcx > ,
@@ -155,16 +162,19 @@ fn check_struct<'tcx>(
155162 item : & ' tcx Item < ' tcx > ,
156163 data : & VariantData < ' _ > ,
157164) {
165+ // Is there a "direct" field access anywhere (i.e. self.foo)?
166+ // We don't want to lint if there is not, because the user might have
167+ // a newtype struct and use fields from the wrapped type only.
158168 let mut has_direct_field_access = false ;
159169 let mut field_accesses = FxHashSet :: default ( ) ;
160170
161171 for_each_expr ( block, |expr| {
162172 if let ExprKind :: Field ( target, ident) = expr. kind
163- && let target_ty = typeck_results. expr_ty ( target) . peel_refs ( )
173+ && let target_ty = typeck_results. expr_ty_adjusted ( target) . peel_refs ( )
164174 && target_ty == self_ty
165175 {
166- has_direct_field_access = true ;
167176 field_accesses. insert ( ident. name ) ;
177+ has_direct_field_access = true ;
168178 } else if let Some ( sym) = as_field_call ( cx, typeck_results, expr) {
169179 field_accesses. insert ( sym) ;
170180 }
@@ -175,7 +185,8 @@ fn check_struct<'tcx>(
175185 . fields ( )
176186 . iter ( )
177187 . filter_map ( |field| {
178- if field_accesses. contains ( & field. ident . name ) {
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 ( ) {
179190 None
180191 } else {
181192 Some ( ( field. span , "this field is unused" ) )
@@ -204,7 +215,7 @@ fn check_enum<'tcx>(
204215) {
205216 let Some ( arms) = for_each_expr ( block, |expr| {
206217 if let ExprKind :: Match ( val, arms, MatchSource :: Normal ) = expr. kind
207- && let match_ty = typeck_results. expr_ty ( val) . peel_refs ( )
218+ && let match_ty = typeck_results. expr_ty_adjusted ( val) . peel_refs ( )
208219 && match_ty == self_ty
209220 {
210221 ControlFlow :: Break ( arms)
@@ -234,20 +245,22 @@ fn check_enum<'tcx>(
234245 } ) ;
235246
236247 let mut field_accesses = FxHashSet :: default ( ) ;
237- let mut check_field_access = |sym| {
238- arm. pat . each_binding ( |_, _, _, pat_ident| {
239- if sym == pat_ident. name {
240- field_accesses. insert ( pat_ident) ;
241- }
242- } ) ;
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+ }
243256 } ;
244257
245258 for_each_expr ( arm. body , |expr| {
246259 if let ExprKind :: Path ( QPath :: Resolved ( _, path) ) = expr. kind && let Some ( segment) = path. segments . first ( )
247260 {
248- check_field_access ( segment. ident . name ) ;
261+ check_field_access ( segment. ident . name , expr ) ;
249262 } else if let Some ( sym) = as_field_call ( cx, typeck_results, expr) {
250- check_field_access ( sym) ;
263+ check_field_access ( sym, expr ) ;
251264 }
252265 ControlFlow :: < !, _ > :: Continue ( ( ) )
253266 } ) ;
0 commit comments