7406: if_let_match: don't assume happy path r=matklad a=bugadani

Closes #7392

Co-authored-by: Dániel Buga <bugadani@gmail.com>
This commit is contained in:
bors[bot] 2021-01-26 12:12:45 +00:00 committed by GitHub
commit 3a5cd6bbc6
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 106 additions and 22 deletions

View file

@ -8,7 +8,7 @@ use syntax::ast::{self, make, AstNode, MatchArm, NameOwner, Pat};
use test_utils::mark;
use crate::{
utils::{render_snippet, Cursor},
utils::{does_pat_match_variant, render_snippet, Cursor},
AssistContext, AssistId, AssistKind, Assists,
};
@ -147,25 +147,6 @@ fn is_variant_missing(existing_arms: &mut Vec<MatchArm>, var: &Pat) -> bool {
})
}
fn does_pat_match_variant(pat: &Pat, var: &Pat) -> bool {
let first_node_text = |pat: &Pat| pat.syntax().first_child().map(|node| node.text());
let pat_head = match pat {
Pat::IdentPat(bind_pat) => {
if let Some(p) = bind_pat.pat() {
first_node_text(&p)
} else {
return false;
}
}
pat => first_node_text(pat),
};
let var_head = first_node_text(var);
pat_head == var_head
}
fn resolve_enum_def(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<hir::Enum> {
sema.type_of_expr(&expr)?.autoderef(sema.db).find_map(|ty| match ty.as_adt() {
Some(Adt::Enum(e)) => Some(e),

View file

@ -10,7 +10,10 @@ use syntax::{
AstNode,
};
use crate::{utils::unwrap_trivial_block, AssistContext, AssistId, AssistKind, Assists};
use crate::{
utils::{does_pat_match_variant, unwrap_trivial_block},
AssistContext, AssistId, AssistKind, Assists,
};
// Assist: replace_if_let_with_match
//
@ -66,7 +69,13 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
.sema
.type_of_pat(&pat)
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
.map(|it| it.sad_pattern())
.map(|it| {
if does_pat_match_variant(&pat, &it.sad_pattern()) {
it.happy_pattern()
} else {
it.sad_pattern()
}
})
.unwrap_or_else(|| make::wildcard_pat().into());
let else_expr = unwrap_trivial_block(else_block);
make::match_arm(vec![pattern], else_expr)
@ -278,6 +287,36 @@ fn foo(x: Option<i32>) {
);
}
#[test]
fn special_case_inverted_option() {
check_assist(
replace_if_let_with_match,
r#"
enum Option<T> { Some(T), None }
use Option::*;
fn foo(x: Option<i32>) {
$0if let None = x {
println!("none")
} else {
println!("some")
}
}
"#,
r#"
enum Option<T> { Some(T), None }
use Option::*;
fn foo(x: Option<i32>) {
match x {
None => println!("none"),
Some(_) => println!("some"),
}
}
"#,
);
}
#[test]
fn special_case_result() {
check_assist(
@ -308,6 +347,36 @@ fn foo(x: Result<i32, ()>) {
);
}
#[test]
fn special_case_inverted_result() {
check_assist(
replace_if_let_with_match,
r#"
enum Result<T, E> { Ok(T), Err(E) }
use Result::*;
fn foo(x: Result<i32, ()>) {
$0if let Err(x) = x {
println!("{}", x)
} else {
println!("ok")
}
}
"#,
r#"
enum Result<T, E> { Ok(T), Err(E) }
use Result::*;
fn foo(x: Result<i32, ()>) {
match x {
Err(x) => println!("{}", x),
Ok(_) => println!("ok"),
}
}
"#,
);
}
#[test]
fn nested_indent() {
check_assist(

View file

@ -248,3 +248,22 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
[Direction::Next, Direction::Prev].iter().copied()
}
pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
let first_node_text = |pat: &ast::Pat| pat.syntax().first_child().map(|node| node.text());
let pat_head = match pat {
ast::Pat::IdentPat(bind_pat) => {
if let Some(p) = bind_pat.pat() {
first_node_text(&p)
} else {
return pat.syntax().text() == var.syntax().text();
}
}
pat => first_node_text(pat),
};
let var_head = first_node_text(var);
pat_head == var_head
}

View file

@ -49,6 +49,21 @@ impl TryEnum {
}
}
pub fn happy_pattern(self) -> ast::Pat {
match self {
TryEnum::Result => make::tuple_struct_pat(
make::path_unqualified(make::path_segment(make::name_ref("Ok"))),
iter::once(make::wildcard_pat().into()),
)
.into(),
TryEnum::Option => make::tuple_struct_pat(
make::path_unqualified(make::path_segment(make::name_ref("Some"))),
iter::once(make::wildcard_pat().into()),
)
.into(),
}
}
fn type_name(self) -> &'static str {
match self {
TryEnum::Result => "Result",