Skip to content

Commit 9db970e

Browse files
lbrandeVeykril
authored andcommitted
De Morgan's Law assist now correctly inverts <, <=, >, >=.
1 parent f7a4a87 commit 9db970e

File tree

7 files changed

+68
-13
lines changed

7 files changed

+68
-13
lines changed

crates/ide_assists/src/handlers/apply_demorgan.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,13 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
1111
//
1212
// ```
1313
// fn main() {
14-
// if x != 4 ||$0 y < 3 {}
14+
// if x != 4 ||$0 y < 3.14 {}
1515
// }
1616
// ```
1717
// ->
1818
// ```
1919
// fn main() {
20-
// if !(x == 4 && !(y < 3)) {}
20+
// if !(x == 4 && !(y < 3.14)) {}
2121
// }
2222
// ```
2323
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<(
3232

3333
let lhs = expr.lhs()?;
3434
let lhs_range = lhs.syntax().text_range();
35-
let not_lhs = invert_boolean_expression(lhs);
35+
let not_lhs = invert_boolean_expression(&ctx.sema, lhs);
3636

3737
let rhs = expr.rhs()?;
3838
let rhs_range = rhs.syntax().text_range();
39-
let not_rhs = invert_boolean_expression(rhs);
39+
let not_rhs = invert_boolean_expression(&ctx.sema, rhs);
4040

4141
acc.add(
4242
AssistId("apply_demorgan", AssistKind::RefactorRewrite),

crates/ide_assists/src/handlers/early_return.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
111111
let new_expr = {
112112
let then_branch =
113113
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
114-
let cond = invert_boolean_expression(cond_expr);
114+
let cond = invert_boolean_expression(&ctx.sema, cond_expr);
115115
make::expr_if(make::condition(cond, None), then_branch, None)
116116
.indent(if_indent_level)
117117
};

crates/ide_assists/src/handlers/invert_if.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub(crate) fn invert_if(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
5050
};
5151

5252
acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| {
53-
let flip_cond = invert_boolean_expression(cond.clone());
53+
let flip_cond = invert_boolean_expression(&ctx.sema, cond.clone());
5454
edit.replace_ast(cond, flip_cond);
5555

5656
let else_node = else_block.syntax();

crates/ide_assists/src/tests/generated.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,12 @@ fn doctest_apply_demorgan() {
147147
"apply_demorgan",
148148
r#####"
149149
fn main() {
150-
if x != 4 ||$0 y < 3 {}
150+
if x != 4 ||$0 y < 3.14 {}
151151
}
152152
"#####,
153153
r#####"
154154
fn main() {
155-
if !(x == 4 && !(y < 3)) {}
155+
if !(x == 4 && !(y < 3.14)) {}
156156
}
157157
"#####,
158158
)

crates/ide_assists/src/utils.rs

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@
33
use std::ops;
44

55
use ast::TypeBoundsOwner;
6-
use hir::{Adt, HasSource};
7-
use ide_db::{helpers::SnippetCap, RootDatabase};
6+
use hir::{Adt, HasSource, Semantics};
7+
use ide_db::{
8+
helpers::{FamousDefs, SnippetCap},
9+
RootDatabase,
10+
};
811
use itertools::Itertools;
912
use stdx::format_to;
1013
use syntax::{
@@ -205,18 +208,34 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
205208
.unwrap_or_else(|| node.text_range().start())
206209
}
207210

208-
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
209-
if let Some(expr) = invert_special_case(&expr) {
211+
pub(crate) fn invert_boolean_expression(
212+
sema: &Semantics<RootDatabase>,
213+
expr: ast::Expr,
214+
) -> ast::Expr {
215+
if let Some(expr) = invert_special_case(sema, &expr) {
210216
return expr;
211217
}
212218
make::expr_prefix(T![!], expr)
213219
}
214220

215-
fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
221+
fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> {
216222
match expr {
217223
ast::Expr::BinExpr(bin) => match bin.op_kind()? {
218224
ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
219225
ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()),
226+
// Swap `<` with `>=`, `<=` with `>`, ... if operands `impl Ord`
227+
ast::BinOp::LesserTest if bin_impls_ord(sema, bin) => {
228+
bin.replace_op(T![>=]).map(|it| it.into())
229+
}
230+
ast::BinOp::LesserEqualTest if bin_impls_ord(sema, bin) => {
231+
bin.replace_op(T![>]).map(|it| it.into())
232+
}
233+
ast::BinOp::GreaterTest if bin_impls_ord(sema, bin) => {
234+
bin.replace_op(T![<=]).map(|it| it.into())
235+
}
236+
ast::BinOp::GreaterEqualTest if bin_impls_ord(sema, bin) => {
237+
bin.replace_op(T![<]).map(|it| it.into())
238+
}
220239
// Parenthesize other expressions before prefixing `!`
221240
_ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
222241
},
@@ -247,6 +266,27 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
247266
}
248267
}
249268

269+
fn bin_impls_ord(sema: &Semantics<RootDatabase>, bin: &ast::BinExpr) -> bool {
270+
if let (Some(lhs), Some(rhs)) = (bin.lhs(), bin.rhs()) {
271+
return sema.type_of_expr(&lhs) == sema.type_of_expr(&rhs)
272+
&& impls_ord(sema, &lhs)
273+
&& impls_ord(sema, &rhs);
274+
}
275+
false
276+
}
277+
278+
fn impls_ord(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> bool {
279+
let krate = sema.scope(expr.syntax()).module().map(|it| it.krate());
280+
let famous_defs = FamousDefs(&sema, krate);
281+
282+
if let Some(ty) = sema.type_of_expr(expr) {
283+
if let Some(ord_trait) = famous_defs.core_cmp_Ord() {
284+
return ty.autoderef(sema.db).any(|ty| ty.impls_trait(sema.db, ord_trait, &[]));
285+
}
286+
}
287+
false
288+
}
289+
250290
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
251291
[Direction::Next, Direction::Prev].iter().copied()
252292
}

crates/ide_db/src/helpers.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ impl FamousDefs<'_, '_> {
4545
self.find_crate("core")
4646
}
4747

48+
pub fn core_cmp_Ord(&self) -> Option<Trait> {
49+
self.find_trait("core:cmp:Ord")
50+
}
51+
4852
pub fn core_convert_From(&self) -> Option<Trait> {
4953
self.find_trait("core:convert:From")
5054
}

crates/ide_db/src/helpers/famous_defs_fixture.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
11
//- /libcore.rs crate:core
22
//! Signatures of traits, types and functions from the core lib for use in tests.
3+
pub mod cmp {
4+
5+
pub trait Ord {
6+
fn cmp(&self, other: &Self) -> Ordering;
7+
fn max(self, other: Self) -> Self;
8+
fn min(self, other: Self) -> Self;
9+
fn clamp(self, min: Self, max: Self) -> Self;
10+
}
11+
}
12+
313
pub mod convert {
414
pub trait From<T> {
515
fn from(t: T) -> Self;
@@ -109,6 +119,7 @@ pub mod option {
109119

110120
pub mod prelude {
111121
pub use crate::{
122+
cmp::Ord,
112123
convert::From,
113124
default::Default,
114125
iter::{IntoIterator, Iterator},

0 commit comments

Comments
 (0)