diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index 0ceb63477c809..bc64cb81179dd 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -7,7 +7,7 @@ use rustc_errors::{ }; use rustc_hir as hir; use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{walk_expr, Visitor}; +use rustc_hir::intravisit::{walk_block, walk_expr, Visitor}; use rustc_hir::{AsyncGeneratorKind, GeneratorKind}; use rustc_infer::infer::TyCtxtInferExt; use rustc_infer::traits::ObligationCause; @@ -1500,7 +1500,70 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> { | BorrowExplanation::UsedLaterInLoop(..) | BorrowExplanation::UsedLaterWhenDropped { .. } => { // Only give this note and suggestion if it could be relevant. - err.note("consider using a `let` binding to create a longer lived value"); + let sm = self.infcx.tcx.sess.source_map(); + let mut suggested = false; + let msg = "consider using a `let` binding to create a longer lived value"; + + /// We check that there's a single level of block nesting to ensure always correct + /// suggestions. If we don't, then we only provide a free-form message to avoid + /// misleading users in cases like `src/test/ui/nll/borrowed-temporary-error.rs`. + /// We could expand the analysis to suggest hoising all of the relevant parts of + /// the users' code to make the code compile, but that could be too much. + struct NestedStatementVisitor { + span: Span, + current: usize, + found: usize, + } + + impl<'tcx> Visitor<'tcx> for NestedStatementVisitor { + fn visit_block(&mut self, block: &hir::Block<'tcx>) { + self.current += 1; + walk_block(self, block); + self.current -= 1; + } + fn visit_expr(&mut self, expr: &hir::Expr<'tcx>) { + if self.span == expr.span { + self.found = self.current; + } + walk_expr(self, expr); + } + } + let source_info = self.body.source_info(location); + if let Some(scope) = self.body.source_scopes.get(source_info.scope) + && let ClearCrossCrate::Set(scope_data) = &scope.local_data + && let Some(node) = self.infcx.tcx.hir().find(scope_data.lint_root) + && let Some(id) = node.body_id() + && let hir::ExprKind::Block(block, _) = self.infcx.tcx.hir().body(id).value.kind + { + for stmt in block.stmts { + let mut visitor = NestedStatementVisitor { + span: proper_span, + current: 0, + found: 0, + }; + visitor.visit_stmt(stmt); + if visitor.found == 0 + && stmt.span.contains(proper_span) + && let Some(p) = sm.span_to_margin(stmt.span) + && let Ok(s) = sm.span_to_snippet(proper_span) + { + let addition = format!("let binding = {};\n{}", s, " ".repeat(p)); + err.multipart_suggestion_verbose( + msg, + vec![ + (stmt.span.shrink_to_lo(), addition), + (proper_span, "binding".to_string()), + ], + Applicability::MaybeIncorrect, + ); + suggested = true; + break; + } + } + } + if !suggested { + err.note(msg); + } } _ => {} } diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed new file mode 100644 index 0000000000000..8bf6a2f6db396 --- /dev/null +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed @@ -0,0 +1,14 @@ +// run-rustfix + +use std::collections::HashMap; + +fn main() { + let tmp: Box<_>; + let mut buggy_map: HashMap = HashMap::new(); + let binding = Box::new(1); + buggy_map.insert(42, &*binding); //~ ERROR temporary value dropped while borrowed + + // but it is ok if we use a temporary + tmp = Box::new(2); + buggy_map.insert(43, &*tmp); +} diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs index 1d05845fc6b20..85481336a305f 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.rs @@ -1,8 +1,6 @@ -use std::collections::HashMap; - - - +// run-rustfix +use std::collections::HashMap; fn main() { let tmp: Box<_>; diff --git a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr index 01379ed851200..dea8ac90bec2e 100644 --- a/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr +++ b/src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-borrowed-uniq-rvalue.rs:10:28 + --> $DIR/borrowck-borrowed-uniq-rvalue.rs:8:28 | LL | buggy_map.insert(42, &*Box::new(1)); | ^^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -9,7 +9,11 @@ LL | buggy_map.insert(42, &*Box::new(1)); LL | buggy_map.insert(43, &*tmp); | --------------------------- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = Box::new(1); +LL ~ buggy_map.insert(42, &*binding); + | error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-11493.fixed b/src/test/ui/borrowck/issue-11493.fixed new file mode 100644 index 0000000000000..139bd9a073973 --- /dev/null +++ b/src/test/ui/borrowck/issue-11493.fixed @@ -0,0 +1,9 @@ +// run-rustfix +fn id(x: T) -> T { x } + +fn main() { + let x = Some(3); + let binding = id(5); + let y = x.as_ref().unwrap_or(&binding); //~ ERROR + let _ = &y; +} diff --git a/src/test/ui/issues/issue-11493.rs b/src/test/ui/borrowck/issue-11493.rs similarity index 78% rename from src/test/ui/issues/issue-11493.rs rename to src/test/ui/borrowck/issue-11493.rs index b28c173b19b71..cb77f89fb2b1e 100644 --- a/src/test/ui/issues/issue-11493.rs +++ b/src/test/ui/borrowck/issue-11493.rs @@ -1,7 +1,8 @@ +// run-rustfix fn id(x: T) -> T { x } fn main() { let x = Some(3); let y = x.as_ref().unwrap_or(&id(5)); //~ ERROR - &y; + let _ = &y; } diff --git a/src/test/ui/issues/issue-11493.stderr b/src/test/ui/borrowck/issue-11493.stderr similarity index 64% rename from src/test/ui/issues/issue-11493.stderr rename to src/test/ui/borrowck/issue-11493.stderr index f954d64ac5bf4..a5d1f2816f1ca 100644 --- a/src/test/ui/issues/issue-11493.stderr +++ b/src/test/ui/borrowck/issue-11493.stderr @@ -1,14 +1,18 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-11493.rs:5:35 + --> $DIR/issue-11493.rs:6:35 | LL | let y = x.as_ref().unwrap_or(&id(5)); | ^^^^^ - temporary value is freed at the end of this statement | | | creates a temporary which is freed while still in use -LL | &y; - | -- borrow later used here +LL | let _ = &y; + | -- borrow later used here + | +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id(5); +LL ~ let y = x.as_ref().unwrap_or(&binding); | - = note: consider using a `let` binding to create a longer lived value error: aborting due to previous error diff --git a/src/test/ui/borrowck/issue-36082.fixed b/src/test/ui/borrowck/issue-36082.fixed new file mode 100644 index 0000000000000..8640ca7a50964 --- /dev/null +++ b/src/test/ui/borrowck/issue-36082.fixed @@ -0,0 +1,17 @@ +// run-rustfix +use std::cell::RefCell; + +fn main() { + let mut r = 0; + let s = 0; + let x = RefCell::new((&mut r,s)); + + let binding = x.borrow(); + let val: &_ = binding.0; + //~^ ERROR temporary value dropped while borrowed [E0716] + //~| NOTE temporary value is freed at the end of this statement + //~| NOTE creates a temporary which is freed while still in use + //~| HELP consider using a `let` binding to create a longer lived value + println!("{}", val); + //~^ borrow later used here +} diff --git a/src/test/ui/issues/issue-36082.rs b/src/test/ui/borrowck/issue-36082.rs similarity index 83% rename from src/test/ui/issues/issue-36082.rs rename to src/test/ui/borrowck/issue-36082.rs index a2ff477eb816a..877d372fb8484 100644 --- a/src/test/ui/issues/issue-36082.rs +++ b/src/test/ui/borrowck/issue-36082.rs @@ -1,3 +1,4 @@ +// run-rustfix use std::cell::RefCell; fn main() { @@ -9,7 +10,7 @@ fn main() { //~^ ERROR temporary value dropped while borrowed [E0716] //~| NOTE temporary value is freed at the end of this statement //~| NOTE creates a temporary which is freed while still in use - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value println!("{}", val); //~^ borrow later used here } diff --git a/src/test/ui/issues/issue-36082.stderr b/src/test/ui/borrowck/issue-36082.stderr similarity index 73% rename from src/test/ui/issues/issue-36082.stderr rename to src/test/ui/borrowck/issue-36082.stderr index 26bf4cb1be8be..4bd586db1cdce 100644 --- a/src/test/ui/issues/issue-36082.stderr +++ b/src/test/ui/borrowck/issue-36082.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-36082.rs:8:19 + --> $DIR/issue-36082.rs:9:19 | LL | let val: &_ = x.borrow().0; | ^^^^^^^^^^ - temporary value is freed at the end of this statement @@ -9,7 +9,11 @@ LL | let val: &_ = x.borrow().0; LL | println!("{}", val); | --- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = x.borrow(); +LL ~ let val: &_ = binding.0; + | error: aborting due to previous error diff --git a/src/test/ui/cleanup-rvalue-scopes-cf.stderr b/src/test/ui/cleanup-rvalue-scopes-cf.stderr index 04e599755fb29..40f14c389842e 100644 --- a/src/test/ui/cleanup-rvalue-scopes-cf.stderr +++ b/src/test/ui/cleanup-rvalue-scopes-cf.stderr @@ -9,7 +9,11 @@ LL | let x1 = arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x1 = arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:27:14 @@ -22,7 +26,11 @@ LL | let x2 = AddFlags(1).get(); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x2 = binding.get(); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:28:21 @@ -35,7 +43,11 @@ LL | let x3 = &*arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x3 = &*arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:29:24 @@ -48,7 +60,11 @@ LL | let ref x4 = *arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let ref x4 = *arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:30:24 @@ -61,7 +77,11 @@ LL | let &ref x5 = arg(&AddFlags(1)); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let &ref x5 = arg(&binding); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:31:14 @@ -74,7 +94,11 @@ LL | let x6 = AddFlags(1).get(); LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let x6 = binding.get(); + | error[E0716]: temporary value dropped while borrowed --> $DIR/cleanup-rvalue-scopes-cf.rs:32:44 @@ -87,7 +111,11 @@ LL | LL | (x1, x2, x3, x4, x5, x6, x7); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = AddFlags(1); +LL ~ let StackBox { f: x7 } = StackBox { f: binding.get() }; + | error: aborting due to 7 previous errors diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs index 4744f3710ce53..6240f103c99b0 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.rs +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.rs @@ -20,7 +20,7 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value { @@ -41,7 +41,7 @@ fn f() { //~^ ERROR temporary value dropped while borrowed //~| NOTE creates a temporary which is freed while still in use //~| NOTE temporary value is freed at the end of this statement - //~| NOTE consider using a `let` binding to create a longer lived value + //~| HELP consider using a `let` binding to create a longer lived value v1.push(&old[0]); diff --git a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr index 7c5caba6eae32..a236dab3ae562 100644 --- a/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr +++ b/src/test/ui/span/borrowck-let-suggestion-suffixes.stderr @@ -21,7 +21,11 @@ LL | v3.push(&id('x')); // statement 6 LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id('x'); +LL ~ v3.push(&binding); // statement 6 + | error[E0716]: temporary value dropped while borrowed --> $DIR/borrowck-let-suggestion-suffixes.rs:29:18 @@ -47,7 +51,11 @@ LL | v5.push(&id('z')); LL | (v1, v2, v3, /* v4 is above. */ v5).use_ref(); | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id('z'); +LL ~ v5.push(&binding); + | error: aborting due to 4 previous errors diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.fixed b/src/test/ui/span/borrowck-ref-into-rvalue.fixed new file mode 100644 index 0000000000000..51f65e5345d2a --- /dev/null +++ b/src/test/ui/span/borrowck-ref-into-rvalue.fixed @@ -0,0 +1,13 @@ +// run-rustfix +fn main() { + let msg; + let binding = Some("Hello".to_string()); + match binding { + //~^ ERROR temporary value dropped while borrowed + Some(ref m) => { + msg = m; + }, + None => { panic!() } + } + println!("{}", *msg); +} diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.rs b/src/test/ui/span/borrowck-ref-into-rvalue.rs index c11aa1af54095..7b09fad927fdf 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.rs +++ b/src/test/ui/span/borrowck-ref-into-rvalue.rs @@ -1,3 +1,4 @@ +// run-rustfix fn main() { let msg; match Some("Hello".to_string()) { diff --git a/src/test/ui/span/borrowck-ref-into-rvalue.stderr b/src/test/ui/span/borrowck-ref-into-rvalue.stderr index 4f529ce9511db..cb5289d24b4fc 100644 --- a/src/test/ui/span/borrowck-ref-into-rvalue.stderr +++ b/src/test/ui/span/borrowck-ref-into-rvalue.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/borrowck-ref-into-rvalue.rs:3:11 + --> $DIR/borrowck-ref-into-rvalue.rs:4:11 | LL | match Some("Hello".to_string()) { | ^^^^^^^^^^^^^^^^^^^^^^^^^ creates a temporary which is freed while still in use @@ -9,7 +9,11 @@ LL | } LL | println!("{}", *msg); | ---- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = Some("Hello".to_string()); +LL ~ match binding { + | error: aborting due to previous error diff --git a/src/test/ui/span/issue-15480.fixed b/src/test/ui/span/issue-15480.fixed new file mode 100644 index 0000000000000..e6d1a4dd32806 --- /dev/null +++ b/src/test/ui/span/issue-15480.fixed @@ -0,0 +1,14 @@ +// run-rustfix +fn id(x: T) -> T { x } + +fn main() { + let binding = id(3); + let v = vec![ + &binding + ]; + //~^^ ERROR temporary value dropped while borrowed + + for &&x in &v { + println!("{}", x + 3); + } +} diff --git a/src/test/ui/span/issue-15480.rs b/src/test/ui/span/issue-15480.rs index b286d94178a1e..916ce4b1edb26 100644 --- a/src/test/ui/span/issue-15480.rs +++ b/src/test/ui/span/issue-15480.rs @@ -1,3 +1,4 @@ +// run-rustfix fn id(x: T) -> T { x } fn main() { diff --git a/src/test/ui/span/issue-15480.stderr b/src/test/ui/span/issue-15480.stderr index 23ee2256dd85b..460ad9ac74445 100644 --- a/src/test/ui/span/issue-15480.stderr +++ b/src/test/ui/span/issue-15480.stderr @@ -1,5 +1,5 @@ error[E0716]: temporary value dropped while borrowed - --> $DIR/issue-15480.rs:5:10 + --> $DIR/issue-15480.rs:6:10 | LL | &id(3) | ^^^^^ creates a temporary which is freed while still in use @@ -9,7 +9,12 @@ LL | ]; LL | for &&x in &v { | -- borrow later used here | - = note: consider using a `let` binding to create a longer lived value +help: consider using a `let` binding to create a longer lived value + | +LL ~ let binding = id(3); +LL ~ let v = vec![ +LL ~ &binding + | error: aborting due to previous error