@@ -9,6 +9,7 @@ use rustc_lint::{LateContext, LateLintPass};
99use rustc_middle:: ty:: { self , Ty } ;
1010use rustc_session:: { declare_lint_pass, declare_tool_lint} ;
1111use rustc_span:: { sym, Symbol } ;
12+ use std:: iter;
1213
1314declare_clippy_lint ! {
1415 /// ### What it does
@@ -52,7 +53,8 @@ declare_clippy_lint! {
5253declare_clippy_lint ! {
5354 /// ### What it does
5455 /// This is the opposite of the `iter_without_into_iter` lint.
55- /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method.
56+ /// It looks for `IntoIterator for (&|&mut) Type` implementations without an inherent `iter` or `iter_mut` method
57+ /// on the type or on any of the types in its `Deref` chain.
5658 ///
5759 /// ### Why is this bad?
5860 /// It's not bad, but having them is idiomatic and allows the type to be used in iterator chains
@@ -102,7 +104,20 @@ fn is_nameable_in_impl_trait(ty: &rustc_hir::Ty<'_>) -> bool {
102104 !matches ! ( ty. kind, TyKind :: OpaqueDef ( ..) )
103105}
104106
105- fn type_has_inherent_method ( cx : & LateContext < ' _ > , ty : Ty < ' _ > , method_name : Symbol ) -> bool {
107+ /// Returns the deref chain of a type, starting with the type itself.
108+ fn deref_chain < ' cx , ' tcx > ( cx : & ' cx LateContext < ' tcx > , ty : Ty < ' tcx > ) -> impl Iterator < Item = Ty < ' tcx > > + ' cx {
109+ iter:: successors ( Some ( ty) , |& ty| {
110+ if let Some ( deref_did) = cx. tcx . lang_items ( ) . deref_trait ( )
111+ && implements_trait ( cx, ty, deref_did, & [ ] )
112+ {
113+ make_normalized_projection ( cx. tcx , cx. param_env , deref_did, sym:: Target , [ ty] )
114+ } else {
115+ None
116+ }
117+ } )
118+ }
119+
120+ fn adt_has_inherent_method ( cx : & LateContext < ' _ > , ty : Ty < ' _ > , method_name : Symbol ) -> bool {
106121 if let Some ( ty_did) = ty. ty_adt_def ( ) . map ( ty:: AdtDef :: did) {
107122 cx. tcx . inherent_impls ( ty_did) . iter ( ) . any ( |& did| {
108123 cx. tcx
@@ -127,7 +142,11 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
127142 Mutability :: Mut => sym:: iter_mut,
128143 Mutability :: Not => sym:: iter,
129144 }
130- && !type_has_inherent_method ( cx, ty, expected_method_name)
145+ && !deref_chain ( cx, ty)
146+ . any ( |ty| {
147+ // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
148+ ty. peel_refs ( ) . is_slice ( ) || adt_has_inherent_method ( cx, ty, expected_method_name)
149+ } )
131150 && let Some ( iter_assoc_span) = imp. items . iter ( ) . find_map ( |item| {
132151 if item. ident . name == sym ! ( IntoIter ) {
133152 Some ( cx. tcx . hir ( ) . impl_item ( item. id ) . expect_type ( ) . span )
0 commit comments