Handle not let if expressions in replace_if_let_with_match
This commit is contained in:
parent
20be999304
commit
8967856d78
@ -1,5 +1,6 @@
|
|||||||
use std::iter::{self, successors};
|
use std::iter::{self, successors};
|
||||||
|
|
||||||
|
use either::Either;
|
||||||
use ide_db::{ty_filter::TryEnum, RootDatabase};
|
use ide_db::{ty_filter::TryEnum, RootDatabase};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{
|
ast::{
|
||||||
@ -53,17 +54,30 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||||||
});
|
});
|
||||||
let scrutinee_to_be_expr = if_expr.condition()?.expr()?;
|
let scrutinee_to_be_expr = if_expr.condition()?.expr()?;
|
||||||
|
|
||||||
let mut pat_bodies = Vec::new();
|
let mut pat_seen = false;
|
||||||
|
let mut cond_bodies = Vec::new();
|
||||||
for if_expr in if_exprs {
|
for if_expr in if_exprs {
|
||||||
let cond = if_expr.condition()?;
|
let cond = if_expr.condition()?;
|
||||||
let expr = cond.expr()?;
|
let expr = cond.expr()?;
|
||||||
|
let cond = match cond.pat() {
|
||||||
|
Some(pat) => {
|
||||||
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
|
if scrutinee_to_be_expr.syntax().text() != expr.syntax().text() {
|
||||||
// Only if all condition expressions are equal we can merge them into a match
|
// Only if all condition expressions are equal we can merge them into a match
|
||||||
return None;
|
return None;
|
||||||
|
} else {
|
||||||
|
pat_seen = true;
|
||||||
|
Either::Left(pat)
|
||||||
}
|
}
|
||||||
let pat = cond.pat()?;
|
}
|
||||||
|
None => Either::Right(expr),
|
||||||
|
};
|
||||||
let body = if_expr.then_branch()?;
|
let body = if_expr.then_branch()?;
|
||||||
pat_bodies.push((pat, body));
|
cond_bodies.push((cond, body));
|
||||||
|
}
|
||||||
|
|
||||||
|
if !pat_seen {
|
||||||
|
// Don't offer turning an if (chain) without patterns into a match
|
||||||
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let target = if_expr.syntax().text_range();
|
let target = if_expr.syntax().text_range();
|
||||||
@ -76,8 +90,8 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||||||
let else_arm = {
|
let else_arm = {
|
||||||
match else_block {
|
match else_block {
|
||||||
Some(else_block) => {
|
Some(else_block) => {
|
||||||
let pattern = match &*pat_bodies {
|
let pattern = match &*cond_bodies {
|
||||||
[(pat, _)] => ctx
|
[(Either::Left(pat), _)] => ctx
|
||||||
.sema
|
.sema
|
||||||
.type_of_pat(&pat)
|
.type_of_pat(&pat)
|
||||||
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
|
.and_then(|ty| TryEnum::from_ty(&ctx.sema, &ty))
|
||||||
@ -99,19 +113,30 @@ pub(crate) fn replace_if_let_with_match(acc: &mut Assists, ctx: &AssistContext)
|
|||||||
),
|
),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let arms = pat_bodies
|
let arms = cond_bodies
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(pat, body)| {
|
.map(|(pat, body)| {
|
||||||
let body = body.reset_indent().indent(IndentLevel(1));
|
let body = body.reset_indent().indent(IndentLevel(1));
|
||||||
make::match_arm(vec![pat], unwrap_trivial_block(body))
|
match pat {
|
||||||
|
Either::Left(pat) => {
|
||||||
|
make::match_arm(iter::once(pat), unwrap_trivial_block(body))
|
||||||
|
}
|
||||||
|
Either::Right(expr) => make::match_arm_with_guard(
|
||||||
|
iter::once(make::wildcard_pat().into()),
|
||||||
|
expr,
|
||||||
|
unwrap_trivial_block(body),
|
||||||
|
),
|
||||||
|
}
|
||||||
})
|
})
|
||||||
.chain(iter::once(else_arm));
|
.chain(iter::once(else_arm));
|
||||||
let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
|
let match_expr = make::expr_match(scrutinee_to_be_expr, make::match_arm_list(arms));
|
||||||
match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
|
match_expr.indent(IndentLevel::from_node(if_expr.syntax()))
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr =
|
let has_preceding_if_expr =
|
||||||
if if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind())) {
|
if_expr.syntax().parent().map_or(false, |it| ast::IfExpr::can_cast(it.kind()));
|
||||||
|
let expr = if has_preceding_if_expr {
|
||||||
|
// make sure we replace the `else if let ...` with a block so we don't end up with `else expr`
|
||||||
make::block_expr(None, Some(match_expr)).into()
|
make::block_expr(None, Some(match_expr)).into()
|
||||||
} else {
|
} else {
|
||||||
match_expr
|
match_expr
|
||||||
@ -210,7 +235,19 @@ fn is_pat_wildcard_or_sad(sema: &hir::Semantics<RootDatabase>, pat: &ast::Pat) -
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::tests::{check_assist, check_assist_target};
|
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_if_let_with_match_unapplicable_for_simple_ifs() {
|
||||||
|
check_assist_not_applicable(
|
||||||
|
replace_if_let_with_match,
|
||||||
|
r#"
|
||||||
|
fn main() {
|
||||||
|
if $0true {} else if false {} else {}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_if_let_with_match_no_else() {
|
fn test_if_let_with_match_no_else() {
|
||||||
@ -223,7 +260,8 @@ pub fn foo(&self) {
|
|||||||
self.foo();
|
self.foo();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
r#"
|
r#"
|
||||||
impl VariantData {
|
impl VariantData {
|
||||||
pub fn foo(&self) {
|
pub fn foo(&self) {
|
||||||
@ -234,7 +272,8 @@ pub fn foo(&self) {
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -249,19 +288,23 @@ pub fn is_struct(&self) -> bool {
|
|||||||
true
|
true
|
||||||
} else if let VariantData::Tuple(..) = *self {
|
} else if let VariantData::Tuple(..) = *self {
|
||||||
false
|
false
|
||||||
|
} else if cond() {
|
||||||
|
true
|
||||||
} else {
|
} else {
|
||||||
bar(
|
bar(
|
||||||
123
|
123
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
r#"
|
r#"
|
||||||
impl VariantData {
|
impl VariantData {
|
||||||
pub fn is_struct(&self) -> bool {
|
pub fn is_struct(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
VariantData::Struct(..) => true,
|
VariantData::Struct(..) => true,
|
||||||
VariantData::Tuple(..) => false,
|
VariantData::Tuple(..) => false,
|
||||||
|
_ if cond() => true,
|
||||||
_ => {
|
_ => {
|
||||||
bar(
|
bar(
|
||||||
123
|
123
|
||||||
@ -269,7 +312,8 @@ pub fn is_struct(&self) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +332,8 @@ pub fn is_struct(&self) -> bool {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
r#"
|
r#"
|
||||||
impl VariantData {
|
impl VariantData {
|
||||||
pub fn is_struct(&self) -> bool {
|
pub fn is_struct(&self) -> bool {
|
||||||
@ -301,7 +346,8 @@ pub fn is_struct(&self) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} "#,
|
}
|
||||||
|
"#,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -430,6 +430,19 @@ fn from_text(text: &str) -> ast::MatchArm {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn match_arm_with_guard(
|
||||||
|
pats: impl IntoIterator<Item = ast::Pat>,
|
||||||
|
guard: ast::Expr,
|
||||||
|
expr: ast::Expr,
|
||||||
|
) -> ast::MatchArm {
|
||||||
|
let pats_str = pats.into_iter().join(" | ");
|
||||||
|
return from_text(&format!("{} if {} => {}", pats_str, guard, expr));
|
||||||
|
|
||||||
|
fn from_text(text: &str) -> ast::MatchArm {
|
||||||
|
ast_from_text(&format!("fn f() {{ match () {{{}}} }}", text))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
|
pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::MatchArmList {
|
||||||
let arms_str = arms
|
let arms_str = arms
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
Loading…
Reference in New Issue
Block a user