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