@@ -4,9 +4,8 @@ use clippy_utils::{
44 msrvs:: { self , Msrv } ,
55 path_to_local,
66} ;
7- use itertools:: Itertools ;
87use rustc_ast:: LitKind ;
9- use rustc_hir:: { Expr , ExprKind , Node , Pat } ;
8+ use rustc_hir:: { Expr , ExprKind , HirId , Node , Pat } ;
109use rustc_lint:: { LateContext , LateLintPass , LintContext } ;
1110use rustc_middle:: { lint:: in_external_macro, ty} ;
1211use rustc_session:: { declare_tool_lint, impl_lint_pass} ;
@@ -17,8 +16,8 @@ declare_clippy_lint! {
1716 /// Checks for tuple<=>array conversions that are not done with `.into()`.
1817 ///
1918 /// ### 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!
2221 ///
2322 /// ### Example
2423 /// ```rust,ignore
@@ -45,26 +44,28 @@ pub struct TupleArrayConversions {
4544impl LateLintPass < ' _ > for TupleArrayConversions {
4645 fn check_expr < ' tcx > ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
4746 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+ }
4952 }
5053 }
5154
5255 extract_msrv_attr ! ( LateContext ) ;
5356}
5457
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) | {
6869 if let Node :: Pat ( pat) = local
6970 && let parent = parent_pat ( cx, pat)
7071 && parent. hir_id == first_id
@@ -76,27 +77,18 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
7677 }
7778
7879 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) | {
10092 if let Node :: Pat ( pat) = local
10193 && let parent = parent_pat ( cx, pat)
10294 && parent. hir_id == first_id
@@ -108,29 +100,20 @@ fn check_array<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
108100 }
109101
110102 false
111- } )
112- {
113- return emit_lint ( cx, expr, ToType :: Array ) ;
103+ } ,
104+ ) {
105+ emit_lint ( cx, expr, ToType :: Array ) ;
114106 }
115-
116- false
117107}
118108
109+ #[ expect(
110+ clippy:: blocks_in_if_conditions,
111+ reason = "not a FP, but this is much easier to understand"
112+ ) ]
119113#[ 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
134117 && let parent = parent_pat ( cx, pat)
135118 && parent. hir_id == first_id
136119 {
@@ -140,32 +123,22 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
140123 ) ;
141124 }
142125
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+ } ;
148138
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) | {
169142 if let Node :: Pat ( pat) = local
170143 && let parent = parent_pat ( cx, pat)
171144 && parent. hir_id == first_id
@@ -177,12 +150,10 @@ fn check_tuple<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
177150 }
178151
179152 false
180- } )
181- {
182- return emit_lint ( cx, expr, ToType :: Tuple ) ;
153+ } ,
154+ ) {
155+ emit_lint ( cx, expr, ToType :: Tuple ) ;
183156 }
184-
185- false
186157}
187158
188159/// 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
198169 end
199170}
200171
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-
208172#[ derive( Clone , Copy ) ]
209173enum ToType {
210174 Array ,
@@ -243,3 +207,29 @@ fn emit_lint<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, to_type: ToTy
243207
244208 false
245209}
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