10491: Support nested type on replace if let with match r=k-nasa a=k-nasa

## Why

close: https://github.com/rust-analyzer/rust-analyzer/issues/8690

Now, Replacing if-let with match cant't output exhaustive patterns code.
This was because the `else` conversion used specific types (ex. Option, Result) instead of wildcards.

I thought it was more of a problem to generate non-exhaustive patterns than the benefits of using the concrete one.

How about using wildcards in `else`? 
Is this change policy acceptable?

## What

- using wildcards on `make_else_arm`
- Change test cases

Co-authored-by: k-nasa <htilcs1115@gmail.com>
This commit is contained in:
bors[bot] 2021-10-14 22:41:06 +00:00 committed by GitHub
commit 3a79af7e27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 71 additions and 1 deletions

View File

@ -12,7 +12,7 @@
};
use crate::{
utils::{does_pat_match_variant, unwrap_trivial_block},
utils::{does_nested_pattern, does_pat_match_variant, unwrap_trivial_block},
AssistContext, AssistId, AssistKind, Assists,
};
@ -143,6 +143,8 @@ fn make_else_arm(
Some((it, pat)) => {
if does_pat_match_variant(pat, &it.sad_pattern()) {
it.happy_pattern_wildcard()
} else if does_nested_pattern(pat) {
make::wildcard_pat().into()
} else {
it.sad_pattern()
}
@ -574,6 +576,33 @@ fn main() {
)
}
#[test]
fn nested_type() {
check_assist(
replace_if_let_with_match,
r#"
//- minicore: result
fn foo(x: Result<i32, ()>) {
let bar: Result<_, ()> = Ok(Some(1));
$0if let Ok(Some(_)) = bar {
()
} else {
()
}
}
"#,
r#"
fn foo(x: Result<i32, ()>) {
let bar: Result<_, ()> = Ok(Some(1));
match bar {
Ok(Some(_)) => (),
_ => (),
}
}
"#,
);
}
#[test]
fn test_replace_match_with_if_let_unwraps_simple_expressions() {
check_assist(

View File

@ -285,6 +285,47 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
pat_head == var_head
}
pub(crate) fn does_nested_pattern(pat: &ast::Pat) -> bool {
let depth = calc_depth(pat, 0);
if 1 < depth {
return true;
}
false
}
fn calc_depth(pat: &ast::Pat, depth: usize) -> usize {
match pat {
ast::Pat::IdentPat(_)
| 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`
ast::Pat::TupleStructPat(pat) => {
let mut max_depth = depth;
for p in pat.fields() {
let d = calc_depth(&p, depth + 1);
if d > max_depth {
max_depth = d
}
}
max_depth
}
}
}
// Uses a syntax-driven approach to find any impl blocks for the struct that
// exist within the module/file
//