Suggest Box::new when appropriate

When encountering a boxed value as expected and a stack allocated value
that could be boxed to fulfill the expectation, like in the following
snippet, suggest `Box::new` wrapping.
This commit is contained in:
Esteban Küber 2019-08-12 20:22:58 -07:00
parent 1e6f753762
commit fb2511c3c5
6 changed files with 100 additions and 4 deletions

View File

@ -650,11 +650,31 @@ impl<'hir> Map<'hir> {
}
pub fn is_const_scope(&self, hir_id: HirId) -> bool {
self.walk_parent_nodes(hir_id, |node| match *node {
Node::Item(Item { node: ItemKind::Const(_, _), .. }) => true,
Node::Item(Item { node: ItemKind::Fn(_, header, _, _), .. }) => header.is_const(),
let parent_id = self.get_parent_item(hir_id);
match self.get(parent_id) {
Node::Item(&Item {
node: ItemKind::Const(..),
..
})
| Node::TraitItem(&TraitItem {
node: TraitItemKind::Const(..),
..
})
| Node::ImplItem(&ImplItem {
node: ImplItemKind::Const(..),
..
})
| Node::AnonConst(_)
| Node::Item(&Item {
node: ItemKind::Static(..),
..
}) => true,
Node::Item(&Item {
node: ItemKind::Fn(_, header, ..),
..
}) => header.constness == Constness::Const,
_ => false,
}, |_| false).map(|id| id != CRATE_HIR_ID).unwrap_or(false)
}
}
/// If there is some error when walking the parents (e.g., a node does not

View File

@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
self.suggest_boxing_when_appropriate(&mut err, expr, expected, expr_ty);
self.suggest_missing_await(&mut err, expr, expected, expr_ty);
(expected, Some(err))

View File

@ -3820,6 +3820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
err, &fn_decl, expected, found, can_suggest);
}
self.suggest_ref_or_into(err, expression, expected, found);
self.suggest_boxing_when_appropriate(err, expression, expected, found);
pointing_at_return_type
}
@ -3980,6 +3981,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
/// in the heap by calling `Box::new()`.
fn suggest_boxing_when_appropriate(
&self,
err: &mut DiagnosticBuilder<'tcx>,
expr: &hir::Expr,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) {
if self.tcx.hir().is_const_scope(expr.hir_id) {
// Do not suggest `Box::new` in const context.
return;
}
if expected.is_box() && !found.is_box() {
let boxed_found = self.tcx.mk_box(found);
if let (true, Ok(snippet)) = (
self.can_coerce(boxed_found, expected),
self.sess().source_map().span_to_snippet(expr.span),
) {
err.span_suggestion(
expr.span,
"you can store this in the heap calling `Box::new`",
format!("Box::new({})", snippet),
Applicability::MachineApplicable,
);
err.note("for more information about the distinction between the stack and the \
heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \
https://doc.rust-lang.org/rust-by-example/std/box.html and \
https://doc.rust-lang.org/std/boxed/index.html");
}
}
}
/// A common error is to forget to add a semicolon at the end of a block, e.g.,
///
/// ```

View File

@ -0,0 +1,8 @@
// run-rustfix
fn main() {
let _x: Box<dyn Fn() -> Result<(), ()>> = Box::new(|| { //~ ERROR mismatched types
Err(())?;
Ok(())
});
}

View File

@ -0,0 +1,8 @@
// run-rustfix
fn main() {
let _x: Box<dyn Fn() -> Result<(), ()>> = || { //~ ERROR mismatched types
Err(())?;
Ok(())
};
}

View File

@ -0,0 +1,24 @@
error[E0308]: mismatched types
--> $DIR/suggest-box.rs:4:47
|
LL | let _x: Box<dyn Fn() -> Result<(), ()>> = || {
| _______________________________________________^
LL | | Err(())?;
LL | | Ok(())
LL | | };
| |_____^ expected struct `std::boxed::Box`, found closure
|
= note: expected type `std::boxed::Box<dyn std::ops::Fn() -> std::result::Result<(), ()>>`
found type `[closure@$DIR/suggest-box.rs:4:47: 7:6]`
= note: for more information about the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html and https://doc.rust-lang.org/std/boxed/index.html
help: you can store this in the heap calling `Box::new`
|
LL | let _x: Box<dyn Fn() -> Result<(), ()>> = Box::new(|| {
LL | Err(())?;
LL | Ok(())
LL | });
|
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.