Skip to content

Commit d14a323

Browse files
committed
Use more accurate return path spans
No longer suggest `Box::new(if foo { Type1 } else { Type2 })`, instead suggesting `if foo { Box::new(Type1) } else { Box::new(Type2) }`.
1 parent 55dce72 commit d14a323

File tree

7 files changed

+91
-42
lines changed

7 files changed

+91
-42
lines changed

src/librustc/traits/error_reporting/suggestions.rs

+42-11
Original file line numberDiff line numberDiff line change
@@ -600,12 +600,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
600600

601601
// Visit to make sure there's a single `return` type to suggest `impl Trait`,
602602
// otherwise suggest using `Box<dyn Trait>` or an enum.
603-
let mut visitor = ReturnsVisitor(vec![]);
603+
let mut visitor = ReturnsVisitor::new();
604604
visitor.visit_body(&body);
605605

606606
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
607607

608-
let mut ret_types = visitor.0.iter().filter_map(|expr| tables.node_type_opt(expr.hir_id));
608+
let mut ret_types =
609+
visitor.returns.iter().filter_map(|expr| tables.node_type_opt(expr.hir_id));
609610
let (last_ty, all_returns_have_same_type) = ret_types.clone().fold(
610611
(None, true),
611612
|(last_ty, mut same): (std::option::Option<Ty<'_>>, bool), ty| {
@@ -677,7 +678,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
677678
// Suggest `-> Box<dyn Trait>` and `Box::new(returned_value)`.
678679
// Get all the return values and collect their span and suggestion.
679680
let mut suggestions = visitor
680-
.0
681+
.returns
681682
.iter()
682683
.map(|expr| {
683684
(
@@ -737,10 +738,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
737738
{
738739
let body = hir.body(*body_id);
739740
// Point at all the `return`s in the function as they have failed trait bounds.
740-
let mut visitor = ReturnsVisitor(vec![]);
741+
let mut visitor = ReturnsVisitor::new();
741742
visitor.visit_body(&body);
742743
let tables = self.in_progress_tables.map(|t| t.borrow()).unwrap();
743-
for expr in &visitor.0 {
744+
for expr in &visitor.returns {
744745
if let Some(returned_ty) = tables.node_type_opt(expr.hir_id) {
745746
let ty = self.resolve_vars_if_possible(&returned_ty);
746747
err.span_label(expr.span, &format!("this returned value is of type `{}`", ty));
@@ -1691,7 +1692,16 @@ pub fn suggest_constraining_type_param(
16911692

16921693
/// Collect all the returned expressions within the input expression.
16931694
/// Used to point at the return spans when we want to suggest some change to them.
1694-
struct ReturnsVisitor<'v>(Vec<&'v hir::Expr<'v>>);
1695+
struct ReturnsVisitor<'v> {
1696+
returns: Vec<&'v hir::Expr<'v>>,
1697+
in_block_tail: bool,
1698+
}
1699+
1700+
impl ReturnsVisitor<'_> {
1701+
fn new() -> Self {
1702+
ReturnsVisitor { returns: vec![], in_block_tail: false }
1703+
}
1704+
}
16951705

16961706
impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
16971707
type Map = rustc::hir::map::Map<'v>;
@@ -1701,20 +1711,41 @@ impl<'v> Visitor<'v> for ReturnsVisitor<'v> {
17011711
}
17021712

17031713
fn visit_expr(&mut self, ex: &'v hir::Expr<'v>) {
1704-
if let hir::ExprKind::Ret(Some(ex)) = ex.kind {
1705-
self.0.push(ex);
1714+
match ex.kind {
1715+
hir::ExprKind::Ret(Some(ex)) => {
1716+
self.returns.push(ex);
1717+
}
1718+
hir::ExprKind::Block(block, _) if self.in_block_tail => {
1719+
self.in_block_tail = false;
1720+
for stmt in block.stmts {
1721+
hir::intravisit::walk_stmt(self, stmt);
1722+
}
1723+
self.in_block_tail = true;
1724+
if let Some(expr) = block.expr {
1725+
self.visit_expr(expr);
1726+
}
1727+
}
1728+
hir::ExprKind::Match(_, arms, _) if self.in_block_tail => {
1729+
for arm in arms {
1730+
self.visit_expr(arm.body);
1731+
}
1732+
}
1733+
// We need to walk to find `return`s in the entire body.
1734+
_ if !self.in_block_tail => hir::intravisit::walk_expr(self, ex),
1735+
_ => self.returns.push(ex),
17061736
}
1707-
hir::intravisit::walk_expr(self, ex);
17081737
}
17091738

17101739
fn visit_body(&mut self, body: &'v hir::Body<'v>) {
1740+
let prev = self.in_block_tail;
17111741
if body.generator_kind().is_none() {
17121742
if let hir::ExprKind::Block(block, None) = body.value.kind {
1713-
if let Some(expr) = block.expr {
1714-
self.0.push(expr);
1743+
if block.expr.is_some() {
1744+
self.in_block_tail = true;
17151745
}
17161746
}
17171747
}
17181748
hir::intravisit::walk_body(self, body);
1749+
self.in_block_tail = prev;
17191750
}
17201751
}

src/test/ui/error-codes/E0746.fixed

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ fn foo() -> impl Trait { Struct }
1010

1111
fn bar() -> impl Trait { //~ ERROR E0746
1212
if true {
13-
return 0;
13+
return 0u32;
1414
}
15-
42
15+
42u32
1616
}
1717

1818
fn main() {}

src/test/ui/error-codes/E0746.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ fn foo() -> dyn Trait { Struct }
1010

1111
fn bar() -> dyn Trait { //~ ERROR E0746
1212
if true {
13-
return 0;
13+
return 0u32;
1414
}
15-
42
15+
42u32
1616
}
1717

1818
fn main() {}

src/test/ui/error-codes/E0746.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ LL | fn bar() -> dyn Trait {
1717
| ^^^^^^^^^ doesn't have a size known at compile-time
1818
|
1919
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
20-
help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait`
20+
help: return `impl Trait` instead, as all return paths are of type `u32`, which implements `Trait`
2121
|
2222
LL | fn bar() -> impl Trait {
2323
| ^^^^^^^^^^

src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.rs

+9-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ fn bal() -> dyn Trait { //~ ERROR E0746
2222
}
2323
42
2424
}
25+
fn bax() -> dyn Trait { //~ ERROR E0746
26+
if true {
27+
Struct
28+
} else {
29+
42
30+
}
31+
}
2532
fn bam() -> Box<dyn Trait> {
2633
if true {
2734
return Struct; //~ ERROR mismatched types
@@ -52,9 +59,9 @@ fn baw() -> Box<dyn Trait> {
5259
// Suggest using `impl Trait`
5360
fn bat() -> dyn Trait { //~ ERROR E0746
5461
if true {
55-
return 0;
62+
return 0u32;
5663
}
57-
42
64+
42u32
5865
}
5966
fn bay() -> dyn Trait { //~ ERROR E0746
6067
if true {

src/test/ui/impl-trait/dyn-trait-return-should-be-impl-trait.stderr

+34-23
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,27 @@ LL | }
9595
LL | Box::new(42)
9696
|
9797

98+
error[E0746]: return type cannot have an unboxed trait object
99+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:25:13
100+
|
101+
LL | fn bax() -> dyn Trait {
102+
| ^^^^^^^^^ doesn't have a size known at compile-time
103+
|
104+
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
105+
= note: if all the returned values were of the same type you could use `impl Trait` as the return type
106+
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
107+
= note: you can create a new `enum` with a variant for each returned type
108+
help: return a boxed trait object instead
109+
|
110+
LL | fn bax() -> Box<dyn Trait> {
111+
LL | if true {
112+
LL | Box::new(Struct)
113+
LL | } else {
114+
LL | Box::new(42)
115+
|
116+
98117
error[E0308]: mismatched types
99-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:27:16
118+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:34:16
100119
|
101120
LL | fn bam() -> Box<dyn Trait> {
102121
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -112,7 +131,7 @@ LL | return Struct;
112131
= note: for more on 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
113132

114133
error[E0308]: mismatched types
115-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:29:5
134+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:36:5
116135
|
117136
LL | fn bam() -> Box<dyn Trait> {
118137
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -128,7 +147,7 @@ LL | 42
128147
= note: for more on 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
129148

130149
error[E0308]: mismatched types
131-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:33:16
150+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:40:16
132151
|
133152
LL | fn baq() -> Box<dyn Trait> {
134153
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -144,7 +163,7 @@ LL | return 0;
144163
= note: for more on 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
145164

146165
error[E0308]: mismatched types
147-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:35:5
166+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:42:5
148167
|
149168
LL | fn baq() -> Box<dyn Trait> {
150169
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -160,7 +179,7 @@ LL | 42
160179
= note: for more on 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
161180

162181
error[E0308]: mismatched types
163-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:39:9
182+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:46:9
164183
|
165184
LL | fn baz() -> Box<dyn Trait> {
166185
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -176,7 +195,7 @@ LL | Struct
176195
= note: for more on 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
177196

178197
error[E0308]: mismatched types
179-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:41:9
198+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:48:9
180199
|
181200
LL | fn baz() -> Box<dyn Trait> {
182201
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -192,7 +211,7 @@ LL | 42
192211
= note: for more on 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
193212

194213
error[E0308]: mismatched types
195-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:46:9
214+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:53:9
196215
|
197216
LL | fn baw() -> Box<dyn Trait> {
198217
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -208,7 +227,7 @@ LL | 0
208227
= note: for more on 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
209228

210229
error[E0308]: mismatched types
211-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:48:9
230+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:55:9
212231
|
213232
LL | fn baw() -> Box<dyn Trait> {
214233
| -------------- expected `std::boxed::Box<(dyn Trait + 'static)>` because of return type
@@ -224,38 +243,30 @@ LL | 42
224243
= note: for more on 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
225244

226245
error[E0746]: return type cannot have an unboxed trait object
227-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:53:13
246+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:60:13
228247
|
229248
LL | fn bat() -> dyn Trait {
230249
| ^^^^^^^^^ doesn't have a size known at compile-time
231250
|
232251
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
233-
help: return `impl Trait` instead, as all return paths are of type `{integer}`, which implements `Trait`
252+
help: return `impl Trait` instead, as all return paths are of type `u32`, which implements `Trait`
234253
|
235254
LL | fn bat() -> impl Trait {
236255
| ^^^^^^^^^^
237256

238257
error[E0746]: return type cannot have an unboxed trait object
239-
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:59:13
258+
--> $DIR/dyn-trait-return-should-be-impl-trait.rs:66:13
240259
|
241260
LL | fn bay() -> dyn Trait {
242261
| ^^^^^^^^^ doesn't have a size known at compile-time
243262
|
244-
= note: for information on trait objects, see <https://doc.rust-lang.org/book/ch17-02-trait-objects.html#using-trait-objects-that-allow-for-values-of-different-types>
245-
= note: if all the returned values were of the same type you could use `impl Trait` as the return type
246263
= note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
247-
= note: you can create a new `enum` with a variant for each returned type
248-
help: return a boxed trait object instead
249-
|
250-
LL | fn bay() -> Box<dyn Trait> {
251-
LL | Box::new(if true {
252-
LL | 0u32
253-
LL | } else {
254-
LL | 42u32
255-
LL | })
264+
help: return `impl Trait` instead, as all return paths are of type `u32`, which implements `Trait`
256265
|
266+
LL | fn bay() -> impl Trait {
267+
| ^^^^^^^^^^
257268

258-
error: aborting due to 18 previous errors
269+
error: aborting due to 19 previous errors
259270

260271
Some errors have detailed explanations: E0277, E0308, E0746.
261272
For more information about an error, try `rustc --explain E0277`.

src/test/ui/never_type/feature-gate-never_type_fallback.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ LL | fn should_ret_unit() -> impl T {
55
| ^^^^^^ the trait `T` is not implemented for `()`
66
LL |
77
LL | panic!()
8-
| -------- this returned value is of type `()`
8+
| -------- this returned value is of type `!`
99
|
1010
= note: the return type of a function must have a statically known size
1111
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

0 commit comments

Comments
 (0)