1
1
use 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} ;
3
4
use clippy_utils:: sugg:: Sugg ;
4
5
use clippy_utils:: { get_parent_expr, is_refutable, peel_blocks} ;
5
6
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { Arm , Expr , ExprKind , Local , Node , PatKind } ;
7
+ use rustc_hir:: { Arm , Expr , ExprKind , Node , PatKind } ;
7
8
use rustc_lint:: LateContext ;
9
+ use rustc_span:: Span ;
8
10
9
11
use super :: MATCH_SINGLE_BINDING ;
10
12
13
+ enum AssignmentExpr {
14
+ Assign { span : Span , match_span : Span } ,
15
+ Local { span : Span , pat_span : Span } ,
16
+ }
17
+
11
18
#[ 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 > ) {
13
20
if expr. span . from_expansion ( ) || arms. len ( ) != 1 || is_refutable ( cx, arms[ 0 ] . pat ) {
14
21
return ;
15
22
}
@@ -42,61 +49,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
42
49
let mut applicability = Applicability :: MaybeIncorrect ;
43
50
match arms[ 0 ] . pat . kind {
44
51
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,
49
77
format ! (
50
78
"let {} = {};\n {}let {} = {};" ,
51
79
snippet_with_applicability( cx, bind_names, ".." , & mut applicability) ,
52
80
snippet_with_applicability( cx, matched_vars, ".." , & mut applicability) ,
53
81
" " . 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) ,
55
83
snippet_body
56
84
) ,
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
+ } ,
93
97
} ;
98
+
94
99
span_lint_and_sugg (
95
100
cx,
96
101
MATCH_SINGLE_BINDING ,
97
102
target_span,
98
103
"this match could be written as a `let` statement" ,
99
- "consider using `let` statement" ,
104
+ "consider using a `let` statement" ,
100
105
sugg,
101
106
applicability,
102
107
) ;
@@ -110,6 +115,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
110
115
indent,
111
116
snippet_body
112
117
) ;
118
+
113
119
span_lint_and_sugg (
114
120
cx,
115
121
MATCH_SINGLE_BINDING ,
@@ -135,15 +141,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
135
141
}
136
142
}
137
143
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 > {
140
146
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
+ } ;
147
163
}
164
+
148
165
None
149
166
}
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