Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit aa17eeb

Browse files
authoredNov 18, 2021
Rollup merge of rust-lang#90575 - m-ou-se:compatible-variant-improvements, r=estebank
Improve suggestions for compatible variants on type mismatch. Fixes rust-lang#90553. Before: ![image](https://user-images.githubusercontent.com/783247/140385675-6ff41090-eca2-41bc-b161-99c5dabfec61.png) After: ![image](https://user-images.githubusercontent.com/783247/140385748-20cf26b5-ea96-4e56-8af2-5fe1ab16fd3b.png) r? `@estebank`
2 parents 38f2cfa + b66fb64 commit aa17eeb

15 files changed

+309
-58
lines changed
 

‎compiler/rustc_span/src/source_map.rs

+12-7
Original file line numberDiff line numberDiff line change
@@ -593,14 +593,19 @@ impl SourceMap {
593593
}
594594

595595
pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
596-
match self.span_to_prev_source(sp) {
597-
Err(_) => None,
598-
Ok(source) => {
599-
let last_line = source.rsplit_once('\n').unwrap_or(("", &source)).1;
596+
Some(self.indentation_before(sp)?.len())
597+
}
600598

601-
Some(last_line.len() - last_line.trim_start().len())
602-
}
603-
}
599+
pub fn indentation_before(&self, sp: Span) -> Option<String> {
600+
self.span_to_source(sp, |src, start_index, _| {
601+
let before = &src[..start_index];
602+
let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last);
603+
Ok(last_line
604+
.split_once(|c: char| !c.is_whitespace())
605+
.map_or(last_line, |(indent, _)| indent)
606+
.to_string())
607+
})
608+
.ok()
604609
}
605610

606611
/// Returns the source snippet as `String` before the given `Span`.

‎compiler/rustc_typeck/src/check/demand.rs

+70-13
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
199199
return;
200200
}
201201

202-
let mut compatible_variants = expected_adt
202+
// If the expression is of type () and it's the return expression of a block,
203+
// we suggest adding a separate return expression instead.
204+
// (To avoid things like suggesting `Ok(while .. { .. })`.)
205+
if expr_ty.is_unit() {
206+
if let Some(hir::Node::Block(&hir::Block {
207+
span: block_span, expr: Some(e), ..
208+
})) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
209+
{
210+
if e.hir_id == expr.hir_id {
211+
if let Some(span) = expr.span.find_ancestor_inside(block_span) {
212+
let return_suggestions =
213+
if self.tcx.is_diagnostic_item(sym::Result, expected_adt.did) {
214+
vec!["Ok(())".to_string()]
215+
} else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did)
216+
{
217+
vec!["None".to_string(), "Some(())".to_string()]
218+
} else {
219+
return;
220+
};
221+
if let Some(indent) =
222+
self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
223+
{
224+
// Add a semicolon, except after `}`.
225+
let semicolon =
226+
match self.tcx.sess.source_map().span_to_snippet(span) {
227+
Ok(s) if s.ends_with('}') => "",
228+
_ => ";",
229+
};
230+
err.span_suggestions(
231+
span.shrink_to_hi(),
232+
"try adding an expression at the end of the block",
233+
return_suggestions
234+
.into_iter()
235+
.map(|r| format!("{}\n{}{}", semicolon, indent, r)),
236+
Applicability::MaybeIncorrect,
237+
);
238+
}
239+
return;
240+
}
241+
}
242+
}
243+
}
244+
245+
let compatible_variants: Vec<String> = expected_adt
203246
.variants
204247
.iter()
205248
.filter(|variant| variant.fields.len() == 1)
@@ -220,19 +263,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
220263
None
221264
}
222265
})
223-
.peekable();
266+
.collect();
224267

