@@ -3,12 +3,13 @@ use crate::{
33 LateContext , LateLintPass , LintContext ,
44} ;
55use rustc_hir as hir;
6- use rustc_middle:: ty;
76use rustc_middle:: ty:: adjustment:: { Adjust , Adjustment } ;
7+ use rustc_middle:: ty:: { self , Ty } ;
88use rustc_session:: lint:: FutureIncompatibilityReason ;
99use rustc_span:: edition:: Edition ;
1010use rustc_span:: symbol:: sym;
1111use rustc_span:: Span ;
12+ use std:: ops:: ControlFlow ;
1213
1314declare_lint ! {
1415 /// The `array_into_iter` lint detects calling `into_iter` on arrays.
@@ -38,16 +39,119 @@ declare_lint! {
3839 reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2021/IntoIterator-for-arrays.html>" ,
3940 } ;
4041}
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+ }
4169
42- #[ derive( Copy , Clone , Default ) ]
43- pub struct ArrayIntoIter {
70+ #[ derive( Copy , Clone ) ]
71+ pub struct CommonIntoIter < F , N > {
4472 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+ }
4599}
46100
47101impl_lint_pass ! ( ArrayIntoIter => [ ARRAY_INTO_ITER ] ) ;
102+ impl_lint_pass ! ( BoxedSliceIntoIter => [ BOXED_SLICE_INTO_ITER ] ) ;
48103
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 > ) > {
51155 // Save the span of expressions in `for _ in expr` syntax,
52156 // so we can give a better suggestion for those later.
53157 if let hir:: ExprKind :: Match ( arg, [ _] , hir:: MatchSource :: ForLoopDesugar ) = & expr. kind {
@@ -65,61 +169,43 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
65169 // We only care about method call expressions.
66170 if let hir:: ExprKind :: MethodCall ( call, receiver_arg, ..) = & expr. kind {
67171 if call. ident . name != sym:: into_iter {
68- return ;
172+ return None ;
69173 }
70174
71175 // Check if the method call actually calls the libcore
72176 // `IntoIterator::into_iter`.
73177 let def_id = cx. typeck_results ( ) . type_dependent_def_id ( expr. hir_id ) . unwrap ( ) ;
74178 match cx. tcx . trait_of_item ( def_id) {
75179 Some ( trait_id) if cx. tcx . is_diagnostic_item ( sym:: IntoIterator , trait_id) => { }
76- _ => return ,
180+ _ => return None ,
77181 } ;
78182
79183 // As this is a method call expression, we have at least one argument.
80184 let receiver_ty = cx. typeck_results ( ) . expr_ty ( receiver_arg) ;
81185 let adjustments = cx. typeck_results ( ) . expr_adjustments ( receiver_arg) ;
82186
83187 let Some ( Adjustment { kind : Adjust :: Borrow ( _) , target } ) = adjustments. last ( ) else {
84- return ;
188+ return None ;
85189 } ;
86190
87191 let types =
88192 std:: iter:: once ( receiver_ty) . chain ( adjustments. iter ( ) . map ( |adj| adj. target ) ) ;
89193
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 ( ( ) ) => ( ) ,
103199 }
104- _ => { }
105200 }
106- }
107-
108- if !found_array {
109- return ;
201+ false
202+ } ;
203+ if !found_it {
204+ return None ;
110205 }
111206
112207 // 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) ;
123209 let sub = if self . for_expr_span == expr. span {
124210 Some ( ArrayIntoIterDiagSub :: RemoveIntoIter {
125211 span : receiver_arg. span . shrink_to_hi ( ) . to ( expr. span . shrink_to_hi ( ) ) ,
@@ -132,11 +218,25 @@ impl<'tcx> LateLintPass<'tcx> for ArrayIntoIter {
132218 } else {
133219 None
134220 } ;
135- cx. emit_span_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) ;
140240 }
141241 }
142242}
0 commit comments