diff options
author | lbrande <[email protected]> | 2021-02-22 15:23:42 +0000 |
---|---|---|
committer | Lukas Wirth <[email protected]> | 2021-02-24 10:58:37 +0000 |
commit | 9db970ee082e315cfa04db163fe2e0268b618531 (patch) | |
tree | 9fef7832ba257eba27566653987b90ca089018de /crates/ide_assists | |
parent | f7a4a87de2840789e12722afc7df9f4db2db013c (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.rs | 8 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/early_return.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/handlers/invert_if.rs | 2 | ||||
-rw-r--r-- | crates/ide_assists/src/tests/generated.rs | 4 | ||||
-rw-r--r-- | crates/ide_assists/src/utils.rs | 50 |
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 | // ``` |
23 | pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> { | 23 | 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<( | |||
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#####" |
149 | fn main() { | 149 | fn main() { |
150 | if x != 4 ||$0 y < 3 {} | 150 | if x != 4 ||$0 y < 3.14 {} |
151 | } | 151 | } |
152 | "#####, | 152 | "#####, |
153 | r#####" | 153 | r#####" |
154 | fn main() { | 154 | fn 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 @@ | |||
3 | use std::ops; | 3 | use std::ops; |
4 | 4 | ||
5 | use ast::TypeBoundsOwner; | 5 | use ast::TypeBoundsOwner; |
6 | use hir::{Adt, HasSource}; | 6 | use hir::{Adt, HasSource, Semantics}; |
7 | use ide_db::{helpers::SnippetCap, RootDatabase}; | 7 | use ide_db::{ |
8 | helpers::{FamousDefs, SnippetCap}, | ||
9 | RootDatabase, | ||
10 | }; | ||
8 | use itertools::Itertools; | 11 | use itertools::Itertools; |
9 | use stdx::format_to; | 12 | use stdx::format_to; |
10 | use syntax::{ | 13 | use 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 | ||
208 | pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr { | 211 | pub(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 | ||
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> { |
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 | ||
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 | |||
250 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { | 290 | pub(crate) fn next_prev() -> impl Iterator<Item = Direction> { |
251 | [Direction::Next, Direction::Prev].iter().copied() | 291 | [Direction::Next, Direction::Prev].iter().copied() |
252 | } | 292 | } |