1
- use clippy_utils:: diagnostics:: { span_lint, span_lint_and_sugg, span_lint_hir_and_then} ;
1
+ use clippy_utils:: diagnostics:: { span_lint, span_lint_and_sugg, span_lint_and_then , span_lint_hir_and_then} ;
2
2
use clippy_utils:: source:: { snippet, snippet_opt, snippet_with_context} ;
3
+ use clippy_utils:: sugg:: Sugg ;
4
+ use clippy_utils:: {
5
+ any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, in_constant, is_integer_literal,
6
+ is_lint_allowed, is_no_std_crate, iter_input_pats, last_path_segment, SpanlessEq ,
7
+ } ;
3
8
use if_chain:: if_chain;
4
9
use rustc_errors:: Applicability ;
10
+ use rustc_hir:: def:: Res ;
5
11
use rustc_hir:: intravisit:: FnKind ;
6
12
use rustc_hir:: {
7
- self as hir , def , BinOpKind , BindingAnnotation , Body , ByRef , Expr , ExprKind , FnDecl , Mutability , PatKind , Stmt ,
8
- StmtKind , TyKind ,
13
+ BinOpKind , BindingAnnotation , Body , ByRef , Expr , ExprKind , FnDecl , Mutability , PatKind , QPath , Stmt , StmtKind , Ty ,
14
+ TyKind ,
9
15
} ;
10
- use rustc_lint:: { LateContext , LateLintPass } ;
16
+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
17
use rustc_middle:: lint:: in_external_macro;
12
18
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
13
19
use rustc_span:: def_id:: LocalDefId ;
14
- use rustc_span:: hygiene:: DesugaringKind ;
15
- use rustc_span:: source_map:: { ExpnKind , Span } ;
16
-
17
- use clippy_utils:: sugg:: Sugg ;
18
- use clippy_utils:: {
19
- get_parent_expr, in_constant, is_integer_literal, is_lint_allowed, is_no_std_crate, iter_input_pats,
20
- last_path_segment, SpanlessEq ,
21
- } ;
20
+ use rustc_span:: source_map:: Span ;
22
21
23
22
use crate :: ref_patterns:: REF_PATTERNS ;
24
23
@@ -257,46 +256,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
257
256
self . check_cast ( cx, expr. span , e, ty) ;
258
257
return ;
259
258
}
260
- if in_attributes_expansion ( expr) || expr. span . is_desugaring ( DesugaringKind :: Await ) {
261
- // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
259
+ if in_external_macro ( cx. sess ( ) , expr. span )
260
+ || expr. span . desugaring_kind ( ) . is_some ( )
261
+ || any_parent_is_automatically_derived ( cx. tcx , expr. hir_id )
262
+ {
262
263
return ;
263
264
}
264
- let sym;
265
- let binding = match expr. kind {
266
- ExprKind :: Path ( ref qpath) if !matches ! ( qpath, hir:: QPath :: LangItem ( ..) ) => {
267
- let binding = last_path_segment ( qpath) . ident . as_str ( ) ;
268
- if binding. starts_with ( '_' ) &&
269
- !binding. starts_with ( "__" ) &&
270
- binding != "_result" && // FIXME: #944
271
- is_used ( cx, expr) &&
272
- // don't lint if the declaration is in a macro
273
- non_macro_local ( cx, cx. qpath_res ( qpath, expr. hir_id ) )
265
+ let ( definition_hir_id, ident) = match expr. kind {
266
+ ExprKind :: Path ( ref qpath) => {
267
+ if let QPath :: Resolved ( None , path) = qpath
268
+ && let Res :: Local ( id) = path. res
269
+ && is_used ( cx, expr)
274
270
{
275
- Some ( binding )
271
+ ( id , last_path_segment ( qpath ) . ident )
276
272
} else {
277
- None
273
+ return ;
278
274
}
279
275
} ,
280
- ExprKind :: Field ( _, ident) => {
281
- sym = ident. name ;
282
- let name = sym. as_str ( ) ;
283
- if name. starts_with ( '_' ) && !name. starts_with ( "__" ) {
284
- Some ( name)
276
+ ExprKind :: Field ( recv, ident) => {
277
+ if let Some ( adt_def) = cx. typeck_results ( ) . expr_ty_adjusted ( recv) . ty_adt_def ( )
278
+ && let Some ( field) = adt_def. all_fields ( ) . find ( |field| field. name == ident. name )
279
+ && let Some ( local_did) = field. did . as_local ( )
280
+ && let Some ( hir_id) = cx. tcx . opt_local_def_id_to_hir_id ( local_did)
281
+ && !cx. tcx . type_of ( field. did ) . skip_binder ( ) . is_phantom_data ( )
282
+ {
283
+ ( hir_id, ident)
285
284
} else {
286
- None
285
+ return ;
287
286
}
288
287
} ,
289
- _ => None ,
288
+ _ => return ,
290
289
} ;
291
- if let Some ( binding) = binding {
292
- span_lint (
290
+
291
+ let name = ident. name . as_str ( ) ;
292
+ if name. starts_with ( '_' )
293
+ && !name. starts_with ( "__" )
294
+ && let definition_span = cx. tcx . hir ( ) . span ( definition_hir_id)
295
+ && !definition_span. from_expansion ( )
296
+ && !fulfill_or_allowed ( cx, USED_UNDERSCORE_BINDING , [ expr. hir_id , definition_hir_id] )
297
+ {
298
+ span_lint_and_then (
293
299
cx,
294
300
USED_UNDERSCORE_BINDING ,
295
301
expr. span ,
296
302
& format ! (
297
- "used binding `{binding }` which is prefixed with an underscore. A leading \
303
+ "used binding `{name }` which is prefixed with an underscore. A leading \
298
304
underscore signals that a binding will not be used"
299
305
) ,
306
+ |diag| {
307
+ diag. span_note ( definition_span, format ! ( "`{name}` is defined here" ) ) ;
308
+ }
300
309
) ;
301
310
}
302
311
}
@@ -312,29 +321,8 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
312
321
} )
313
322
}
314
323
315
- /// Tests whether an expression is in a macro expansion (e.g., something
316
- /// generated by `#[derive(...)]` or the like).
317
- fn in_attributes_expansion ( expr : & Expr < ' _ > ) -> bool {
318
- use rustc_span:: hygiene:: MacroKind ;
319
- if expr. span . from_expansion ( ) {
320
- let data = expr. span . ctxt ( ) . outer_expn_data ( ) ;
321
- matches ! ( data. kind, ExpnKind :: Macro ( MacroKind :: Attr | MacroKind :: Derive , _) )
322
- } else {
323
- false
324
- }
325
- }
326
-
327
- /// Tests whether `res` is a variable defined outside a macro.
328
- fn non_macro_local ( cx : & LateContext < ' _ > , res : def:: Res ) -> bool {
329
- if let def:: Res :: Local ( id) = res {
330
- !cx. tcx . hir ( ) . span ( id) . from_expansion ( )
331
- } else {
332
- false
333
- }
334
- }
335
-
336
324
impl LintPass {
337
- fn check_cast ( & self , cx : & LateContext < ' _ > , span : Span , e : & Expr < ' _ > , ty : & hir :: Ty < ' _ > ) {
325
+ fn check_cast ( & self , cx : & LateContext < ' _ > , span : Span , e : & Expr < ' _ > , ty : & Ty < ' _ > ) {
338
326
if_chain ! {
339
327
if let TyKind :: Ptr ( ref mut_ty) = ty. kind;
340
328
if is_integer_literal( e, 0 ) ;
0 commit comments