2 ast::{self, edit::AstNodeEdit, make},
7 assist_context::{AssistContext, Assists},
11 // Assist: pull_assignment_up
13 // Extracts variable assignment to outside an if or match statement.
38 pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
39 let assign_expr = ctx.find_node_at_offset::<ast::BinExpr>()?;
41 let op_kind = assign_expr.op_kind()?;
42 if op_kind != ast::BinOp::Assignment {
43 cov_mark::hit!(test_cant_pull_non_assignments);
47 let name_expr = assign_expr.lhs()?;
49 let old_stmt: ast::Expr;
50 let new_stmt: ast::Expr;
52 if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
53 new_stmt = exprify_if(&if_expr, &ctx.sema, &name_expr)?.indent(if_expr.indent_level());
54 old_stmt = if_expr.into();
55 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
56 new_stmt = exprify_match(&match_expr, &ctx.sema, &name_expr)?;
57 old_stmt = match_expr.into()
62 let expr_stmt = make::expr_stmt(new_stmt);
65 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
67 old_stmt.syntax().text_range(),
69 edit.replace(old_stmt.syntax().text_range(), format!("{} = {};", name_expr, expr_stmt));
75 match_expr: &ast::MatchExpr,
76 sema: &hir::Semantics<ide_db::RootDatabase>,
78 ) -> Option<ast::Expr> {
79 let new_arm_list = match_expr
83 if let ast::Expr::BlockExpr(block) = arm.expr()? {
84 let new_block = exprify_block(&block, sema, name)?.indent(block.indent_level());
85 Some(arm.replace_descendant(block, new_block))
90 .collect::<Option<Vec<_>>>()?;
91 let new_arm_list = match_expr
93 .replace_descendants(match_expr.match_arm_list()?.arms().zip(new_arm_list));
94 Some(make::expr_match(match_expr.expr()?, new_arm_list))
98 statement: &ast::IfExpr,
99 sema: &hir::Semantics<ide_db::RootDatabase>,
101 ) -> Option<ast::Expr> {
102 let then_branch = exprify_block(&statement.then_branch()?, sema, name)?;
103 let else_branch = match statement.else_branch()? {
104 ast::ElseBranch::Block(block) => ast::ElseBranch::Block(exprify_block(&block, sema, name)?),
105 ast::ElseBranch::IfExpr(expr) => {
106 cov_mark::hit!(test_pull_assignment_up_chained_if);
107 ast::ElseBranch::IfExpr(ast::IfExpr::cast(
108 exprify_if(&expr, sema, name)?.syntax().to_owned(),
112 Some(make::expr_if(statement.condition()?, then_branch, Some(else_branch)))
116 block: &ast::BlockExpr,
117 sema: &hir::Semantics<ide_db::RootDatabase>,
119 ) -> Option<ast::BlockExpr> {
120 if block.tail_expr().is_some() {
124 let mut stmts: Vec<_> = block.statements().collect();
125 let stmt = stmts.pop()?;
127 if let ast::Stmt::ExprStmt(stmt) = stmt {
128 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
129 if expr.op_kind()? == ast::BinOp::Assignment && is_equivalent(sema, &expr.lhs()?, name)
131 // The last statement in the block is an assignment to the name we want
132 return Some(make::block_expr(stmts, Some(expr.rhs()?)));
140 sema: &hir::Semantics<ide_db::RootDatabase>,
144 match (expr0, expr1) {
145 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
146 cov_mark::hit!(test_pull_assignment_up_field_assignment);
147 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
149 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
150 let path0 = path0.path();
151 let path1 = path1.path();
152 if let (Some(path0), Some(path1)) = (path0, path1) {
153 sema.resolve_path(&path0) == sema.resolve_path(&path1)
158 (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
159 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
160 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
162 cov_mark::hit!(test_pull_assignment_up_deref);
163 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
164 is_equivalent(sema, &prefix0, &prefix1)
177 use crate::tests::{check_assist, check_assist_not_applicable};
180 fn test_pull_assignment_up_if() {
207 fn test_pull_assignment_up_match() {
246 fn test_pull_assignment_up_not_last_not_applicable() {
247 check_assist_not_applicable(
264 fn test_pull_assignment_up_chained_if() {
265 cov_mark::check!(test_pull_assignment_up_chained_if);
296 fn test_pull_assignment_up_retains_stmts() {
327 fn pull_assignment_up_let_stmt_not_applicable() {
328 check_assist_not_applicable(
344 fn pull_assignment_up_if_missing_assigment_not_applicable() {
345 check_assist_not_applicable(
359 fn pull_assignment_up_match_missing_assigment_not_applicable() {
360 check_assist_not_applicable(
380 fn test_pull_assignment_up_field_assignment() {
381 cov_mark::check!(test_pull_assignment_up_field_assignment);
412 fn test_pull_assignment_up_deref() {
413 cov_mark::check!(test_pull_assignment_up_deref);
444 fn test_cant_pull_non_assignments() {
445 cov_mark::check!(test_cant_pull_non_assignments);
446 check_assist_not_applicable(