Rollup merge of #90575 - m-ou-se:compatible-variant-improvements, r=estebank

Improve suggestions for compatible variants on type mismatch.

Fixes #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`````
This commit is contained in:
Matthias Krüger 2021-11-20 10:21:12 +01:00 committed by GitHub
commit 7354bb331e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 309 additions and 58 deletions

View File

@ -593,14 +593,19 @@ impl SourceMap {
}
pub fn span_to_margin(&self, sp: Span) -> Option<usize> {
match self.span_to_prev_source(sp) {
Err(_) => None,
Ok(source) => {
let last_line = source.rsplit_once('\n').unwrap_or(("", &source)).1;
Some(self.indentation_before(sp)?.len())
}
Some(last_line.len() - last_line.trim_start().len())
}
}
pub fn indentation_before(&self, sp: Span) -> Option<String> {
self.span_to_source(sp, |src, start_index, _| {
let before = &src[..start_index];
let last_line = before.rsplit_once('\n').map_or(before, |(_, last)| last);
Ok(last_line
.split_once(|c: char| !c.is_whitespace())
.map_or(last_line, |(indent, _)| indent)
.to_string())
})
.ok()
}
/// Returns the source snippet as `String` before the given `Span`.

View File

@ -199,7 +199,50 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return;
}
let mut compatible_variants = expected_adt
// If the expression is of type () and it's the return expression of a block,
// we suggest adding a separate return expression instead.
// (To avoid things like suggesting `Ok(while .. { .. })`.)
if expr_ty.is_unit() {
if let Some(hir::Node::Block(&hir::Block {
span: block_span, expr: Some(e), ..
})) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id))
{
if e.hir_id == expr.hir_id {
if let Some(span) = expr.span.find_ancestor_inside(block_span) {
let return_suggestions =
if self.tcx.is_diagnostic_item(sym::Result, expected_adt.did) {
vec!["Ok(())".to_string()]
} else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did)
{
vec!["None".to_string(), "Some(())".to_string()]
} else {
return;
};
if let Some(indent) =
self.tcx.sess.source_map().indentation_before(span.shrink_to_lo())
{
// Add a semicolon, except after `}`.
let semicolon =
match self.tcx.sess.source_map().span_to_snippet(span) {
Ok(s) if s.ends_with('}') => "",
_ => ";",
};
err.span_suggestions(
span.shrink_to_hi(),
"try adding an expression at the end of the block",
return_suggestions
.into_iter()
.map(|r| format!("{}\n{}{}", semicolon, indent, r)),
Applicability::MaybeIncorrect,
);
}
return;
}
}
}
}
let compatible_variants: Vec<String> = expected_adt
.variants
.iter()
.filter(|variant| variant.fields.len() == 1)
@ -220,19 +263,33 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
None
}
})
.peekable();
.collect();
if compatible_variants.peek().is_some() {
if let Ok(expr_text) = self.tcx.sess.source_map().span_to_snippet(expr.span) {
let suggestions = compatible_variants.map(|v| format!("{}({})", v, expr_text));
let msg = "try using a variant of the expected enum";
err.span_suggestions(
expr.span,
msg,
suggestions,
Applicability::MaybeIncorrect,
);
}
if let [variant] = &compatible_variants[..] {
// Just a single matching variant.
err.multipart_suggestion(
&format!("try wrapping the expression in `{}`", variant),
vec![
(expr.span.shrink_to_lo(), format!("{}(", variant)),
(expr.span.shrink_to_hi(), ")".to_string()),
],
Applicability::MaybeIncorrect,
);
} else if compatible_variants.len() > 1 {
// More than one matching variant.
err.multipart_suggestions(
&format!(
"try wrapping the expression in a variant of `{}`",
self.tcx.def_path_str(expected_adt.did)
),
compatible_variants.into_iter().map(|variant| {
vec![
(expr.span.shrink_to_lo(), format!("{}(", variant)),
(expr.span.shrink_to_hi(), ")".to_string()),
]
}),
Applicability::MaybeIncorrect,
);
}
}
}

View File

@ -0,0 +1,43 @@
enum Hey<A, B> {
A(A),
B(B),
}
fn f() {}
fn a() -> Option<()> {
while false {
//~^ ERROR mismatched types
f();
}
//~^ HELP try adding an expression
}
fn b() -> Result<(), ()> {
f()
//~^ ERROR mismatched types
//~| HELP try adding an expression
}
fn main() {
let _: Option<()> = while false {};
//~^ ERROR mismatched types
//~| HELP try wrapping
let _: Option<()> = {
while false {}
//~^ ERROR mismatched types
//~| HELP try adding an expression
};
let _: Result<i32, i32> = 1;
//~^ ERROR mismatched types
//~| HELP try wrapping
let _: Option<i32> = 1;
//~^ ERROR mismatched types
//~| HELP try wrapping
let _: Hey<i32, i32> = 1;
//~^ ERROR mismatched types
//~| HELP try wrapping
let _: Hey<i32, bool> = false;
//~^ ERROR mismatched types
//~| HELP try wrapping
}

