mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 13:48:50 +00:00
Use specific pattern when translating if-let-else to match
We *probably* should actually use the same machinery here, as we do for fill match arms, but just special-casing options and results seems to be a good first step.
This commit is contained in:
parent
73bef854ab
commit
7c3c289dab
4 changed files with 124 additions and 21 deletions
|
@ -1,11 +1,10 @@
|
|||
use ra_fmt::unwrap_trivial_block;
|
||||
use ra_syntax::{
|
||||
ast::{self, make},
|
||||
ast::{self, edit::IndentLevel, make},
|
||||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{Assist, AssistCtx, AssistId};
|
||||
use ast::edit::IndentLevel;
|
||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: replace_if_let_with_match
|
||||
//
|
||||
|
@ -44,15 +43,21 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
ast::ElseBranch::IfExpr(_) => return None,
|
||||
};
|
||||
|
||||
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", |edit| {
|
||||
let sema = ctx.sema;
|
||||
ctx.add_assist(AssistId("replace_if_let_with_match"), "Replace with match", move |edit| {
|
||||
let match_expr = {
|
||||
let then_arm = {
|
||||
let then_expr = unwrap_trivial_block(then_block);
|
||||
make::match_arm(vec![pat], then_expr)
|
||||
make::match_arm(vec![pat.clone()], then_expr)
|
||||
};
|
||||
let else_arm = {
|
||||
let pattern = sema
|
||||
.type_of_pat(&pat)
|
||||
.and_then(|ty| TryEnum::from_ty(sema, &ty))
|
||||
.map(|it| it.sad_pattern())
|
||||
.unwrap_or_else(|| make::placeholder_pat().into());
|
||||
let else_expr = unwrap_trivial_block(else_block);
|
||||
make::match_arm(vec![make::placeholder_pat().into()], else_expr)
|
||||
make::match_arm(vec![pattern], else_expr)
|
||||
};
|
||||
make::expr_match(expr, make::match_arm_list(vec![then_arm, else_arm]))
|
||||
};
|
||||
|
@ -68,6 +73,7 @@ pub(crate) fn replace_if_let_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
use crate::helpers::{check_assist, check_assist_target};
|
||||
|
||||
#[test]
|
||||
|
@ -145,4 +151,64 @@ impl VariantData {
|
|||
}",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_case_option() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
<|>if let Some(x) = x {
|
||||
println!("{}", x)
|
||||
} else {
|
||||
println!("none")
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
enum Option<T> { Some(T), None }
|
||||
use Option::*;
|
||||
|
||||
fn foo(x: Option<i32>) {
|
||||
<|>match x {
|
||||
Some(x) => println!("{}", x),
|
||||
None => println!("none"),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn special_case_result() {
|
||||
check_assist(
|
||||
replace_if_let_with_match,
|
||||
r#"
|
||||
enum Result<T, E> { Ok(T), Err(E) }
|
||||
use Result::*;
|
||||
|
||||
fn foo(x: Result<i32, ()>) {
|
||||
<|>if let Ok(x) = x {
|
||||
println!("{}", x)
|
||||
} else {
|
||||
println!("none")
|
||||
}
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
enum Result<T, E> { Ok(T), Err(E) }
|
||||
use Result::*;
|
||||
|
||||
fn foo(x: Result<i32, ()>) {
|
||||
<|>match x {
|
||||
Ok(x) => println!("{}", x),
|
||||
Err(_) => println!("none"),
|
||||
}
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ use ra_syntax::{
|
|||
|
||||
use crate::{
|
||||
assist_ctx::{Assist, AssistCtx},
|
||||
utils::happy_try_variant,
|
||||
utils::TryEnum,
|
||||
AssistId,
|
||||
};
|
||||
|
||||
|
@ -45,7 +45,7 @@ pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> {
|
|||
let init = let_stmt.initializer()?;
|
||||
let original_pat = let_stmt.pat()?;
|
||||
let ty = ctx.sema.type_of_expr(&init)?;
|
||||
let happy_variant = happy_try_variant(ctx.sema, &ty);
|
||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty).map(|it| it.happy_case());
|
||||
|
||||
ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| {
|
||||
let with_placeholder: ast::Pat = match happy_variant {
|
||||
|
|
|
@ -5,7 +5,7 @@ use ra_syntax::{
|
|||
AstNode,
|
||||
};
|
||||
|
||||
use crate::{utils::happy_try_variant, Assist, AssistCtx, AssistId};
|
||||
use crate::{utils::TryEnum, Assist, AssistCtx, AssistId};
|
||||
|
||||
// Assist: replace_unwrap_with_match
|
||||
//
|
||||
|
@ -37,7 +37,7 @@ pub(crate) fn replace_unwrap_with_match(ctx: AssistCtx) -> Option<Assist> {
|
|||
}
|
||||
let caller = method_call.expr()?;
|
||||
let ty = ctx.sema.type_of_expr(&caller)?;
|
||||
let happy_variant = happy_try_variant(ctx.sema, &ty)?;
|
||||
let happy_variant = TryEnum::from_ty(ctx.sema, &ty)?.happy_case();
|
||||
|
||||
ctx.add_assist(AssistId("replace_unwrap_with_match"), "Replace unwrap with match", |edit| {
|
||||
let ok_path = make::path_unqualified(make::path_segment(make::name_ref(happy_variant)));
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
//! Assorted functions shared by several assists.
|
||||
pub(crate) mod insert_use;
|
||||
|
||||
use std::iter;
|
||||
|
||||
use hir::{Adt, Semantics, Type};
|
||||
use ra_ide_db::RootDatabase;
|
||||
use ra_syntax::{
|
||||
|
@ -100,15 +102,50 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn happy_try_variant(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<&'static str> {
|
||||
let enum_ = match ty.as_adt() {
|
||||
Some(Adt::Enum(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
[("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| {
|
||||
if &enum_.name(sema.db).to_string() == known_type {
|
||||
return Some(*happy_case);
|
||||
}
|
||||
None
|
||||
})
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) enum TryEnum {
|
||||
Result,
|
||||
Option,
|
||||
}
|
||||
|
||||
impl TryEnum {
|
||||
const ALL: [TryEnum; 2] = [TryEnum::Option, TryEnum::Result];
|
||||
|
||||
pub(crate) fn from_ty(sema: &Semantics<RootDatabase>, ty: &Type) -> Option<TryEnum> {
|
||||
let enum_ = match ty.as_adt() {
|
||||
Some(Adt::Enum(it)) => it,
|
||||
_ => return None,
|
||||
};
|
||||
TryEnum::ALL.iter().find_map(|&var| {
|
||||
if &enum_.name(sema.db).to_string() == var.type_name() {
|
||||
return Some(var);
|
||||
}
|
||||
None
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn happy_case(self) -> &'static str {
|
||||
match self {
|
||||
TryEnum::Result => "Ok",
|
||||
TryEnum::Option => "Some",
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn sad_pattern(self) -> ast::Pat {
|
||||
match self {
|
||||
TryEnum::Result => make::tuple_struct_pat(
|
||||
make::path_unqualified(make::path_segment(make::name_ref("Err"))),
|
||||
iter::once(make::placeholder_pat().into()),
|
||||
)
|
||||
.into(),
|
||||
TryEnum::Option => make::bind_pat(make::name("None")).into(),
|
||||
}
|
||||
}
|
||||
|
||||
fn type_name(self) -> &'static str {
|
||||
match self {
|
||||
TryEnum::Result => "Result",
|
||||
TryEnum::Option => "Option",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue