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 mut collector = AssignmentsCollector {
49 common_lhs: assign_expr.lhs()?,
50 assignments: Vec::new(),
53 let tgt: ast::Expr = if let Some(if_expr) = ctx.find_node_at_offset::<ast::IfExpr>() {
54 collector.collect_if(&if_expr)?;
56 } else if let Some(match_expr) = ctx.find_node_at_offset::<ast::MatchExpr>() {
57 collector.collect_match(&match_expr)?;
64 AssistId("pull_assignment_up", AssistKind::RefactorExtract),
66 tgt.syntax().text_range(),
68 let assignments: Vec<_> = collector
71 .map(|(stmt, rhs)| (edit.make_ast_mut(stmt), rhs.clone_for_update()))
74 let tgt = edit.make_ast_mut(tgt);
76 for (stmt, rhs) in assignments {
77 ted::replace(stmt.syntax(), rhs.syntax());
79 let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
80 let assign_stmt = make::expr_stmt(assign_expr);
82 ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
87 struct AssignmentsCollector<'a> {
88 sema: &'a hir::Semantics<'a, ide_db::RootDatabase>,
89 common_lhs: ast::Expr,
90 assignments: Vec<(ast::ExprStmt, ast::Expr)>,
93 impl<'a> AssignmentsCollector<'a> {
94 fn collect_match(&mut self, match_expr: &ast::MatchExpr) -> Option<()> {
95 for arm in match_expr.match_arm_list()?.arms() {
97 ast::Expr::BlockExpr(block) => self.collect_block(&block)?,
98 // TODO: Handle this while we are at it?
105 fn collect_if(&mut self, if_expr: &ast::IfExpr) -> Option<()> {
106 let then_branch = if_expr.then_branch()?;
107 self.collect_block(&then_branch)?;
109 match if_expr.else_branch()? {
110 ast::ElseBranch::Block(block) => self.collect_block(&block),
111 ast::ElseBranch::IfExpr(expr) => {
112 cov_mark::hit!(test_pull_assignment_up_chained_if);
113 self.collect_if(&expr)
117 fn collect_block(&mut self, block: &ast::BlockExpr) -> Option<()> {
118 if block.tail_expr().is_some() {
122 let last_stmt = block.statements().last()?;
123 if let ast::Stmt::ExprStmt(stmt) = last_stmt {
124 if let ast::Expr::BinExpr(expr) = stmt.expr()? {
125 if expr.op_kind()? == ast::BinOp::Assignment
126 && is_equivalent(self.sema, &expr.lhs()?, &self.common_lhs)
128 self.assignments.push((stmt, expr.rhs()?));
139 sema: &hir::Semantics<ide_db::RootDatabase>,
143 match (expr0, expr1) {
144 (ast::Expr::FieldExpr(field_expr0), ast::Expr::FieldExpr(field_expr1)) => {
145 cov_mark::hit!(test_pull_assignment_up_field_assignment);
146 sema.resolve_field(field_expr0) == sema.resolve_field(field_expr1)
148 (ast::Expr::PathExpr(path0), ast::Expr::PathExpr(path1)) => {
149 let path0 = path0.path();
150 let path1 = path1.path();
151 if let (Some(path0), Some(path1)) = (path0, path1) {
152 sema.resolve_path(&path0) == sema.resolve_path(&path1)
157 (ast::Expr::PrefixExpr(prefix0), ast::Expr::PrefixExpr(prefix1))
158 if prefix0.op_kind() == Some(ast::PrefixOp::Deref)
159 && prefix1.op_kind() == Some(ast::PrefixOp::Deref) =>
161 cov_mark::hit!(test_pull_assignment_up_deref);
162 if let (Some(prefix0), Some(prefix1)) = (prefix0.expr(), prefix1.expr()) {
163 is_equivalent(sema, &prefix0, &prefix1)
176 use crate::tests::{check_assist, check_assist_not_applicable};
179 fn test_pull_assignment_up_if() {
206 fn test_pull_assignment_up_match() {
245 fn test_pull_assignment_up_not_last_not_applicable() {
246 check_assist_not_applicable(
263 fn test_pull_assignment_up_chained_if() {
264 cov_mark::check!(test_pull_assignment_up_chained_if);
295 fn test_pull_assignment_up_retains_stmts() {
326 fn pull_assignment_up_let_stmt_not_applicable() {
327 check_assist_not_applicable(
343 fn pull_assignment_up_if_missing_assigment_not_applicable() {
344 check_assist_not_applicable(
358 fn pull_assignment_up_match_missing_assigment_not_applicable() {
359 check_assist_not_applicable(
379 fn test_pull_assignment_up_field_assignment() {
380 cov_mark::check!(test_pull_assignment_up_field_assignment);
411 fn test_pull_assignment_up_deref() {
412 cov_mark::check!(test_pull_assignment_up_deref);
443 fn test_cant_pull_non_assignments() {
444 cov_mark::check!(test_cant_pull_non_assignments);
445 check_assist_not_applicable(