1
+
2
+
1
3
use clippy_utils:: diagnostics:: span_lint_and_sugg;
2
- use clippy_utils:: source:: { indent_of, snippet_block, snippet_with_applicability} ;
4
+ use clippy_utils:: macros:: HirNode ;
5
+ use clippy_utils:: source:: { indent_of, snippet, snippet_block, snippet_with_applicability} ;
3
6
use clippy_utils:: sugg:: Sugg ;
4
7
use clippy_utils:: { get_parent_expr, is_refutable, peel_blocks} ;
5
8
use rustc_errors:: Applicability ;
6
- use rustc_hir:: { Arm , Expr , ExprKind , Local , Node , PatKind } ;
9
+ use rustc_hir:: { Arm , Expr , ExprKind , Node , PatKind } ;
7
10
use rustc_lint:: LateContext ;
11
+ use rustc_span:: Span ;
8
12
9
13
use super :: MATCH_SINGLE_BINDING ;
10
14
15
+ enum AssignmentExpr {
16
+ Assign { span : Span , match_span : Span } ,
17
+ Local { span : Span , pat_span : Span } ,
18
+ }
19
+
11
20
#[ expect( clippy:: too_many_lines) ]
12
- pub ( crate ) fn check < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' _ > ) {
21
+ pub ( crate ) fn check < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > , arms : & [ Arm < ' _ > ] , expr : & Expr < ' a > ) {
13
22
if expr. span . from_expansion ( ) || arms. len ( ) != 1 || is_refutable ( cx, arms[ 0 ] . pat ) {
14
23
return ;
15
24
}
@@ -42,61 +51,59 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
42
51
let mut applicability = Applicability :: MaybeIncorrect ;
43
52
match arms[ 0 ] . pat . kind {
44
53
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 ,
54
+ let ( target_span, sugg) = match opt_parent_assign_span ( cx, ex) {
55
+ Some ( AssignmentExpr :: Assign { span, match_span } ) => {
56
+ let sugg = sugg_with_curlies (
57
+ cx,
58
+ ( ex, expr) ,
59
+ ( bind_names, matched_vars) ,
60
+ & * snippet_body,
61
+ & mut applicability,
62
+ Some ( span) ,
63
+ ) ;
64
+
65
+ span_lint_and_sugg (
66
+ cx,
67
+ MATCH_SINGLE_BINDING ,
68
+ span. to ( match_span) ,
69
+ "this assignment could be simplified" ,
70
+ "consider removing the `match` expression" ,
71
+ sugg,
72
+ applicability,
73
+ ) ;
74
+
75
+ return ;
76
+ } ,
77
+ Some ( AssignmentExpr :: Local { span, pat_span } ) => (
78
+ span,
49
79
format ! (
50
80
"let {} = {};\n {}let {} = {};" ,
51
81
snippet_with_applicability( cx, bind_names, ".." , & mut applicability) ,
52
82
snippet_with_applicability( cx, matched_vars, ".." , & mut applicability) ,
53
83
" " . repeat( indent_of( cx, expr. span) . unwrap_or( 0 ) ) ,
54
- snippet_with_applicability( cx, parent_let_node . pat . span , ".." , & mut applicability) ,
84
+ snippet_with_applicability( cx, pat_span , ".." , & mut applicability) ,
55
85
snippet_body
56
86
) ,
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
- )
87
+ ) ,
88
+ None => {
89
+ let sugg = sugg_with_curlies (
90
+ cx,
91
+ ( ex, expr) ,
92
+ ( bind_names, matched_vars) ,
93
+ & * snippet_body,
94
+ & mut applicability,
95
+ None ,
96
+ ) ;
97
+ ( expr. span , sugg)
98
+ } ,
93
99
} ;
100
+
94
101
span_lint_and_sugg (
95
102
cx,
96
103
MATCH_SINGLE_BINDING ,
97
104
target_span,
98
105
"this match could be written as a `let` statement" ,
99
- "consider using `let` statement" ,
106
+ "consider using a `let` statement" ,
100
107
sugg,
101
108
applicability,
102
109
) ;
@@ -110,6 +117,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
110
117
indent,
111
118
snippet_body
112
119
) ;
120
+
113
121
span_lint_and_sugg (
114
122
cx,
115
123
MATCH_SINGLE_BINDING ,
@@ -135,15 +143,76 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
135
143
}
136
144
}
137
145
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 > > {
146
+ /// Returns true if the `ex` match expression is in a local (`let`) or assign expression
147
+ fn opt_parent_assign_span < ' a > ( cx : & LateContext < ' a > , ex : & Expr < ' a > ) -> Option < AssignmentExpr > {
140
148
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
- }
149
+
150
+ if let Some ( Node :: Expr ( parent_arm_expr) ) = map. find ( map. get_parent_node ( ex. hir_id ) ) {
151
+ return match map. find ( map. get_parent_node ( parent_arm_expr. hir_id ) ) {
152
+ Some ( Node :: Local ( parent_let_expr) ) => Some ( AssignmentExpr :: Local {
153
+ span : parent_let_expr. span ,
154
+ pat_span : parent_let_expr. pat . span ( ) ,
155
+ } ) ,
156
+ Some ( Node :: Expr ( Expr {
157
+ kind : ExprKind :: Assign ( parent_assign_expr, match_expr, _) ,
158
+ ..
159
+ } ) ) => Some ( AssignmentExpr :: Assign {
160
+ span : parent_assign_expr. span ,
161
+ match_span : match_expr. span ,
162
+ } ) ,
163
+ _ => None ,
164
+ } ;
147
165
}
166
+
148
167
None
149
168
}
169
+
170
+ fn sugg_with_curlies < ' a > (
171
+ cx : & LateContext < ' a > ,
172
+ ( ex, match_expr) : ( & Expr < ' a > , & Expr < ' a > ) ,
173
+ ( bind_names, matched_vars) : ( Span , Span ) ,
174
+ snippet_body : & str ,
175
+ applicability : & mut Applicability ,
176
+ assignment : Option < Span > ,
177
+ ) -> String {
178
+ let mut indent = " " . repeat ( indent_of ( cx, ex. span ) . unwrap_or ( 0 ) ) ;
179
+
180
+ let ( mut cbrace_start, mut cbrace_end) = ( String :: new ( ) , String :: new ( ) ) ;
181
+ if let Some ( parent_expr) = get_parent_expr ( cx, match_expr) {
182
+ if let ExprKind :: Closure ( ..) = parent_expr. kind {
183
+ cbrace_end = format ! ( "\n {}}}" , indent) ;
184
+ // Fix body indent due to the closure
185
+ indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
186
+ cbrace_start = format ! ( "{{\n {}" , indent) ;
187
+ }
188
+ }
189
+
190
+ // If the parent is already an arm, and the body is another match statement,
191
+ // we need curly braces around suggestion
192
+ let parent_node_id = cx. tcx . hir ( ) . get_parent_node ( match_expr. hir_id ) ;
193
+ if let Node :: Arm ( arm) = & cx. tcx . hir ( ) . get ( parent_node_id) {
194
+ if let ExprKind :: Match ( ..) = arm. body . kind {
195
+ cbrace_end = format ! ( "\n {}}}" , indent) ;
196
+ // Fix body indent due to the match
197
+ indent = " " . repeat ( indent_of ( cx, bind_names) . unwrap_or ( 0 ) ) ;
198
+ cbrace_start = format ! ( "{{\n {}" , indent) ;
199
+ }
200
+ }
201
+
202
+ let assignment_str = assignment. map_or_else ( String :: new, |span| {
203
+ let mut s = snippet ( cx, span, ".." ) . to_string ( ) ;
204
+ s. push_str ( " = " ) ;
205
+ s
206
+ } ) ;
207
+
208
+ format ! (
209
+ "{}let {} = {};\n {}{}{}{}" ,
210
+ cbrace_start,
211
+ snippet_with_applicability( cx, bind_names, ".." , applicability) ,
212
+ snippet_with_applicability( cx, matched_vars, ".." , applicability) ,
213
+ indent,
214
+ assignment_str,
215
+ snippet_body,
216
+ cbrace_end
217
+ )
218
+ }
0 commit comments