mirror of
https://github.com/rust-lang/rust.git
synced 2025-01-27 07:03:45 +00:00
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:
commit
7354bb331e
@ -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`.
|
||||
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
43
src/test/ui/did_you_mean/compatible-variants.rs
Normal file
43
src/test/ui/did_you_mean/compatible-variants.rs
Normal 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
|
||||
}
|
137
src/test/ui/did_you_mean/compatible-variants.stderr
Normal file
137
src/test/ui/did_you_mean/compatible-variants.stderr
Normal 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`.
|
@ -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`
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user