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