44#![ allow( rustc:: untranslatable_diagnostic) ]
55
66use either:: Either ;
7+ use hir:: Path ;
78use rustc_data_structures:: captures:: Captures ;
89use rustc_data_structures:: fx:: FxIndexSet ;
910use rustc_errors:: { codes:: * , struct_span_code_err, Applicability , Diag , MultiSpan } ;
@@ -27,11 +28,13 @@ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
2728use rustc_span:: def_id:: LocalDefId ;
2829use rustc_span:: hygiene:: DesugaringKind ;
2930use rustc_span:: symbol:: { kw, sym, Ident } ;
31+ use rustc_span:: FileName ;
3032use rustc_span:: { BytePos , Span , Symbol } ;
3133use rustc_trait_selection:: infer:: InferCtxtExt ;
3234use rustc_trait_selection:: traits:: error_reporting:: FindExprBySpan ;
3335use rustc_trait_selection:: traits:: ObligationCtxt ;
3436use std:: iter;
37+ use std:: path:: PathBuf ;
3538
3639use crate :: borrow_set:: TwoPhaseActivation ;
3740use crate :: borrowck_errors;
@@ -463,19 +466,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
463466 self . suggest_cloning ( err, ty, expr, move_span) ;
464467 }
465468 }
469+
470+ self . suggest_ref_for_dbg_args ( expr, span, move_span, err) ;
471+
466472 if let Some ( pat) = finder. pat {
467- * in_pattern = true ;
468- let mut sugg = vec ! [ ( pat. span. shrink_to_lo( ) , "ref " . to_string( ) ) ] ;
469- if let Some ( pat) = finder. parent_pat {
470- sugg. insert ( 0 , ( pat. span . shrink_to_lo ( ) , "ref " . to_string ( ) ) ) ;
471- }
472- err. multipart_suggestion_verbose (
473- "borrow this binding in the pattern to avoid moving the value" ,
474- sugg,
475- Applicability :: MachineApplicable ,
473+ // FIXME: any better way to check this?
474+ let from_std = self . infcx . tcx . sess . opts . real_rust_source_base_dir . clone ( ) . map_or (
475+ false ,
476+ |root| {
477+ let file_path =
478+ match self . infcx . tcx . sess . source_map ( ) . span_to_filename ( move_span) {
479+ FileName :: Real ( name) => {
480+ name. clone ( ) . into_local_path ( ) . unwrap_or_default ( )
481+ }
482+ other => PathBuf :: from ( other. prefer_local ( ) . to_string ( ) ) ,
483+ } ;
484+ file_path. starts_with ( & root. join ( "library/std/" ) )
485+ } ,
476486 ) ;
487+ // it's useless to suggest inserting `ref` when the span comes from std library
488+ // anyway, user can not modify std library in most cases, so let's keep it quite?
489+ if !from_std {
490+ * in_pattern = true ;
491+ let mut sugg = vec ! [ ( pat. span. shrink_to_lo( ) , "ref " . to_string( ) ) ] ;
492+ if let Some ( pat) = finder. parent_pat {
493+ sugg. insert ( 0 , ( pat. span . shrink_to_lo ( ) , "ref " . to_string ( ) ) ) ;
494+ }
495+ err. multipart_suggestion_verbose (
496+ "borrow this binding in the pattern to avoid moving the value" ,
497+ sugg,
498+ Applicability :: MachineApplicable ,
499+ ) ;
500+ }
501+ }
502+ }
503+ }
504+
505+ // for dbg!(x) which may take onwership, suggest dbg!(&x) instead
506+ // but here we actually does not checking the macro name is `dbg!`
507+ // so that we may extend the scope a bit larger to cover more cases
508+ fn suggest_ref_for_dbg_args (
509+ & self ,
510+ body : & hir:: Expr < ' _ > ,
511+ span : Option < Span > ,
512+ move_span : Span ,
513+ err : & mut Diag < ' tcx > ,
514+ ) {
515+ // only suggest for macro
516+ if move_span. source_callsite ( ) == move_span {
517+ return ;
518+ }
519+ let sm = self . infcx . tcx . sess . source_map ( ) ;
520+ let arg_code = if let Some ( span) = span
521+ && let Ok ( code) = sm. span_to_snippet ( span)
522+ {
523+ code
524+ } else {
525+ return ;
526+ } ;
527+ struct MatchArgFinder {
528+ expr_span : Span ,
529+ match_arg_span : Option < Span > ,
530+ arg_code : String ,
531+ }
532+ impl Visitor < ' _ > for MatchArgFinder {
533+ fn visit_expr ( & mut self , e : & hir:: Expr < ' _ > ) {
534+ // dbg! is expanded into a match pattern, we need to find the right argument span
535+ if let hir:: ExprKind :: Match ( expr, ..) = & e. kind
536+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved (
537+ _,
538+ path @ Path { segments : [ seg] , .. } ,
539+ ) ) = & expr. kind
540+ && seg. ident . name . as_str ( ) == & self . arg_code
541+ && self . expr_span . source_callsite ( ) . contains ( expr. span )
542+ {
543+ self . match_arg_span = Some ( path. span ) ;
544+ }
545+ hir:: intravisit:: walk_expr ( self , e) ;
477546 }
478547 }
548+
549+ let mut finder = MatchArgFinder { expr_span : move_span, match_arg_span : None , arg_code } ;
550+ finder. visit_expr ( body) ;
551+ if let Some ( macro_arg_span) = finder. match_arg_span {
552+ err. span_suggestion_verbose (
553+ macro_arg_span. shrink_to_lo ( ) ,
554+ "consider borrowing instead of transferring ownership" ,
555+ "&" ,
556+ Applicability :: MachineApplicable ,
557+ ) ;
558+ }
479559 }
480560
481561 fn report_use_of_uninitialized (
0 commit comments