@@ -48,6 +48,7 @@ use tracing::{debug, info, instrument};
4848use super :: probe:: { AutorefOrPtrAdjustment , IsSuggestion , Mode , ProbeScope } ;
4949use super :: { CandidateSource , MethodError , NoMatchData } ;
5050use crate :: errors:: { self , CandidateTraitNote , NoAssociatedItem } ;
51+ use crate :: expr_use_visitor:: expr_place;
5152use crate :: method:: probe:: UnsatisfiedPredicates ;
5253use crate :: { Expectation , FnCtxt } ;
5354
@@ -189,6 +190,70 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
189190 false
190191 }
191192
193+ // Pick the iterator method to suggest: `.into_iter()` by default, and
194+ // `.iter()`/`.iter_mut()` for projections through references.
195+ fn preferred_iterator_method (
196+ & self ,
197+ source : SelfSource < ' tcx > ,
198+ rcvr_ty : Ty < ' tcx > ,
199+ ) -> Option < Symbol > {
200+ let SelfSource :: MethodCall ( rcvr_expr) = source else {
201+ return Some ( sym:: into_iter) ;
202+ } ;
203+
204+ let rcvr_expr = rcvr_expr. peel_drop_temps ( ) . peel_blocks ( ) ;
205+ let Ok ( place_with_id) = expr_place ( self , rcvr_expr) else {
206+ return None ;
207+ } ;
208+
209+ let mut projection_mutability = None ;
210+ for pointer_ty in place_with_id. place . deref_tys ( ) {
211+ match self . structurally_resolve_type ( rcvr_expr. span , pointer_ty) . kind ( ) {
212+ ty:: Ref ( .., Mutability :: Not ) => {
213+ projection_mutability = Some ( Mutability :: Not ) ;
214+ break ;
215+ }
216+ ty:: Ref ( .., Mutability :: Mut ) => {
217+ projection_mutability. get_or_insert ( Mutability :: Mut ) ;
218+ }
219+ ty:: RawPtr ( ..) => return None ,
220+ _ => { }
221+ }
222+ }
223+
224+ // Keep `.into_iter()` for receivers like `&Vec<_>`; only projections that
225+ // dereference a reference need to switch to `iter`/`iter_mut`.
226+ let Some ( projection_mutability) = projection_mutability else {
227+ return Some ( sym:: into_iter) ;
228+ } ;
229+
230+ let call_expr = self . tcx . hir_expect_expr ( self . tcx . parent_hir_id ( rcvr_expr. hir_id ) ) ;
231+ // `IntoIterator` does not imply inherent `iter`/`iter_mut` methods.
232+ let has_method = |method_name| {
233+ self . lookup_probe_for_diagnostic (
234+ Ident :: with_dummy_span ( method_name) ,
235+ rcvr_ty,
236+ call_expr,
237+ ProbeScope :: TraitsInScope ,
238+ None ,
239+ )
240+ . is_ok ( )
241+ } ;
242+
243+ match projection_mutability {
244+ Mutability :: Not => has_method ( sym:: iter) . then_some ( sym:: iter) ,
245+ Mutability :: Mut => {
246+ if has_method ( sym:: iter_mut) {
247+ Some ( sym:: iter_mut)
248+ } else if has_method ( sym:: iter) {
249+ Some ( sym:: iter)
250+ } else {
251+ None
252+ }
253+ }
254+ }
255+ }
256+
192257 #[ instrument( level = "debug" , skip( self ) ) ]
193258 pub ( crate ) fn report_method_error (
194259 & self ,
@@ -855,10 +920,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
855920 } else if self . impl_into_iterator_should_be_iterator ( rcvr_ty, span, unsatisfied_predicates)
856921 {
857922 err. span_label ( span, format ! ( "`{rcvr_ty}` is not an iterator" ) ) ;
858- if !span. in_external_macro ( self . tcx . sess . source_map ( ) ) {
923+ if !span. in_external_macro ( self . tcx . sess . source_map ( ) )
924+ && let Some ( method_name) = self . preferred_iterator_method ( source, rcvr_ty)
925+ {
859926 err. multipart_suggestion (
860- "call `.into_iter ()` first" ,
861- vec ! [ ( span. shrink_to_lo( ) , format!( "into_iter ()." ) ) ] ,
927+ format ! ( "call `.{method_name} ()` first" ) ,
928+ vec ! [ ( span. shrink_to_lo( ) , format!( "{method_name} ()." ) ) ] ,
862929 Applicability :: MaybeIncorrect ,
863930 ) ;
864931 }
0 commit comments