Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/handlers/apply_demorgan.rs')
-rw-r--r--crates/ide-assists/src/handlers/apply_demorgan.rs123
1 files changed, 73 insertions, 50 deletions
diff --git a/crates/ide-assists/src/handlers/apply_demorgan.rs b/crates/ide-assists/src/handlers/apply_demorgan.rs
index 491727a30a..77562c588e 100644
--- a/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -3,12 +3,19 @@ use std::collections::VecDeque;
use ide_db::{
assists::GroupLabel,
famous_defs::FamousDefs,
- source_change::SourceChangeBuilder,
syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
};
use syntax::{
- ast::{self, make, AstNode, Expr::BinExpr, HasArgList},
- ted, SyntaxKind, T,
+ ast::{
+ self,
+ prec::{precedence, ExprPrecedence},
+ syntax_factory::SyntaxFactory,
+ AstNode,
+ Expr::BinExpr,
+ HasArgList,
+ },
+ syntax_editor::{Position, SyntaxEditor},
+ SyntaxKind, T,
};
use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKind, Assists};
@@ -52,53 +59,60 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
}
let op = bin_expr.op_kind()?;
- let inv_token = match op {
- ast::BinaryOp::LogicOp(ast::LogicOp::And) => SyntaxKind::PIPE2,
- ast::BinaryOp::LogicOp(ast::LogicOp::Or) => SyntaxKind::AMP2,
+ let (inv_token, prec) = match op {
+ ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
+ ast::BinaryOp::LogicOp(ast::LogicOp::Or) => (SyntaxKind::AMP2, ExprPrecedence::LAnd),
_ => return None,
};
- let demorganed = bin_expr.clone_subtree().clone_for_update();
+ let make = SyntaxFactory::new();
+
+ let demorganed = bin_expr.clone_subtree();
+ let mut editor = SyntaxEditor::new(demorganed.syntax().clone());
+ editor.replace(demorganed.op_token()?, make.token(inv_token));
- ted::replace(demorganed.op_token()?, ast::make::token(inv_token));
let mut exprs = VecDeque::from([
- (bin_expr.lhs()?, demorganed.lhs()?),
- (bin_expr.rhs()?, demorganed.rhs()?),
+ (bin_expr.lhs()?, demorganed.lhs()?, prec),
+ (bin_expr.rhs()?, demorganed.rhs()?, prec),
]);
- while let Some((expr, dm)) = exprs.pop_front() {
+ while let Some((expr, demorganed, prec)) = exprs.pop_front() {
if let BinExpr(bin_expr) = &expr {
- if let BinExpr(cbin_expr) = &dm {
+ if let BinExpr(cbin_expr) = &demorganed {
if op == bin_expr.op_kind()? {
- ted::replace(cbin_expr.op_token()?, ast::make::token(inv_token));
- exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?));
- exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?));
+ editor.replace(cbin_expr.op_token()?, make.token(inv_token));
+ exprs.push_back((bin_expr.lhs()?, cbin_expr.lhs()?, prec));
+ exprs.push_back((bin_expr.rhs()?, cbin_expr.rhs()?, prec));
} else {
- let mut inv = invert_boolean_expression(expr);
- if inv.needs_parens_in(dm.syntax().parent()?) {
- inv = ast::make::expr_paren(inv).clone_for_update();
+ let mut inv = invert_boolean_expression(&make, expr);
+ if precedence(&inv).needs_parentheses_in(prec) {
+ inv = make.expr_paren(inv).into();
}
- ted::replace(dm.syntax(), inv.syntax());
+ editor.replace(demorganed.syntax(), inv.syntax());
}
} else {
return None;
}
} else {
- let mut inv = invert_boolean_expression(dm.clone_subtree()).clone_for_update();
- if inv.needs_parens_in(dm.syntax().parent()?) {
- inv = ast::make::expr_paren(inv).clone_for_update();
+ let mut inv = invert_boolean_expression(&make, demorganed.clone());
+ if precedence(&inv).needs_parentheses_in(prec) {
+ inv = make.expr_paren(inv).into();
}
- ted::replace(dm.syntax(), inv.syntax());
+ editor.replace(demorganed.syntax(), inv.syntax());
}
}
+ editor.add_mappings(make.finish_with_mappings());
+ let edit = editor.finish();
+ let demorganed = ast::Expr::cast(edit.new_root().clone())?;
+
acc.add_group(
&GroupLabel("Apply De Morgan's law".to_owned()),
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
"Apply De Morgan's law",
op_range,
- |edit| {
- let demorganed = ast::Expr::BinExpr(demorganed);
+ |builder| {
+ let make = SyntaxFactory::new();
let paren_expr = bin_expr.syntax().parent().and_then(ast::ParenExpr::cast);
let neg_expr = paren_expr
.clone()
@@ -107,24 +121,32 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
.map(ast::Expr::PrefixExpr);
+ let mut editor;
if let Some(paren_expr) = paren_expr {
if let Some(neg_expr) = neg_expr {
cov_mark::hit!(demorgan_double_negation);
let parent = neg_expr.syntax().parent();
+ editor = builder.make_editor(neg_expr.syntax());
- if parent.is_some_and(|parent| demorganed.needs_parens_in(parent)) {
+ if parent.is_some_and(|parent| demorganed.needs_parens_in(&parent)) {
cov_mark::hit!(demorgan_keep_parens_for_op_precedence2);
- edit.replace_ast(neg_expr, make::expr_paren(demorganed));
+ editor.replace(neg_expr.syntax(), make.expr_paren(demorganed).syntax());
} else {
- edit.replace_ast(neg_expr, demorganed);
+ editor.replace(neg_expr.syntax(), demorganed.syntax());
};
} else {
cov_mark::hit!(demorgan_double_parens);
- edit.replace_ast(paren_expr.into(), add_bang_paren(demorganed));
+ editor = builder.make_editor(paren_expr.syntax());
+
+ editor.replace(paren_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
} else {
- edit.replace_ast(bin_expr.into(), add_bang_paren(demorganed));
+ editor = builder.make_editor(bin_expr.syntax());
+ editor.replace(bin_expr.syntax(), add_bang_paren(&make, demorganed).syntax());
}
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
@@ -161,7 +183,7 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
let (name, arg_expr) = validate_method_call_expr(ctx, &method_call)?;
let ast::Expr::ClosureExpr(closure_expr) = arg_expr else { return None };
- let closure_body = closure_expr.body()?;
+ let closure_body = closure_expr.body()?.clone_for_update();
let op_range = method_call.syntax().text_range();
let label = format!("Apply De Morgan's law to `Iterator::{}`", name.text().as_str());
@@ -170,18 +192,19 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
AssistId("apply_demorgan_iterator", AssistKind::RefactorRewrite),
label,
op_range,
- |edit| {
+ |builder| {
+ let make = SyntaxFactory::new();
+ let mut editor = builder.make_editor(method_call.syntax());
// replace the method name
let new_name = match name.text().as_str() {
- "all" => make::name_ref("any"),
- "any" => make::name_ref("all"),
+ "all" => make.name_ref("any"),
+ "any" => make.name_ref("all"),
_ => unreachable!(),
- }
- .clone_for_update();
- edit.replace_ast(name, new_name);
+ };
+ editor.replace(name.syntax(), new_name.syntax());
// negate all tail expressions in the closure body
- let tail_cb = &mut |e: &_| tail_cb_impl(edit, e);
+ let tail_cb = &mut |e: &_| tail_cb_impl(&mut editor, &make, e);
walk_expr(&closure_body, &mut |expr| {
if let ast::Expr::ReturnExpr(ret_expr) = expr {
if let Some(ret_expr_arg) = &ret_expr.expr() {
@@ -198,15 +221,15 @@ pub(crate) fn apply_demorgan_iterator(acc: &mut Assists, ctx: &AssistContext<'_>
.and_then(ast::PrefixExpr::cast)
.filter(|prefix_expr| matches!(prefix_expr.op_kind(), Some(ast::UnaryOp::Not)))
{
- edit.delete(
- prefix_expr
- .op_token()
- .expect("prefix expression always has an operator")
- .text_range(),
+ editor.delete(
+ prefix_expr.op_token().expect("prefix expression always has an operator"),
);
} else {
- edit.insert(method_call.syntax().text_range().start(), "!");
+ editor.insert(Position::before(method_call.syntax()), make.token(SyntaxKind::BANG));
}
+
+ editor.add_mappings(make.finish_with_mappings());
+ builder.add_file_edits(ctx.file_id(), editor);
},
)
}
@@ -233,26 +256,26 @@ fn validate_method_call_expr(
it_type.impls_trait(sema.db, iter_trait, &[]).then_some((name_ref, arg_expr))
}
-fn tail_cb_impl(edit: &mut SourceChangeBuilder, e: &ast::Expr) {
+fn tail_cb_impl(editor: &mut SyntaxEditor, make: &SyntaxFactory, e: &ast::Expr) {
match e {
ast::Expr::BreakExpr(break_expr) => {
if let Some(break_expr_arg) = break_expr.expr() {
- for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(edit, e))
+ for_each_tail_expr(&break_expr_arg, &mut |e| tail_cb_impl(editor, make, e))
}
}
ast::Expr::ReturnExpr(_) => {
// all return expressions have already been handled by the walk loop
}
e => {
- let inverted_body = invert_boolean_expression(e.clone());
- edit.replace(e.syntax().text_range(), inverted_body.syntax().text());
+ let inverted_body = invert_boolean_expression(make, e.clone());
+ editor.replace(e.syntax(), inverted_body.syntax());
}
}
}
/// Add bang and parentheses to the expression.
-fn add_bang_paren(expr: ast::Expr) -> ast::Expr {
- make::expr_prefix(T![!], make::expr_paren(expr)).into()
+fn add_bang_paren(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr {
+ make.expr_prefix(T![!], make.expr_paren(expr).into()).into()
}
#[cfg(test)]