@@ -3,12 +3,13 @@ use crate::{
3
3
LateContext , LateLintPass , LintContext ,
4
4
} ;
5
5
use rustc_hir as hir;
6
- use rustc_middle:: ty;
7
6
use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
7
+ use rustc_middle:: ty:: { self , Ty } ;
8
8
use rustc_session:: lint:: FutureIncompatibilityReason ;
9
9
use rustc_span:: edition:: Edition ;
10
10
use rustc_span:: symbol:: sym;
11
11
use rustc_span:: Span ;
12
+ use std:: ops:: ControlFlow ;
12
13
13
14
declare_lint ! {
14
15
/// The `array_into_iter` lint detects calling `into_iter` on arrays.
@@ -38,16 +39,119 @@ declare_lint! {
38
39
reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>" ,
39
40
} ;
40
41
}
42
+ declare_lint ! {
43
+ /// The `boxed_slice_into_iter` lint detects calling `into_iter` on boxed slices.
44
+ ///
45
+ /// ### Example
46
+ ///
47
+ /// ```rust,edition2021
48
+ /// # #![allow(unused)]
49
+ /// vec![1, 2, 3].into_boxed_slice().into_iter().for_each(|n| { *n; });
50
+ /// ```
51
+ ///
52
+ /// {{produces}}
53
+ ///
54
+ /// ### Explanation
55
+ ///
56
+ /// Since Rust 1.??, boxed slices implement `IntoIterator`. However, to avoid
57
+ /// breakage, `boxed_slice.into_iter()` in Rust 2015, 2018, and 2021 code will still
58
+ /// behave as `(&boxed_slice).into_iter()`, returning an iterator over
59
+ /// references, just like in Rust 1.?? and earlier.
60
+ /// This only applies to the method call syntax `boxed_slice.into_iter()`, not to
61
+ /// any other syntax such as `for _ in boxed_slice` or `IntoIterator::into_iter(boxed_slice)`.
62
+ pub BOXED_SLICE_INTO_ITER ,
63
+ Warn ,
64
+ "detects calling `into_iter` on boxed slices in Rust 2015, 2018, and 2021" ,
65
+ @future_incompatible = FutureIncompatibleInfo {
66
+ reason: FutureIncompatibilityReason :: EditionSemanticsChange ( Edition :: Edition2024 ) ,
67
+ } ;
68
+ }
41
69
42
- #[ derive( Copy , Clone , Default ) ]
43
- pub struct ArrayIntoIter {
70
+ #[ derive( Copy , Clone ) ]
71
+ pub struct CommonIntoIter < F , N > {
44
72
for_expr_span : Span ,
73
+ filter : F ,
74
+ namer : N ,
75
+ }
76
+
77
+ #[ derive( Copy , Clone ) ]
78
+ pub struct ArrayIntoIter ( CommonIntoIter < ArrayFilter , ArrayNamer > ) ;
79
+ impl Default for ArrayIntoIter {
80
+ fn default ( ) -> ArrayIntoIter {
81
+ ArrayIntoIter ( CommonIntoIter {
82
+ for_expr_span : Span :: default ( ) ,
83
+ filter : array_filter,
84
+ namer : array_namer,
85
+ } )
86
+ }
87
+ }
88
+
89
+ #[ derive( Copy , Clone ) ]
90
+ pub struct BoxedSliceIntoIter ( CommonIntoIter < BoxedSliceFilter , BoxedSliceNamer > ) ;
91
+ impl Default for BoxedSliceIntoIter {
92
+ fn default ( ) -> BoxedSliceIntoIter {
93
+ BoxedSliceIntoIter ( CommonIntoIter {
94
+ for_expr_span : Span :: default ( ) ,
95
+ filter : boxed_slice_filter,
96
+ namer : boxed_slice_namer,
97
+ } )
98
+ }
45
99
}
46
100
47
101
impl_lint_pass ! ( ArrayIntoIter => [ ARRAY_INTO_ITER ] ) ;
102
+ impl_lint_pass ! ( BoxedSliceIntoIter => [ BOXED_SLICE_INTO_ITER ] ) ;
48
103
49
- impl < ' tcx > LateLintPass < ' tcx > for ArrayIntoIter {
50
- fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
104
+ type ArrayFilter = impl Copy + FnMut ( Ty < ' _ > ) -> ControlFlow < bool > ;
105
+ type BoxedSliceFilter = impl Copy + FnMut ( Ty < ' _ > ) -> ControlFlow < bool > ;
106
+ type ArrayNamer = impl Copy + FnMut ( Ty < ' _ > ) -> & ' static str ;
107
+ type BoxedSliceNamer = impl Copy + FnMut ( Ty < ' _ > ) -> & ' static str ;
108
+
109
+ fn array_filter ( ty : Ty < ' _ > ) -> ControlFlow < bool > {
110
+ match ty. kind ( ) {
111
+ // If we run into a &[T; N] or &[T] first, there's nothing to warn about.
112
+ // It'll resolve to the reference version.
113
+ ty:: Ref ( _, inner_ty, _) if inner_ty. is_array ( ) => ControlFlow :: Break ( false ) ,
114
+ ty:: Ref ( _, inner_ty, _) if matches ! ( inner_ty. kind( ) , ty:: Slice ( ..) ) => {
115
+ ControlFlow :: Break ( false )
116
+ }
117
+ // Found an actual array type without matching a &[T; N] first.
118
+ // This is the problematic case.
119
+ ty:: Array ( ..) => ControlFlow :: Break ( true ) ,
120
+ _ => ControlFlow :: Continue ( ( ) ) ,
121
+ }
122
+ }
123
+
124
+ fn boxed_slice_filter ( _ty : Ty < ' _ > ) -> ControlFlow < bool > {
125
+ todo ! ( )
126
+ }
127
+
128
+ fn array_namer ( ty : Ty < ' _ > ) -> & ' static str {
129
+ match * ty. kind ( ) {
130
+ ty:: Ref ( _, inner_ty, _) if inner_ty. is_array ( ) => "[T; N]" ,
131
+ ty:: Ref ( _, inner_ty, _) if matches ! ( inner_ty. kind( ) , ty:: Slice ( ..) ) => "[T]" ,
132
+ // We know the original first argument type is an array type,
133
+ // we know that the first adjustment was an autoref coercion
134
+ // and we know that `IntoIterator` is the trait involved. The
135
+ // array cannot be coerced to something other than a reference
136
+ // to an array or to a slice.
137
+ _ => bug ! ( "array type coerced to something other than array or slice" ) ,
138
+ }
139
+ }
140
+
141
+ fn boxed_slice_namer ( _ty : Ty < ' _ > ) -> & ' static str {
142
+ todo ! ( )
143
+ }
144
+
145
+ impl < F , N > CommonIntoIter < F , N >
146
+ where
147
+ F : FnMut ( Ty < ' _ > ) -> ControlFlow < bool > ,
148
+ N : FnMut ( Ty < ' _ > ) -> & ' static str ,
149
+ {
150
+ fn check_expr < ' tcx > (
151
+ & mut self ,
152
+ cx : & LateContext < ' tcx > ,
153
+ expr : & ' tcx hir:: Expr < ' tcx > ,
154
+ ) -> Option < ( Span , ArrayIntoIterDiag < ' tcx > ) > {
51
155
// Save the span of expressions in `for _ in expr` syntax,
52
156
// so we can give a better suggestion for those later.
53
157
if let hir:: ExprKind :: Match ( arg, [ _] , hir:: MatchSource :: ForLoopDesugar ) = & expr. kind {
@@ -65,61 +169,43 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
65
169
// We only care about method call expressions.
66
170
if let hir:: ExprKind :: MethodCall ( call, receiver_arg, ..) = & expr. kind {
67
171
if call. ident . name != sym:: into_iter {
68
- return ;
172
+ return None ;
69
173
}
70
174
71
175
// Check if the method call actually calls the libcore
72
176
// `IntoIterator::into_iter`.
73
177
let def_id = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) . unwrap ( ) ;
74
178
match cx. tcx . trait_of_item ( def_id) {
75
179
Some ( trait_id) if cx. tcx . is_diagnostic_item ( sym:: IntoIterator , trait_id) => { }
76
- _ => return ,
180
+ _ => return None ,
77
181
} ;
78
182
79
183
// As this is a method call expression, we have at least one argument.
80
184
let receiver_ty = cx. typeck_results ( ) . expr_ty ( receiver_arg) ;
81
185
let adjustments = cx. typeck_results ( ) . expr_adjustments ( receiver_arg) ;
82
186
83
187
let Some ( Adjustment { kind : Adjust :: Borrow ( _) , target } ) = adjustments. last ( ) else {
84
- return ;
188
+ return None ;
85
189
} ;
86
190
87
191
let types =
88
192
std:: iter:: once ( receiver_ty) . chain ( adjustments. iter ( ) . map ( |adj| adj. target ) ) ;
89
193
90
- let mut found_array = false ;
91
-
92
- for ty in types {
93
- match ty. kind ( ) {
94
- // If we run into a &[T; N] or &[T] first, there's nothing to warn about.
95
- // It'll resolve to the reference version.
96
- ty:: Ref ( _, inner_ty, _) if inner_ty. is_array ( ) => return ,
97
- ty:: Ref ( _, inner_ty, _) if matches ! ( inner_ty. kind( ) , ty:: Slice ( ..) ) => return ,
98
- // Found an actual array type without matching a &[T; N] first.
99
- // This is the problematic case.
100
- ty:: Array ( ..) => {
101
- found_array = true ;
102
- break ;
194
+ let found_it = ' outer: {
195
+ for ty in types {
196
+ match ( self . filter ) ( ty) {
197
+ ControlFlow :: Break ( b) => break ' outer b,
198
+ ControlFlow :: Continue ( ( ) ) => ( ) ,
103
199
}
104
- _ => { }
105
200
}
106
- }
107
-
108
- if !found_array {
109
- return ;
201
+ false
202
+ } ;
203
+ if !found_it {
204
+ return None ;
110
205
}
111
206
112
207
// Emit lint diagnostic.
113
- let target = match * target. kind ( ) {
114
- ty:: Ref ( _, inner_ty, _) if inner_ty. is_array ( ) => "[T; N]" ,
115
- ty:: Ref ( _, inner_ty, _) if matches ! ( inner_ty. kind( ) , ty:: Slice ( ..) ) => "[T]" ,
116
- // We know the original first argument type is an array type,
117
- // we know that the first adjustment was an autoref coercion
118
- // and we know that `IntoIterator` is the trait involved. The
119
- // array cannot be coerced to something other than a reference
120
- // to an array or to a slice.
121
- _ => bug ! ( "array type coerced to something other than array or slice" ) ,
122
- } ;
208
+ let target = ( self . namer ) ( * target) ;
123
209
let sub = if self . for_expr_span == expr. span {
124
210
Some ( ArrayIntoIterDiagSub :: RemoveIntoIter {
125
211
span : receiver_arg. span . shrink_to_hi ( ) . to ( expr. span . shrink_to_hi ( ) ) ,
@@ -132,11 +218,25 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
132
218
} else {
133
219
None
134
220
} ;
135
- cx. emit_spanned_lint (
136
- ARRAY_INTO_ITER ,
137
- call. ident . span ,
138
- ArrayIntoIterDiag { target, suggestion : call. ident . span , sub } ,
139
- ) ;
221
+
222
+ Some ( ( call. ident . span , ArrayIntoIterDiag { target, suggestion : call. ident . span , sub } ) )
223
+ } else {
224
+ None
225
+ }
226
+ }
227
+ }
228
+
229
+ impl < ' tcx > LateLintPass < ' tcx > for ArrayIntoIter {
230
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
231
+ if let Some ( ( span, decorator) ) = self . 0 . check_expr ( cx, expr) {
232
+ cx. emit_spanned_lint ( ARRAY_INTO_ITER , span, decorator) ;
233
+ }
234
+ }
235
+ }
236
+ impl < ' tcx > LateLintPass < ' tcx > for BoxedSliceIntoIter {
237
+ fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx hir:: Expr < ' tcx > ) {
238
+ if let Some ( ( span, decorator) ) = self . 0 . check_expr ( cx, expr) {
239
+ cx. emit_spanned_lint ( BOXED_SLICE_INTO_ITER , span, decorator) ;
140
240
}
141
241
}
142
242
}
0 commit comments