225-
if compatible_variants.peek().is_some() {
226-
if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
227-
let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
228-
let msg = "try using a variant of the expected enum";
229-
err.span_suggestions(
230-
expr.span,
231-
msg,
232-
suggestions,
233-
Applicability::MaybeIncorrect,
234-
);
235-
}
268+
if let [variant] = &compatible_variants[..] {
269+
// Just a single matching variant.
270+
err.multipart_suggestion(
271+
&format!("try wrapping the expression in `{}`", variant),
272+
vec![
273+
(expr.span.shrink_to_lo(), format!("{}(", variant)),
274+
(expr.span.shrink_to_hi(), ")".to_string()),
275+
],
276+
Applicability::MaybeIncorrect,
277+
);
278+
} else if compatible_variants.len() > 1 {
279+
// More than one matching variant.
280+
err.multipart_suggestions(
281+
&format!(
282+
"try wrapping the expression in a variant of `{}`",
283+
self.tcx.def_path_str(expected_adt.did)
284+
),
285+
compatible_variants.into_iter().map(|variant| {
286+
vec![
287+
(expr.span.shrink_to_lo(), format!("{}(", variant)),
288+
(expr.span.shrink_to_hi(), ")".to_string()),
289+
]
290+
}),
291+
Applicability::MaybeIncorrect,
292+
);
236293
}
237294
}
238295
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
enum Hey<A, B> {
2+
A(A),
3+
B(B),
4+
}
5+
6+
fn f() {}
7+
8+
fn a() -> Option<()> {
9+
while false {
10+
//~^ ERROR mismatched types
11+
f();
12+
}
13+
//~^ HELP try adding an expression
14+
}
15+
16+
fn b() -> Result<(), ()> {
17+
f()
18+
//~^ ERROR mismatched types
19+
//~| HELP try adding an expression
20+
}
21+
22+
fn main() {
23+
let _: Option<()> = while false {};
24+
//~^ ERROR mismatched types
25+
//~| HELP try wrapping
26+
let _: Option<()> = {
27+
while false {}
28+
//~^ ERROR mismatched types
29+
//~| HELP try adding an expression
30+
};
31+
let _: Result<i32, i32> = 1;
32+
//~^ ERROR mismatched types
33+
//~| HELP try wrapping
34+
let _: Option<i32> = 1;
35+
//~^ ERROR mismatched types
36+
//~| HELP try wrapping
37+
let _: Hey<i32, i32> = 1;
38+
//~^ ERROR mismatched types
39+
//~| HELP try wrapping
40+
let _: Hey<i32, bool> = false;
41+
//~^ ERROR mismatched types
42+
//~| HELP try wrapping
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/compatible-variants.rs:9:5
3+
|
4+
LL | fn a() -> Option<()> {
5+
| ---------- expected `Option<()>` because of return type
6+
LL | / while false {
7+
LL | |
8+
LL | | f();
9+
LL | | }
10+
| |_____^ expected enum `Option`, found `()`
11+
|
12+
= note: expected enum `Option<()>`
13+
found unit type `()`
14+
help: try adding an expression at the end of the block
15+
|
16+
LL ~ }
17+
LL + None
18+
|
19+
LL ~ }
20+
LL + Some(())
21+
|
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/compatible-variants.rs:17:5
25+
|
26+
LL | fn b() -> Result<(), ()> {
27+
| -------------- expected `Result<(), ()>` because of return type
28+
LL | f()
29+
| ^^^ expected enum `Result`, found `()`
30+
|
31+
= note: expected enum `Result<(), ()>`
32+
found unit type `()`
33+
help: try adding an expression at the end of the block
34+
|
35+
LL ~ f();
36+
LL + Ok(())
37+
|
38+
39+
error[E0308]: mismatched types
40+
--> $DIR/compatible-variants.rs:23:25
41+
|
42+
LL | let _: Option<()> = while false {};
43+
| ---------- ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
44+
| |
45+
| expected due to this
46+
|
47+
= note: expected enum `Option<()>`
48+
found unit type `()`
49+
help: try wrapping the expression in `Some`
50+
|
51+
LL | let _: Option<()> = Some(while false {});
52+
| +++++ +
53+
54+
error[E0308]: mismatched types
55+
--> $DIR/compatible-variants.rs:27:9
56+
|
57+
LL | while false {}
58+
| ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
59+
|
60+
= note: expected enum `Option<()>`
61+
found unit type `()`
62+
help: try adding an expression at the end of the block
63+
|
64+
LL ~ while false {}
65+
LL + None
66+
|
67+
LL ~ while false {}
68+
LL + Some(())
69+
|
70+
71+
error[E0308]: mismatched types
72+
--> $DIR/compatible-variants.rs:31:31
73+
|
74+
LL | let _: Result<i32, i32> = 1;
75+
| ---------------- ^ expected enum `Result`, found integer
76+
| |
77+
| expected due to this
78+
|
79+
= note: expected enum `Result<i32, i32>`
80+
found type `{integer}`
81+
help: try wrapping the expression in a variant of `Result`
82+
|
83+
LL | let _: Result<i32, i32> = Ok(1);
84+
| +++ +
85+
LL | let _: Result<i32, i32> = Err(1);
86+
| ++++ +
87+
88+
error[E0308]: mismatched types
89+
--> $DIR/compatible-variants.rs:34:26
90+
|
91+
LL | let _: Option<i32> = 1;
92+
| ----------- ^ expected enum `Option`, found integer
93+
| |
94+
| expected due to this
95+
|
96+
= note: expected enum `Option<i32>`
97+
found type `{integer}`
98+
help: try wrapping the expression in `Some`
99+
|
100+
LL | let _: Option<i32> = Some(1);
101+
| +++++ +
102+
103+
error[E0308]: mismatched types
104+
--> $DIR/compatible-variants.rs:37:28
105+
|
106+
LL | let _: Hey<i32, i32> = 1;
107+
| ------------- ^ expected enum `Hey`, found integer
108+
| |
109+
| expected due to this
110+
|
111+
= note: expected enum `Hey<i32, i32>`
112+
found type `{integer}`
113+
help: try wrapping the expression in a variant of `Hey`
114+
|
115+
LL | let _: Hey<i32, i32> = Hey::A(1);
116+
| +++++++ +
117+
LL | let _: Hey<i32, i32> = Hey::B(1);
118+
| +++++++ +
119+
120+
error[E0308]: mismatched types
121+
--> $DIR/compatible-variants.rs:40:29
122+
|
123+
LL | let _: Hey<i32, bool> = false;
124+
| -------------- ^^^^^ expected enum `Hey`, found `bool`
125+
| |
126+
| expected due to this
127+
|
128+
= note: expected enum `Hey<i32, bool>`
129+
found type `bool`
130+
help: try wrapping the expression in `Hey::B`
131+
|
132+
LL | let _: Hey<i32, bool> = Hey::B(false);
133+
| +++++++ +
134+
135+
error: aborting due to 8 previous errors
136+
137+
For more information about this error, try `rustc --explain E0308`.

‎src/test/ui/did_you_mean/issue-42764.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ fn main() {
1010
let n: usize = 42;
1111
this_function_expects_a_double_option(n);
1212
//~^ ERROR mismatched types
13-
//~| HELP try using a variant of the expected enum
13+
//~| HELP try wrapping the expression in a variant of `DoubleOption`
1414
}
1515

1616

‎src/test/ui/did_you_mean/issue-42764.stderr

+4-4
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ LL | this_function_expects_a_double_option(n);
66
|
77
= note: expected enum `DoubleOption<_>`
88
found type `usize`
9-
help: try using a variant of the expected enum
9+
help: try wrapping the expression in a variant of `DoubleOption`
1010
|
11-
LL | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n));
12-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1311
LL | this_function_expects_a_double_option(DoubleOption::FirstSome(n));
14-
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
12+
| ++++++++++++++++++++++++ +
13+
LL | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n));
14+
| ++++++++++++++++++++++++++++++ +
1515

