Rollup merge of #99258 - estebank:suggest-let, r=wesleywiser
Provide structured suggestion for dropped temp value
This commit is contained in:
commit
984ef421fd
@ -7,7 +7,7 @@
|
||||
};
|
||||
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 @@ fn report_temporary_value_does_not_live_long_enough(
|
||||
| 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);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
14
src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed
Normal file
14
src/test/ui/borrowck/borrowck-borrowed-uniq-rvalue.fixed
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
fn main() {
|
||||
let tmp: Box<_>;
|
||||
let mut buggy_map: HashMap<usize, &usize> = 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);
|
||||
}
|
@ -1,9 +1,7 @@
|
||||
// run-rustfix
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let tmp: Box<_>;
|
||||
let mut buggy_map: HashMap<usize, &usize> = HashMap::new();
|
||||
|
@ -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
|
||||
|
||||
|
9
src/test/ui/borrowck/issue-11493.fixed
Normal file
9
src/test/ui/borrowck/issue-11493.fixed
Normal file
@ -0,0 +1,9 @@
|
||||
// run-rustfix
|
||||
fn id<T>(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;
|
||||
}
|
@ -1,7 +1,8 @@
|
||||
// run-rustfix
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
let x = Some(3);
|
||||
let y = x.as_ref().unwrap_or(&id(5)); //~ ERROR
|
||||
&y;
|
||||
let _ = &y;
|
||||
}
|
@ -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
|
||||
|
17
src/test/ui/borrowck/issue-36082.fixed
Normal file
17
src/test/ui/borrowck/issue-36082.fixed
Normal file
@ -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
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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]);
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
13
src/test/ui/span/borrowck-ref-into-rvalue.fixed
Normal file
13
src/test/ui/span/borrowck-ref-into-rvalue.fixed
Normal file
@ -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);
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
// run-rustfix
|
||||
fn main() {
|
||||
let msg;
|
||||
match Some("Hello".to_string()) {
|
||||
|
@ -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
|
||||
|
||||
|
14
src/test/ui/span/issue-15480.fixed
Normal file
14
src/test/ui/span/issue-15480.fixed
Normal file
@ -0,0 +1,14 @@
|
||||
// run-rustfix
|
||||
fn id<T>(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);
|
||||
}
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
// run-rustfix
|
||||
fn id<T>(x: T) -> T { x }
|
||||
|
||||
fn main() {
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user