Merge #7406
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:
commit
3a5cd6bbc6
4 changed files with 106 additions and 22 deletions
|
@ -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),
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Add table
Reference in a new issue