@@ -4,21 +4,27 @@ use std::ops;
4
4
5
5
pub ( crate ) use gen_trait_fn_body:: gen_trait_fn_body;
6
6
use hir:: { db:: HirDatabase , HirDisplay , Semantics } ;
7
- use ide_db:: { famous_defs:: FamousDefs , path_transform:: PathTransform , RootDatabase , SnippetCap } ;
7
+ use ide_db:: {
8
+ assists:: { AssistId , AssistKind } ,
9
+ famous_defs:: FamousDefs ,
10
+ path_transform:: PathTransform ,
11
+ RootDatabase , SnippetCap ,
12
+ } ;
8
13
use stdx:: format_to;
9
14
use syntax:: {
10
15
ast:: {
11
16
self ,
12
17
edit:: { self , AstNodeEdit } ,
13
18
edit_in_place:: { AttrsOwnerEdit , Removable } ,
14
- make, HasArgList , HasAttrs , HasGenericParams , HasName , HasTypeBounds , Whitespace ,
19
+ make, ArithOp , BinExpr , BinaryOp , Expr , HasArgList , HasAttrs , HasGenericParams , HasName ,
20
+ HasTypeBounds , Whitespace ,
15
21
} ,
16
- ted, AstNode , AstToken , Direction , SourceFile ,
22
+ ted, AstNode , AstToken , Direction , SmolStr , SourceFile ,
17
23
SyntaxKind :: * ,
18
24
SyntaxNode , TextRange , TextSize , T ,
19
25
} ;
20
26
21
- use crate :: assist_context:: { AssistContext , SourceChangeBuilder } ;
27
+ use crate :: assist_context:: { AssistContext , Assists , SourceChangeBuilder } ;
22
28
23
29
pub ( crate ) mod suggest_name;
24
30
mod gen_trait_fn_body;
@@ -705,3 +711,102 @@ pub(crate) fn convert_param_list_to_arg_list(list: ast::ParamList) -> ast::ArgLi
705
711
}
706
712
make:: arg_list ( args)
707
713
}
714
+
715
+ pub ( crate ) enum ArithKind {
716
+ Saturating ,
717
+ Wrapping ,
718
+ Checked ,
719
+ }
720
+
721
+ impl ArithKind {
722
+ fn assist_id ( & self ) -> AssistId {
723
+ let s = match self {
724
+ ArithKind :: Saturating => "replace_arith_with_saturating" ,
725
+ ArithKind :: Checked => "replace_arith_with_saturating" ,
726
+ ArithKind :: Wrapping => "replace_arith_with_saturating" ,
727
+ } ;
728
+
729
+ AssistId ( s, AssistKind :: RefactorRewrite )
730
+ }
731
+
732
+ fn label ( & self ) -> & ' static str {
733
+ match self {
734
+ ArithKind :: Saturating => "Replace arithmetic with call to saturating_*" ,
735
+ ArithKind :: Checked => "Replace arithmetic with call to checked_*" ,
736
+ ArithKind :: Wrapping => "Replace arithmetic with call to wrapping_*" ,
737
+ }
738
+ }
739
+
740
+ fn method_name ( & self , op : ArithOp ) -> SmolStr {
741
+ // is this too much effort to avoid an allocation? is there a better way?
742
+ let mut bytes = [ 0u8 ; 14 ] ;
743
+ let prefix = match self {
744
+ ArithKind :: Checked => "checked_" ,
745
+ ArithKind :: Wrapping => "wrapping_" ,
746
+ ArithKind :: Saturating => "saturating_" ,
747
+ } ;
748
+
749
+ bytes[ 0 ..( prefix. len ( ) ) ] . copy_from_slice ( prefix. as_bytes ( ) ) ;
750
+
751
+ let suffix = match op {
752
+ ArithOp :: Add => "add" ,
753
+ ArithOp :: Sub => "sub" ,
754
+ ArithOp :: Mul => "mul" ,
755
+ ArithOp :: Div => "div" ,
756
+ _ => unreachable ! ( "this function should only be called with +, -, / or *" ) ,
757
+ } ;
758
+
759
+ bytes[ ( prefix. len ( ) ) ..( prefix. len ( ) + suffix. len ( ) ) ] . copy_from_slice ( suffix. as_bytes ( ) ) ;
760
+
761
+ let len = prefix. len ( ) + suffix. len ( ) ;
762
+ let s = core:: str:: from_utf8 ( & bytes[ 0 ..len] ) . unwrap ( ) ;
763
+ SmolStr :: from ( s)
764
+ }
765
+ }
766
+
767
+ pub ( crate ) fn replace_arith (
768
+ acc : & mut Assists ,
769
+ ctx : & AssistContext < ' _ > ,
770
+ kind : ArithKind ,
771
+ ) -> Option < ( ) > {
772
+ let ( lhs, op, rhs) = parse_binary_op ( ctx) ?;
773
+
774
+ let start = lhs. syntax ( ) . text_range ( ) . start ( ) ;
775
+ let end = rhs. syntax ( ) . text_range ( ) . end ( ) ;
776
+ let range = TextRange :: new ( start, end) ;
777
+
778
+ acc. add ( kind. assist_id ( ) , kind. label ( ) , range, |builder| {
779
+ let method_name = kind. method_name ( op) ;
780
+
781
+ builder. replace ( range, format ! ( "{lhs}.{method_name}({rhs})" ) )
782
+ } )
783
+ }
784
+
785
+ /// Extract the operands of an arithmetic expression (e.g. `1 + 2` or `1.checked_add(2)`)
786
+ fn parse_binary_op ( ctx : & AssistContext < ' _ > ) -> Option < ( Expr , ArithOp , Expr ) > {
787
+ let expr = ctx. find_node_at_offset :: < BinExpr > ( ) ?;
788
+
789
+ let op = match expr. op_kind ( ) {
790
+ Some ( BinaryOp :: ArithOp ( ArithOp :: Add ) ) => ArithOp :: Add ,
791
+ Some ( BinaryOp :: ArithOp ( ArithOp :: Sub ) ) => ArithOp :: Sub ,
792
+ Some ( BinaryOp :: ArithOp ( ArithOp :: Mul ) ) => ArithOp :: Mul ,
793
+ Some ( BinaryOp :: ArithOp ( ArithOp :: Div ) ) => ArithOp :: Div ,
794
+ _ => return None ,
795
+ } ;
796
+
797
+ let lhs = expr. lhs ( ) ?;
798
+ let rhs = expr. rhs ( ) ?;
799
+
800
+ Some ( ( lhs, op, rhs) )
801
+ }
802
+
803
+ #[ cfg( test) ]
804
+ mod tests {
805
+ use super :: * ;
806
+
807
+ #[ test]
808
+ fn arith_kind_method_name ( ) {
809
+ assert_eq ! ( ArithKind :: Saturating . method_name( ArithOp :: Add ) , "saturating_add" ) ;
810
+ assert_eq ! ( ArithKind :: Checked . method_name( ArithOp :: Sub ) , "checked_sub" ) ;
811
+ }
812
+ }
0 commit comments