mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Merge #3742
3742: Replace if with if-let r=matklad a=matklad
bors r+
🤖
Co-authored-by: Aleksey Kladov <aleksey.kladov@gmail.com>
This commit is contained in:
commit
a4901fdcfd
6 changed files with 170 additions and 5 deletions
|
@ -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]
|
#[test]
|
||||||
fn doctest_replace_qualified_name_with_use() {
|
fn doctest_replace_qualified_name_with_use() {
|
||||||
check(
|
check(
|
||||||
|
|
108
crates/ra_assists/src/handlers/replace_let_with_if_let.rs
Normal file
108
crates/ra_assists/src/handlers/replace_let_with_if_let.rs
Normal file
|
@ -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) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,6 +118,7 @@ mod handlers {
|
||||||
mod remove_dbg;
|
mod remove_dbg;
|
||||||
mod remove_mut;
|
mod remove_mut;
|
||||||
mod replace_if_let_with_match;
|
mod replace_if_let_with_match;
|
||||||
|
mod replace_let_with_if_let;
|
||||||
mod replace_qualified_name_with_use;
|
mod replace_qualified_name_with_use;
|
||||||
mod replace_unwrap_with_match;
|
mod replace_unwrap_with_match;
|
||||||
mod split_import;
|
mod split_import;
|
||||||
|
@ -154,6 +155,7 @@ mod handlers {
|
||||||
remove_dbg::remove_dbg,
|
remove_dbg::remove_dbg,
|
||||||
remove_mut::remove_mut,
|
remove_mut::remove_mut,
|
||||||
replace_if_let_with_match::replace_if_let_with_match,
|
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,
|
replace_qualified_name_with_use::replace_qualified_name_with_use,
|
||||||
replace_unwrap_with_match::replace_unwrap_with_match,
|
replace_unwrap_with_match::replace_unwrap_with_match,
|
||||||
split_import::split_import,
|
split_import::split_import,
|
||||||
|
|
|
@ -251,7 +251,7 @@ impl ast::UseItem {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem {
|
pub fn with_use_tree(&self, use_tree: ast::UseTree) -> ast::UseItem {
|
||||||
if let Some(old) = self.use_tree() {
|
if let Some(old) = self.use_tree() {
|
||||||
return self.replace_descendants(iter::once((old, use_tree)));
|
return self.replace_descendant(old, use_tree);
|
||||||
}
|
}
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
@ -283,7 +283,7 @@ impl ast::UseTree {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
|
pub fn with_path(&self, path: ast::Path) -> ast::UseTree {
|
||||||
if let Some(old) = self.path() {
|
if let Some(old) = self.path() {
|
||||||
return self.replace_descendants(iter::once((old, path)));
|
return self.replace_descendant(old, path);
|
||||||
}
|
}
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
@ -291,7 +291,7 @@ impl ast::UseTree {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
|
pub fn with_use_tree_list(&self, use_tree_list: ast::UseTreeList) -> ast::UseTree {
|
||||||
if let Some(old) = self.use_tree_list() {
|
if let Some(old) = self.use_tree_list() {
|
||||||
return self.replace_descendants(iter::once((old, use_tree_list)));
|
return self.replace_descendant(old, use_tree_list);
|
||||||
}
|
}
|
||||||
self.clone()
|
self.clone()
|
||||||
}
|
}
|
||||||
|
@ -465,6 +465,11 @@ pub trait AstNodeEdit: AstNode + Sized {
|
||||||
Self::cast(new_syntax).unwrap()
|
Self::cast(new_syntax).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn replace_descendant<D: AstNode>(&self, old: D, new: D) -> Self {
|
||||||
|
self.replace_descendants(iter::once((old, new)))
|
||||||
|
}
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
fn replace_descendants<D: AstNode>(
|
fn replace_descendants<D: AstNode>(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -127,7 +127,7 @@ pub fn condition(expr: ast::Expr, pattern: Option<ast::Pat>) -> ast::Condition {
|
||||||
match pattern {
|
match pattern {
|
||||||
None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
|
None => ast_from_text(&format!("const _: () = while {} {{}};", expr)),
|
||||||
Some(pattern) => {
|
Some(pattern) => {
|
||||||
ast_from_text(&format!("const _: () = while {} = {} {{}};", pattern, expr))
|
ast_from_text(&format!("const _: () = while let {} = {} {{}};", pattern, expr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -245,7 +245,8 @@ pub fn let_stmt(pattern: ast::Pat, initializer: Option<ast::Expr>) -> ast::LetSt
|
||||||
ast_from_text(&format!("fn f() {{ {} }}", text))
|
ast_from_text(&format!("fn f() {{ {} }}", text))
|
||||||
}
|
}
|
||||||
pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
|
pub fn expr_stmt(expr: ast::Expr) -> ast::ExprStmt {
|
||||||
ast_from_text(&format!("fn f() {{ {}; }}", expr))
|
let semi = if expr.is_block_like() { "" } else { ";" };
|
||||||
|
ast_from_text(&format!("fn f() {{ {}{} (); }}", expr, semi))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn token(kind: SyntaxKind) -> SyntaxToken {
|
pub fn token(kind: SyntaxKind) -> SyntaxToken {
|
||||||
|
|
|
@ -583,6 +583,29 @@ fn handle(action: Action) {
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## `replace_let_with_if_let`
|
||||||
|
|
||||||
|
Replaces `if let` with an else branch with a `match` expression.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// BEFORE
|
||||||
|
|
||||||
|
fn main(action: Action) {
|
||||||
|
┃let x = compute();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute() -> Option<i32> { None }
|
||||||
|
|
||||||
|
// AFTER
|
||||||
|
|
||||||
|
fn main(action: Action) {
|
||||||
|
if let Some(x) = compute() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute() -> Option<i32> { None }
|
||||||
|
```
|
||||||
|
|
||||||
## `replace_qualified_name_with_use`
|
## `replace_qualified_name_with_use`
|
||||||
|
|
||||||
Adds a use statement for a given fully-qualified name.
|
Adds a use statement for a given fully-qualified name.
|
||||||
|
|
Loading…
Reference in a new issue