@@ -745,6 +745,51 @@ declare_clippy_lint! {
745745 "using `filter_map` when a more succinct alternative exists"
746746}
747747
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+
748793impl LintPass for Pass {
749794 fn get_lints ( & self ) -> LintArray {
750795 lint_array ! (
@@ -779,7 +824,9 @@ impl LintPass for Pass {
779824 ITER_CLONED_COLLECT ,
780825 USELESS_ASREF ,
781826 UNNECESSARY_FOLD ,
782- UNNECESSARY_FILTER_MAP
827+ UNNECESSARY_FILTER_MAP ,
828+ INTO_ITER_ON_ARRAY ,
829+ INTO_ITER_ON_REF ,
783830 )
784831 }
785832}
@@ -843,6 +890,9 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Pass {
843890 lint_single_char_pattern ( cx, expr, & args[ pos] ) ;
844891 }
845892 } ,
893+ ty:: Ref ( ..) if method_call. ident . name == "into_iter" => {
894+ lint_into_iter ( cx, expr, self_ty, * method_span) ;
895+ } ,
846896 _ => ( ) ,
847897 }
848898 } ,
@@ -2084,6 +2134,71 @@ fn lint_asref(cx: &LateContext<'_, '_>, expr: &hir::Expr, call_name: &str, as_re
20842134 }
20852135}
20862136
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+
20872202/// Given a `Result<T, E>` type, return its error type (`E`).
20882203fn get_error_type < ' a > ( cx : & LateContext < ' _ , ' _ > , ty : Ty < ' a > ) -> Option < Ty < ' a > > {
20892204 if let ty:: Adt ( _, substs) = ty. sty {
0 commit comments