@@ -109,6 +109,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
109
109
self . autoderef ( span, ty) . any ( |( ty, _) | matches ! ( ty. kind( ) , ty:: Slice ( ..) | ty:: Array ( ..) ) )
110
110
}
111
111
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
+
112
199
#[ instrument( level = "debug" , skip( self ) ) ]
113
200
pub fn report_method_error (
114
201
& self ,
@@ -555,6 +642,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
555
642
"`count` is defined on `{iterator_trait}`, which `{rcvr_ty}` does not implement"
556
643
) ) ;
557
644
}
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) ;
558
654
} else if !unsatisfied_predicates. is_empty ( ) && matches ! ( rcvr_ty. kind( ) , ty:: Param ( _) ) {
559
655
// We special case the situation where we are looking for `_` in
560
656
// `<TypeParam as _>::method` because otherwise the machinery will look for blanket
0 commit comments