Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/utils.rs')
| -rw-r--r-- | crates/ide-assists/src/utils.rs | 78 |
1 files changed, 74 insertions, 4 deletions
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index c1332d99bf..39686f065a 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -17,7 +17,9 @@ use syntax::{ self, edit::{AstNodeEdit, IndentLevel}, edit_in_place::{AttrsOwnerEdit, Indent, Removable}, - make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, + make, + syntax_factory::SyntaxFactory, + HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, }, ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile, SyntaxKind::*, @@ -245,11 +247,79 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize { .unwrap_or_else(|| node.text_range().start()) } -pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { - invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) +pub(crate) fn invert_boolean_expression(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr { + invert_special_case(make, &expr).unwrap_or_else(|| make.expr_prefix(T![!], expr).into()) } -fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { +// FIXME: Migrate usages of this function to the above function and remove this. +pub(crate) fn invert_boolean_expression_legacy(expr: ast::Expr) -> ast::Expr { + invert_special_case_legacy(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into()) +} + +fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Expr> { + match expr { + ast::Expr::BinExpr(bin) => { + let op_kind = bin.op_kind()?; + let rev_kind = match op_kind { + ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated }) => { + ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: !negated }) + } + ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict }) => { + ast::BinaryOp::CmpOp(ast::CmpOp::Ord { + ordering: ast::Ordering::Greater, + strict: !strict, + }) + } + ast::BinaryOp::CmpOp(ast::CmpOp::Ord { + ordering: ast::Ordering::Greater, + strict, + }) => ast::BinaryOp::CmpOp(ast::CmpOp::Ord { + ordering: ast::Ordering::Less, + strict: !strict, + }), + // Parenthesize other expressions before prefixing `!` + _ => { + return Some( + make.expr_prefix(T![!], make.expr_paren(expr.clone()).into()).into(), + ); + } + }; + + Some(make.expr_bin(bin.lhs()?, rev_kind, bin.rhs()?).into()) + } + ast::Expr::MethodCallExpr(mce) => { + let receiver = mce.receiver()?; + let method = mce.name_ref()?; + let arg_list = mce.arg_list()?; + + let method = match method.text().as_str() { + "is_some" => "is_none", + "is_none" => "is_some", + "is_ok" => "is_err", + "is_err" => "is_ok", + _ => return None, + }; + + Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into()) + } + ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? { + ast::Expr::ParenExpr(parexpr) => { + parexpr.expr().map(|e| e.clone_subtree().clone_for_update()) + } + _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()), + }, + ast::Expr::Literal(lit) => match lit.kind() { + ast::LiteralKind::Bool(b) => match b { + true => Some(ast::Expr::Literal(make.expr_literal("false"))), + false => Some(ast::Expr::Literal(make.expr_literal("true"))), + }, + _ => None, + }, + _ => None, + } +} + +fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> { match expr { ast::Expr::BinExpr(bin) => { let bin = bin.clone_for_update(); |