mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-29 21:35:20 +00:00
De Morgan's Law assist now correctly inverts <, <=, >, >=.
This commit is contained in:
parent
f7a4a87de2
commit
9db970ee08
7 changed files with 68 additions and 13 deletions
|
@ -11,13 +11,13 @@ use crate::{utils::invert_boolean_expression, AssistContext, AssistId, AssistKin
|
||||||
//
|
//
|
||||||
// ```
|
// ```
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// if x != 4 ||$0 y < 3 {}
|
// if x != 4 ||$0 y < 3.14 {}
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
// ->
|
// ->
|
||||||
// ```
|
// ```
|
||||||
// fn main() {
|
// fn main() {
|
||||||
// if !(x == 4 && !(y < 3)) {}
|
// if !(x == 4 && !(y < 3.14)) {}
|
||||||
// }
|
// }
|
||||||
// ```
|
// ```
|
||||||
pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
|
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 = expr.lhs()?;
|
||||||
let lhs_range = lhs.syntax().text_range();
|
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 = expr.rhs()?;
|
||||||
let rhs_range = rhs.syntax().text_range();
|
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(
|
acc.add(
|
||||||
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
AssistId("apply_demorgan", AssistKind::RefactorRewrite),
|
||||||
|
|
|
@ -111,7 +111,7 @@ pub(crate) fn convert_to_guarded_return(acc: &mut Assists, ctx: &AssistContext)
|
||||||
let new_expr = {
|
let new_expr = {
|
||||||
let then_branch =
|
let then_branch =
|
||||||
make::block_expr(once(make::expr_stmt(early_expression).into()), None);
|
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)
|
make::expr_if(make::condition(cond, None), then_branch, None)
|
||||||
.indent(if_indent_level)
|
.indent(if_indent_level)
|
||||||
};
|
};
|
||||||
|
|
|
@ -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| {
|
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);
|
edit.replace_ast(cond, flip_cond);
|
||||||
|
|
||||||
let else_node = else_block.syntax();
|
let else_node = else_block.syntax();
|
||||||
|
|
|
@ -147,12 +147,12 @@ fn doctest_apply_demorgan() {
|
||||||
"apply_demorgan",
|
"apply_demorgan",
|
||||||
r#####"
|
r#####"
|
||||||
fn main() {
|
fn main() {
|
||||||
if x != 4 ||$0 y < 3 {}
|
if x != 4 ||$0 y < 3.14 {}
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
r#####"
|
r#####"
|
||||||
fn main() {
|
fn main() {
|
||||||
if !(x == 4 && !(y < 3)) {}
|
if !(x == 4 && !(y < 3.14)) {}
|
||||||
}
|
}
|
||||||
"#####,
|
"#####,
|
||||||
)
|
)
|
||||||
|
|
|
@ -3,8 +3,11 @@
|
||||||
use std::ops;
|
use std::ops;
|
||||||
|
|
||||||
use ast::TypeBoundsOwner;
|
use ast::TypeBoundsOwner;
|
||||||
use hir::{Adt, HasSource};
|
use hir::{Adt, HasSource, Semantics};
|
||||||
use ide_db::{helpers::SnippetCap, RootDatabase};
|
use ide_db::{
|
||||||
|
helpers::{FamousDefs, SnippetCap},
|
||||||
|
RootDatabase,
|
||||||
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
|
@ -205,18 +208,34 @@ pub(crate) fn vis_offset(node: &SyntaxNode) -> TextSize {
|
||||||
.unwrap_or_else(|| node.text_range().start())
|
.unwrap_or_else(|| node.text_range().start())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn invert_boolean_expression(expr: ast::Expr) -> ast::Expr {
|
pub(crate) fn invert_boolean_expression(
|
||||||
if let Some(expr) = invert_special_case(&expr) {
|
sema: &Semantics<RootDatabase>,
|
||||||
|
expr: ast::Expr,
|
||||||
|
) -> ast::Expr {
|
||||||
|
if let Some(expr) = invert_special_case(sema, &expr) {
|
||||||
return expr;
|
return expr;
|
||||||
}
|
}
|
||||||
make::expr_prefix(T![!], expr)
|
make::expr_prefix(T![!], expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
|
fn invert_special_case(sema: &Semantics<RootDatabase>, expr: &ast::Expr) -> Option<ast::Expr> {
|
||||||
match expr {
|
match expr {
|
||||||
ast::Expr::BinExpr(bin) => match bin.op_kind()? {
|
ast::Expr::BinExpr(bin) => match bin.op_kind()? {
|
||||||
ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
|
ast::BinOp::NegatedEqualityTest => bin.replace_op(T![==]).map(|it| it.into()),
|
||||||
ast::BinOp::EqualityTest => 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 `!`
|
// Parenthesize other expressions before prefixing `!`
|
||||||
_ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
|
_ => Some(make::expr_prefix(T![!], make::expr_paren(expr.clone()))),
|
||||||
},
|
},
|
||||||
|
@ -247,6 +266,27 @@ fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn bin_impls_ord(sema: &Semantics<RootDatabase>, 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<RootDatabase>, 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<Item = Direction> {
|
pub(crate) fn next_prev() -> impl Iterator<Item = Direction> {
|
||||||
[Direction::Next, Direction::Prev].iter().copied()
|
[Direction::Next, Direction::Prev].iter().copied()
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ impl FamousDefs<'_, '_> {
|
||||||
self.find_crate("core")
|
self.find_crate("core")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn core_cmp_Ord(&self) -> Option<Trait> {
|
||||||
|
self.find_trait("core:cmp:Ord")
|
||||||
|
}
|
||||||
|
|
||||||
pub fn core_convert_From(&self) -> Option<Trait> {
|
pub fn core_convert_From(&self) -> Option<Trait> {
|
||||||
self.find_trait("core:convert:From")
|
self.find_trait("core:convert:From")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
//- /libcore.rs crate:core
|
//- /libcore.rs crate:core
|
||||||
//! Signatures of traits, types and functions from the core lib for use in tests.
|
//! 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 mod convert {
|
||||||
pub trait From<T> {
|
pub trait From<T> {
|
||||||
fn from(t: T) -> Self;
|
fn from(t: T) -> Self;
|
||||||
|
@ -109,6 +119,7 @@ pub mod option {
|
||||||
|
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
|
cmp::Ord,
|
||||||
convert::From,
|
convert::From,
|
||||||
default::Default,
|
default::Default,
|
||||||
iter::{IntoIterator, Iterator},
|
iter::{IntoIterator, Iterator},
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue