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 rustc_ast
::ast
::{LitIntType, LitKind}
;
6 use rustc_errors
::Applicability
;
7 use rustc_hir
::{BinOpKind, Block, Expr, ExprKind, Stmt, StmtKind}
;
8 use rustc_lint
::{LateContext, LateLintPass}
;
9 use rustc_middle
::ty
::{Int, IntTy, Ty, Uint, UintTy}
;
10 use rustc_session
::declare_lint_pass
;
12 declare_clippy_lint
! {
14 /// Checks for implicit saturating addition.
16 /// ### Why is this bad?
17 /// The built-in function is more readable and may be faster.
21 ///let mut u:u32 = 7000;
23 /// if u != u32::MAX {
29 ///let mut u:u32 = 7000;
31 /// u = u.saturating_add(1);
33 #[clippy::version = "1.66.0"]
34 pub IMPLICIT_SATURATING_ADD
,
36 "Perform saturating addition instead of implicitly checking max bound of data type"
38 declare_lint_pass
!(ImplicitSaturatingAdd
=> [IMPLICIT_SATURATING_ADD
]);
40 impl<'tcx
> LateLintPass
<'tcx
> for ImplicitSaturatingAdd
{
41 fn check_expr(&mut self, cx
: &LateContext
<'tcx
>, expr
: &'tcx Expr
<'tcx
>) {
42 if let ExprKind
::If(cond
, then
, None
) = expr
.kind
43 && let ExprKind
::DropTemps(expr1
) = cond
.kind
44 && let Some((c
, op_node
, l
)) = get_const(cx
, expr1
)
45 && let BinOpKind
::Ne
| BinOpKind
::Lt
= op_node
46 && let ExprKind
::Block(block
, None
) = then
.kind
51 kind
: StmtKind
::Expr(ex
) | StmtKind
::Semi(ex
),
63 && let ExprKind
::AssignOp(op1
, target
, value
) = ex
.kind
64 && let ty
= cx
.typeck_results().expr_ty(target
)
65 && Some(c
) == get_int_max(ty
)
66 && let ctxt
= expr
.span
.ctxt()
67 && ex
.span
.ctxt() == ctxt
68 && expr1
.span
.ctxt() == ctxt
69 && clippy_utils
::SpanlessEq
::new(cx
).eq_expr(l
, target
)
70 && BinOpKind
::Add
== op1
.node
71 && let ExprKind
::Lit(lit
) = value
.kind
72 && let LitKind
::Int(1, LitIntType
::Unsuffixed
) = lit
.node
73 && block
.expr
.is_none()
75 let mut app
= Applicability
::MachineApplicable
;
76 let code
= snippet_with_context(cx
, target
.span
, ctxt
, "_", &mut app
).0;
77 let sugg
= if let Some(parent
) = get_parent_expr(cx
, expr
)
78 && let ExprKind
::If(_cond
, _then
, Some(else_
)) = parent
.kind
79 && else_
.hir_id
== expr
.hir_id
81 format
!("{{{code} = {code}.saturating_add(1); }}")
83 format
!("{code} = {code}.saturating_add(1);")
87 IMPLICIT_SATURATING_ADD
,
89 "manual saturating add detected",
98 fn get_int_max(ty
: Ty
<'_
>) -> Option
<u128
> {
99 match ty
.peel_refs().kind() {
100 Int(IntTy
::I8
) => i8::MAX
.try_into().ok(),
101 Int(IntTy
::I16
) => i16::MAX
.try_into().ok(),
102 Int(IntTy
::I32
) => i32::MAX
.try_into().ok(),
103 Int(IntTy
::I64
) => i64::MAX
.try_into().ok(),
104 Int(IntTy
::I128
) => i128
::MAX
.try_into().ok(),
105 Int(IntTy
::Isize
) => isize::MAX
.try_into().ok(),
106 Uint(UintTy
::U8
) => Some(u8::MAX
.into()),
107 Uint(UintTy
::U16
) => Some(u16::MAX
.into()),
108 Uint(UintTy
::U32
) => Some(u32::MAX
.into()),
109 Uint(UintTy
::U64
) => Some(u64::MAX
.into()),
110 Uint(UintTy
::U128
) => Some(u128
::MAX
),
111 Uint(UintTy
::Usize
) => usize::MAX
.try_into().ok(),
116 fn get_const
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &Expr
<'tcx
>) -> Option
<(u128
, BinOpKind
, &'tcx Expr
<'tcx
>)> {
117 if let ExprKind
::Binary(op
, l
, r
) = expr
.kind
{
118 let tr
= cx
.typeck_results();
119 if let Some(Constant
::Int(c
)) = constant(cx
, tr
, r
) {
120 return Some((c
, op
.node
, l
));
122 if let Some(Constant
::Int(c
)) = constant(cx
, tr
, l
) {
123 return Some((c
, invert_op(op
.node
)?
, r
));
129 fn invert_op(op
: BinOpKind
) -> Option
<BinOpKind
> {
130 use rustc_hir
::BinOpKind
::{Ge, Gt, Le, Lt, Ne}
;