mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-12 05:08:52 +00:00
Merge pull request #18797 from profetia/issue10583
Fix replace-if-let-with-match generates non-exhausive match
This commit is contained in:
commit
085ad10794
2 changed files with 762 additions and 38 deletions
|
@ -17,7 +17,7 @@ use syntax::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block},
|
utils::{does_pat_match_variant, does_pat_variant_nested_or_literal, unwrap_trivial_block},
|
||||||
AssistContext, AssistId, AssistKind, Assists,
|
AssistContext, AssistId, AssistKind, Assists,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -163,7 +163,7 @@ fn make_else_arm(
|
||||||
Some(it) => {
|
Some(it) => {
|
||||||
if does_pat_match_variant(pat, &it.sad_pattern()) {
|
if does_pat_match_variant(pat, &it.sad_pattern()) {
|
||||||
it.happy_pattern_wildcard()
|
it.happy_pattern_wildcard()
|
||||||
} else if does_nested_pattern(pat) {
|
} else if does_pat_variant_nested_or_literal(ctx, pat) {
|
||||||
make::wildcard_pat().into()
|
make::wildcard_pat().into()
|
||||||
} else {
|
} else {
|
||||||
it.sad_pattern()
|
it.sad_pattern()
|
||||||
|
@ -702,11 +702,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn nested_type() {
|
fn test_if_let_with_match_nested_tuple_struct() {
|
||||||
check_assist(
|
check_assist(
|
||||||
replace_if_let_with_match,
|
replace_if_let_with_match,
|
||||||
r#"
|
r#"
|
||||||
//- minicore: result
|
//- minicore: result, option
|
||||||
fn foo(x: Result<i32, ()>) {
|
fn foo(x: Result<i32, ()>) {
|
||||||
let bar: Result<_, ()> = Ok(Some(1));
|
let bar: Result<_, ()> = Ok(Some(1));
|
||||||
$0if let Ok(Some(_)) = bar {
|
$0if let Ok(Some(_)) = bar {
|
||||||
|
@ -724,6 +724,700 @@ fn foo(x: Result<i32, ()>) {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
struct MyStruct(i32, i32);
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
|
||||||
|
$0if let Ok(MyStruct(a, b)) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct MyStruct(i32, i32);
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct(1, 2));
|
||||||
|
match bar {
|
||||||
|
Ok(MyStruct(a, b)) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_slice() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<&[i32], ()>) {
|
||||||
|
let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
|
||||||
|
$0if let Ok([]) = foo {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<&[i32], ()>) {
|
||||||
|
let foo: Result<&[_], ()> = Ok(&[0, 1, 2]);
|
||||||
|
match foo {
|
||||||
|
Ok([]) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<[&'static str; 2], ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(["foo", "bar"]);
|
||||||
|
$0if let Ok([_, "bar"]) = foobar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<[&'static str; 2], ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(["foo", "bar"]);
|
||||||
|
match foobar {
|
||||||
|
Ok([_, "bar"]) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<[&'static str; 2], ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(["foo", "bar"]);
|
||||||
|
$0if let Ok([..]) = foobar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<[&'static str; 2], ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(["foo", "bar"]);
|
||||||
|
match foobar {
|
||||||
|
Ok([..]) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<&[&'static str], ()>) {
|
||||||
|
let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
|
||||||
|
$0if let Ok([a, ..]) = foobar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<&[&'static str], ()>) {
|
||||||
|
let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
|
||||||
|
match foobar {
|
||||||
|
Ok([a, ..]) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<&[&'static str], ()>) {
|
||||||
|
let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
|
||||||
|
$0if let Ok([a, .., b, c]) = foobar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<&[&'static str], ()>) {
|
||||||
|
let foobar: Result<&[&'static str], ()> = Ok(&["foo", "bar"]);
|
||||||
|
match foobar {
|
||||||
|
Ok([a, .., b, c]) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
|
||||||
|
$0if let Ok(Some([_, "bar"])) = foobar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<Option<[&'static str; 2]>, ()>) {
|
||||||
|
let foobar: Result<_, ()> = Ok(Some(["foo", "bar"]));
|
||||||
|
match foobar {
|
||||||
|
Ok(Some([_, "bar"])) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_literal() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<&'static str, ()>) {
|
||||||
|
let bar: Result<&_, ()> = Ok("bar");
|
||||||
|
$0if let Ok("foo") = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<&'static str, ()>) {
|
||||||
|
let bar: Result<&_, ()> = Ok("bar");
|
||||||
|
match bar {
|
||||||
|
Ok("foo") => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_tuple() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
|
||||||
|
$0if let Ok((1, second, third)) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
|
||||||
|
match bar {
|
||||||
|
Ok((1, second, third)) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
|
||||||
|
$0if let Ok((first, second, third)) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32, i32), ()> = Ok((1, 2, 3));
|
||||||
|
match bar {
|
||||||
|
Ok((first, second, third)) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_or() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(1 | 2) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(1 | 2) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 2));
|
||||||
|
$0if let Ok((b, a) | (a, b)) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 2));
|
||||||
|
match bar {
|
||||||
|
Ok((b, a) | (a, b)) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 2));
|
||||||
|
$0if let Ok((1, a) | (a, 2)) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 2));
|
||||||
|
match bar {
|
||||||
|
Ok((1, a) | (a, 2)) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_range() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(1..2) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(1..2) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_paren() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 1));
|
||||||
|
$0if let Ok(((1, 2))) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 1));
|
||||||
|
match bar {
|
||||||
|
Ok(((1, 2))) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 1));
|
||||||
|
$0if let Ok(((a, b))) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<(i32, i32), ()>) {
|
||||||
|
let bar: Result<(i32, i32), ()> = Ok((1, 1));
|
||||||
|
match bar {
|
||||||
|
Ok(((a, b))) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_macro() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
macro_rules! is_42 {
|
||||||
|
() => {
|
||||||
|
42
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(is_42!()) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
macro_rules! is_42 {
|
||||||
|
() => {
|
||||||
|
42
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(is_42!()) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_path() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
enum MyEnum {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyEnum, ()>) {
|
||||||
|
let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
|
||||||
|
$0if let Ok(MyEnum::Foo) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum MyEnum {
|
||||||
|
Foo,
|
||||||
|
Bar,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyEnum, ()>) {
|
||||||
|
let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo);
|
||||||
|
match bar {
|
||||||
|
Ok(MyEnum::Foo) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_record() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
$0if let Ok(MyStruct { foo, bar }) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
match bar {
|
||||||
|
Ok(MyStruct { foo, bar }) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
$0if let Ok(MyStruct { foo, bar: 12 }) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
match bar {
|
||||||
|
Ok(MyStruct { foo, bar: 12 }) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
$0if let Ok(MyStruct { foo, .. }) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
struct MyStruct {
|
||||||
|
foo: i32,
|
||||||
|
bar: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyStruct, ()>) {
|
||||||
|
let bar: Result<MyStruct, ()> = Ok(MyStruct { foo: 1, bar: 2 });
|
||||||
|
match bar {
|
||||||
|
Ok(MyStruct { foo, .. }) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
enum MyEnum {
|
||||||
|
Foo(i32, i32),
|
||||||
|
Bar { a: i32, b: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyEnum, ()>) {
|
||||||
|
let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
|
||||||
|
$0if let Ok(MyEnum::Bar { a, b }) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
enum MyEnum {
|
||||||
|
Foo(i32, i32),
|
||||||
|
Bar { a: i32, b: i32 },
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(x: Result<MyEnum, ()>) {
|
||||||
|
let bar: Result<MyEnum, ()> = Ok(MyEnum::Foo(1, 2));
|
||||||
|
match bar {
|
||||||
|
Ok(MyEnum::Bar { a, b }) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_nested_ident() {
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(a @ 1..2) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(a @ 1..2) => (),
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(a) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(a) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
|
||||||
|
check_assist(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
//- minicore: result
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
$0if let Ok(a @ b @ c @ d) = bar {
|
||||||
|
()
|
||||||
|
} else {
|
||||||
|
()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
r#"
|
||||||
|
fn foo(x: Result<i32, ()>) {
|
||||||
|
let bar: Result<i32, ()> = Ok(1);
|
||||||
|
match bar {
|
||||||
|
Ok(a @ b @ c @ d) => (),
|
||||||
|
Err(_) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -316,43 +316,73 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
|
||||||
pat_head == var_head
|
pat_head == var_head
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool {
|
pub(crate) fn does_pat_variant_nested_or_literal(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool {
|
||||||
let depth = calc_depth(pat, 0);
|
check_pat_variant_nested_or_literal_with_depth(ctx, pat, 0)
|
||||||
|
|
||||||
if 1 < depth {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
|
fn check_pat_variant_from_enum(ctx: &AssistContext<'_>, pat: &ast::Pat) -> bool {
|
||||||
match pat {
|
ctx.sema.type_of_pat(pat).map_or(true, |ty: hir::TypeInfo| {
|
||||||
ast::Pat::IdentPat(_)
|
ty.adjusted().as_adt().map_or(false, |adt| matches!(adt, hir::Adt::Enum(_)))
|
||||||
| ast::Pat::BoxPat(_)
|
})
|
||||||
| ast::Pat::RestPat(_)
|
}
|
||||||
| ast::Pat::LiteralPat(_)
|
|
||||||
| ast::Pat::MacroPat(_)
|
|
||||||
| ast::Pat::OrPat(_)
|
|
||||||
| ast::Pat::ParenPat(_)
|
|
||||||
| ast::Pat::PathPat(_)
|
|
||||||
| ast::Pat::WildcardPat(_)
|
|
||||||
| ast::Pat::RangePat(_)
|
|
||||||
| ast::Pat::RecordPat(_)
|
|
||||||
| ast::Pat::RefPat(_)
|
|
||||||
| ast::Pat::SlicePat(_)
|
|
||||||
| ast::Pat::TuplePat(_)
|
|
||||||
| ast::Pat::ConstBlockPat(_) => depth,
|
|
||||||
|
|
||||||
// FIXME: Other patterns may also be nested. Currently it simply supports only `TupleStructPat`
|
fn check_pat_variant_nested_or_literal_with_depth(
|
||||||
ast::Pat::TupleStructPat(pat) => {
|
ctx: &AssistContext<'_>,
|
||||||
let mut max_depth = depth;
|
pat: &ast::Pat,
|
||||||
for p in pat.fields() {
|
depth_after_refutable: usize,
|
||||||
let d = calc_depth(&p, depth + 1);
|
) -> bool {
|
||||||
if d > max_depth {
|
if depth_after_refutable > 1 {
|
||||||
max_depth = d
|
return true;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
max_depth
|
match pat {
|
||||||
|
ast::Pat::RestPat(_) | ast::Pat::WildcardPat(_) | ast::Pat::RefPat(_) => false,
|
||||||
|
|
||||||
|
ast::Pat::LiteralPat(_)
|
||||||
|
| ast::Pat::RangePat(_)
|
||||||
|
| ast::Pat::MacroPat(_)
|
||||||
|
| ast::Pat::PathPat(_)
|
||||||
|
| ast::Pat::BoxPat(_)
|
||||||
|
| ast::Pat::ConstBlockPat(_) => true,
|
||||||
|
|
||||||
|
ast::Pat::IdentPat(ident_pat) => ident_pat.pat().map_or(false, |pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable)
|
||||||
|
}),
|
||||||
|
ast::Pat::ParenPat(paren_pat) => paren_pat.pat().map_or(true, |pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable)
|
||||||
|
}),
|
||||||
|
ast::Pat::TuplePat(tuple_pat) => tuple_pat.fields().any(|pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable)
|
||||||
|
}),
|
||||||
|
ast::Pat::RecordPat(record_pat) => {
|
||||||
|
let adjusted_next_depth =
|
||||||
|
depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 };
|
||||||
|
record_pat.record_pat_field_list().map_or(true, |pat| {
|
||||||
|
pat.fields().any(|pat| {
|
||||||
|
pat.pat().map_or(true, |pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(
|
||||||
|
ctx,
|
||||||
|
&pat,
|
||||||
|
adjusted_next_depth,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ast::Pat::OrPat(or_pat) => or_pat.pats().any(|pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(ctx, &pat, depth_after_refutable)
|
||||||
|
}),
|
||||||
|
ast::Pat::TupleStructPat(tuple_struct_pat) => {
|
||||||
|
let adjusted_next_depth =
|
||||||
|
depth_after_refutable + if check_pat_variant_from_enum(ctx, pat) { 1 } else { 0 };
|
||||||
|
tuple_struct_pat.fields().any(|pat| {
|
||||||
|
check_pat_variant_nested_or_literal_with_depth(ctx, &pat, adjusted_next_depth)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ast::Pat::SlicePat(slice_pat) => {
|
||||||
|
let mut pats = slice_pat.pats();
|
||||||
|
pats.next() // Edge case for `[..]`
|
||||||
|
.map_or(true, |pat| !matches!(pat, ast::Pat::RestPat(_)) || pats.next().is_some())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue