4
4
#![ allow( rustc:: untranslatable_diagnostic) ]
5
5
6
6
use either:: Either ;
7
+ use hir:: Path ;
7
8
use rustc_data_structures:: captures:: Captures ;
8
9
use rustc_data_structures:: fx:: FxIndexSet ;
9
10
use rustc_errors:: {
@@ -29,11 +30,13 @@ use rustc_mir_dataflow::move_paths::{InitKind, MoveOutIndex, MovePathIndex};
29
30
use rustc_span:: def_id:: LocalDefId ;
30
31
use rustc_span:: hygiene:: DesugaringKind ;
31
32
use rustc_span:: symbol:: { kw, sym, Ident } ;
33
+ use rustc_span:: FileName ;
32
34
use rustc_span:: { BytePos , Span , Symbol } ;
33
35
use rustc_trait_selection:: infer:: InferCtxtExt ;
34
36
use rustc_trait_selection:: traits:: error_reporting:: FindExprBySpan ;
35
37
use rustc_trait_selection:: traits:: ObligationCtxt ;
36
38
use std:: iter;
39
+ use std:: path:: PathBuf ;
37
40
38
41
use crate :: borrow_set:: TwoPhaseActivation ;
39
42
use crate :: borrowck_errors;
@@ -459,19 +462,96 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
459
462
self . suggest_cloning ( err, ty, expr, move_span) ;
460
463
}
461
464
}
465
+
466
+ self . suggest_ref_for_dbg_args ( expr, span, move_span, err) ;
467
+
462
468
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
+ } ,
472
482
) ;
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) ;
473
542
}
474
543
}
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
+ }
475
555
}
476
556
477
557
fn report_use_of_uninitialized (
0 commit comments