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} ;
22use 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+ } ;
38use if_chain:: if_chain;
49use rustc_errors:: Applicability ;
10+ use rustc_hir:: def:: Res ;
511use rustc_hir:: intravisit:: FnKind ;
612use 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 ,
915} ;
10- use rustc_lint:: { LateContext , LateLintPass } ;
16+ use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1117use rustc_middle:: lint:: in_external_macro;
1218use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
1319use 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 ;
2221
2322use crate :: ref_patterns:: REF_PATTERNS ;
2423
@@ -257,46 +256,56 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
257256 self . check_cast ( cx, expr. span , e, ty) ;
258257 return ;
259258 }
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+ {
262263 return ;
263264 }
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)
274270 {
275- Some ( binding )
271+ ( id , last_path_segment ( qpath ) . ident )
276272 } else {
277- None
273+ return ;
278274 }
279275 } ,
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)
285284 } else {
286- None
285+ return ;
287286 }
288287 } ,
289- _ => None ,
288+ _ => return ,
290289 } ;
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 (
293299 cx,
294300 USED_UNDERSCORE_BINDING ,
295301 expr. span ,
296302 & 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 \
298304 underscore signals that a binding will not be used"
299305 ) ,
306+ |diag| {
307+ diag. span_note ( definition_span, format ! ( "`{name}` is defined here" ) ) ;
308+ }
300309 ) ;
301310 }
302311 }
@@ -312,29 +321,8 @@ fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
312321 } )
313322}
314323
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-
336324impl 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 < ' _ > ) {
338326 if_chain ! {
339327 if let TyKind :: Ptr ( ref mut_ty) = ty. kind;
340328 if is_integer_literal( e, 0 ) ;
0 commit comments