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:: {
@@ -29,11 +30,13 @@ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
2930use rustc_span:: def_id:: LocalDefId ;
3031use rustc_span:: hygiene:: DesugaringKind ;
3132use rustc_span:: symbol:: { kw, sym, Ident } ;
33+ use rustc_span:: FileName ;
3234use rustc_span:: { BytePos , Span , Symbol } ;
3335use rustc_trait_selection:: infer:: InferCtxtExt ;
3436use rustc_trait_selection:: traits:: error_reporting:: FindExprBySpan ;
3537use rustc_trait_selection:: traits:: ObligationCtxt ;
3638use std:: iter;
39+ use std:: path:: PathBuf ;
3740
3841use crate :: borrow_set:: TwoPhaseActivation ;
3942use crate :: borrowck_errors;
@@ -459,19 +462,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
459462 self . suggest_cloning ( err, ty, expr, move_span) ;
460463 }
461464 }
465+
466+ self . suggest_ref_for_dbg_args ( expr, span, move_span, err) ;
467+
462468 if let Some ( pat) = finder. pat {
463- * in_pattern = true ;
464- let mut sugg = vec ! [ ( pat. span. shrink_to_lo( ) , "ref " . to_string( ) ) ] ;
465- if let Some ( pat) = finder. parent_pat {
466- sugg. insert ( 0 , ( pat. span . shrink_to_lo ( ) , "ref " . to_string ( ) ) ) ;
467- }
468- err. multipart_suggestion_verbose (
469- "borrow this binding in the pattern to avoid moving the value" ,
470- sugg,
471- Applicability :: MachineApplicable ,
469+ // FIXME: any better way to check this?
470+ let from_std = self . infcx . tcx . sess . opts . real_rust_source_base_dir . clone ( ) . map_or (
471+ false ,
472+ |root| {
473+ let file_path =
474+ match self . infcx . tcx . sess . source_map ( ) . span_to_filename ( move_span) {
475+ FileName :: Real ( name) => {
476+ name. clone ( ) . into_local_path ( ) . unwrap_or_default ( )
477+ }
478+ other => PathBuf :: from ( other. prefer_local ( ) . to_string ( ) ) ,
479+ } ;
480+ file_path. starts_with ( & root. join ( "library/std/" ) )
481+ } ,
472482 ) ;
483+ // it's useless to suggest inserting `ref` when the span comes from std library
484+ // anyway, user can not modify std library in most cases, so let's keep it quite?
485+ if !from_std {
486+ * in_pattern = true ;
487+ let mut sugg = vec ! [ ( pat. span. shrink_to_lo( ) , "ref " . to_string( ) ) ] ;
488+ if let Some ( pat) = finder. parent_pat {
489+ sugg. insert ( 0 , ( pat. span . shrink_to_lo ( ) , "ref " . to_string ( ) ) ) ;
490+ }
491+ err. multipart_suggestion_verbose (
492+ "borrow this binding in the pattern to avoid moving the value" ,
493+ sugg,
494+ Applicability :: MachineApplicable ,
495+ ) ;
496+ }
497+ }
498+ }
499+ }
500+
501+ // for dbg!(x) which may take onwership, suggest dbg!(&x) instead
502+ // but here we actually does not checking the macro name is `dbg!`
503+ // so that we may extend the scope a bit larger to cover more cases
504+ fn suggest_ref_for_dbg_args (
505+ & self ,
506+ body : & hir:: Expr < ' _ > ,
507+ span : Option < Span > ,
508+ move_span : Span ,
509+ err : & mut DiagnosticBuilder < ' tcx > ,
510+ ) {
511+ // only suggest for macro
512+ if move_span. source_callsite ( ) == move_span {
513+ return ;
514+ }
515+ let sm = self . infcx . tcx . sess . source_map ( ) ;
516+ let arg_code = if let Some ( span) = span
517+ && let Ok ( code) = sm. span_to_snippet ( span)
518+ {
519+ code
520+ } else {
521+ return ;
522+ } ;
523+ struct MatchArgFinder {
524+ expr_span : Span ,
525+ match_arg_span : Option < Span > ,
526+ arg_code : String ,
527+ }
528+ impl Visitor < ' _ > for MatchArgFinder {
529+ fn visit_expr ( & mut self , e : & hir:: Expr < ' _ > ) {
530+ // dbg! is expanded into a match pattern, we need to find the right argument span
531+ if let hir:: ExprKind :: Match ( expr, ..) = & e. kind
532+ && let hir:: ExprKind :: Path ( hir:: QPath :: Resolved (
533+ _,
534+ path @ Path { segments : [ seg] , .. } ,
535+ ) ) = & expr. kind
536+ && seg. ident . name . as_str ( ) == & self . arg_code
537+ && self . expr_span . source_callsite ( ) . contains ( expr. span )
538+ {
539+ self . match_arg_span = Some ( path. span ) ;
540+ }
541+ hir:: intravisit:: walk_expr ( self , e) ;
473542 }
474543 }
544+
545+ let mut finder = MatchArgFinder { expr_span : move_span, match_arg_span : None , arg_code } ;
546+ finder. visit_expr ( body) ;
547+ if let Some ( macro_arg_span) = finder. match_arg_span {
548+ err. span_suggestion_verbose (
549+ macro_arg_span. shrink_to_lo ( ) ,
550+ "consider borrowing instead of transferring ownership" ,
551+ "&" ,
552+ Applicability :: MachineApplicable ,
553+ ) ;
554+ }
475555 }
476556
477557 fn report_use_of_uninitialized (
0 commit comments