44#![ allow( rustc:: untranslatable_diagnostic) ]
55
66use either:: Either ;
7- use hir:: ClosureKind ;
7+ use hir:: { ClosureKind , Path } ;
88use rustc_data_structures:: captures:: Captures ;
99use rustc_data_structures:: fx:: FxIndexSet ;
1010use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
@@ -16,6 +16,7 @@ use rustc_hir::{CoroutineKind, CoroutineSource, LangItem};
1616use rustc_middle:: bug;
1717use rustc_middle:: hir:: nested_filter:: OnlyBodies ;
1818use rustc_middle:: mir:: tcx:: PlaceTy ;
19+ use rustc_middle:: mir:: VarDebugInfoContents ;
1920use rustc_middle:: mir:: {
2021 self , AggregateKind , BindingForm , BorrowKind , CallSource , ClearCrossCrate , ConstraintCategory ,
2122 FakeBorrowKind , FakeReadCause , LocalDecl , LocalInfo , LocalKind , Location , MutBorrowKind ,
@@ -546,7 +547,14 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
546547 self . suggest_cloning ( err, ty, expr, None , Some ( move_spans) ) ;
547548 }
548549 }
549- if let Some ( pat) = finder. pat {
550+
551+ self . suggest_ref_for_dbg_args ( expr, place, move_span, err) ;
552+
553+ // it's useless to suggest inserting `ref` when the span don't comes from local code
554+ if let Some ( pat) = finder. pat
555+ && !move_span. is_dummy ( )
556+ && !self . infcx . tcx . sess . source_map ( ) . is_imported ( move_span)
557+ {
550558 * in_pattern = true ;
551559 let mut sugg = vec ! [ ( pat. span. shrink_to_lo( ) , "ref " . to_string( ) ) ] ;
552560 if let Some ( pat) = finder. parent_pat {
@@ -561,6 +569,59 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, '_, 'infcx, 'tcx> {
561569 }
562570 }
563571
572+ // for dbg!(x) which may take ownership, suggest dbg!(&x) instead
573+ // but here we actually do not check whether the macro name is `dbg!`
574+ // so that we may extend the scope a bit larger to cover more cases
575+ fn suggest_ref_for_dbg_args (
576+ & self ,
577+ body : & hir:: Expr < ' _ > ,
578+ place : & Place < ' tcx > ,
579+ move_span : Span ,
580+ err : & mut Diag < ' infcx > ,
581+ ) {
582+ let var_info = self . body . var_debug_info . iter ( ) . find ( |info| match info. value {
583+ VarDebugInfoContents :: Place ( ref p) => p == place,
584+ _ => false ,
585+ } ) ;
586+ let arg_name = if let Some ( var_info) = var_info {
587+ var_info. name
588+ } else {
589+ return ;
590+ } ;
591+ struct MatchArgFinder {
592+ expr_span : Span ,
593+ match_arg_span : Option < Span > ,
594+ arg_name : Symbol ,
595+ }
596+ impl Visitor < ' _ > for MatchArgFinder {
597+ fn visit_expr ( & mut self , e : & hir:: Expr < ' _ > ) {
598+ // dbg! is expanded into a match pattern, we need to find the right argument span
599+ if let hir:: ExprKind :: Match ( expr, ..) = & e. kind
600+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved (
601+ _,
602+ path @ Path { segments : [ seg] , .. } ,
603+ ) ) = & expr. kind
604+ && seg. ident . name == self . arg_name
605+ && self . expr_span . source_callsite ( ) . contains ( expr. span )
606+ {
607+ self . match_arg_span = Some ( path. span ) ;
608+ }
609+ hir:: intravisit:: walk_expr ( self , e) ;
610+ }
611+ }
612+
613+ let mut finder = MatchArgFinder { expr_span : move_span, match_arg_span : None , arg_name } ;
614+ finder. visit_expr ( body) ;
615+ if let Some ( macro_arg_span) = finder. match_arg_span {
616+ err. span_suggestion_verbose (
617+ macro_arg_span. shrink_to_lo ( ) ,
618+ "consider borrowing instead of transferring ownership" ,
619+ "&" ,
620+ Applicability :: MachineApplicable ,
621+ ) ;
622+ }
623+ }
624+
564625 fn report_use_of_uninitialized (
565626 & self ,
566627 mpi : MovePathIndex ,
0 commit comments