@@ -4,7 +4,7 @@ use super::method::MethodCallee;
4
4
use super :: { has_expected_num_generic_args, FnCtxt } ;
5
5
use crate :: Expectation ;
6
6
use rustc_ast as ast;
7
- use rustc_errors:: { self , struct_span_err, Applicability , Diagnostic } ;
7
+ use rustc_errors:: { self , struct_span_err, Applicability , Diagnostic , DiagnosticBuilder } ;
8
8
use rustc_hir as hir;
9
9
use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
10
10
use rustc_infer:: traits:: ObligationCauseCode ;
@@ -380,33 +380,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
380
380
}
381
381
} ;
382
382
383
- let mut suggest_deref_binop = |lhs_deref_ty : Ty < ' tcx > | {
384
- if self
385
- . lookup_op_method (
386
- lhs_deref_ty,
387
- Some ( ( rhs_expr, rhs_ty) ) ,
388
- Op :: Binary ( op, is_assign) ,
389
- expected,
390
- )
391
- . is_ok ( )
392
- {
393
- let msg = format ! (
394
- "`{}{}` can be used on `{}` if you dereference the left-hand side" ,
395
- op. node. as_str( ) ,
396
- match is_assign {
397
- IsAssign :: Yes => "=" ,
398
- IsAssign :: No => "" ,
399
- } ,
400
- lhs_deref_ty,
401
- ) ;
402
- err. span_suggestion_verbose (
403
- lhs_expr. span . shrink_to_lo ( ) ,
404
- msg,
405
- "*" ,
406
- rustc_errors:: Applicability :: MachineApplicable ,
407
- ) ;
408
- }
409
- } ;
383
+ let suggest_deref_binop =
384
+ |err : & mut DiagnosticBuilder < ' _ , _ > , lhs_deref_ty : Ty < ' tcx > | {
385
+ if self
386
+ . lookup_op_method (
387
+ lhs_deref_ty,
388
+ Some ( ( rhs_expr, rhs_ty) ) ,
389
+ Op :: Binary ( op, is_assign) ,
390
+ expected,
391
+ )
392
+ . is_ok ( )
393
+ {
394
+ let msg = format ! (
395
+ "`{}{}` can be used on `{}` if you dereference the left-hand side" ,
396
+ op. node. as_str( ) ,
397
+ match is_assign {
398
+ IsAssign :: Yes => "=" ,
399
+ IsAssign :: No => "" ,
400
+ } ,
401
+ lhs_deref_ty,
402
+ ) ;
403
+ err. span_suggestion_verbose (
404
+ lhs_expr. span . shrink_to_lo ( ) ,
405
+ msg,
406
+ "*" ,
407
+ rustc_errors:: Applicability :: MachineApplicable ,
408
+ ) ;
409
+ }
410
+ } ;
411
+
412
+ let suggest_different_borrow =
413
+ |err : & mut DiagnosticBuilder < ' _ , _ > ,
414
+ lhs_adjusted_ty,
415
+ lhs_new_mutbl : Option < ast:: Mutability > ,
416
+ rhs_adjusted_ty,
417
+ rhs_new_mutbl : Option < ast:: Mutability > | {
418
+ if self
419
+ . lookup_op_method (
420
+ lhs_adjusted_ty,
421
+ Some ( ( rhs_expr, rhs_adjusted_ty) ) ,
422
+ Op :: Binary ( op, is_assign) ,
423
+ expected,
424
+ )
425
+ . is_ok ( )
426
+ {
427
+ let op_str = op. node . as_str ( ) ;
428
+ err. note ( format ! ( "an implementation for `{lhs_adjusted_ty} {op_str} {rhs_adjusted_ty}` exists" ) ) ;
429
+
430
+ if let Some ( lhs_new_mutbl) = lhs_new_mutbl
431
+ && let Some ( rhs_new_mutbl) = rhs_new_mutbl
432
+ && lhs_new_mutbl. is_not ( )
433
+ && rhs_new_mutbl. is_not ( ) {
434
+ err. multipart_suggestion_verbose (
435
+ "consider reborrowing both sides" ,
436
+ vec ! [
437
+ ( lhs_expr. span. shrink_to_lo( ) , "&*" . to_string( ) ) ,
438
+ ( rhs_expr. span. shrink_to_lo( ) , "&*" . to_string( ) )
439
+ ] ,
440
+ rustc_errors:: Applicability :: MachineApplicable ,
441
+ ) ;
442
+ } else {
443
+ let mut suggest_new_borrow = |new_mutbl : ast:: Mutability , sp : Span | {
444
+ // Can reborrow (&mut -> &)
445
+ if new_mutbl. is_not ( ) {
446
+ err. span_suggestion_verbose (
447
+ sp. shrink_to_lo ( ) ,
448
+ "consider reborrowing this side" ,
449
+ "&*" ,
450
+ rustc_errors:: Applicability :: MachineApplicable ,
451
+ ) ;
452
+ // Works on &mut but have &
453
+ } else {
454
+ err. span_help (
455
+ sp,
456
+ "consider making this expression a mutable borrow" ,
457
+ ) ;
458
+ }
459
+ } ;
460
+
461
+ if let Some ( lhs_new_mutbl) = lhs_new_mutbl {
462
+ suggest_new_borrow ( lhs_new_mutbl, lhs_expr. span ) ;
463
+ }
464
+ if let Some ( rhs_new_mutbl) = rhs_new_mutbl {
465
+ suggest_new_borrow ( rhs_new_mutbl, rhs_expr. span ) ;
466
+ }
467
+ }
468
+ }
469
+ } ;
410
470
411
471
let is_compatible_after_call = |lhs_ty, rhs_ty| {
412
472
self . lookup_op_method (
@@ -429,15 +489,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
429
489
} else if is_assign == IsAssign :: Yes
430
490
&& let Some ( lhs_deref_ty) = self . deref_once_mutably_for_diagnostic ( lhs_ty)
431
491
{
432
- suggest_deref_binop ( lhs_deref_ty) ;
492
+ suggest_deref_binop ( & mut err , lhs_deref_ty) ;
433
493
} else if is_assign == IsAssign :: No
434
- && let Ref ( _ , lhs_deref_ty, _ ) = lhs_ty. kind ( )
494
+ && let Ref ( region , lhs_deref_ty, mutbl ) = lhs_ty. kind ( )
435
495
{
436
496
if self . type_is_copy_modulo_regions (
437
497
self . param_env ,
438
498
* lhs_deref_ty,
439
499
) {
440
- suggest_deref_binop ( * lhs_deref_ty) ;
500
+ suggest_deref_binop ( & mut err, * lhs_deref_ty) ;
501
+ } else {
502
+ let lhs_inv_mutbl = mutbl. invert ( ) ;
503
+ let lhs_inv_mutbl_ty = Ty :: new_ref (
504
+ self . tcx ,
505
+ * region,
506
+ ty:: TypeAndMut {
507
+ ty : * lhs_deref_ty,
508
+ mutbl : lhs_inv_mutbl,
509
+ } ,
510
+ ) ;
511
+
512
+ suggest_different_borrow (
513
+ & mut err,
514
+ lhs_inv_mutbl_ty,
515
+ Some ( lhs_inv_mutbl) ,
516
+ rhs_ty,
517
+ None ,
518
+ ) ;
519
+
520
+ if let Ref ( region, rhs_deref_ty, mutbl) = rhs_ty. kind ( ) {
521
+ let rhs_inv_mutbl = mutbl. invert ( ) ;
522
+ let rhs_inv_mutbl_ty = Ty :: new_ref (
523
+ self . tcx ,
524
+ * region,
525
+ ty:: TypeAndMut {
526
+ ty : * rhs_deref_ty,
527
+ mutbl : rhs_inv_mutbl,
528
+ } ,
529
+ ) ;
530
+
531
+ suggest_different_borrow (
532
+ & mut err,
533
+ lhs_ty,
534
+ None ,
535
+ rhs_inv_mutbl_ty,
536
+ Some ( rhs_inv_mutbl) ,
537
+ ) ;
538
+ suggest_different_borrow (
539
+ & mut err,
540
+ lhs_inv_mutbl_ty,
541
+ Some ( lhs_inv_mutbl) ,
542
+ rhs_inv_mutbl_ty,
543
+ Some ( rhs_inv_mutbl) ,
544
+ ) ;
545
+ }
441
546
}
442
547
} else if self . suggest_fn_call ( & mut err, lhs_expr, lhs_ty, |lhs_ty| {
443
548
is_compatible_after_call ( lhs_ty, rhs_ty)
0 commit comments