Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/ide-assists/src/utils.rs')
-rw-r--r--crates/ide-assists/src/utils.rs122
1 files changed, 106 insertions, 16 deletions
diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs
index c1332d99bf..a6fa170671 100644
--- a/crates/ide-assists/src/utils.rs
+++ b/crates/ide-assists/src/utils.rs
@@ -3,7 +3,8 @@
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
use hir::{
db::{ExpandDatabase, HirDatabase},
- HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution, Semantics,
+ DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution,
+ Semantics,
};
use ide_db::{
famous_defs::FamousDefs,
@@ -17,9 +18,11 @@ use syntax::{
self,
edit::{AstNodeEdit, IndentLevel},
edit_in_place::{AttrsOwnerEdit, Indent, Removable},
- make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
+ make,
+ syntax_factory::SyntaxFactory,
+ HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace,
},
- ted, AstNode, AstToken, Direction, Edition, NodeOrToken, SourceFile,
+ ted, AstNode, AstToken, Direction, NodeOrToken, SourceFile,
SyntaxKind::*,
SyntaxNode, SyntaxToken, TextRange, TextSize, WalkEvent, T,
};
@@ -245,11 +248,79 @@ 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 {
- invert_special_case(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
+pub(crate) fn invert_boolean_expression(make: &SyntaxFactory, expr: ast::Expr) -> ast::Expr {
+ invert_special_case(make, &expr).unwrap_or_else(|| make.expr_prefix(T![!], expr).into())
}
-fn invert_special_case(expr: &ast::Expr) -> Option<ast::Expr> {
+// FIXME: Migrate usages of this function to the above function and remove this.
+pub(crate) fn invert_boolean_expression_legacy(expr: ast::Expr) -> ast::Expr {
+ invert_special_case_legacy(&expr).unwrap_or_else(|| make::expr_prefix(T![!], expr).into())
+}
+
+fn invert_special_case(make: &SyntaxFactory, expr: &ast::Expr) -> Option<ast::Expr> {
+ match expr {
+ ast::Expr::BinExpr(bin) => {
+ let op_kind = bin.op_kind()?;
+ let rev_kind = match op_kind {
+ ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated }) => {
+ ast::BinaryOp::CmpOp(ast::CmpOp::Eq { negated: !negated })
+ }
+ ast::BinaryOp::CmpOp(ast::CmpOp::Ord { ordering: ast::Ordering::Less, strict }) => {
+ ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
+ ordering: ast::Ordering::Greater,
+ strict: !strict,
+ })
+ }
+ ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
+ ordering: ast::Ordering::Greater,
+ strict,
+ }) => ast::BinaryOp::CmpOp(ast::CmpOp::Ord {
+ ordering: ast::Ordering::Less,
+ strict: !strict,
+ }),
+ // Parenthesize other expressions before prefixing `!`
+ _ => {
+ return Some(
+ make.expr_prefix(T![!], make.expr_paren(expr.clone()).into()).into(),
+ );
+ }
+ };
+
+ Some(make.expr_bin(bin.lhs()?, rev_kind, bin.rhs()?).into())
+ }
+ ast::Expr::MethodCallExpr(mce) => {
+ let receiver = mce.receiver()?;
+ let method = mce.name_ref()?;
+ let arg_list = mce.arg_list()?;
+
+ let method = match method.text().as_str() {
+ "is_some" => "is_none",
+ "is_none" => "is_some",
+ "is_ok" => "is_err",
+ "is_err" => "is_ok",
+ _ => return None,
+ };
+
+ Some(make.expr_method_call(receiver, make.name_ref(method), arg_list).into())
+ }
+ ast::Expr::PrefixExpr(pe) if pe.op_kind()? == ast::UnaryOp::Not => match pe.expr()? {
+ ast::Expr::ParenExpr(parexpr) => {
+ parexpr.expr().map(|e| e.clone_subtree().clone_for_update())
+ }
+ _ => pe.expr().map(|e| e.clone_subtree().clone_for_update()),
+ },
+ ast::Expr::Literal(lit) => match lit.kind() {
+ ast::LiteralKind::Bool(b) => match b {
+ true => Some(ast::Expr::Literal(make.expr_literal("false"))),
+ false => Some(ast::Expr::Literal(make.expr_literal("true"))),
+ },
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+fn invert_special_case_legacy(expr: &ast::Expr) -> Option<ast::Expr> {
match expr {
ast::Expr::BinExpr(bin) => {
let bin = bin.clone_for_update();
@@ -723,31 +794,50 @@ enum ReferenceConversionType {
}
impl ReferenceConversion {
- pub(crate) fn convert_type(&self, db: &dyn HirDatabase, edition: Edition) -> ast::Type {
+ pub(crate) fn convert_type(
+ &self,
+ db: &dyn HirDatabase,
+ display_target: DisplayTarget,
+ ) -> ast::Type {
let ty = match self.conversion {
- ReferenceConversionType::Copy => self.ty.display(db, edition).to_string(),
+ ReferenceConversionType::Copy => self.ty.display(db, display_target).to_string(),
ReferenceConversionType::AsRefStr => "&str".to_owned(),
ReferenceConversionType::AsRefSlice => {
- let type_argument_name =
- self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+ let type_argument_name = self
+ .ty
+ .type_arguments()
+ .next()
+ .unwrap()
+ .display(db, display_target)
+ .to_string();
format!("&[{type_argument_name}]")
}
ReferenceConversionType::Dereferenced => {
- let type_argument_name =
- self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+ let type_argument_name = self
+ .ty
+ .type_arguments()
+ .next()
+ .unwrap()
+ .display(db, display_target)
+ .to_string();
format!("&{type_argument_name}")
}
ReferenceConversionType::Option => {
- let type_argument_name =
- self.ty.type_arguments().next().unwrap().display(db, edition).to_string();
+ let type_argument_name = self
+ .ty
+ .type_arguments()
+ .next()
+ .unwrap()
+ .display(db, display_target)
+ .to_string();
format!("Option<&{type_argument_name}>")
}
ReferenceConversionType::Result => {
let mut type_arguments = self.ty.type_arguments();
let first_type_argument_name =
- type_arguments.next().unwrap().display(db, edition).to_string();
+ type_arguments.next().unwrap().display(db, display_target).to_string();
let second_type_argument_name =
- type_arguments.next().unwrap().display(db, edition).to_string();
+ type_arguments.next().unwrap().display(db, display_target).to_string();
format!("Result<&{first_type_argument_name}, &{second_type_argument_name}>")
}
};