@@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv;
4
4
use clippy_utils:: source:: snippet;
5
5
use clippy_utils:: visitors:: is_local_used;
6
6
use clippy_utils:: {
7
- SpanlessEq , is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators,
7
+ SpanlessEq , get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt,
8
+ peel_ref_operators,
8
9
} ;
10
+ use rustc_ast:: BorrowKind ;
9
11
use rustc_errors:: MultiSpan ;
10
12
use rustc_hir:: LangItem :: OptionNone ;
11
- use rustc_hir:: { Arm , Expr , HirId , Pat , PatExpr , PatExprKind , PatKind } ;
13
+ use rustc_hir:: { Arm , Expr , ExprKind , HirId , Pat , PatExpr , PatExprKind , PatKind } ;
12
14
use rustc_lint:: LateContext ;
13
15
use rustc_span:: Span ;
14
16
15
17
use super :: { COLLAPSIBLE_MATCH , pat_contains_disallowed_or} ;
16
18
17
- pub ( super ) fn check_match < ' tcx > ( cx : & LateContext < ' tcx > , arms : & ' tcx [ Arm < ' _ > ] , msrv : Msrv ) {
19
+ pub ( super ) fn check_match < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' _ > , arms : & ' tcx [ Arm < ' _ > ] , msrv : Msrv ) {
18
20
if let Some ( els_arm) = arms. iter ( ) . rfind ( |arm| arm_is_wild_like ( cx, arm) ) {
19
21
for arm in arms {
20
- check_arm ( cx, true , arm. pat , arm. body , arm. guard , Some ( els_arm. body ) , msrv) ;
22
+ check_arm ( cx, true , arm. pat , expr , arm. body , arm. guard , Some ( els_arm. body ) , msrv) ;
21
23
}
22
24
}
23
25
}
@@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>(
27
29
pat : & ' tcx Pat < ' _ > ,
28
30
body : & ' tcx Expr < ' _ > ,
29
31
else_expr : Option < & ' tcx Expr < ' _ > > ,
32
+ let_expr : & ' tcx Expr < ' _ > ,
30
33
msrv : Msrv ,
31
34
) {
32
- check_arm ( cx, false , pat, body, None , else_expr, msrv) ;
35
+ check_arm ( cx, false , pat, let_expr , body, None , else_expr, msrv) ;
33
36
}
34
37
38
+ #[ allow( clippy:: too_many_arguments) ]
35
39
fn check_arm < ' tcx > (
36
40
cx : & LateContext < ' tcx > ,
37
41
outer_is_match : bool ,
38
42
outer_pat : & ' tcx Pat < ' tcx > ,
43
+ outer_cond : & ' tcx Expr < ' tcx > ,
39
44
outer_then_body : & ' tcx Expr < ' tcx > ,
40
45
outer_guard : Option < & ' tcx Expr < ' tcx > > ,
41
46
outer_else_body : Option < & ' tcx Expr < ' tcx > > ,
@@ -82,6 +87,9 @@ fn check_arm<'tcx>(
82
87
} ,
83
88
IfLetOrMatch :: Match ( _, arms, ..) => !arms. iter ( ) . any ( |arm| is_local_used ( cx, arm, binding_id) ) ,
84
89
}
90
+ // Check if the inner expression contains any borrows/dereferences
91
+ && let ref_types = get_ref_operators ( cx, inner_scrutinee)
92
+ && let Some ( method) = build_ref_method_chain ( ref_types)
85
93
{
86
94
let msg = format ! (
87
95
"this `{}` can be collapsed into the outer `{}`" ,
@@ -103,6 +111,10 @@ fn check_arm<'tcx>(
103
111
let mut help_span = MultiSpan :: from_spans ( vec ! [ binding_span, inner_then_pat. span] ) ;
104
112
help_span. push_span_label ( binding_span, "replace this binding" ) ;
105
113
help_span. push_span_label ( inner_then_pat. span , format ! ( "with this pattern{replace_msg}" ) ) ;
114
+ if !method. is_empty ( ) {
115
+ let outer_cond_msg = format ! ( "use: `{}{}`" , snippet( cx, outer_cond. span, ".." ) , method) ;
116
+ help_span. push_span_label ( outer_cond. span , outer_cond_msg) ;
117
+ }
106
118
diag. span_help (
107
119
help_span,
108
120
"the outer pattern can be modified to include the inner pattern" ,
@@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
148
160
} ) ;
149
161
( span, is_innermost_parent_pat_struct)
150
162
}
163
+
164
+ /// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`,
165
+ /// `.copied()`) based on reference operators
166
+ fn build_ref_method_chain ( expr : Vec < & Expr < ' _ > > ) -> Option < String > {
167
+ let mut req_method_calls = String :: new ( ) ;
168
+
169
+ for ref_operator in expr {
170
+ match ref_operator. kind {
171
+ ExprKind :: AddrOf ( BorrowKind :: Raw , _, _) => {
172
+ return None ;
173
+ } ,
174
+ ExprKind :: AddrOf ( _, m, _) if m. is_mut ( ) => {
175
+ req_method_calls. push_str ( ".as_mut()" ) ;
176
+ } ,
177
+ ExprKind :: AddrOf ( _, _, _) => {
178
+ req_method_calls. push_str ( ".as_ref()" ) ;
179
+ } ,
180
+ // Deref operator is the only operator that this function should have received
181
+ ExprKind :: Unary ( _, _) => {
182
+ req_method_calls. push_str ( ".copied()" ) ;
183
+ } ,
184
+ _ => ( ) ,
185
+ }
186
+ }
187
+
188
+ Some ( req_method_calls)
189
+ }
0 commit comments