@@ -7,7 +7,7 @@ use rustc_errors::{
7
7
} ;
8
8
use rustc_hir as hir;
9
9
use rustc_hir:: def_id:: DefId ;
10
- use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
10
+ use rustc_hir:: intravisit:: { walk_block , walk_expr, Visitor } ;
11
11
use rustc_hir:: { AsyncGeneratorKind , GeneratorKind } ;
12
12
use rustc_infer:: infer:: TyCtxtInferExt ;
13
13
use rustc_infer:: traits:: ObligationCause ;
@@ -1500,7 +1500,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
1500
1500
| BorrowExplanation :: UsedLaterInLoop ( ..)
1501
1501
| BorrowExplanation :: UsedLaterWhenDropped { .. } => {
1502
1502
// Only give this note and suggestion if it could be relevant.
1503
- err. note ( "consider using a `let` binding to create a longer lived value" ) ;
1503
+ let sm = self . infcx . tcx . sess . source_map ( ) ;
1504
+ let mut suggested = false ;
1505
+ let msg = "consider using a `let` binding to create a longer lived value" ;
1506
+
1507
+ /// We check that there's a single level of block nesting to ensure always correct
1508
+ /// suggestions. If we don't, then we only provide a free-form message to avoid
1509
+ /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`.
1510
+ /// We could expand the analysis to suggest hoising all of the relevant parts of
1511
+ /// the users' code to make the code compile, but that could be too much.
1512
+ struct NestedStatementVisitor {
1513
+ span : Span ,
1514
+ current : usize ,
1515
+ found : usize ,
1516
+ }
1517
+
1518
+ impl < ' tcx > Visitor < ' tcx > for NestedStatementVisitor {
1519
+ fn visit_block ( & mut self , block : & hir:: Block < ' tcx > ) {
1520
+ self . current += 1 ;
1521
+ walk_block ( self , block) ;
1522
+ self . current -= 1 ;
1523
+ }
1524
+ fn visit_expr ( & mut self , expr : & hir:: Expr < ' tcx > ) {
1525
+ if self . span == expr. span {
1526
+ self . found = self . current ;
1527
+ }
1528
+ walk_expr ( self , expr) ;
1529
+ }
1530
+ }
1531
+ let source_info = self . body . source_info ( location) ;
1532
+ if let Some ( scope) = self . body . source_scopes . get ( source_info. scope )
1533
+ && let ClearCrossCrate :: Set ( scope_data) = & scope. local_data
1534
+ && let Some ( node) = self . infcx . tcx . hir ( ) . find ( scope_data. lint_root )
1535
+ && let Some ( id) = node. body_id ( )
1536
+ && let hir:: ExprKind :: Block ( block, _) = self . infcx . tcx . hir ( ) . body ( id) . value . kind
1537
+ {
1538
+ for stmt in block. stmts {
1539
+ let mut visitor = NestedStatementVisitor {
1540
+ span : proper_span,
1541
+ current : 0 ,
1542
+ found : 0 ,
1543
+ } ;
1544
+ visitor. visit_stmt ( stmt) ;
1545
+ if visitor. found == 0
1546
+ && stmt. span . contains ( proper_span)
1547
+ && let Some ( p) = sm. span_to_margin ( stmt. span )
1548
+ && let Ok ( s) = sm. span_to_snippet ( proper_span)
1549
+ {
1550
+ let addition = format ! ( "let binding = {};\n {}" , s, " " . repeat( p) ) ;
1551
+ err. multipart_suggestion_verbose (
1552
+ msg,
1553
+ vec ! [
1554
+ ( stmt. span. shrink_to_lo( ) , addition) ,
1555
+ ( proper_span, "binding" . to_string( ) ) ,
1556
+ ] ,
1557
+ Applicability :: MaybeIncorrect ,
1558
+ ) ;
1559
+ suggested = true ;
1560
+ break ;
1561
+ }
1562
+ }
1563
+ }
1564
+ if !suggested {
1565
+ err. note ( msg) ;
1566
+ }
1504
1567
}
1505
1568
_ => { }
1506
1569
}
0 commit comments