1616
error[E0308]: mismatched types
1717
--> $DIR/issue-42764.rs:27:33

‎src/test/ui/fully-qualified-type/fully-qualified-type-name1.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ error[E0308]: mismatched types
22
--> $DIR/fully-qualified-type-name1.rs:5:9
33
|
44
LL | x = 5;
5-
| ^
6-
| |
7-
| expected enum `Option`, found integer
8-
| help: try using a variant of the expected enum: `Some(5)`
5+
| ^ expected enum `Option`, found integer
96
|
107
= note: expected enum `Option<usize>`
118
found type `{integer}`
9+
help: try wrapping the expression in `Some`
10+
|
11+
LL | x = Some(5);
12+
| +++++ +
1213

1314
error: aborting due to previous error
1415

‎src/test/ui/fully-qualified-type/fully-qualified-type-name4.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@ error[E0308]: mismatched types
44
LL | fn bar(x: usize) -> Option<usize> {
55
| ------------- expected `Option<usize>` because of return type
66
LL | return x;
7-
| ^
8-
| |
9-
| expected enum `Option`, found `usize`
10-
| help: try using a variant of the expected enum: `Some(x)`
7+
| ^ expected enum `Option`, found `usize`
118
|
129
= note: expected enum `Option<usize>`
1310
found type `usize`
11+
help: try wrapping the expression in `Some`
12+
|
13+
LL | return Some(x);
14+
| +++++ +
1415

1516
error: aborting due to previous error
1617

‎src/test/ui/issues/issue-51632-try-desugar-incompatible-types.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,10 @@ help: try removing this `?`
1212
LL - missing_discourses()?
1313
LL + missing_discourses()
1414
|
15-
help: try using a variant of the expected enum
15+
help: try wrapping the expression in `Ok`
1616
|
1717
LL | Ok(missing_discourses()?)
18-
|
18+
| +++ +
1919

2020
error: aborting due to previous error
2121

‎src/test/ui/mismatched_types/abridged.stderr

+10-8
Original file line numberDiff line numberDiff line change
@@ -26,27 +26,29 @@ error[E0308]: mismatched types
2626
LL | fn b() -> Option<Foo> {
2727
| ----------- expected `Option<Foo>` because of return type
2828
LL | Foo { bar: 1 }
29-
| ^^^^^^^^^^^^^^
30-
| |
31-
| expected enum `Option`, found struct `Foo`
32-
| help: try using a variant of the expected enum: `Some(Foo { bar: 1 })`
29+
| ^^^^^^^^^^^^^^ expected enum `Option`, found struct `Foo`
3330
|
3431
= note: expected enum `Option<Foo>`
3532
found struct `Foo`
33+
help: try wrapping the expression in `Some`
34+
|
35+
LL | Some(Foo { bar: 1 })
36+
| +++++ +
3637

3738
error[E0308]: mismatched types
3839
--> $DIR/abridged.rs:28:5
3940
|
4041
LL | fn c() -> Result<Foo, Bar> {
4142
| ---------------- expected `Result<Foo, Bar>` because of return type
4243
LL | Foo { bar: 1 }
43-
| ^^^^^^^^^^^^^^
44-
| |
45-
| expected enum `Result`, found struct `Foo`
46-
| help: try using a variant of the expected enum: `Ok(Foo { bar: 1 })`
44+
| ^^^^^^^^^^^^^^ expected enum `Result`, found struct `Foo`
4745
|
4846
= note: expected enum `Result<Foo, Bar>`
4947
found struct `Foo`
48+
help: try wrapping the expression in `Ok`
49+
|
50+
LL | Ok(Foo { bar: 1 })
51+
| +++ +
5052

5153
error[E0308]: mismatched types
5254
--> $DIR/abridged.rs:39:5

‎src/test/ui/pattern/pat-type-err-let-stmt.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ error[E0308]: mismatched types
22
--> $DIR/pat-type-err-let-stmt.rs:6:29
33
|
44
LL | let Ok(0): Option<u8> = 42u8;
5-
| ---------- ^^^^
6-
| | |
7-
| | expected enum `Option`, found `u8`
8-
| | help: try using a variant of the expected enum: `Some(42u8)`
5+
| ---------- ^^^^ expected enum `Option`, found `u8`
6+
| |
97
| expected due to this
108
|
119
= note: expected enum `Option<u8>`
1210
found type `u8`
11+
help: try wrapping the expression in `Some`
12+
|
13+
LL | let Ok(0): Option<u8> = Some(42u8);
14+
| +++++ +
1315

1416
error[E0308]: mismatched types
1517
--> $DIR/pat-type-err-let-stmt.rs:6:9

‎src/test/ui/suggestions/boxed-variant-field.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ fn foo(x: Ty) -> Ty {
99
Ty::List(elem) => foo(elem),
1010
//~^ ERROR mismatched types
1111
//~| HELP try dereferencing the `Box`
12-
//~| HELP try using a variant of the expected enum
12+
//~| HELP try wrapping
1313
}
1414
}
1515

‎src/test/ui/suggestions/boxed-variant-field.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@ help: try dereferencing the `Box`
1010
|
1111
LL | Ty::List(elem) => foo(*elem),
1212
| +
13-
help: try using a variant of the expected enum
13+
help: try wrapping the expression in `Ty::List`
1414
|
1515
LL | Ty::List(elem) => foo(Ty::List(elem)),
16-
| ~~~~~~~~~~~~~~
16+
| +++++++++ +
1717

1818
error: aborting due to previous error
1919

‎src/test/ui/suggestions/suggest-full-enum-variant-for-local-module.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,16 @@ error[E0308]: mismatched types
22
--> $DIR/suggest-full-enum-variant-for-local-module.rs:9:28
33
|
44
LL | let _: option::O<()> = ();
5-
| ------------- ^^
6-
| | |
7-
| | expected enum `O`, found `()`
8-
| | help: try using a variant of the expected enum: `option::O::Some(())`
5+
| ------------- ^^ expected enum `O`, found `()`
6+
| |
97
| expected due to this
108
|
119
= note: expected enum `O<()>`
1210
found unit type `()`
11+
help: try wrapping the expression in `option::O::Some`
12+
|
13+
LL | let _: option::O<()> = option::O::Some(());
14+
| ++++++++++++++++ +
1315

1416
error: aborting due to previous error
1517

‎src/test/ui/typeck/issue-46112.stderr

+5-4
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@ error[E0308]: mismatched types
22
--> $DIR/issue-46112.rs:9:21
33
|
44
LL | fn main() { test(Ok(())); }
5-
| ^^
6-
| |
7-
| expected enum `Option`, found `()`
8-
| help: try using a variant of the expected enum: `Some(())`
5+
| ^^ expected enum `Option`, found `()`
96
|
107
= note: expected enum `Option<()>`
118
found unit type `()`
9+
help: try wrapping the expression in `Some`
10+
|
11+
LL | fn main() { test(Ok(Some(()))); }
12+
| +++++ +
1213

1314
error: aborting due to previous error
1415

0 commit comments

Comments
 (0)
Please sign in to comment.