@@ -109,6 +109,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
109109 self . autoderef ( span, ty) . any ( |( ty, _) | matches ! ( ty. kind( ) , ty:: Slice ( ..) | ty:: Array ( ..) ) )
110110 }
111111
112+ fn impl_into_iterator_should_be_iterator (
113+ & self ,
114+ ty : Ty < ' tcx > ,
115+ span : Span ,
116+ unsatisfied_predicates : & Vec < (
117+ ty:: Predicate < ' _ > ,
118+ Option < ty:: Predicate < ' _ > > ,
119+ Option < ObligationCause < ' _ > > ,
120+ ) > ,
121+ ) -> bool {
122+ fn predicate_bounds_generic_param < ' tcx > (
123+ predicate : ty:: Predicate < ' _ > ,
124+ generics : & ' tcx ty:: Generics ,
125+ generic_param : & ty:: GenericParamDef ,
126+ tcx : TyCtxt < ' tcx > ,
127+ ) -> bool {
128+ if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( trait_pred) ) =
129+ predicate. kind ( ) . as_ref ( ) . skip_binder ( )
130+ {
131+ let ty:: TraitPredicate { trait_ref : ty:: TraitRef { args, .. } , .. } = trait_pred;
132+ if args. is_empty ( ) {
133+ return false ;
134+ }
135+ let Some ( arg_ty) = args[ 0 ] . as_type ( ) else {
136+ return false ;
137+ } ;
138+ let ty:: Param ( param) = arg_ty. kind ( ) else {
139+ return false ;
140+ } ;
141+ // Is `generic_param` the same as the arg for this trait predicate?
142+ generic_param. index == generics. type_param ( & param, tcx) . index
143+ } else {
144+ false
145+ }
146+ }
147+
148+ fn is_iterator_predicate ( predicate : ty:: Predicate < ' _ > , tcx : TyCtxt < ' _ > ) -> bool {
149+ if let ty:: PredicateKind :: Clause ( ty:: ClauseKind :: Trait ( trait_pred) ) =
150+ predicate. kind ( ) . as_ref ( ) . skip_binder ( )
151+ {
152+ tcx. is_diagnostic_item ( sym:: Iterator , trait_pred. trait_ref . def_id )
153+ } else {
154+ false
155+ }
156+ }
157+
158+ // Does the `ty` implement `IntoIterator`?
159+ let Some ( into_iterator_trait) = self . tcx . get_diagnostic_item ( sym:: IntoIterator ) else {
160+ return false ;
161+ } ;
162+ let trait_ref = ty:: TraitRef :: new ( self . tcx , into_iterator_trait, [ ty] ) ;
163+ let cause = ObligationCause :: new ( span, self . body_id , ObligationCauseCode :: MiscObligation ) ;
164+ let obligation = Obligation :: new ( self . tcx , cause, self . param_env , trait_ref) ;
165+ if !self . predicate_must_hold_modulo_regions ( & obligation) {
166+ return false ;
167+ }
168+
169+ match ty. kind ( ) {
170+ ty:: Param ( param) => {
171+ let generics = self . tcx . generics_of ( self . body_id ) ;
172+ let generic_param = generics. type_param ( & param, self . tcx ) ;
173+ for unsatisfied in unsatisfied_predicates. iter ( ) {
174+ // The parameter implements `IntoIterator`
175+ // but it has called a method that requires it to implement `Iterator`
176+ if predicate_bounds_generic_param (
177+ unsatisfied. 0 ,
178+ generics,
179+ generic_param,
180+ self . tcx ,
181+ ) && is_iterator_predicate ( unsatisfied. 0 , self . tcx )
182+ {
183+ return true ;
184+ }
185+ }
186+ }
187+ ty:: Alias ( ty:: AliasKind :: Opaque , _) => {
188+ for unsatisfied in unsatisfied_predicates. iter ( ) {
189+ if is_iterator_predicate ( unsatisfied. 0 , self . tcx ) {
190+ return true ;
191+ }
192+ }
193+ }
194+ _ => return false ,
195+ }
196+ false
197+ }
198+
112199 #[ instrument( level = "debug" , skip( self ) ) ]
113200 pub fn report_method_error (
114201 & self ,
@@ -555,6 +642,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
555642 "`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
556643 ) ) ;
557644 }
645+ } else if self . impl_into_iterator_should_be_iterator ( rcvr_ty, span, unsatisfied_predicates)
646+ {
647+ err. span_label ( span, format ! ( "`{rcvr_ty}` is not an iterator" ) ) ;
648+ err. multipart_suggestion_verbose (
649+ "call `.into_iter()` first" ,
650+ vec ! [ ( span. shrink_to_lo( ) , format!( "into_iter()." ) ) ] ,
651+ Applicability :: MaybeIncorrect ,
652+ ) ;
653+ return Some ( err) ;
558654 } else if !unsatisfied_predicates. is_empty ( ) && matches ! ( rcvr_ty. kind( ) , ty:: Param ( _) ) {
559655 // We special case the situation where we are looking for `_` in
560656 // `<TypeParam as _>::method` because otherwise the machinery will look for blanket
0 commit comments