@@ -7,7 +7,7 @@ use rustc_errors::{
77} ;
88use rustc_hir as hir;
99use rustc_hir:: def_id:: DefId ;
10- use rustc_hir:: intravisit:: { walk_expr, Visitor } ;
10+ use rustc_hir:: intravisit:: { walk_block , walk_expr, Visitor } ;
1111use rustc_hir:: { AsyncGeneratorKind , GeneratorKind } ;
1212use rustc_infer:: infer:: TyCtxtInferExt ;
1313use rustc_infer:: traits:: ObligationCause ;
@@ -1500,7 +1500,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
15001500 | BorrowExplanation :: UsedLaterInLoop ( ..)
15011501 | BorrowExplanation :: UsedLaterWhenDropped { .. } => {
15021502 // 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+ }
15041567 }
15051568 _ => { }
15061569 }
0 commit comments