@@ -4,9 +4,8 @@ use clippy_utils::{
4
4
msrvs:: { self , Msrv } ,
5
5
path_to_local,
6
6
} ;
7
- use itertools:: Itertools ;
8
7
use rustc_ast:: LitKind ;
9
- use rustc_hir:: { Expr , ExprKind , Node , Pat } ;
8
+ use rustc_hir:: { Expr , ExprKind , HirId , Node , Pat } ;
10
9
use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
11
10
use rustc_middle:: { lint:: in_external_macro, ty} ;
12
11
use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -17,8 +16,8 @@ declare_clippy_lint! {
17
16
/// Checks for tuple<=>array conversions that are not done with `.into()`.
18
17
///
19
18
/// ### Why is this bad?
20
- /// It's overly complex . `.into()` works for tuples<=>arrays with less than 13 elements and
21
- /// conveys the intent a lot better, while also leaving less room for bugs!
19
+ /// It's unnecessary complexity . `.into()` works for tuples<=>arrays at or below 12 elements and
20
+ /// conveys the intent a lot better, while also leaving less room for hard to spot bugs!
22
21
///
23
22
/// ### Example
24
23
/// ```rust,ignore
@@ -45,26 +44,28 @@ pub struct TupleArrayConversions {
45
44
impl LateLintPass < ' _ > for TupleArrayConversions {
46
45
fn check_expr < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
47
46
if !in_external_macro ( cx. sess ( ) , expr. span ) && self . msrv . meets ( msrvs:: TUPLE_ARRAY_CONVERSIONS ) {
48
- _ = check_array ( cx, expr) || check_tuple ( cx, expr) ;
47
+ match expr. kind {
48
+ ExprKind :: Array ( elements) if ( 1 ..=12 ) . contains ( & elements. len ( ) ) => check_array ( cx, expr, elements) ,
49
+ ExprKind :: Tup ( elements) if ( 1 ..=12 ) . contains ( & elements. len ( ) ) => check_tuple ( cx, expr, elements) ,
50
+ _ => { } ,
51
+ }
49
52
}
50
53
}
51
54
52
55
extract_msrv_attr ! ( LateContext ) ;
53
56
}
54
57
55
- fn check_array < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
56
- let ExprKind :: Array ( elements) = expr. kind else {
57
- return false ;
58
- } ;
59
- if !( 1 ..=12 ) . contains ( & elements. len ( ) ) {
60
- return false ;
61
- }
62
-
63
- if let Some ( locals) = path_to_locals ( cx, & elements. iter ( ) . collect_vec ( ) )
64
- && let [ first, rest @ ..] = & * locals
65
- && let Node :: Pat ( first_pat) = first
66
- && let first_id = parent_pat ( cx, first_pat) . hir_id
67
- && rest. iter ( ) . chain ( once ( first) ) . all ( |local| {
58
+ #[ expect(
59
+ clippy:: blocks_in_if_conditions,
60
+ reason = "not a FP, but this is much easier to understand"
61
+ ) ]
62
+ fn check_array < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , elements : & ' tcx [ Expr < ' tcx > ] ) {
63
+ if should_lint (
64
+ cx,
65
+ elements,
66
+ // This is cursed.
67
+ Some ,
68
+ |( first_id, local) | {
68
69
if let Node :: Pat ( pat) = local
69
70
&& let parent = parent_pat ( cx, pat)
70
71
&& parent. hir_id == first_id
@@ -76,27 +77,18 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
76
77
}
77
78
78
79
false
79
- } )
80
- {
81
- return emit_lint ( cx, expr, ToType :: Array ) ;
82
- }
83
-
84
- if let Some ( elements) = elements
85
- . iter ( )
86
- . enumerate ( )
87
- . map ( |( i, expr) | {
88
- if let ExprKind :: Field ( path, field) = expr. kind && field. as_str ( ) == i. to_string ( ) {
89
- return Some ( path) ;
90
- } ;
91
-
92
- None
93
- } )
94
- . collect :: < Option < Vec < & Expr < ' _ > > > > ( )
95
- && let Some ( locals) = path_to_locals ( cx, & elements)
96
- && let [ first, rest @ ..] = & * locals
97
- && let Node :: Pat ( first_pat) = first
98
- && let first_id = parent_pat ( cx, first_pat) . hir_id
99
- && rest. iter ( ) . chain ( once ( first) ) . all ( |local| {
80
+ } ,
81
+ ) || should_lint (
82
+ cx,
83
+ elements,
84
+ |( i, expr) | {
85
+ if let ExprKind :: Field ( path, field) = expr. kind && field. as_str ( ) == i. to_string ( ) {
86
+ return Some ( ( i, path) ) ;
87
+ } ;
88
+
89
+ None
90
+ } ,
91
+ |( first_id, local) | {
100
92
if let Node :: Pat ( pat) = local
101
93
&& let parent = parent_pat ( cx, pat)
102
94
&& parent. hir_id == first_id
@@ -108,29 +100,20 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
108
100
}
109
101
110
102
false
111
- } )
112
- {
113
- return emit_lint ( cx, expr, ToType :: Array ) ;
103
+ } ,
104
+ ) {
105
+ emit_lint ( cx, expr, ToType :: Array ) ;
114
106
}
115
-
116
- false
117
107
}
118
108
109
+ #[ expect(
110
+ clippy:: blocks_in_if_conditions,
111
+ reason = "not a FP, but this is much easier to understand"
112
+ ) ]
119
113
#[ expect( clippy:: cast_possible_truncation) ]
120
- fn check_tuple < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) -> bool {
121
- let ExprKind :: Tup ( elements) = expr. kind else {
122
- return false ;
123
- } ;
124
- if !( 1 ..=12 ) . contains ( & elements. len ( ) ) {
125
- return false ;
126
- } ;
127
-
128
- if let Some ( locals) = path_to_locals ( cx, & elements. iter ( ) . collect_vec ( ) )
129
- && let [ first, rest @ ..] = & * locals
130
- && let Node :: Pat ( first_pat) = first
131
- && let first_id = parent_pat ( cx, first_pat) . hir_id
132
- && rest. iter ( ) . chain ( once ( first) ) . all ( |local| {
133
- if let Node :: Pat ( pat) = local
114
+ fn check_tuple < ' tcx > ( cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > , elements : & ' tcx [ Expr < ' tcx > ] ) {
115
+ if should_lint ( cx, elements, Some , |( first_id, local) | {
116
+ if let Node :: Pat ( pat) = local
134
117
&& let parent = parent_pat ( cx, pat)
135
118
&& parent. hir_id == first_id
136
119
{
@@ -140,32 +123,22 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
140
123
) ;
141
124
}
142
125
143
- false
144
- } )
145
- {
146
- return emit_lint ( cx, expr, ToType :: Tuple ) ;
147
- }
126
+ false
127
+ } ) || should_lint (
128
+ cx,
129
+ elements,
130
+ |( i, expr) | {
131
+ if let ExprKind :: Index ( path, index) = expr. kind
132
+ && let ExprKind :: Lit ( lit) = index. kind
133
+ && let LitKind :: Int ( val, _) = lit. node
134
+ && val as usize == i
135
+ {
136
+ return Some ( ( i, path) ) ;
137
+ } ;
148
138
149
- if let Some ( elements) = elements
150
- . iter ( )
151
- . enumerate ( )
152
- . map ( |( i, expr) | {
153
- if let ExprKind :: Index ( path, index) = expr. kind
154
- && let ExprKind :: Lit ( lit) = index. kind
155
- && let LitKind :: Int ( val, _) = lit. node
156
- && val as usize == i
157
- {
158
- return Some ( path) ;
159
- } ;
160
-
161
- None
162
- } )
163
- . collect :: < Option < Vec < & Expr < ' _ > > > > ( )
164
- && let Some ( locals) = path_to_locals ( cx, & elements)
165
- && let [ first, rest @ ..] = & * locals
166
- && let Node :: Pat ( first_pat) = first
167
- && let first_id = parent_pat ( cx, first_pat) . hir_id
168
- && rest. iter ( ) . chain ( once ( first) ) . all ( |local| {
139
+ None
140
+ } ,
141
+ |( first_id, local) | {
169
142
if let Node :: Pat ( pat) = local
170
143
&& let parent = parent_pat ( cx, pat)
171
144
&& parent. hir_id == first_id
@@ -177,12 +150,10 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
177
150
}
178
151
179
152
false
180
- } )
181
- {
182
- return emit_lint ( cx, expr, ToType :: Tuple ) ;
153
+ } ,
154
+ ) {
155
+ emit_lint ( cx, expr, ToType :: Tuple ) ;
183
156
}
184
-
185
- false
186
157
}
187
158
188
159
/// Walks up the `Pat` until it's reached the final containing `Pat`.
@@ -198,13 +169,6 @@ fn parent_pat<'tcx>(cx: &LateContext<'tcx>, start: &'tcx Pat<'tcx>) -> &'tcx Pat
198
169
end
199
170
}
200
171
201
- fn path_to_locals < ' tcx > ( cx : & LateContext < ' tcx > , exprs : & [ & ' tcx Expr < ' tcx > ] ) -> Option < Vec < Node < ' tcx > > > {
202
- exprs
203
- . iter ( )
204
- . map ( |element| path_to_local ( element) . and_then ( |local| cx. tcx . hir ( ) . find ( local) ) )
205
- . collect ( )
206
- }
207
-
208
172
#[ derive( Clone , Copy ) ]
209
173
enum ToType {
210
174
Array ,
@@ -243,3 +207,29 @@ fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToTy
243
207
244
208
false
245
209
}
210
+
211
+ fn should_lint < ' tcx > (
212
+ cx : & LateContext < ' tcx > ,
213
+ elements : & ' tcx [ Expr < ' tcx > ] ,
214
+ map : impl FnMut ( ( usize , & ' tcx Expr < ' tcx > ) ) -> Option < ( usize , & Expr < ' _ > ) > ,
215
+ predicate : impl FnMut ( ( HirId , & Node < ' tcx > ) ) -> bool ,
216
+ ) -> bool {
217
+ if let Some ( elements) = elements
218
+ . iter ( )
219
+ . enumerate ( )
220
+ . map ( map)
221
+ . collect :: < Option < Vec < _ > > > ( )
222
+ && let Some ( locals) = elements
223
+ . iter ( )
224
+ . map ( |( _, element) | path_to_local ( element) . and_then ( |local| cx. tcx . hir ( ) . find ( local) ) )
225
+ . collect :: < Option < Vec < _ > > > ( )
226
+ && let [ first, rest @ ..] = & * locals
227
+ && let Node :: Pat ( first_pat) = first
228
+ && let parent = parent_pat ( cx, first_pat) . hir_id
229
+ && rest. iter ( ) . chain ( once ( first) ) . map ( |i| ( parent, i) ) . all ( predicate)
230
+ {
231
+ return true ;
232
+ }
233
+
234
+ false
235
+ }
0 commit comments