Skip to content

Commit

Permalink
Suggest Box::new when appropriate
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
estebank committed Aug 13, 2019
1 parent 1e6f753 commit fb2511c
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 4 deletions.
28 changes: 24 additions & 4 deletions src/librustc/hir/map/mod.rs
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions src/librustc_typeck/check/demand.rs
Expand Up @@ -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))
Expand Down
35 changes: 35 additions & 0 deletions src/librustc_typeck/check/mod.rs
Expand Up @@ -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
}

Expand Down Expand Up @@ -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.,
///
/// ```
Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/suggest-box.fixed
@@ -0,0 +1,8 @@
// run-rustfix

fn main() {
let _x: Box<dyn Fn() -> Result<(), ()>> = Box::new(|| { //~ ERROR mismatched types
Err(())?;
Ok(())
});
}
8 changes: 8 additions & 0 deletions src/test/ui/suggestions/suggest-box.rs
@@ -0,0 +1,8 @@
// run-rustfix

fn main() {
let _x: Box<dyn Fn() -> Result<(), ()>> = || { //~ ERROR mismatched types
Err(())?;
Ok(())
};
}
24 changes: 24 additions & 0 deletions src/test/ui/suggestions/suggest-box.stderr
@@ -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`.

0 comments on commit fb2511c

Please sign in to comment.