From 9db970ee082e315cfa04db163fe2e0268b618531 Mon Sep 17 00:00:00 2001 From: lbrande Date: Mon, 22 Feb 2021 16:23:42 +0100 Subject: De Morgan's Law assist now correctly inverts <, <=, >, >=. --- crates/ide_assists/src/utils.rs | 50 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 5 deletions(-) (limited to 'crates/ide_assists/src/utils.rs') diff --git a/crates/ide_assists/src/utils.rs b/crates/ide_assists/src/utils.rs index cd026d432..38ed74673 100644 --- a/crates/ide_assists/src/utils.rs +++ b/crates/ide_assists/src/utils.rs @@ -3,8 +3,11 @@ use std::ops; use ast::TypeBoundsOwner; -use hir::{Adt, HasSource}; -use ide_db::{helpers::SnippetCap, RootDatabase}; +use hir::{Adt, HasSource, Semantics}; +use ide_db::{ + helpers::{FamousDefs, SnippetCap}, + RootDatabase, +}; use itertools::Itertools; use stdx::format_to; use syntax::{ @@ -205,18 +208,34 @@ 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 { - if let Some(expr) = invert_special_case(&expr) { +pub(crate) fn invert_boolean_expression( + sema: &Semantics, + expr: ast::Expr, +) -> ast::Expr { + if let Some(expr) = invert_special_case(sema, &expr) { return expr; } make::expr_prefix(T![!], expr) } -fn invert_special_case(expr: &ast::Expr) -> Option { +fn invert_special_case(sema: &Semantics, expr: &ast::Expr) -> Option { match expr { ast::Expr::BinExpr(bin) => match bin.op_kind()? { ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), + // Swap `<` with `>=`, `<=` with `>`, ... if operands `impl Ord` + ast::BinOp::LesserTest if bin_impls_ord(sema, bin) => { + bin.replace_op(T![>=]).map(|it| it.into()) + } + ast::BinOp::LesserEqualTest if bin_impls_ord(sema, bin) => { + bin.replace_op(T![>]).map(|it| it.into()) + } + ast::BinOp::GreaterTest if bin_impls_ord(sema, bin) => { + bin.replace_op(T![<=]).map(|it| it.into()) + } + ast::BinOp::GreaterEqualTest if bin_impls_ord(sema, bin) => { + bin.replace_op(T![<]).map(|it| it.into()) + } // Parenthesize other expressions before prefixing `!` _ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))), }, @@ -247,6 +266,27 @@ fn invert_special_case(expr: &ast::Expr) -> Option { } } +fn bin_impls_ord(sema: &Semantics, bin: &ast::BinExpr) -> bool { + if let (Some(lhs), Some(rhs)) = (bin.lhs(), bin.rhs()) { + return sema.type_of_expr(&lhs) == sema.type_of_expr(&rhs) + && impls_ord(sema, &lhs) + && impls_ord(sema, &rhs); + } + false +} + +fn impls_ord(sema: &Semantics, expr: &ast::Expr) -> bool { + let krate = sema.scope(expr.syntax()).module().map(|it| it.krate()); + let famous_defs = FamousDefs(&sema, krate); + + if let Some(ty) = sema.type_of_expr(expr) { + if let Some(ord_trait) = famous_defs.core_cmp_Ord() { + return ty.autoderef(sema.db).any(|ty| ty.impls_trait(sema.db, ord_trait, &[])); + } + } + false +} + pub(crate) fn next_prev() -> impl Iterator { [Direction::Next, Direction::Prev].iter().copied() } -- cgit v1.2.3