@@ -738,6 +738,51 @@ declare_clippy_lint! {
738
738
"using `filter_map` when a more succinct alternative exists"
739
739
}
740
740
741
+ /// **What it does:** Checks for `into_iter` calls on types which should be replaced by `iter` or
742
+ /// `iter_mut`.
743
+ ///
744
+ /// **Why is this bad?** Arrays and `PathBuf` do not yet have an `into_iter` method which move out
745
+ /// their content into an iterator. Calling `into_iter` instead just forwards to `iter` or
746
+ /// `iter_mut` due to auto-referencing, of which only yield references. Furthermore, when the
747
+ /// standard library actually [implements the `into_iter` method][25725] which moves the content out
748
+ /// of the array, the original use of `into_iter` got inferred with the wrong type and the code will
749
+ /// be broken.
750
+ ///
751
+ /// **Known problems:** None
752
+ ///
753
+ /// **Example:**
754
+ ///
755
+ /// ```rust
756
+ /// let _ = [1, 2, 3].into_iter().map(|x| *x).collect::<Vec<u32>>();
757
+ /// ```
758
+ ///
759
+ /// [25725]: https://github.com/rust-lang/rust/issues/25725
760
+ declare_clippy_lint ! {
761
+ pub INTO_ITER_ON_ARRAY ,
762
+ correctness,
763
+ "using `.into_iter()` on an array"
764
+ }
765
+
766
+ /// **What it does:** Checks for `into_iter` calls on references which should be replaced by `iter`
767
+ /// or `iter_mut`.
768
+ ///
769
+ /// **Why is this bad?** Readability. Calling `into_iter` on a reference will not move out its
770
+ /// content into the resulting iterator, which is confusing. It is better just call `iter` or
771
+ /// `iter_mut` directly.
772
+ ///
773
+ /// **Known problems:** None
774
+ ///
775
+ /// **Example:**
776
+ ///
777
+ /// ```rust
778
+ /// let _ = (&vec![3, 4, 5]).into_iter();
779
+ /// ```
780
+ declare_clippy_lint ! {
781
+ pub INTO_ITER_ON_REF ,
782
+ style,
783
+ "using `.into_iter()` on a reference"
784
+ }
785
+
741
786
impl LintPass for Pass {
742
787
fn get_lints ( & self ) -> LintArray {
743
788
lint_array ! (
@@ -772,7 +817,9 @@ impl LintPass for Pass {
772
817
ITER_CLONED_COLLECT ,
773
818
USELESS_ASREF ,
774
819
UNNECESSARY_FOLD ,
775
- UNNECESSARY_FILTER_MAP
820
+ UNNECESSARY_FILTER_MAP ,
821
+ INTO_ITER_ON_ARRAY ,
822
+ INTO_ITER_ON_REF ,
776
823
)
777
824
}
778
825
}
@@ -857,6 +904,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
857
904
lint_single_char_pattern ( cx, expr, & args[ pos] ) ;
858
905
}
859
906
} ,
907
+ ty:: Ref ( ..) if method_call. ident . name == "into_iter" => {
908
+ lint_into_iter ( cx, expr, self_ty, * method_span) ;
909
+ } ,
860
910
_ => ( ) ,
861
911
}
862
912
} ,
@@ -2093,6 +2143,71 @@ fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr, call_name: &str, as_re
2093
2143
}
2094
2144
}
2095
2145
2146
+ fn ty_has_iter_method ( cx : & LateContext < ' _ , ' _ > , self_ref_ty : ty:: Ty < ' _ > ) -> Option < ( & ' static Lint , & ' static str , & ' static str ) > {
2147
+ let ( self_ty, mutbl) = match self_ref_ty. sty {
2148
+ ty:: TyKind :: Ref ( _, self_ty, mutbl) => ( self_ty, mutbl) ,
2149
+ _ => unreachable ! ( ) ,
2150
+ } ;
2151
+ let method_name = match mutbl {
2152
+ hir:: MutImmutable => "iter" ,
2153
+ hir:: MutMutable => "iter_mut" ,
2154
+ } ;
2155
+
2156
+ let def_id = match self_ty. sty {
2157
+ ty:: TyKind :: Array ( ..) => return Some ( ( INTO_ITER_ON_ARRAY , "array" , method_name) ) ,
2158
+ ty:: TyKind :: Slice ( ..) => return Some ( ( INTO_ITER_ON_REF , "slice" , method_name) ) ,
2159
+ ty:: Adt ( adt, _) => adt. did ,
2160
+ _ => return None ,
2161
+ } ;
2162
+
2163
+ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
2164
+ // exists and has the desired signature. Unfortunately FnCtxt is not exported
2165
+ // so we can't use its `lookup_method` method.
2166
+ static INTO_ITER_COLLECTIONS : [ ( & Lint , & [ & str ] ) ; 13 ] = [
2167
+ ( INTO_ITER_ON_REF , & paths:: VEC ) ,
2168
+ ( INTO_ITER_ON_REF , & paths:: OPTION ) ,
2169
+ ( INTO_ITER_ON_REF , & paths:: RESULT ) ,
2170
+ ( INTO_ITER_ON_REF , & paths:: BTREESET ) ,
2171
+ ( INTO_ITER_ON_REF , & paths:: BTREEMAP ) ,
2172
+ ( INTO_ITER_ON_REF , & paths:: VEC_DEQUE ) ,
2173
+ ( INTO_ITER_ON_REF , & paths:: LINKED_LIST ) ,
2174
+ ( INTO_ITER_ON_REF , & paths:: BINARY_HEAP ) ,
2175
+ ( INTO_ITER_ON_REF , & paths:: HASHSET ) ,
2176
+ ( INTO_ITER_ON_REF , & paths:: HASHMAP ) ,
2177
+ ( INTO_ITER_ON_ARRAY , & [ "std" , "path" , "PathBuf" ] ) ,
2178
+ ( INTO_ITER_ON_REF , & [ "std" , "path" , "Path" ] ) ,
2179
+ ( INTO_ITER_ON_REF , & [ "std" , "sync" , "mpsc" , "Receiver" ] ) ,
2180
+ ] ;
2181
+
2182
+ for ( lint, path) in & INTO_ITER_COLLECTIONS {
2183
+ if match_def_path ( cx. tcx , def_id, path) {
2184
+ return Some ( ( lint, path. last ( ) . unwrap ( ) , method_name) )
2185
+ }
2186
+ }
2187
+ None
2188
+ }
2189
+
2190
+ fn lint_into_iter ( cx : & LateContext < ' _ , ' _ > , expr : & hir:: Expr , self_ref_ty : ty:: Ty < ' _ > , method_span : Span ) {
2191
+ if !match_trait_method ( cx, expr, & paths:: INTO_ITERATOR ) {
2192
+ return ;
2193
+ }
2194
+ if let Some ( ( lint, kind, method_name) ) = ty_has_iter_method ( cx, self_ref_ty) {
2195
+ span_lint_and_sugg (
2196
+ cx,
2197
+ lint,
2198
+ method_span,
2199
+ & format ! (
2200
+ "this .into_iter() call is equivalent to .{}() and will not move the {}" ,
2201
+ method_name,
2202
+ kind,
2203
+ ) ,
2204
+ "call directly" ,
2205
+ method_name. to_owned ( ) ,
2206
+ ) ;
2207
+ }
2208
+ }
2209
+
2210
+
2096
2211
/// Given a `Result<T, E>` type, return its error type (`E`).
2097
2212
fn get_error_type < ' a > ( cx : & LateContext < ' _ , ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
2098
2213
if let ty:: Adt ( _, substs) = ty. sty {
0 commit comments