@@ -4,7 +4,7 @@ use super::method::MethodCallee;
44use super :: { has_expected_num_generic_args, FnCtxt } ;
55use crate :: Expectation ;
66use 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 } ;
88use rustc_hir as hir;
99use rustc_infer:: infer:: type_variable:: { TypeVariableOrigin , TypeVariableOriginKind } ;
1010use rustc_infer:: traits:: ObligationCauseCode ;
@@ -380,33 +380,93 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
380380 }
381381 } ;
382382
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+ } ;
410470
411471 let is_compatible_after_call = |lhs_ty, rhs_ty| {
412472 self . lookup_op_method (
@@ -429,15 +489,60 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
429489 } else if is_assign == IsAssign :: Yes
430490 && let Some ( lhs_deref_ty) = self . deref_once_mutably_for_diagnostic ( lhs_ty)
431491 {
432- suggest_deref_binop ( lhs_deref_ty) ;
492+ suggest_deref_binop ( & mut err , lhs_deref_ty) ;
433493 } 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 ( )
435495 {
436496 if self . type_is_copy_modulo_regions (
437497 self . param_env ,
438498 * lhs_deref_ty,
439499 ) {
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+ }
441546 }
442547 } else if self . suggest_fn_call ( & mut err, lhs_expr, lhs_ty, |lhs_ty| {
443548 is_compatible_after_call ( lhs_ty, rhs_ty)
0 commit comments