From 91e482b46d43a24cd0a48ea1119b93105140cff2 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov <aleksey.kladov@gmail.com> Date: Fri, 27 Mar 2020 12:12:17 +0100 Subject: Replace if with if-let --- crates/ra_assists/src/doc_tests/generated.rs | 26 +++++ .../src/handlers/replace_let_with_if_let.rs | 108 +++++++++++++++++++++ crates/ra_assists/src/lib.rs | 2 + 3 files changed, 136 insertions(+) create mode 100644 crates/ra_assists/src/handlers/replace_let_with_if_let.rs (limited to 'crates/ra_assists/src') diff --git a/crates/ra_assists/src/doc_tests/generated.rs b/crates/ra_assists/src/doc_tests/generated.rs index 62dcb3808..c0486a8a9 100644 --- a/crates/ra_assists/src/doc_tests/generated.rs +++ b/crates/ra_assists/src/doc_tests/generated.rs @@ -607,6 +607,32 @@ fn handle(action: Action) { ) } +#[test] +fn doctest_replace_let_with_if_let() { + check( + "replace_let_with_if_let", + r#####" +enum Option<T> { Some(T), None } + +fn main(action: Action) { + <|>let x = compute(); +} + +fn compute() -> Option<i32> { None } +"#####, + r#####" +enum Option<T> { Some(T), None } + +fn main(action: Action) { + if let Some(x) = compute() { + } +} + +fn compute() -> Option<i32> { None } +"#####, + ) +} + #[test] fn doctest_replace_qualified_name_with_use() { check( diff --git a/crates/ra_assists/src/handlers/replace_let_with_if_let.rs b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs new file mode 100644 index 000000000..10e41f97e --- /dev/null +++ b/crates/ra_assists/src/handlers/replace_let_with_if_let.rs @@ -0,0 +1,108 @@ +use hir::Adt; +use ra_syntax::{ + ast::{self, make}, + AstNode, T, +}; + +use crate::{ + assist_ctx::{Assist, AssistCtx}, + AssistId, +}; +use ast::edit::{AstNodeEdit, IndentLevel}; +use std::iter::once; + +// Assist: replace_let_with_if_let +// +// Replaces `if let` with an else branch with a `match` expression. +// +// ``` +// # enum Option<T> { Some(T), None } +// +// fn main(action: Action) { +// <|>let x = compute(); +// } +// +// fn compute() -> Option<i32> { None } +// ``` +// -> +// ``` +// # enum Option<T> { Some(T), None } +// +// fn main(action: Action) { +// if let Some(x) = compute() { +// } +// } +// +// fn compute() -> Option<i32> { None } +// ``` +pub(crate) fn replace_let_with_if_let(ctx: AssistCtx) -> Option<Assist> { + let let_kw = ctx.find_token_at_offset(T![let])?; + let let_stmt = let_kw.ancestors().find_map(ast::LetStmt::cast)?; + let init = let_stmt.initializer()?; + let original_pat = let_stmt.pat()?; + let ty = ctx.sema.type_of_expr(&init)?; + let enum_ = match ty.as_adt() { + Some(Adt::Enum(it)) => it, + _ => return None, + }; + let happy_case = + [("Result", "Ok"), ("Option", "Some")].iter().find_map(|(known_type, happy_case)| { + if &enum_.name(ctx.db).to_string() == known_type { + return Some(happy_case); + } + None + }); + + ctx.add_assist(AssistId("replace_let_with_if_let"), "Replace with if-let", |edit| { + let with_placeholder: ast::Pat = match happy_case { + None => make::placeholder_pat().into(), + Some(var_name) => make::tuple_struct_pat( + make::path_unqualified(make::path_segment(make::name_ref(var_name))), + once(make::placeholder_pat().into()), + ) + .into(), + }; + let block = + IndentLevel::from_node(let_stmt.syntax()).increase_indent(make::block_expr(None, None)); + let if_ = make::expr_if(make::condition(init, Some(with_placeholder)), block); + let stmt = make::expr_stmt(if_); + + let placeholder = stmt.syntax().descendants().find_map(ast::PlaceholderPat::cast).unwrap(); + let target_offset = + let_stmt.syntax().text_range().start() + placeholder.syntax().text_range().start(); + let stmt = stmt.replace_descendant(placeholder.into(), original_pat); + + edit.replace_ast(ast::Stmt::from(let_stmt), ast::Stmt::from(stmt)); + edit.target(let_kw.text_range()); + edit.set_cursor(target_offset); + }) +} + +#[cfg(test)] +mod tests { + use crate::helpers::check_assist; + + use super::*; + + #[test] + fn replace_let_unknown_enum() { + check_assist( + replace_let_with_if_let, + r" +enum E<T> { X(T), Y(T) } + +fn main() { + <|>let x = E::X(92); +} + ", + r" +enum E<T> { X(T), Y(T) } + +fn main() { + if let <|>x = E::X(92) { + } +} + ", + ) + } +} diff --git a/crates/ra_assists/src/lib.rs b/crates/ra_assists/src/lib.rs index bcc9b3f10..67a58fc1f 100644 --- a/crates/ra_assists/src/lib.rs +++ b/crates/ra_assists/src/lib.rs @@ -118,6 +118,7 @@ mod handlers { mod remove_dbg; mod remove_mut; mod replace_if_let_with_match; + mod replace_let_with_if_let; mod replace_qualified_name_with_use; mod split_import; @@ -153,6 +154,7 @@ mod handlers { remove_dbg::remove_dbg, remove_mut::remove_mut, replace_if_let_with_match::replace_if_let_with_match, + replace_let_with_if_let::replace_let_with_if_let, replace_qualified_name_with_use::replace_qualified_name_with_use, split_import::split_import, ] -- cgit v1.2.3