View File

@ -0,0 +1,137 @@
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:9:5
|
LL | fn a() -> Option<()> {
| ---------- expected `Option<()>` because of return type
LL | / while false {
LL | |
LL | | f();
LL | | }
| |_____^ expected enum `Option`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
help: try adding an expression at the end of the block
|
LL ~ }
LL + None
|
LL ~ }
LL + Some(())
|
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:17:5
|
LL | fn b() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
LL | f()
| ^^^ expected enum `Result`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
help: try adding an expression at the end of the block
|
LL ~ f();
LL + Ok(())
|
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:23:25
|
LL | let _: Option<()> = while false {};
| ---------- ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
| |
| expected due to this
|
= note: expected enum `Option<()>`
found unit type `()`
help: try wrapping the expression in `Some`
|
LL | let _: Option<()> = Some(while false {});
| +++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:27:9
|
LL | while false {}
| ^^^^^^^^^^^^^^ expected enum `Option`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
help: try adding an expression at the end of the block
|
LL ~ while false {}
LL + None
|
LL ~ while false {}
LL + Some(())
|
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:31:31
|
LL | let _: Result<i32, i32> = 1;
| ---------------- ^ expected enum `Result`, found integer
| |
| expected due to this
|
= note: expected enum `Result<i32, i32>`
found type `{integer}`
help: try wrapping the expression in a variant of `Result`
|
LL | let _: Result<i32, i32> = Ok(1);
| +++ +
LL | let _: Result<i32, i32> = Err(1);
| ++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:34:26
|
LL | let _: Option<i32> = 1;
| ----------- ^ expected enum `Option`, found integer
| |
| expected due to this
|
= note: expected enum `Option<i32>`
found type `{integer}`
help: try wrapping the expression in `Some`
|
LL | let _: Option<i32> = Some(1);
| +++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:37:28
|
LL | let _: Hey<i32, i32> = 1;
| ------------- ^ expected enum `Hey`, found integer
| |
| expected due to this
|
= note: expected enum `Hey<i32, i32>`
found type `{integer}`
help: try wrapping the expression in a variant of `Hey`
|
LL | let _: Hey<i32, i32> = Hey::A(1);
| +++++++ +
LL | let _: Hey<i32, i32> = Hey::B(1);
| +++++++ +
error[E0308]: mismatched types
--> $DIR/compatible-variants.rs:40:29
|
LL | let _: Hey<i32, bool> = false;
| -------------- ^^^^^ expected enum `Hey`, found `bool`
| |
| expected due to this
|
= note: expected enum `Hey<i32, bool>`
found type `bool`
help: try wrapping the expression in `Hey::B`
|
LL | let _: Hey<i32, bool> = Hey::B(false);
| +++++++ +
error: aborting due to 8 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -10,7 +10,7 @@ fn main() {
let n: usize = 42;
this_function_expects_a_double_option(n);
//~^ ERROR mismatched types
//~| HELP try using a variant of the expected enum
//~| HELP try wrapping the expression in a variant of `DoubleOption`
}

View File

@ -6,12 +6,12 @@ LL | this_function_expects_a_double_option(n);
|
= note: expected enum `DoubleOption<_>`
found type `usize`
help: try using a variant of the expected enum
help: try wrapping the expression in a variant of `DoubleOption`
|
LL | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
LL | this_function_expects_a_double_option(DoubleOption::FirstSome(n));
| ~~~~~~~~~~~~~~~~~~~~~~~~~~
| ++++++++++++++++++++++++ +
LL | this_function_expects_a_double_option(DoubleOption::AlternativeSome(n));
| ++++++++++++++++++++++++++++++ +
error[E0308]: mismatched types
--> $DIR/issue-42764.rs:27:33

View File

@ -2,13 +2,14 @@ error[E0308]: mismatched types
--> $DIR/fully-qualified-type-name1.rs:5:9
|
LL | x = 5;
| ^
| |
| expected enum `Option`, found integer
| help: try using a variant of the expected enum: `Some(5)`
| ^ expected enum `Option`, found integer
|
= note: expected enum `Option<usize>`
found type `{integer}`
help: try wrapping the expression in `Some`
|
LL | x = Some(5);
| +++++ +
error: aborting due to previous error

View File

@ -4,13 +4,14 @@ error[E0308]: mismatched types
LL | fn bar(x: usize) -> Option<usize> {
| ------------- expected `Option<usize>` because of return type
LL | return x;
| ^
| |
| expected enum `Option`, found `usize`
| help: try using a variant of the expected enum: `Some(x)`
| ^ expected enum `Option`, found `usize`
|
= note: expected enum `Option<usize>`
found type `usize`
help: try wrapping the expression in `Some`
|
LL | return Some(x);
| +++++ +
error: aborting due to previous error

View File

@ -12,10 +12,10 @@ help: try removing this `?`
LL - missing_discourses()?
LL + missing_discourses()
|
help: try using a variant of the expected enum
help: try wrapping the expression in `Ok`
|
LL | Ok(missing_discourses()?)
|
| +++ +
error: aborting due to previous error

View File

@ -26,13 +26,14 @@ error[E0308]: mismatched types
LL | fn b() -> Option<Foo> {
| ----------- expected `Option<Foo>` because of return type
LL | Foo { bar: 1 }
| ^^^^^^^^^^^^^^
| |
| expected enum `Option`, found struct `Foo`
| help: try using a variant of the expected enum: `Some(Foo { bar: 1 })`
| ^^^^^^^^^^^^^^ expected enum `Option`, found struct `Foo`
|
= note: expected enum `Option<Foo>`
found struct `Foo`
help: try wrapping the expression in `Some`
|
LL | Some(Foo { bar: 1 })
| +++++ +
error[E0308]: mismatched types
--> $DIR/abridged.rs:28:5
@ -40,13 +41,14 @@ error[E0308]: mismatched types
LL | fn c() -> Result<Foo, Bar> {
| ---------------- expected `Result<Foo, Bar>` because of return type
LL | Foo { bar: 1 }
| ^^^^^^^^^^^^^^
| |
| expected enum `Result`, found struct `Foo`
| help: try using a variant of the expected enum: `Ok(Foo { bar: 1 })`
| ^^^^^^^^^^^^^^ expected enum `Result`, found struct `Foo`
|
= note: expected enum `Result<Foo, Bar>`
found struct `Foo`
help: try wrapping the expression in `Ok`
|
LL | Ok(Foo { bar: 1 })
| +++ +
error[E0308]: mismatched types
--> $DIR/abridged.rs:39:5

View File

@ -2,14 +2,16 @@ error[E0308]: mismatched types
--> $DIR/pat-type-err-let-stmt.rs:6:29
|
LL | let Ok(0): Option<u8> = 42u8;
| ---------- ^^^^
| | |
| | expected enum `Option`, found `u8`
| | help: try using a variant of the expected enum: `Some(42u8)`
| ---------- ^^^^ expected enum `Option`, found `u8`
| |
| expected due to this
|
= note: expected enum `Option<u8>`
found type `u8`
help: try wrapping the expression in `Some`
|
LL | let Ok(0): Option<u8> = Some(42u8);
| +++++ +
error[E0308]: mismatched types
--> $DIR/pat-type-err-let-stmt.rs:6:9

View File

@ -9,7 +9,7 @@ fn foo(x: Ty) -> Ty {
Ty::List(elem) => foo(elem),
//~^ ERROR mismatched types
//~| HELP try dereferencing the `Box`
//~| HELP try using a variant of the expected enum
//~| HELP try wrapping
}
}

View File

@ -10,10 +10,10 @@ help: try dereferencing the `Box`
|
LL | Ty::List(elem) => foo(*elem),
| +
help: try using a variant of the expected enum
help: try wrapping the expression in `Ty::List`
|
LL | Ty::List(elem) => foo(Ty::List(elem)),
| ~~~~~~~~~~~~~~
| +++++++++ +
error: aborting due to previous error

View File

@ -2,14 +2,16 @@ error[E0308]: mismatched types
--> $DIR/suggest-full-enum-variant-for-local-module.rs:9:28
|
LL | let _: option::O<()> = ();
| ------------- ^^
| | |
| | expected enum `O`, found `()`
| | help: try using a variant of the expected enum: `option::O::Some(())`
| ------------- ^^ expected enum `O`, found `()`
| |
| expected due to this
|
= note: expected enum `O<()>`
found unit type `()`
help: try wrapping the expression in `option::O::Some`
|
LL | let _: option::O<()> = option::O::Some(());
| ++++++++++++++++ +
error: aborting due to previous error

View File

@ -2,13 +2,14 @@ error[E0308]: mismatched types
--> $DIR/issue-46112.rs:9:21
|
LL | fn main() { test(Ok(())); }
| ^^
| |
| expected enum `Option`, found `()`
| help: try using a variant of the expected enum: `Some(())`
| ^^ expected enum `Option`, found `()`
|
= note: expected enum `Option<()>`
found unit type `()`
help: try wrapping the expression in `Some`
|
LL | fn main() { test(Ok(Some(()))); }
| +++++ +
error: aborting due to previous error