@@ -2,11 +2,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
22use clippy_utils:: path_to_local;
33use clippy_utils:: source:: snippet_with_applicability;
44use clippy_utils:: visitors:: { for_each_expr, is_local_used} ;
5- use rustc_ast:: LitKind ;
5+ use rustc_ast:: { BorrowKind , LitKind } ;
66use rustc_errors:: Applicability ;
77use rustc_hir:: def:: { DefKind , Res } ;
88use rustc_hir:: { Arm , BinOpKind , Expr , ExprKind , Guard , MatchSource , Node , Pat , PatKind } ;
99use rustc_lint:: LateContext ;
10+ use rustc_span:: symbol:: Ident ;
1011use rustc_span:: Span ;
1112use std:: ops:: ControlFlow ;
1213
@@ -34,24 +35,37 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
3435 ] ,
3536 MatchSource :: Normal ,
3637 ) = if_expr. kind
38+ && let Some ( binding) = get_pat_binding ( cx, scrutinee, outer_arm)
3739 {
40+ let pat_span = match ( arm. pat . kind , binding. byref_ident ) {
41+ ( PatKind :: Ref ( pat, _) , Some ( _) ) => pat. span ,
42+ ( PatKind :: Ref ( ..) , None ) | ( _, Some ( _) ) => continue ,
43+ _ => arm. pat . span ,
44+ } ;
3845 emit_redundant_guards (
3946 cx,
4047 outer_arm,
4148 if_expr. span ,
42- scrutinee ,
43- arm . pat . span ,
49+ pat_span ,
50+ & binding ,
4451 arm. guard ,
4552 ) ;
4653 }
4754 // `Some(x) if let Some(2) = x`
48- else if let Guard :: IfLet ( let_expr) = guard {
55+ else if let Guard :: IfLet ( let_expr) = guard
56+ && let Some ( binding) = get_pat_binding ( cx, let_expr. init , outer_arm)
57+ {
58+ let pat_span = match ( let_expr. pat . kind , binding. byref_ident ) {
59+ ( PatKind :: Ref ( pat, _) , Some ( _) ) => pat. span ,
60+ ( PatKind :: Ref ( ..) , None ) | ( _, Some ( _) ) => continue ,
61+ _ => let_expr. pat . span ,
62+ } ;
4963 emit_redundant_guards (
5064 cx,
5165 outer_arm,
5266 let_expr. span ,
53- let_expr . init ,
54- let_expr . pat . span ,
67+ pat_span ,
68+ & binding ,
5569 None ,
5670 ) ;
5771 }
@@ -67,43 +81,63 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
6781 //
6882 // This isn't necessary in the other two checks, as they must be a pattern already.
6983 && cx. typeck_results ( ) . expr_ty ( local) == cx. typeck_results ( ) . expr_ty ( pat)
84+ && let Some ( binding) = get_pat_binding ( cx, local, outer_arm)
7085 {
86+ let pat_span = match ( pat. kind , binding. byref_ident ) {
87+ ( ExprKind :: AddrOf ( BorrowKind :: Ref , _, expr) , Some ( _) ) => expr. span ,
88+ ( ExprKind :: AddrOf ( ..) , None ) | ( _, Some ( _) ) => continue ,
89+ _ => pat. span ,
90+ } ;
7191 emit_redundant_guards (
7292 cx,
7393 outer_arm,
7494 if_expr. span ,
75- local ,
76- pat . span ,
95+ pat_span ,
96+ & binding ,
7797 None ,
7898 ) ;
7999 }
80100 }
81101}
82102
83- fn get_pat_binding < ' tcx > ( cx : & LateContext < ' tcx > , guard_expr : & Expr < ' _ > , outer_arm : & Arm < ' tcx > ) -> Option < ( Span , bool ) > {
103+ struct PatBindingInfo {
104+ span : Span ,
105+ byref_ident : Option < Ident > ,
106+ is_field : bool ,
107+ }
108+
109+ fn get_pat_binding < ' tcx > (
110+ cx : & LateContext < ' tcx > ,
111+ guard_expr : & Expr < ' _ > ,
112+ outer_arm : & Arm < ' tcx > ,
113+ ) -> Option < PatBindingInfo > {
84114 if let Some ( local) = path_to_local ( guard_expr) && !is_local_used ( cx, outer_arm. body , local) {
85115 let mut span = None ;
116+ let mut byref_ident = None ;
86117 let mut multiple_bindings = false ;
87118 // `each_binding` gives the `HirId` of the `Pat` itself, not the binding
88119 outer_arm. pat . walk ( |pat| {
89- if let PatKind :: Binding ( _ , hir_id, _ , _) = pat. kind
120+ if let PatKind :: Binding ( bind_annot , hir_id, ident , _) = pat. kind
90121 && hir_id == local
91- && span. replace ( pat. span ) . is_some ( )
92122 {
93- multiple_bindings = true ;
94- return false ;
123+ if matches ! ( bind_annot. 0 , rustc_ast:: ByRef :: Yes ) {
124+ let _ = byref_ident. insert ( ident) ;
125+ }
126+ // the second call of `replace()` returns a `Some(span)`, meaning a multi-binding pattern
127+ if span. replace ( pat. span ) . is_some ( ) {
128+ multiple_bindings = true ;
129+ return false ;
130+ }
95131 }
96-
97132 true
98133 } ) ;
99134
100135 // Ignore bindings from or patterns, like `First(x) | Second(x, _) | Third(x, _, _)`
101136 if !multiple_bindings {
102- return span. map ( |span| {
103- (
104- span,
105- !matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
106- )
137+ return span. map ( |span| PatBindingInfo {
138+ span,
139+ byref_ident,
140+ is_field : matches ! ( cx. tcx. hir( ) . get_parent( local) , Node :: PatField ( _) ) ,
107141 } ) ;
108142 }
109143 }
@@ -115,14 +149,11 @@ fn emit_redundant_guards<'tcx>(
115149 cx : & LateContext < ' tcx > ,
116150 outer_arm : & Arm < ' tcx > ,
117151 guard_span : Span ,
118- local : & Expr < ' _ > ,
119152 pat_span : Span ,
153+ pat_binding : & PatBindingInfo ,
120154 inner_guard : Option < Guard < ' _ > > ,
121155) {
122156 let mut app = Applicability :: MaybeIncorrect ;
123- let Some ( ( pat_binding, can_use_shorthand) ) = get_pat_binding ( cx, local, outer_arm) else {
124- return ;
125- } ;
126157
127158 span_lint_and_then (
128159 cx,
@@ -131,14 +162,21 @@ fn emit_redundant_guards<'tcx>(
131162 "redundant guard" ,
132163 |diag| {
133164 let binding_replacement = snippet_with_applicability ( cx, pat_span, "<binding_repl>" , & mut app) ;
165+ let suggestion_span = match * pat_binding {
166+ PatBindingInfo {
167+ span,
168+ byref_ident : Some ( ident) ,
169+ is_field : true ,
170+ } => ( span, format ! ( "{ident}: {binding_replacement}" ) ) ,
171+ PatBindingInfo {
172+ span, is_field : true , ..
173+ } => ( span. shrink_to_hi ( ) , format ! ( ": {binding_replacement}" ) ) ,
174+ PatBindingInfo { span, .. } => ( span, binding_replacement. into_owned ( ) ) ,
175+ } ;
134176 diag. multipart_suggestion_verbose (
135177 "try" ,
136178 vec ! [
137- if can_use_shorthand {
138- ( pat_binding, binding_replacement. into_owned( ) )
139- } else {
140- ( pat_binding. shrink_to_hi( ) , format!( ": {binding_replacement}" ) )
141- } ,
179+ suggestion_span,
142180 (
143181 guard_span. source_callsite( ) . with_lo( outer_arm. pat. span. hi( ) ) ,
144182 inner_guard. map_or_else( String :: new, |guard| {
0 commit comments