@@ -575,7 +575,7 @@ declare_clippy_lint! {
575
575
/// temporary placeholder for dealing with the `Option` type, then this does
576
576
/// not mitigate the need for error handling. If there is a chance that `.get()`
577
577
/// will be `None` in your program, then it is advisable that the `None` case
578
- /// is handled in a future refactor instead of using `.unwrap()` or the Index
578
+ /// is handled in a future refactor instead of using `.unwrap()` or the Index
579
579
/// trait.
580
580
///
581
581
/// **Example:**
@@ -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. Auto-referencing resolves the `into_iter` call to its reference
753
+ /// instead, like `<&[T; N] as IntoIterator>::into_iter`, which just iterates over item references
754
+ /// like calling `iter` would. Furthermore, when the standard library actually
755
+ /// [implements the `into_iter` method][25725] which moves the content out of the array, the
756
+ /// original use of `into_iter` got inferred with the wrong type and the code will 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
+ // FIXME: instead of this hard-coded list, we should check if `<adt>::iter`
2139
+ // exists and has the desired signature. Unfortunately FnCtxt is not exported
2140
+ // so we can't use its `lookup_method` method.
2141
+ static INTO_ITER_COLLECTIONS : [ ( & Lint , & [ & str ] ) ; 13 ] = [
2142
+ ( INTO_ITER_ON_REF , & paths:: VEC ) ,
2143
+ ( INTO_ITER_ON_REF , & paths:: OPTION ) ,
2144
+ ( INTO_ITER_ON_REF , & paths:: RESULT ) ,
2145
+ ( INTO_ITER_ON_REF , & paths:: BTREESET ) ,
2146
+ ( INTO_ITER_ON_REF , & paths:: BTREEMAP ) ,
2147
+ ( INTO_ITER_ON_REF , & paths:: VEC_DEQUE ) ,
2148
+ ( INTO_ITER_ON_REF , & paths:: LINKED_LIST ) ,
2149
+ ( INTO_ITER_ON_REF , & paths:: BINARY_HEAP ) ,
2150
+ ( INTO_ITER_ON_REF , & paths:: HASHSET ) ,
2151
+ ( INTO_ITER_ON_REF , & paths:: HASHMAP ) ,
2152
+ ( INTO_ITER_ON_ARRAY , & [ "std" , "path" , "PathBuf" ] ) ,
2153
+ ( INTO_ITER_ON_REF , & [ "std" , "path" , "Path" ] ) ,
2154
+ ( INTO_ITER_ON_REF , & [ "std" , "sync" , "mpsc" , "Receiver" ] ) ,
2155
+ ] ;
2156
+
2157
+ let ( self_ty, mutbl) = match self_ref_ty. sty {
2158
+ ty:: TyKind :: Ref ( _, self_ty, mutbl) => ( self_ty, mutbl) ,
2159
+ _ => unreachable ! ( ) ,
2160
+ } ;
2161
+ let method_name = match mutbl {
2162
+ hir:: MutImmutable => "iter" ,
2163
+ hir:: MutMutable => "iter_mut" ,
2164
+ } ;
2165
+
2166
+ let def_id = match self_ty. sty {
2167
+ ty:: TyKind :: Array ( ..) => return Some ( ( INTO_ITER_ON_ARRAY , "array" , method_name) ) ,
2168
+ ty:: TyKind :: Slice ( ..) => return Some ( ( INTO_ITER_ON_REF , "slice" , method_name) ) ,
2169
+ ty:: Adt ( adt, _) => adt. did ,
2170
+ _ => return None ,
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