@@ -1413,57 +1413,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
14131413 err : & mut Diagnostic ,
14141414 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
14151415 ) -> bool {
1416- let span = obligation. cause . span ;
1416+ let mut span = obligation. cause . span ;
1417+ let mut trait_pred = trait_pred;
1418+ let mut code = obligation. cause . code ( ) ;
1419+ while let Some ( ( c, Some ( parent_trait_pred) ) ) = code. parent ( ) {
1420+ // We want the root obligation, in order to detect properly handle
1421+ // `for _ in &mut &mut vec![] {}`.
1422+ code = c;
1423+ trait_pred = parent_trait_pred;
1424+ }
1425+ while span. desugaring_kind ( ) . is_some ( ) {
1426+ // Remove all the hir desugaring contexts while maintaining the macro contexts.
1427+ span. remove_mark ( ) ;
1428+ }
1429+ let mut expr_finder = super :: FindExprBySpan :: new ( span) ;
1430+ let Some ( hir:: Node :: Expr ( body) ) = self . tcx . hir ( ) . find ( obligation. cause . body_id ) else {
1431+ return false ;
1432+ } ;
1433+ expr_finder. visit_expr ( & body) ;
1434+ let mut maybe_suggest = |suggested_ty, count, suggestions| {
1435+ // Remapping bound vars here
1436+ let trait_pred_and_suggested_ty =
1437+ trait_pred. map_bound ( |trait_pred| ( trait_pred, suggested_ty) ) ;
1438+
1439+ let new_obligation = self . mk_trait_obligation_with_new_self_ty (
1440+ obligation. param_env ,
1441+ trait_pred_and_suggested_ty,
1442+ ) ;
14171443
1418- let mut suggested = false ;
1419- if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1420- let refs_number =
1421- snippet. chars ( ) . filter ( |c| !c. is_whitespace ( ) ) . take_while ( |c| * c == '&' ) . count ( ) ;
1422- if let Some ( '\'' ) = snippet. chars ( ) . filter ( |c| !c. is_whitespace ( ) ) . nth ( refs_number) {
1423- // Do not suggest removal of borrow from type arguments.
1424- return false ;
1444+ if self . predicate_may_hold ( & new_obligation) {
1445+ let msg = if count == 1 {
1446+ "consider removing the leading `&`-reference" . to_string ( )
1447+ } else {
1448+ format ! ( "consider removing {count} leading `&`-references" )
1449+ } ;
1450+
1451+ err. multipart_suggestion_verbose (
1452+ & msg,
1453+ suggestions,
1454+ Applicability :: MachineApplicable ,
1455+ ) ;
1456+ true
1457+ } else {
1458+ false
14251459 }
1460+ } ;
14261461
1427- // Skipping binder here, remapping below
1428- let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1462+ // Maybe suggest removal of borrows from types in type parameters, like in
1463+ // `src/test/ui/not-panic/not-panic-safe.rs`.
1464+ let mut count = 0 ;
1465+ let mut suggestions = vec ! [ ] ;
1466+ // Skipping binder here, remapping below
1467+ let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1468+ if let Some ( mut hir_ty) = expr_finder. ty_result {
1469+ while let hir:: TyKind :: Ref ( _, mut_ty) = & hir_ty. kind {
1470+ count += 1 ;
1471+ let span = hir_ty. span . until ( mut_ty. ty . span ) ;
1472+ suggestions. push ( ( span, String :: new ( ) ) ) ;
14291473
1430- for refs_remaining in 0 ..refs_number {
14311474 let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
14321475 break ;
14331476 } ;
14341477 suggested_ty = * inner_ty;
14351478
1436- // Remapping bound vars here
1437- let trait_pred_and_suggested_ty =
1438- trait_pred. map_bound ( |trait_pred| ( trait_pred, suggested_ty) ) ;
1479+ hir_ty = mut_ty. ty ;
14391480
1440- let new_obligation = self . mk_trait_obligation_with_new_self_ty (
1441- obligation. param_env ,
1442- trait_pred_and_suggested_ty,
1443- ) ;
1481+ if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
1482+ return true ;
1483+ }
1484+ }
1485+ }
14441486
1445- if self . predicate_may_hold ( & new_obligation) {
1446- let sp = self
1447- . tcx
1448- . sess
1449- . source_map ( )
1450- . span_take_while ( span, |c| c. is_whitespace ( ) || * c == '&' ) ;
1487+ // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1488+ let Some ( mut expr) = expr_finder. result else { return false ; } ;
1489+ let mut count = 0 ;
1490+ let mut suggestions = vec ! [ ] ;
1491+ // Skipping binder here, remapping below
1492+ let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1493+ ' outer: loop {
1494+ while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1495+ count += 1 ;
1496+ let span = if expr. span . eq_ctxt ( borrowed. span ) {
1497+ expr. span . until ( borrowed. span )
1498+ } else {
1499+ expr. span . with_hi ( expr. span . lo ( ) + BytePos ( 1 ) )
1500+ } ;
1501+ suggestions. push ( ( span, String :: new ( ) ) ) ;
14511502
1452- let remove_refs = refs_remaining + 1 ;
1503+ let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
1504+ break ' outer;
1505+ } ;
1506+ suggested_ty = * inner_ty;
14531507
1454- let msg = if remove_refs == 1 {
1455- "consider removing the leading `&`-reference" . to_string ( )
1456- } else {
1457- format ! ( "consider removing {} leading `&`-references" , remove_refs)
1458- } ;
1508+ expr = borrowed;
14591509
1460- err. span_suggestion_short ( sp, & msg, "" , Applicability :: MachineApplicable ) ;
1461- suggested = true ;
1462- break ;
1510+ if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
1511+ return true ;
14631512 }
14641513 }
1514+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1515+ && let hir:: def:: Res :: Local ( hir_id) = path. res
1516+ && let Some ( hir:: Node :: Pat ( binding) ) = self . tcx . hir ( ) . find ( hir_id)
1517+ && let Some ( hir:: Node :: Local ( local) ) = self . tcx . hir ( ) . find_parent ( binding. hir_id )
1518+ && let None = local. ty
1519+ && let Some ( binding_expr) = local. init
1520+ {
1521+ expr = binding_expr;
1522+ } else {
1523+ break ' outer;
1524+ }
14651525 }
1466- suggested
1526+ false
14671527 }
14681528
14691529 fn suggest_remove_await ( & self , obligation : & PredicateObligation < ' tcx > , err : & mut Diagnostic ) {
0 commit comments