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/handlers/apply_demorgan.rs | 8 ++-- crates/ide_assists/src/handlers/early_return.rs | 2 +- crates/ide_assists/src/handlers/invert_if.rs | 2 +- crates/ide_assists/src/tests/generated.rs | 4 +- crates/ide_assists/src/utils.rs | 50 ++++++++++++++++++++--- crates/ide_db/src/helpers.rs | 4 ++ crates/ide_db/src/helpers/famous_defs_fixture.rs | 11 +++++ 7 files changed, 68 insertions(+), 13 deletions(-) diff --git a/crates/ide_assists/src/handlers/apply_demorgan.rs b/crates/ide_assists/src/handlers/apply_demorgan.rs index e478ff2ce..3cd6699c3 100644 --- a/crates/ide_assists/src/handlers/apply_demorgan.rs +++ b/crates/ide_assists/src/handlers/apply_demorgan.rs @@ -11,13 +11,13 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin // // ``` // fn main() { -// if x != 4 ||$0 y < 3 {} +// if x != 4 ||$0 y < 3.14 {} // } // ``` // -> // ``` // fn main() { -// if !(x == 4 && !(y < 3)) {} +// if !(x == 4 && !(y < 3.14)) {} // } // ``` pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { @@ -32,11 +32,11 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<( let lhs = expr.lhs()?; let lhs_range = lhs.syntax().text_range(); - let not_lhs = invert_boolean_expression(lhs); + let not_lhs = invert_boolean_expression(&ctx.sema, lhs); let rhs = expr.rhs()?; let rhs_range = rhs.syntax().text_range(); - let not_rhs = invert_boolean_expression(rhs); + let not_rhs = invert_boolean_expression(&ctx.sema, rhs); acc.add( AssistId("apply_demorgan", AssistKind::RefactorRewrite), diff --git a/crates/ide_assists/src/handlers/early_return.rs b/crates/ide_assists/src/handlers/early_return.rs index 6b87c3c05..9e0918477 100644 --- a/crates/ide_assists/src/handlers/early_return.rs +++ b/crates/ide_assists/src/handlers/early_return.rs @@ -111,7 +111,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext) let new_expr = { let then_branch = make::block_expr(once(make::expr_stmt(early_expression).into()), None); - let cond = invert_boolean_expression(cond_expr); + let cond = invert_boolean_expression(&ctx.sema, cond_expr); make::expr_if(make::condition(cond, None), then_branch, None) .indent(if_indent_level) }; diff --git a/crates/ide_assists/src/handlers/invert_if.rs b/crates/ide_assists/src/handlers/invert_if.rs index 5b69dafd4..b131dc205 100644 --- a/crates/ide_assists/src/handlers/invert_if.rs +++ b/crates/ide_assists/src/handlers/invert_if.rs @@ -50,7 +50,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { }; acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { - let flip_cond = invert_boolean_expression(cond.clone()); + let flip_cond = invert_boolean_expression(&ctx.sema, cond.clone()); edit.replace_ast(cond, flip_cond); let else_node = else_block.syntax(); diff --git a/crates/ide_assists/src/tests/generated.rs b/crates/ide_assists/src/tests/generated.rs index ee9c870e8..701091a6b 100644 --- a/crates/ide_assists/src/tests/generated.rs +++ b/crates/ide_assists/src/tests/generated.rs @@ -147,12 +147,12 @@ fn doctest_apply_demorgan() { "apply_demorgan", r#####" fn main() { - if x != 4 ||$0 y < 3 {} + if x != 4 ||$0 y < 3.14 {} } "#####, r#####" fn main() { - if !(x == 4 && !(y < 3)) {} + if !(x == 4 && !(y < 3.14)) {} } "#####, ) 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() } diff --git a/crates/ide_db/src/helpers.rs b/crates/ide_db/src/helpers.rs index bc7aee110..f9de8ce0e 100644 --- a/crates/ide_db/src/helpers.rs +++ b/crates/ide_db/src/helpers.rs @@ -45,6 +45,10 @@ impl FamousDefs<'_, '_> { self.find_crate("core") } + pub fn core_cmp_Ord(&self) -> Option { + self.find_trait("core:cmp:Ord") + } + pub fn core_convert_From(&self) -> Option { self.find_trait("core:convert:From") } diff --git a/crates/ide_db/src/helpers/famous_defs_fixture.rs b/crates/ide_db/src/helpers/famous_defs_fixture.rs index 5e88de64d..bb4e9666b 100644 --- a/crates/ide_db/src/helpers/famous_defs_fixture.rs +++ b/crates/ide_db/src/helpers/famous_defs_fixture.rs @@ -1,5 +1,15 @@ //- /libcore.rs crate:core //! Signatures of traits, types and functions from the core lib for use in tests. +pub mod cmp { + + pub trait Ord { + fn cmp(&self, other: &Self) -> Ordering; + fn max(self, other: Self) -> Self; + fn min(self, other: Self) -> Self; + fn clamp(self, min: Self, max: Self) -> Self; + } +} + pub mod convert { pub trait From { fn from(t: T) -> Self; @@ -109,6 +119,7 @@ pub mod option { pub mod prelude { pub use crate::{ + cmp::Ord, convert::From, default::Default, iter::{IntoIterator, Iterator}, -- cgit v1.2.3