aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_assists
diff options
context:
space:
mode:
authorlbrande <[email protected]>2021-02-22 15:23:42 +0000
committerLukas Wirth <[email protected]>2021-02-24 10:58:37 +0000
commit9db970ee082e315cfa04db163fe2e0268b618531 (patch)
tree9fef7832ba257eba27566653987b90ca089018de /crates/ide_assists
parentf7a4a87de2840789e12722afc7df9f4db2db013c (diff)
De Morgan's Law assist now correctly inverts <, <=, >, >=.
Diffstat (limited to 'crates/ide_assists')
-rw-r--r--crates/ide_assists/src/handlers/apply_demorgan.rs8
-rw-r--r--crates/ide_assists/src/handlers/early_return.rs2
-rw-r--r--crates/ide_assists/src/handlers/invert_if.rs2
-rw-r--r--crates/ide_assists/src/tests/generated.rs4
-rw-r--r--crates/ide_assists/src/utils.rs50
5 files changed, 53 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
11// 11//
12// ``` 12// ```
13// fn main() { 13// fn main() {
14// if x != 4 ||$0 y < 3 {} 14// if x != 4 ||$0 y < 3.14 {}
15// } 15// }
16// ``` 16// ```
17// -> 17// ->
18// ``` 18// ```
19// fn main() { 19// fn main() {
20// if !(x == 4 && !(y < 3)) {} 20// if !(x == 4 && !(y < 3.14)) {}
21// } 21// }
22// ``` 22// ```
23pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { 23pub(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<(
32 32
33 let lhs = expr.lhs()?; 33 let lhs = expr.lhs()?;
34 let lhs_range = lhs.syntax().text_range(); 34 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);
36 36
37 let rhs = expr.rhs()?; 37 let rhs = expr.rhs()?;
38 let rhs_range = rhs.syntax().text_range(); 38 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);
40 40
41 acc.add( 41 acc.add(
42 AssistId("apply_demorgan", AssistKind::RefactorRewrite), 42 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)
111 let new_expr = { 111 let new_expr = {
112 let then_branch = 112 let then_branch =
113 make::block_expr(once(make::expr_stmt(early_expression).into()), None); 113 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);
115 make::expr_if(make::condition(cond, None), then_branch, None) 115 make::expr_if(make::condition(cond, None), then_branch, None)
116 .indent(if_indent_level) 116 .indent(if_indent_level)
117 }; 117 };
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<()> {
50 }; 50 };
51 51
52 acc.add(AssistId("invert_if", AssistKind::RefactorRewrite), "Invert if", if_range, |edit| { 52 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());
54 edit.replace_ast(cond, flip_cond); 54 edit.replace_ast(cond, flip_cond);
55 55
56 let else_node = else_block.syntax(); 56 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() {
147 "apply_demorgan", 147 "apply_demorgan",
148 r#####" 148 r#####"
149fn main() { 149fn main() {
150 if x != 4 ||$0 y < 3 {} 150 if x != 4 ||$0 y < 3.14 {}
151} 151}
152"#####, 152"#####,
153 r#####" 153 r#####"
154fn main() { 154fn main() {
155 if !(x == 4 && !(y < 3)) {} 155 if !(x == 4 && !(y < 3.14)) {}
156} 156}
157"#####, 157"#####,
158 ) 158 )
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 @@
3use std::ops; 3use std::ops;
4 4
5use ast::TypeBoundsOwner; 5use ast::TypeBoundsOwner;
6use hir::{Adt, HasSource}; 6use hir::{Adt, HasSource, Semantics};
7use ide_db::{helpers::SnippetCap, RootDatabase}; 7use ide_db::{
8 helpers::{FamousDefs, SnippetCap},
9 RootDatabase,
10};
8use itertools::Itertools; 11use itertools::Itertools;
9use stdx::format_to; 12use stdx::format_to;
10use syntax::{ 13use syntax::{
@@ -205,18 +208,34 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
205 .unwrap_or_else(|| node.text_range().start()) 208 .unwrap_or_else(|| node.text_range().start())
206} 209}
207 210
208pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { 211pub(crate) fn invert_boolean_expression(
209 if let Some(expr) = invert_special_case(&expr) { 212 sema: &Semantics<RootDatabase>,
213 expr: ast::Expr,
214) -> ast::Expr {
215 if let Some(expr) = invert_special_case(sema, &expr) {
210 return expr; 216 return expr;
211 } 217 }
212 make::expr_prefix(T![!], expr) 218 make::expr_prefix(T![!], expr)
213} 219}
214 220
215fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> { 221fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> {
216 match expr { 222 match expr {
217 ast::Expr::BinExpr(bin) => match bin.op_kind()? { 223 ast::Expr::BinExpr(bin) => match bin.op_kind()? {
218 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()), 224 ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
219 ast::BinOp::EqualityTest => bin.replace_op(T![!=]).map(|it| it.into()), 225 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 }
220 // Parenthesize other expressions before prefixing `!` 239 // Parenthesize other expressions before prefixing `!`
221 _ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))), 240 _ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
222 }, 241 },
@@ -247,6 +266,27 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
247 } 266 }
248} 267}
249 268
269fn 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
278fn 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
250pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { 290pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
251 [Direction::Next, Direction::Prev].iter().copied() 291 [Direction::Next, Direction::Prev].iter().copied()
252} 292}