1 use clippy_utils
::consts
::{constant, Constant}
;
2 use clippy_utils
::diagnostics
::span_lint_and_sugg
;
3 use clippy_utils
::get_parent_expr
;
4 use clippy_utils
::source
::snippet_with_context
;
5 use if_chain
::if_chain
;
6 use rustc_ast
::ast
::{LitIntType, LitKind}
;
7 use rustc_errors
::Applicability
;
8 use rustc_hir
::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}
;
9 use rustc_lint
::{LateContext, LateLintPass}
;
10 use rustc_middle
::ty
::{Int, IntTy, Ty, Uint, UintTy}
;
11 use rustc_session
::{declare_lint_pass, declare_tool_lint}
;
13 declare_clippy_lint
! {
15 /// Checks for implicit saturating addition.
17 /// ### Why is this bad?
18 /// The built-in function is more readable and may be faster.
22 ///let mut u:u32 = 7000;
24 /// if u != u32::MAX {
30 ///let mut u:u32 = 7000;
32 /// u = u.saturating_add(1);
34 #[clippy::version = "1.66.0"]
35 pub IMPLICIT_SATURATING_ADD
,
37 "Perform saturating addition instead of implicitly checking max bound of data type"
39 declare_lint_pass
!(ImplicitSaturatingAdd
=> [IMPLICIT_SATURATING_ADD
]);
41 impl<'tcx
> LateLintPass
<'tcx
> for ImplicitSaturatingAdd
{
42 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
44 if let ExprKind
::If(cond
, then
, None
) = expr
.kind
;
45 if let ExprKind
::DropTemps(expr1
) = cond
.kind
;
46 if let Some((c
, op_node
, l
)) = get_const(cx
, expr1
);
47 if let BinOpKind
::Ne
| BinOpKind
::Lt
= op_node
;
48 if let ExprKind
::Block(block
, None
) = then
.kind
;
52 { kind: StmtKind::Expr(ex) | StmtKind::Semi(ex), .. }
],
54 Block { stmts: [], expr: Some(ex), ..}
= block
;
55 if let ExprKind
::AssignOp(op1
, target
, value
) = ex
.kind
;
56 let ty
= cx
.typeck_results().expr_ty(target
);
57 if Some(c
) == get_int_max(ty
);
58 let ctxt
= expr
.span
.ctxt();
59 if ex
.span
.ctxt() == ctxt
;
60 if expr1
.span
.ctxt() == ctxt
;
61 if clippy_utils
::SpanlessEq
::new(cx
).eq_expr(l
, target
);
62 if BinOpKind
::Add
== op1
.node
;
63 if let ExprKind
::Lit(lit
) = value
.kind
;
64 if let LitKind
::Int(1, LitIntType
::Unsuffixed
) = lit
.node
;
65 if block
.expr
.is_none();
67 let mut app
= Applicability
::MachineApplicable
;
68 let code
= snippet_with_context(cx
, target
.span
, ctxt
, "_", &mut app
).0;
69 let sugg
= if let Some(parent
) = get_parent_expr(cx
, expr
)
70 && let ExprKind
::If(_cond
, _then
, Some(else_
)) = parent
.kind
71 && else_
.hir_id
== expr
.hir_id
73 format
!("{{{code} = {code}.saturating_add(1); }}")
75 format
!("{code} = {code}.saturating_add(1);")
77 span_lint_and_sugg(cx
, IMPLICIT_SATURATING_ADD
, expr
.span
, "manual saturating add detected", "use instead", sugg
, app
);
83 fn get_int_max(ty
: Ty
<'_
>) -> Option
<u128
> {
84 match ty
.peel_refs().kind() {
85 Int(IntTy
::I8
) => i8::max_value().try_into().ok(),
86 Int(IntTy
::I16
) => i16::max_value().try_into().ok(),
87 Int(IntTy
::I32
) => i32::max_value().try_into().ok(),
88 Int(IntTy
::I64
) => i64::max_value().try_into().ok(),
89 Int(IntTy
::I128
) => i128
::max_value().try_into().ok(),
90 Int(IntTy
::Isize
) => isize::max_value().try_into().ok(),
91 Uint(UintTy
::U8
) => u8::max_value().try_into().ok(),
92 Uint(UintTy
::U16
) => u16::max_value().try_into().ok(),
93 Uint(UintTy
::U32
) => u32::max_value().try_into().ok(),
94 Uint(UintTy
::U64
) => u64::max_value().try_into().ok(),
95 Uint(UintTy
::U128
) => Some(u128
::max_value()),
96 Uint(UintTy
::Usize
) => usize::max_value().try_into().ok(),
101 fn get_const
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &Expr
<'tcx
>) -> Option
<(u128
, BinOpKind
, &'tcx Expr
<'tcx
>)> {
102 if let ExprKind
::Binary(op
, l
, r
) = expr
.kind
{
103 let tr
= cx
.typeck_results();
104 if let Some(Constant
::Int(c
)) = constant(cx
, tr
, r
) {
105 return Some((c
, op
.node
, l
));
107 if let Some(Constant
::Int(c
)) = constant(cx
, tr
, l
) {
108 return Some((c
, invert_op(op
.node
)?
, r
));
114 fn invert_op(op
: BinOpKind
) -> Option
<BinOpKind
> {
115 use rustc_hir
::BinOpKind
::{Ge, Gt, Le, Lt, Ne}
;