diff options
| author | Lukas Wirth <lukastw97@gmail.com> | 2023-01-09 11:59:09 +0100 |
|---|---|---|
| committer | Lukas Wirth <lukastw97@gmail.com> | 2023-01-09 11:59:17 +0100 |
| commit | 0dd26821783ccc809fb0995f178fd6c53f6eebd7 (patch) | |
| tree | e166f125ecad91c83937c8db6d5015b2c7cbd0be | |
| parent | 3dd2f99c49d225b08e38ccd1981e1adef8fd2901 (diff) | |
| download | rust-0dd26821783ccc809fb0995f178fd6c53f6eebd7.tar.gz rust-0dd26821783ccc809fb0995f178fd6c53f6eebd7.zip | |
Refactor replace_arith assists into one module
| -rw-r--r-- | crates/ide-assists/src/handlers/replace_arith_op.rs | 226 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/replace_arith_with_checked.rs | 45 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/replace_arith_with_saturating.rs | 48 | ||||
| -rw-r--r-- | crates/ide-assists/src/handlers/replace_arith_with_wrapping.rs | 48 | ||||
| -rw-r--r-- | crates/ide-assists/src/lib.rs | 10 | ||||
| -rw-r--r-- | crates/ide-assists/src/utils.rs | 124 |
6 files changed, 234 insertions, 267 deletions
diff --git a/crates/ide-assists/src/handlers/replace_arith_op.rs b/crates/ide-assists/src/handlers/replace_arith_op.rs new file mode 100644 index 00000000000..f1ca35cafc3 --- /dev/null +++ b/crates/ide-assists/src/handlers/replace_arith_op.rs @@ -0,0 +1,226 @@ +use ide_db::assists::{AssistId, AssistKind, GroupLabel}; +use syntax::{ + ast::{self, ArithOp, BinaryOp}, + AstNode, TextRange, +}; + +use crate::assist_context::{AssistContext, Assists}; + +// Assist: replace_arith_with_checked +// +// Replaces arithmetic on integers with the `checked_*` equivalent. +// +// ``` +// fn main() { +// let x = 1 $0+ 2; +// } +// ``` +// -> +// ``` +// fn main() { +// let x = 1.checked_add(2); +// } +// ``` +pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { + replace_arith(acc, ctx, ArithKind::Checked) +} + +// Assist: replace_arith_with_saturating +// +// Replaces arithmetic on integers with the `saturating_*` equivalent. +// +// ``` +// fn main() { +// let x = 1 $0+ 2; +// } +// ``` +// -> +// ``` +// fn main() { +// let x = 1.saturating_add(2); +// } +// ``` +pub(crate) fn replace_arith_with_saturating( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + replace_arith(acc, ctx, ArithKind::Saturating) +} + +// Assist: replace_arith_with_wrapping +// +// Replaces arithmetic on integers with the `wrapping_*` equivalent. +// +// ``` +// fn main() { +// let x = 1 $0+ 2; +// } +// ``` +// -> +// ``` +// fn main() { +// let x = 1.wrapping_add(2); +// } +// ``` +pub(crate) fn replace_arith_with_wrapping( + acc: &mut Assists, + ctx: &AssistContext<'_>, +) -> Option<()> { + replace_arith(acc, ctx, ArithKind::Wrapping) +} + +fn replace_arith(acc: &mut Assists, ctx: &AssistContext<'_>, kind: ArithKind) -> Option<()> { + let (lhs, op, rhs) = parse_binary_op(ctx)?; + + if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) { + return None; + } + + let start = lhs.syntax().text_range().start(); + let end = rhs.syntax().text_range().end(); + let range = TextRange::new(start, end); + + acc.add_group( + &GroupLabel("replace_arith".into()), + kind.assist_id(), + kind.label(), + range, + |builder| { + let method_name = kind.method_name(op); + + builder.replace(range, format!("{lhs}.{method_name}({rhs})")) + }, + ) +} + +fn is_primitive_int(ctx: &AssistContext<'_>, expr: &ast::Expr) -> bool { + match ctx.sema.type_of_expr(expr) { + Some(ty) => ty.adjusted().is_int_or_uint(), + _ => false, + } +} + +/// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) +fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(ast::Expr, ArithOp, ast::Expr)> { + let expr = ctx.find_node_at_offset::<ast::BinExpr>()?; + + let op = match expr.op_kind() { + Some(BinaryOp::ArithOp(ArithOp::Add)) => ArithOp::Add, + Some(BinaryOp::ArithOp(ArithOp::Sub)) => ArithOp::Sub, + Some(BinaryOp::ArithOp(ArithOp::Mul)) => ArithOp::Mul, + Some(BinaryOp::ArithOp(ArithOp::Div)) => ArithOp::Div, + _ => return None, + }; + + let lhs = expr.lhs()?; + let rhs = expr.rhs()?; + + Some((lhs, op, rhs)) +} + +pub(crate) enum ArithKind { + Saturating, + Wrapping, + Checked, +} + +impl ArithKind { + fn assist_id(&self) -> AssistId { + let s = match self { + ArithKind::Saturating => "replace_arith_with_saturating", + ArithKind::Checked => "replace_arith_with_checked", + ArithKind::Wrapping => "replace_arith_with_wrapping", + }; + + AssistId(s, AssistKind::RefactorRewrite) + } + + fn label(&self) -> &'static str { + match self { + ArithKind::Saturating => "Replace arithmetic with call to saturating_*", + ArithKind::Checked => "Replace arithmetic with call to checked_*", + ArithKind::Wrapping => "Replace arithmetic with call to wrapping_*", + } + } + + fn method_name(&self, op: ArithOp) -> String { + let prefix = match self { + ArithKind::Checked => "checked_", + ArithKind::Wrapping => "wrapping_", + ArithKind::Saturating => "saturating_", + }; + + let suffix = match op { + ArithOp::Add => "add", + ArithOp::Sub => "sub", + ArithOp::Mul => "mul", + ArithOp::Div => "div", + _ => unreachable!("this function should only be called with +, -, / or *"), + }; + format!("{prefix}{suffix}") + } +} + +#[cfg(test)] +mod tests { + use crate::tests::check_assist; + + use super::*; + + #[test] + fn arith_kind_method_name() { + assert_eq!(ArithKind::Saturating.method_name(ArithOp::Add), "saturating_add"); + assert_eq!(ArithKind::Checked.method_name(ArithOp::Sub), "checked_sub"); + } + + #[test] + fn replace_arith_with_checked_add() { + check_assist( + replace_arith_with_checked, + r#" +fn main() { + let x = 1 $0+ 2; +} +"#, + r#" +fn main() { + let x = 1.checked_add(2); +} +"#, + ) + } + + #[test] + fn replace_arith_with_saturating_add() { + check_assist( + replace_arith_with_saturating, + r#" +fn main() { + let x = 1 $0+ 2; +} +"#, + r#" +fn main() { + let x = 1.saturating_add(2); +} +"#, + ) + } + + #[test] + fn replace_arith_with_wrapping_add() { + check_assist( + replace_arith_with_wrapping, + r#" +fn main() { + let x = 1 $0+ 2; +} +"#, + r#" +fn main() { + let x = 1.wrapping_add(2); +} +"#, + ) + } +} diff --git a/crates/ide-assists/src/handlers/replace_arith_with_checked.rs b/crates/ide-assists/src/handlers/replace_arith_with_checked.rs deleted file mode 100644 index ff1fba5818c..00000000000 --- a/crates/ide-assists/src/handlers/replace_arith_with_checked.rs +++ /dev/null @@ -1,45 +0,0 @@ -use crate::assist_context::{AssistContext, Assists}; -use crate::utils::{replace_arith, ArithKind}; - -// Assist: replace_arith_with_checked -// -// Replaces arithmetic on integers with the `checked_*` equivalent. -// -// ``` -// fn main() { -// let x = 1 $0+ 2; -// } -// ``` -// -> -// ``` -// fn main() { -// let x = 1.checked_add(2); -// } -// ``` -pub(crate) fn replace_arith_with_checked(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> { - replace_arith(acc, ctx, ArithKind::Checked) -} - -#[cfg(test)] -mod tests { - use crate::tests::check_assist; - - use super::*; - - #[test] - fn replace_arith_with_saturating_add() { - check_assist( - replace_arith_with_checked, - r#" -fn main() { - let x = 1 $0+ 2; -} -"#, - r#" -fn main() { - let x = 1.checked_add(2); -} -"#, - ) - } -} diff --git a/crates/ide-assists/src/handlers/replace_arith_with_saturating.rs b/crates/ide-assists/src/handlers/replace_arith_with_saturating.rs deleted file mode 100644 index 717875e84f0..00000000000 --- a/crates/ide-assists/src/handlers/replace_arith_with_saturating.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::assist_context::{AssistContext, Assists}; -use crate::utils::{replace_arith, ArithKind}; - -// Assist: replace_arith_with_saturating -// -// Replaces arithmetic on integers with the `saturating_*` equivalent. -// -// ``` -// fn main() { -// let x = 1 $0+ 2; -// } -// ``` -// -> -// ``` -// fn main() { -// let x = 1.saturating_add(2); -// } -// ``` -pub(crate) fn replace_arith_with_saturating( - acc: &mut Assists, - ctx: &AssistContext<'_>, -) -> Option<()> { - replace_arith(acc, ctx, ArithKind::Saturating) -} - -#[cfg(test)] -mod tests { - use crate::tests::check_assist; - - use super::*; - - #[test] - fn replace_arith_with_saturating_add() { - check_assist( - replace_arith_with_saturating, - r#" -fn main() { - let x = 1 $0+ 2; -} -"#, - r#" -fn main() { - let x = 1.saturating_add(2); -} -"#, - ) - } -} diff --git a/crates/ide-assists/src/handlers/replace_arith_with_wrapping.rs b/crates/ide-assists/src/handlers/replace_arith_with_wrapping.rs deleted file mode 100644 index e0a90292b19..00000000000 --- a/crates/ide-assists/src/handlers/replace_arith_with_wrapping.rs +++ /dev/null @@ -1,48 +0,0 @@ -use crate::assist_context::{AssistContext, Assists}; -use crate::utils::{replace_arith, ArithKind}; - -// Assist: replace_arith_with_wrapping -// -// Replaces arithmetic on integers with the `wrapping_*` equivalent. -// -// ``` -// fn main() { -// let x = 1 $0+ 2; -// } -// ``` -// -> -// ``` -// fn main() { -// let x = 1.wrapping_add(2); -// } -// ``` -pub(crate) fn replace_arith_with_wrapping( - acc: &mut Assists, - ctx: &AssistContext<'_>, -) -> Option<()> { - replace_arith(acc, ctx, ArithKind::Wrapping) -} - -#[cfg(test)] -mod tests { - use crate::tests::check_assist; - - use super::*; - - #[test] - fn replace_arith_with_saturating_add() { - check_assist( - replace_arith_with_wrapping, - r#" -fn main() { - let x = 1 $0+ 2; -} -"#, - r#" -fn main() { - let x = 1.wrapping_add(2); -} -"#, - ) - } -} diff --git a/crates/ide-assists/src/lib.rs b/crates/ide-assists/src/lib.rs index 3938fa09835..0255ffdea3f 100644 --- a/crates/ide-assists/src/lib.rs +++ b/crates/ide-assists/src/lib.rs @@ -183,9 +183,7 @@ mod handlers { mod replace_derive_with_manual_impl; mod replace_if_let_with_match; mod replace_or_with_or_else; - mod replace_arith_with_checked; - mod replace_arith_with_wrapping; - mod replace_arith_with_saturating; + mod replace_arith_op; mod introduce_named_generic; mod replace_let_with_if_let; mod replace_qualified_name_with_use; @@ -289,9 +287,9 @@ mod handlers { replace_or_with_or_else::replace_or_with_or_else, replace_turbofish_with_explicit_type::replace_turbofish_with_explicit_type, replace_qualified_name_with_use::replace_qualified_name_with_use, - replace_arith_with_wrapping::replace_arith_with_wrapping, - replace_arith_with_checked::replace_arith_with_checked, - replace_arith_with_saturating::replace_arith_with_saturating, + replace_arith_op::replace_arith_with_wrapping, + replace_arith_op::replace_arith_with_checked, + replace_arith_op::replace_arith_with_saturating, sort_items::sort_items, split_import::split_import, toggle_ignore::toggle_ignore, diff --git a/crates/ide-assists/src/utils.rs b/crates/ide-assists/src/utils.rs index 0227222c089..db32e7182c4 100644 --- a/crates/ide-assists/src/utils.rs +++ b/crates/ide-assists/src/utils.rs @@ -4,27 +4,21 @@ use std::ops; pub(crate) use gen_trait_fn_body::gen_trait_fn_body; use hir::{db::HirDatabase, HirDisplay, Semantics}; -use ide_db::{ - assists::{AssistId, AssistKind}, - famous_defs::FamousDefs, - path_transform::PathTransform, - RootDatabase, SnippetCap, -}; +use ide_db::{famous_defs::FamousDefs, path_transform::PathTransform, RootDatabase, SnippetCap}; use stdx::format_to; use syntax::{ ast::{ self, edit::{self, AstNodeEdit}, edit_in_place::{AttrsOwnerEdit, Removable}, - make, ArithOp, BinExpr, BinaryOp, Expr, HasArgList, HasAttrs, HasGenericParams, HasName, - HasTypeBounds, Whitespace, + make, HasArgList, HasAttrs, HasGenericParams, HasName, HasTypeBounds, Whitespace, }, - ted, AstNode, AstToken, Direction, SmolStr, SourceFile, + ted, AstNode, AstToken, Direction, SourceFile, SyntaxKind::*, SyntaxNode, TextRange, TextSize, T, }; -use crate::assist_context::{AssistContext, Assists, SourceChangeBuilder}; +use crate::assist_context::{AssistContext, SourceChangeBuilder}; pub(crate) mod suggest_name; mod gen_trait_fn_body; @@ -711,113 +705,3 @@ pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgLi } make::arg_list(args) } - -pub(crate) enum ArithKind { - Saturating, - Wrapping, - Checked, -} - -impl ArithKind { - fn assist_id(&self) -> AssistId { - let s = match self { - ArithKind::Saturating => "replace_arith_with_saturating", - ArithKind::Checked => "replace_arith_with_checked", - ArithKind::Wrapping => "replace_arith_with_wrapping", - }; - - AssistId(s, AssistKind::RefactorRewrite) - } - - fn label(&self) -> &'static str { - match self { - ArithKind::Saturating => "Replace arithmetic with call to saturating_*", - ArithKind::Checked => "Replace arithmetic with call to checked_*", - ArithKind::Wrapping => "Replace arithmetic with call to wrapping_*", - } - } - - fn method_name(&self, op: ArithOp) -> SmolStr { - // is this too much effort to avoid an allocation? is there a better way? - let mut bytes = [0u8; 14]; - let prefix = match self { - ArithKind::Checked => "checked_", - ArithKind::Wrapping => "wrapping_", - ArithKind::Saturating => "saturating_", - }; - - bytes[0..(prefix.len())].copy_from_slice(prefix.as_bytes()); - - let suffix = match op { - ArithOp::Add => "add", - ArithOp::Sub => "sub", - ArithOp::Mul => "mul", - ArithOp::Div => "div", - _ => unreachable!("this function should only be called with +, -, / or *"), - }; - - bytes[(prefix.len())..(prefix.len() + suffix.len())].copy_from_slice(suffix.as_bytes()); - - let len = prefix.len() + suffix.len(); - let s = core::str::from_utf8(&bytes[0..len]).unwrap(); - SmolStr::from(s) - } -} - -pub(crate) fn replace_arith( - acc: &mut Assists, - ctx: &AssistContext<'_>, - kind: ArithKind, -) -> Option<()> { - let (lhs, op, rhs) = parse_binary_op(ctx)?; - - if !is_primitive_int(ctx, &lhs) || !is_primitive_int(ctx, &rhs) { - return None; - } - - let start = lhs.syntax().text_range().start(); - let end = rhs.syntax().text_range().end(); - let range = TextRange::new(start, end); - - acc.add(kind.assist_id(), kind.label(), range, |builder| { - let method_name = kind.method_name(op); - - builder.replace(range, format!("{lhs}.{method_name}({rhs})")) - }) -} - -fn is_primitive_int(ctx: &AssistContext<'_>, expr: &Expr) -> bool { - match ctx.sema.type_of_expr(expr) { - Some(ty) => ty.adjusted().is_int_or_uint(), - _ => false, - } -} - -/// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`) -fn parse_binary_op(ctx: &AssistContext<'_>) -> Option<(Expr, ArithOp, Expr)> { - let expr = ctx.find_node_at_offset::<BinExpr>()?; - - let op = match expr.op_kind() { - Some(BinaryOp::ArithOp(ArithOp::Add)) => ArithOp::Add, - Some(BinaryOp::ArithOp(ArithOp::Sub)) => ArithOp::Sub, - Some(BinaryOp::ArithOp(ArithOp::Mul)) => ArithOp::Mul, - Some(BinaryOp::ArithOp(ArithOp::Div)) => ArithOp::Div, - _ => return None, - }; - - let lhs = expr.lhs()?; - let rhs = expr.rhs()?; - - Some((lhs, op, rhs)) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn arith_kind_method_name() { - assert_eq!(ArithKind::Saturating.method_name(ArithOp::Add), "saturating_add"); - assert_eq!(ArithKind::Checked.method_name(ArithOp::Sub), "checked_sub"); - } -} |
