11use clippy_utils:: diagnostics:: span_lint_and_sugg;
2- use clippy_utils:: source:: { indent_of, snippet_block, snippet_with_applicability} ;
2+ use clippy_utils:: macros:: HirNode ;
3+ use clippy_utils:: source:: { indent_of, snippet, snippet_block, snippet_with_applicability} ;
34use clippy_utils:: sugg:: Sugg ;
45use clippy_utils:: { get_parent_expr, is_refutable, peel_blocks} ;
56use rustc_errors:: Applicability ;
6- use rustc_hir:: { Arm , Expr , ExprKind , Local , Node , PatKind } ;
7+ use rustc_hir:: { Arm , Expr , ExprKind , Node , PatKind } ;
78use rustc_lint:: LateContext ;
9+ use rustc_span:: Span ;
810
911use super :: MATCH_SINGLE_BINDING ;
1012
13+ enum AssignmentExpr {
14+ Assign { span : Span , match_span : Span } ,
15+ Local { span : Span , pat_span : Span } ,
16+ }
17+
1118#[ expect( clippy:: too_many_lines) ]
12- pub ( crate ) fn check < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
19+ pub ( crate ) fn check < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' a > ) {
1320 if expr. span . from_expansion ( ) || arms. len ( ) != 1 || is_refutable ( cx, arms[ 0 ] . pat ) {
1421 return ;
1522 }
@@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
4249 let mut applicability = Applicability :: MaybeIncorrect ;
4350 match arms[ 0 ] . pat . kind {
4451 PatKind :: Binding ( ..) | PatKind :: Tuple ( _, _) | PatKind :: Struct ( ..) => {
45- // If this match is in a local (`let`) stmt
46- let ( target_span, sugg) = if let Some ( parent_let_node) = opt_parent_let ( cx, ex) {
47- (
48- parent_let_node. span ,
52+ let ( target_span, sugg) = match opt_parent_assign_span ( cx, ex) {
53+ Some ( AssignmentExpr :: Assign { span, match_span } ) => {
54+ let sugg = sugg_with_curlies (
55+ cx,
56+ ( ex, expr) ,
57+ ( bind_names, matched_vars) ,
58+ & * snippet_body,
59+ & mut applicability,
60+ Some ( span) ,
61+ ) ;
62+
63+ span_lint_and_sugg (
64+ cx,
65+ MATCH_SINGLE_BINDING ,
66+ span. to ( match_span) ,
67+ "this assignment could be simplified" ,
68+ "consider removing the `match` expression" ,
69+ sugg,
70+ applicability,
71+ ) ;
72+
73+ return ;
74+ } ,
75+ Some ( AssignmentExpr :: Local { span, pat_span } ) => (
76+ span,
4977 format ! (
5078 "let {} = {};\n {}let {} = {};" ,
5179 snippet_with_applicability( cx, bind_names, ".." , & mut applicability) ,
5280 snippet_with_applicability( cx, matched_vars, ".." , & mut applicability) ,
5381 " " . repeat( indent_of( cx, expr. span) . unwrap_or( 0 ) ) ,
54- snippet_with_applicability( cx, parent_let_node . pat . span , ".." , & mut applicability) ,
82+ snippet_with_applicability( cx, pat_span , ".." , & mut applicability) ,
5583 snippet_body
5684 ) ,
57- )
58- } else {
59- // If we are in closure, we need curly braces around suggestion
60- let mut indent = " " . repeat ( indent_of ( cx, ex. span ) . unwrap_or ( 0 ) ) ;
61- let ( mut cbrace_start, mut cbrace_end) = ( "" . to_string ( ) , "" . to_string ( ) ) ;
62- if let Some ( parent_expr) = get_parent_expr ( cx, expr) {
63- if let ExprKind :: Closure ( ..) = parent_expr. kind {
64- cbrace_end = format ! ( "\n {}}}" , indent) ;
65- // Fix body indent due to the closure
66- indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
67- cbrace_start = format ! ( "{{\n {}" , indent) ;
68- }
69- }
70- // If the parent is already an arm, and the body is another match statement,
71- // we need curly braces around suggestion
72- let parent_node_id = cx. tcx . hir ( ) . get_parent_node ( expr. hir_id ) ;
73- if let Node :: Arm ( arm) = & cx. tcx . hir ( ) . get ( parent_node_id) {
74- if let ExprKind :: Match ( ..) = arm. body . kind {
75- cbrace_end = format ! ( "\n {}}}" , indent) ;
76- // Fix body indent due to the match
77- indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
78- cbrace_start = format ! ( "{{\n {}" , indent) ;
79- }
80- }
81- (
82- expr. span ,
83- format ! (
84- "{}let {} = {};\n {}{}{}" ,
85- cbrace_start,
86- snippet_with_applicability( cx, bind_names, ".." , & mut applicability) ,
87- snippet_with_applicability( cx, matched_vars, ".." , & mut applicability) ,
88- indent,
89- snippet_body,
90- cbrace_end
91- ) ,
92- )
85+ ) ,
86+ None => {
87+ let sugg = sugg_with_curlies (
88+ cx,
89+ ( ex, expr) ,
90+ ( bind_names, matched_vars) ,
91+ & * snippet_body,
92+ & mut applicability,
93+ None ,
94+ ) ;
95+ ( expr. span , sugg)
96+ } ,
9397 } ;
98+
9499 span_lint_and_sugg (
95100 cx,
96101 MATCH_SINGLE_BINDING ,
97102 target_span,
98103 "this match could be written as a `let` statement" ,
99- "consider using `let` statement" ,
104+ "consider using a `let` statement" ,
100105 sugg,
101106 applicability,
102107 ) ;
@@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
110115 indent,
111116 snippet_body
112117 ) ;
118+
113119 span_lint_and_sugg (
114120 cx,
115121 MATCH_SINGLE_BINDING ,
@@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
135141 }
136142}
137143
138- /// Returns true if the `ex` match expression is in a local (`let`) statement
139- fn opt_parent_let < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > ) -> Option < & ' a Local < ' a > > {
144+ /// Returns true if the `ex` match expression is in a local (`let`) or assign expression
145+ fn opt_parent_assign_span < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > ) -> Option < AssignmentExpr > {
140146 let map = & cx. tcx . hir ( ) ;
141- if_chain ! {
142- if let Some ( Node :: Expr ( parent_arm_expr) ) = map. find( map. get_parent_node( ex. hir_id) ) ;
143- if let Some ( Node :: Local ( parent_let_expr) ) = map. find( map. get_parent_node( parent_arm_expr. hir_id) ) ;
144- then {
145- return Some ( parent_let_expr) ;
146- }
147+
148+ if let Some ( Node :: Expr ( parent_arm_expr) ) = map. find ( map. get_parent_node ( ex. hir_id ) ) {
149+ return match map. find ( map. get_parent_node ( parent_arm_expr. hir_id ) ) {
150+ Some ( Node :: Local ( parent_let_expr) ) => Some ( AssignmentExpr :: Local {
151+ span : parent_let_expr. span ,
152+ pat_span : parent_let_expr. pat . span ( ) ,
153+ } ) ,
154+ Some ( Node :: Expr ( Expr {
155+ kind : ExprKind :: Assign ( parent_assign_expr, match_expr, _) ,
156+ ..
157+ } ) ) => Some ( AssignmentExpr :: Assign {
158+ span : parent_assign_expr. span ,
159+ match_span : match_expr. span ,
160+ } ) ,
161+ _ => None ,
162+ } ;
147163 }
164+
148165 None
149166}
167+
168+ fn sugg_with_curlies < ' a > (
169+ cx : & LateContext < ' a > ,
170+ ( ex, match_expr) : ( & Expr < ' a > , & Expr < ' a > ) ,
171+ ( bind_names, matched_vars) : ( Span , Span ) ,
172+ snippet_body : & str ,
173+ applicability : & mut Applicability ,
174+ assignment : Option < Span > ,
175+ ) -> String {
176+ let mut indent = " " . repeat ( indent_of ( cx, ex. span ) . unwrap_or ( 0 ) ) ;
177+
178+ let ( mut cbrace_start, mut cbrace_end) = ( String :: new ( ) , String :: new ( ) ) ;
179+ if let Some ( parent_expr) = get_parent_expr ( cx, match_expr) {
180+ if let ExprKind :: Closure ( ..) = parent_expr. kind {
181+ cbrace_end = format ! ( "\n {}}}" , indent) ;
182+ // Fix body indent due to the closure
183+ indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
184+ cbrace_start = format ! ( "{{\n {}" , indent) ;
185+ }
186+ }
187+
188+ // If the parent is already an arm, and the body is another match statement,
189+ // we need curly braces around suggestion
190+ let parent_node_id = cx. tcx . hir ( ) . get_parent_node ( match_expr. hir_id ) ;
191+ if let Node :: Arm ( arm) = & cx. tcx . hir ( ) . get ( parent_node_id) {
192+ if let ExprKind :: Match ( ..) = arm. body . kind {
193+ cbrace_end = format ! ( "\n {}}}" , indent) ;
194+ // Fix body indent due to the match
195+ indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
196+ cbrace_start = format ! ( "{{\n {}" , indent) ;
197+ }
198+ }
199+
200+ let assignment_str = assignment. map_or_else ( String :: new, |span| {
201+ let mut s = snippet ( cx, span, ".." ) . to_string ( ) ;
202+ s. push_str ( " = " ) ;
203+ s
204+ } ) ;
205+
206+ format ! (
207+ "{}let {} = {};\n {}{}{}{}" ,
208+ cbrace_start,
209+ snippet_with_applicability( cx, bind_names, ".." , applicability) ,
210+ snippet_with_applicability( cx, matched_vars, ".." , applicability) ,
211+ indent,
212+ assignment_str,
213+ snippet_body,
214+ cbrace_end
215+ )
216+ }
0 commit comments