Skip to content

Commit fb2511c

Browse files
committed
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.
1 parent 1e6f753 commit fb2511c

File tree

6 files changed

+100
-4
lines changed

6 files changed

+100
-4
lines changed

src/librustc/hir/map/mod.rs

+24-4
Original file line numberDiff line numberDiff line change
@@ -650,11 +650,31 @@ impl<'hir> Map<'hir> {
650650
}
651651

652652
pub fn is_const_scope(&self, hir_id: HirId) -> bool {
653-
self.walk_parent_nodes(hir_id, |node| match *node {
654-
Node::Item(Item { node: ItemKind::Const(_, _), .. }) => true,
655-
Node::Item(Item { node: ItemKind::Fn(_, header, _, _), .. }) => header.is_const(),
653+
let parent_id = self.get_parent_item(hir_id);
654+
match self.get(parent_id) {
655+
Node::Item(&Item {
656+
node: ItemKind::Const(..),
657+
..
658+
})
659+
| Node::TraitItem(&TraitItem {
660+
node: TraitItemKind::Const(..),
661+
..
662+
})
663+
| Node::ImplItem(&ImplItem {
664+
node: ImplItemKind::Const(..),
665+
..
666+
})
667+
| Node::AnonConst(_)
668+
| Node::Item(&Item {
669+
node: ItemKind::Static(..),
670+
..
671+
}) => true,
672+
Node::Item(&Item {
673+
node: ItemKind::Fn(_, header, ..),
674+
..
675+
}) => header.constness == Constness::Const,
656676
_ => false,
657-
}, |_| false).map(|id| id != CRATE_HIR_ID).unwrap_or(false)
677+
}
658678
}
659679

660680
/// If there is some error when walking the parents (e.g., a node does not

src/librustc_typeck/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
127127

128128
self.suggest_compatible_variants(&mut err, expr, expected, expr_ty);
129129
self.suggest_ref_or_into(&mut err, expr, expected, expr_ty);
130+
self.suggest_boxing_when_appropriate(&mut err, expr, expected, expr_ty);
130131
self.suggest_missing_await(&mut err, expr, expected, expr_ty);
131132

132133
(expected, Some(err))

src/librustc_typeck/check/mod.rs

+35
Original file line numberDiff line numberDiff line change
@@ -3820,6 +3820,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
38203820
err, &fn_decl, expected, found, can_suggest);
38213821
}
38223822
self.suggest_ref_or_into(err, expression, expected, found);
3823+
self.suggest_boxing_when_appropriate(err, expression, expected, found);
38233824
pointing_at_return_type
38243825
}
38253826

@@ -3980,6 +3981,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
39803981
}
39813982
}
39823983

3984+
/// When encountering the expected boxed value allocated in the stack, suggest allocating it
3985+
/// in the heap by calling `Box::new()`.
3986+
fn suggest_boxing_when_appropriate(
3987+
&self,
3988+
err: &mut DiagnosticBuilder<'tcx>,
3989+
expr: &hir::Expr,
3990+
expected: Ty<'tcx>,
3991+
found: Ty<'tcx>,
3992+
) {
3993+
if self.tcx.hir().is_const_scope(expr.hir_id) {
3994+
// Do not suggest `Box::new` in const context.
3995+
return;
3996+
}
3997+
if expected.is_box() && !found.is_box() {
3998+
let boxed_found = self.tcx.mk_box(found);
3999+
if let (true, Ok(snippet)) = (
4000+
self.can_coerce(boxed_found, expected),
4001+
self.sess().source_map().span_to_snippet(expr.span),
4002+
) {
4003+
err.span_suggestion(
4004+
expr.span,
4005+
"you can store this in the heap calling `Box::new`",
4006+
format!("Box::new({})", snippet),
4007+
Applicability::MachineApplicable,
4008+
);
4009+
err.note("for more information about the distinction between the stack and the \
4010+
heap, read https://doc.rust-lang.org/book/ch15-01-box.html, \
4011+
https://doc.rust-lang.org/rust-by-example/std/box.html and \
4012+
https://doc.rust-lang.org/std/boxed/index.html");
4013+
}
4014+
}
4015+
}
4016+
4017+
39834018
/// A common error is to forget to add a semicolon at the end of a block, e.g.,
39844019
///
39854020
/// ```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
3+
fn main() {
4+
let _x: Box<dyn Fn() -> Result<(), ()>> = Box::new(|| { //~ ERROR mismatched types
5+
Err(())?;
6+
Ok(())
7+
});
8+
}
+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// run-rustfix
2+
3+
fn main() {
4+
let _x: Box<dyn Fn() -> Result<(), ()>> = || { //~ ERROR mismatched types
5+
Err(())?;
6+
Ok(())
7+
};
8+
}
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/suggest-box.rs:4:47
3+
|
4+
LL | let _x: Box<dyn Fn() -> Result<(), ()>> = || {
5+
| _______________________________________________^
6+
LL | | Err(())?;
7+
LL | | Ok(())
8+
LL | | };
9+
| |_____^ expected struct `std::boxed::Box`, found closure
10+
|
11+
= note: expected type `std::boxed::Box<dyn std::ops::Fn() -> std::result::Result<(), ()>>`
12+
found type `[closure@$DIR/suggest-box.rs:4:47: 7:6]`
13+
= 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
14+
help: you can store this in the heap calling `Box::new`
15+
|
16+
LL | let _x: Box<dyn Fn() -> Result<(), ()>> = Box::new(|| {
17+
LL | Err(())?;
18+
LL | Ok(())
19+
LL | });
20+
|
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)