11use clippy_utils:: diagnostics:: span_lint_and_then;
22use clippy_utils:: source:: snippet_with_applicability;
3- use clippy_utils:: visitors:: for_each_expr;
3+ use clippy_utils:: visitors:: { for_each_expr, is_local_used } ;
44use rustc_errors:: Applicability ;
55use rustc_hir:: def:: { DefKind , Res } ;
6- use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , HirId , MatchSource , Node , Pat , PatField , PatKind } ;
6+ use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , MatchSource , Node , Pat , PatField , PatKind } ;
77use rustc_lint:: LateContext ;
88use rustc_span:: symbol:: Ident ;
99use rustc_span:: Span ;
1010use std:: borrow:: Cow ;
1111use std:: ops:: ControlFlow ;
1212
13- use super :: REDUNDANT_GUARD ;
13+ use super :: REDUNDANT_GUARDS ;
1414
15- pub ( super ) fn check ( cx : & LateContext < ' _ > , arms : & [ Arm < ' _ > ] ) {
15+ pub ( super ) fn check < ' tcx > ( cx : & LateContext < ' tcx > , arms : & ' tcx [ Arm < ' tcx > ] ) {
1616 for outer_arm in arms {
1717 let mut app = Applicability :: MaybeIncorrect ;
1818 let Some ( guard) = outer_arm. guard else {
@@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
3737 ) = if_expr. kind
3838 && let Some ( ( pat_binding, field_name) ) = get_pat_binding ( cx, scrutinee, outer_arm)
3939 {
40- emit_redundant_guard (
40+ emit_redundant_guards (
4141 cx,
4242 outer_arm,
4343 if_expr. span ,
@@ -53,7 +53,7 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
5353 && let pat = let_expr. pat
5454 && let Some ( ( pat_binding, field_name) ) = get_pat_binding ( cx, let_expr. init , outer_arm)
5555 {
56- emit_redundant_guard (
56+ emit_redundant_guards (
5757 cx,
5858 outer_arm,
5959 let_expr. span ,
@@ -78,7 +78,7 @@ pub(super) fn check(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
7878 // This isn't necessary in the other two checks, as they must be a pattern already.
7979 && cx. typeck_results ( ) . expr_ty ( local) == cx. typeck_results ( ) . expr_ty ( pat)
8080 {
81- emit_redundant_guard (
81+ emit_redundant_guards (
8282 cx,
8383 outer_arm,
8484 if_expr. span ,
@@ -103,21 +103,34 @@ fn remove_binding(
103103 ( pat_start, pat_end)
104104}
105105
106- fn get_pat_binding ( cx : & LateContext < ' _ > , guard_expr : & Expr < ' _ > , outer_arm : & Arm < ' _ > ) -> Option < ( Span , Option < Ident > ) > {
106+ fn get_pat_binding < ' tcx > (
107+ cx : & LateContext < ' tcx > ,
108+ guard_expr : & Expr < ' _ > ,
109+ outer_arm : & Arm < ' tcx > ,
110+ ) -> Option < ( Span , Option < Ident > ) > {
107111 if let ExprKind :: Path ( qpath) = guard_expr. kind
108112 && let res = cx. qpath_res ( & qpath, guard_expr. hir_id )
109- && let Res :: Local ( hir_id ) = res
110- && local_came_from_and_not_used ( cx, & res , outer_arm. pat . hir_id , outer_arm . body )
113+ && let Res :: Local ( local ) = res
114+ && ! is_local_used ( cx, outer_arm. body , local )
111115 {
112- return cx. tcx . hir ( ) . res_span ( res) . map ( |span| {
113- (
114- span,
115- match cx. tcx . hir ( ) . get_parent ( hir_id) {
116- Node :: PatField ( PatField { ident, .. } ) => Some ( * ident) ,
117- _ => None ,
118- } ,
119- )
116+ let mut is_from_pat = false ;
117+ outer_arm. pat . each_binding ( |_, hir_id, _, _| {
118+ if hir_id == local {
119+ is_from_pat = true ;
120+ }
120121 } ) ;
122+
123+ if is_from_pat {
124+ return cx. tcx . hir ( ) . res_span ( res) . map ( |span| {
125+ (
126+ span,
127+ match cx. tcx . hir ( ) . get_parent ( local) {
128+ Node :: PatField ( PatField { ident, .. } ) => Some ( * ident) ,
129+ _ => None ,
130+ } ,
131+ )
132+ } ) ;
133+ }
121134 }
122135
123136 None
@@ -140,29 +153,8 @@ fn get_guard(cx: &LateContext<'_>, guard: Option<Guard<'_>>, app: &mut Applicabi
140153 )
141154}
142155
143- fn local_came_from_and_not_used ( cx : & LateContext < ' _ > , local : & Res , target : HirId , body : & Expr < ' _ > ) -> bool {
144- if let Res :: Local ( local) = local
145- && cx. tcx . hir ( ) . parent_iter ( * local) . any ( |( p, _) | p == target)
146- && for_each_expr ( body, |expr| {
147- if let ExprKind :: Path ( qpath) = expr. kind
148- && let Res :: Local ( local_ref) = cx. qpath_res ( & qpath, expr. hir_id )
149- && * local == local_ref
150- {
151- return ControlFlow :: Break ( ( ) ) ;
152- }
153-
154- ControlFlow :: Continue ( ( ) )
155- } )
156- . is_none ( )
157- {
158- return true ;
159- }
160-
161- false
162- }
163-
164156#[ expect( clippy:: too_many_arguments) ]
165- fn emit_redundant_guard (
157+ fn emit_redundant_guards (
166158 cx : & LateContext < ' _ > ,
167159 outer_arm : & Arm < ' _ > ,
168160 guard_span : Span ,
@@ -177,18 +169,26 @@ fn emit_redundant_guard(
177169
178170 span_lint_and_then (
179171 cx,
180- REDUNDANT_GUARD ,
172+ REDUNDANT_GUARDS ,
181173 guard_span. source_callsite ( ) ,
182174 "redundant guard" ,
183175 |diag| {
184- diag. span_suggestion (
185- outer_arm. span . with_hi ( guard_span. source_callsite ( ) . hi ( ) ) ,
176+ diag. multipart_suggestion_verbose (
186177 "try" ,
187- format ! (
188- "{pat_start}{}{binding_replacement}{pat_end}{}" ,
189- field_name. map_or_else( || Cow :: Borrowed ( "" ) , |i| format!( "{}: " , i. as_str( ) ) . into( ) ) ,
190- get_guard( cx, inner_guard, app) ,
191- ) ,
178+ vec ! [
179+ (
180+ guard_span. source_callsite( ) . with_lo( outer_arm. pat. span. hi( ) ) ,
181+ String :: new( ) ,
182+ ) ,
183+ (
184+ outer_arm. pat. span,
185+ format!(
186+ "{pat_start}{}{binding_replacement}{pat_end}{}" ,
187+ field_name. map_or_else( || Cow :: Borrowed ( "" ) , |i| format!( "{}: " , i. as_str( ) ) . into( ) ) ,
188+ get_guard( cx, inner_guard, app) ,
189+ ) ,
190+ ) ,
191+ ] ,
192192 * app,
193193 ) ;
194194 } ,
0 commit comments