@@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv;
44use clippy_utils:: source:: snippet;
55use clippy_utils:: visitors:: is_local_used;
66use 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,
89} ;
10+ use rustc_ast:: BorrowKind ;
911use rustc_errors:: MultiSpan ;
1012use 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 } ;
1214use rustc_lint:: LateContext ;
1315use rustc_span:: Span ;
1416
1517use super :: { COLLAPSIBLE_MATCH , pat_contains_disallowed_or} ;
1618
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 ) {
1820 if let Some ( els_arm) = arms. iter ( ) . rfind ( |arm| arm_is_wild_like ( cx, arm) ) {
1921 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) ;
2123 }
2224 }
2325}
@@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>(
2729 pat : & ' tcx Pat < ' _ > ,
2830 body : & ' tcx Expr < ' _ > ,
2931 else_expr : Option < & ' tcx Expr < ' _ > > ,
32+ let_expr : & ' tcx Expr < ' _ > ,
3033 msrv : Msrv ,
3134) {
32- check_arm ( cx, false , pat, body, None , else_expr, msrv) ;
35+ check_arm ( cx, false , pat, let_expr , body, None , else_expr, msrv) ;
3336}
3437
38+ #[ allow( clippy:: too_many_arguments) ]
3539fn check_arm < ' tcx > (
3640 cx : & LateContext < ' tcx > ,
3741 outer_is_match : bool ,
3842 outer_pat : & ' tcx Pat < ' tcx > ,
43+ outer_cond : & ' tcx Expr < ' tcx > ,
3944 outer_then_body : & ' tcx Expr < ' tcx > ,
4045 outer_guard : Option < & ' tcx Expr < ' tcx > > ,
4146 outer_else_body : Option < & ' tcx Expr < ' tcx > > ,
@@ -82,6 +87,9 @@ fn check_arm<'tcx>(
8287 } ,
8388 IfLetOrMatch :: Match ( _, arms, ..) => !arms. iter ( ) . any ( |arm| is_local_used ( cx, arm, binding_id) ) ,
8489 }
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)
8593 {
8694 let msg = format ! (
8795 "this `{}` can be collapsed into the outer `{}`" ,
@@ -103,6 +111,10 @@ fn check_arm<'tcx>(
103111 let mut help_span = MultiSpan :: from_spans ( vec ! [ binding_span, inner_then_pat. span] ) ;
104112 help_span. push_span_label ( binding_span, "replace this binding" ) ;
105113 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+ }
106118 diag. span_help (
107119 help_span,
108120 "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
148160 } ) ;
149161 ( span, is_innermost_parent_pat_struct)
150162}
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