@@ -1361,57 +1361,117 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
13611361 err : & mut Diagnostic ,
13621362 trait_pred : ty:: PolyTraitPredicate < ' tcx > ,
13631363 ) -> bool {
1364- let span = obligation. cause . span ;
1364+ let mut span = obligation. cause . span ;
1365+ let mut trait_pred = trait_pred;
1366+ let mut code = obligation. cause . code ( ) ;
1367+ while let Some ( ( c, Some ( parent_trait_pred) ) ) = code. parent ( ) {
1368+ // We want the root obligation, in order to detect properly handle
1369+ // `for _ in &mut &mut vec![] {}`.
1370+ code = c;
1371+ trait_pred = parent_trait_pred;
1372+ }
1373+ while span. desugaring_kind ( ) . is_some ( ) {
1374+ // Remove all the hir desugaring contexts while maintaining the macro contexts.
1375+ span. remove_mark ( ) ;
1376+ }
1377+ let mut expr_finder = super :: FindExprBySpan :: new ( span) ;
1378+ let Some ( hir:: Node :: Expr ( body) ) = self . tcx . hir ( ) . find ( obligation. cause . body_id ) else {
1379+ return false ;
1380+ } ;
1381+ expr_finder. visit_expr ( & body) ;
1382+ let mut maybe_suggest = |suggested_ty, count, suggestions| {
1383+ // Remapping bound vars here
1384+ let trait_pred_and_suggested_ty =
1385+ trait_pred. map_bound ( |trait_pred| ( trait_pred, suggested_ty) ) ;
1386+
1387+ let new_obligation = self . mk_trait_obligation_with_new_self_ty (
1388+ obligation. param_env ,
1389+ trait_pred_and_suggested_ty,
1390+ ) ;
13651391
1366- let mut suggested = false ;
1367- if let Ok ( snippet) = self . tcx . sess . source_map ( ) . span_to_snippet ( span) {
1368- let refs_number =
1369- snippet. chars ( ) . filter ( |c| !c. is_whitespace ( ) ) . take_while ( |c| * c == '&' ) . count ( ) ;
1370- if let Some ( '\'' ) = snippet. chars ( ) . filter ( |c| !c. is_whitespace ( ) ) . nth ( refs_number) {
1371- // Do not suggest removal of borrow from type arguments.
1372- return false ;
1392+ if self . predicate_may_hold ( & new_obligation) {
1393+ let msg = if count == 1 {
1394+ "consider removing the leading `&`-reference" . to_string ( )
1395+ } else {
1396+ format ! ( "consider removing {count} leading `&`-references" )
1397+ } ;
1398+
1399+ err. multipart_suggestion_verbose (
1400+ & msg,
1401+ suggestions,
1402+ Applicability :: MachineApplicable ,
1403+ ) ;
1404+ true
1405+ } else {
1406+ false
13731407 }
1408+ } ;
13741409
1375- // Skipping binder here, remapping below
1376- let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1410+ // Maybe suggest removal of borrows from types in type parameters, like in
1411+ // `src/test/ui/not-panic/not-panic-safe.rs`.
1412+ let mut count = 0 ;
1413+ let mut suggestions = vec ! [ ] ;
1414+ // Skipping binder here, remapping below
1415+ let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1416+ if let Some ( mut hir_ty) = expr_finder. ty_result {
1417+ while let hir:: TyKind :: Ref ( _, mut_ty) = & hir_ty. kind {
1418+ count += 1 ;
1419+ let span = hir_ty. span . until ( mut_ty. ty . span ) ;
1420+ suggestions. push ( ( span, String :: new ( ) ) ) ;
13771421
1378- for refs_remaining in 0 ..refs_number {
13791422 let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
13801423 break ;
13811424 } ;
13821425 suggested_ty = * inner_ty;
13831426
1384- // Remapping bound vars here
1385- let trait_pred_and_suggested_ty =
1386- trait_pred. map_bound ( |trait_pred| ( trait_pred, suggested_ty) ) ;
1427+ hir_ty = mut_ty. ty ;
13871428
1388- let new_obligation = self . mk_trait_obligation_with_new_self_ty (
1389- obligation. param_env ,
1390- trait_pred_and_suggested_ty,
1391- ) ;
1429+ if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
1430+ return true ;
1431+ }
1432+ }
1433+ }
13921434
1393- if self . predicate_may_hold ( & new_obligation) {
1394- let sp = self
1395- . tcx
1396- . sess
1397- . source_map ( )
1398- . span_take_while ( span, |c| c. is_whitespace ( ) || * c == '&' ) ;
1435+ // Maybe suggest removal of borrows from expressions, like in `for i in &&&foo {}`.
1436+ let Some ( mut expr) = expr_finder. result else { return false ; } ;
1437+ let mut count = 0 ;
1438+ let mut suggestions = vec ! [ ] ;
1439+ // Skipping binder here, remapping below
1440+ let mut suggested_ty = trait_pred. self_ty ( ) . skip_binder ( ) ;
1441+ ' outer: loop {
1442+ while let hir:: ExprKind :: AddrOf ( _, _, borrowed) = expr. kind {
1443+ count += 1 ;
1444+ let span = if expr. span . eq_ctxt ( borrowed. span ) {
1445+ expr. span . until ( borrowed. span )
1446+ } else {
1447+ expr. span . with_hi ( expr. span . lo ( ) + BytePos ( 1 ) )
1448+ } ;
1449+ suggestions. push ( ( span, String :: new ( ) ) ) ;
13991450
1400- let remove_refs = refs_remaining + 1 ;
1451+ let ty:: Ref ( _, inner_ty, _) = suggested_ty. kind ( ) else {
1452+ break ' outer;
1453+ } ;
1454+ suggested_ty = * inner_ty;
14011455
1402- let msg = if remove_refs == 1 {
1403- "consider removing the leading `&`-reference" . to_string ( )
1404- } else {
1405- format ! ( "consider removing {} leading `&`-references" , remove_refs)
1406- } ;
1456+ expr = borrowed;
14071457
1408- err. span_suggestion_short ( sp, & msg, "" , Applicability :: MachineApplicable ) ;
1409- suggested = true ;
1410- break ;
1458+ if maybe_suggest ( suggested_ty, count, suggestions. clone ( ) ) {
1459+ return true ;
14111460 }
14121461 }
1462+ if let hir:: ExprKind :: Path ( hir:: QPath :: Resolved ( None , path) ) = expr. kind
1463+ && let hir:: def:: Res :: Local ( hir_id) = path. res
1464+ && let Some ( hir:: Node :: Pat ( binding) ) = self . tcx . hir ( ) . find ( hir_id)
1465+ && let Some ( hir:: Node :: Local ( local) ) = self . tcx . hir ( ) . find_parent ( binding. hir_id )
1466+ && let None = local. ty
1467+ && let Some ( binding_expr) = local. init
1468+ {
1469+ expr = binding_expr;
1470+ } else {
1471+ break ' outer;
1472+ }
14131473 }
1414- suggested
1474+ false
14151475 }
14161476
14171477 fn suggest_remove_await ( & self , obligation : & PredicateObligation < ' tcx > , err : & mut Diagnostic ) {
0 commit comments