|
1 | 1 | use super::ARITHMETIC_SIDE_EFFECTS;
|
2 | 2 | use clippy_utils::consts::{constant, constant_simple, Constant};
|
3 | 3 | use clippy_utils::diagnostics::span_lint;
|
| 4 | +use clippy_utils::ty::type_diagnostic_name; |
4 | 5 | use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary};
|
5 | 6 | use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
6 | 7 | use rustc_lint::{LateContext, LateLintPass};
|
7 | 8 | use rustc_middle::ty::Ty;
|
8 | 9 | use rustc_session::impl_lint_pass;
|
9 | 10 | use rustc_span::source_map::{Span, Spanned};
|
| 11 | +use rustc_span::symbol::sym; |
10 | 12 | use rustc_span::Symbol;
|
11 | 13 | use {rustc_ast as ast, rustc_hir as hir};
|
12 | 14 |
|
13 |
| -const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[ |
14 |
| - ["f32", "f32"], |
15 |
| - ["f64", "f64"], |
16 |
| - ["std::num::Saturating", "*"], |
17 |
| - ["std::num::Wrapping", "*"], |
18 |
| - ["std::string::String", "str"], |
19 |
| -]; |
| 15 | +const HARD_CODED_ALLOWED_BINARY: &[[&str; 2]] = &[["f32", "f32"], ["f64", "f64"], ["std::string::String", "str"]]; |
20 | 16 | const HARD_CODED_ALLOWED_UNARY: &[&str] = &["f32", "f64", "std::num::Saturating", "std::num::Wrapping"];
|
21 |
| -const INTEGER_METHODS: &[&str] = &["saturating_div", "wrapping_div", "wrapping_rem", "wrapping_rem_euclid"]; |
| 17 | +const INTEGER_METHODS: &[Symbol] = &[sym::saturating_div, sym::wrapping_div, sym::wrapping_rem, sym::wrapping_rem_euclid]; |
22 | 18 |
|
23 | 19 | #[derive(Debug)]
|
24 | 20 | pub struct ArithmeticSideEffects {
|
@@ -53,7 +49,7 @@ impl ArithmeticSideEffects {
|
53 | 49 | allowed_unary,
|
54 | 50 | const_span: None,
|
55 | 51 | expr_span: None,
|
56 |
| - integer_methods: INTEGER_METHODS.iter().map(|el| Symbol::intern(el)).collect(), |
| 52 | + integer_methods: INTEGER_METHODS.iter().copied().collect(), |
57 | 53 | }
|
58 | 54 | }
|
59 | 55 |
|
@@ -86,6 +82,38 @@ impl ArithmeticSideEffects {
|
86 | 82 | self.allowed_unary.contains(ty_string_elem)
|
87 | 83 | }
|
88 | 84 |
|
| 85 | + /// Verifies built-in types that have specific allowed operations |
| 86 | + fn has_specific_allowed_type_and_operation( |
| 87 | + cx: &LateContext<'_>, |
| 88 | + lhs_ty: Ty<'_>, |
| 89 | + op: &Spanned<hir::BinOpKind>, |
| 90 | + rhs_ty: Ty<'_>, |
| 91 | + ) -> bool { |
| 92 | + let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem); |
| 93 | + let is_non_zero_u = |symbol: Option<Symbol>| { |
| 94 | + matches!( |
| 95 | + symbol, |
| 96 | + Some(sym::NonZeroU128 | sym::NonZeroU16 | sym::NonZeroU32 | sym::NonZeroU64 | sym::NonZeroU8 | sym::NonZeroUsize) |
| 97 | + ) |
| 98 | + }; |
| 99 | + let is_sat_or_wrap = |ty: Ty<'_>| { |
| 100 | + let is_sat = type_diagnostic_name(cx, ty) == Some(sym::Saturating); |
| 101 | + let is_wrap = type_diagnostic_name(cx, ty) == Some(sym::Wrapping); |
| 102 | + is_sat || is_wrap |
| 103 | + }; |
| 104 | + |
| 105 | + // If the RHS is NonZeroU*, then division or module by zero will never occur |
| 106 | + if is_non_zero_u(type_diagnostic_name(cx, rhs_ty)) && is_div_or_rem { |
| 107 | + return true; |
| 108 | + } |
| 109 | + // `Saturation` and `Wrapping` can overflow if the RHS is zero in a division or module |
| 110 | + if is_sat_or_wrap(lhs_ty) { |
| 111 | + return !is_div_or_rem; |
| 112 | + } |
| 113 | + |
| 114 | + false |
| 115 | + } |
| 116 | + |
89 | 117 | // For example, 8i32 or &i64::MAX.
|
90 | 118 | fn is_integral(ty: Ty<'_>) -> bool {
|
91 | 119 | ty.peel_refs().is_integral()
|
@@ -147,6 +175,9 @@ impl ArithmeticSideEffects {
|
147 | 175 | if self.has_allowed_binary(lhs_ty, rhs_ty) {
|
148 | 176 | return;
|
149 | 177 | }
|
| 178 | + if Self::has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) { |
| 179 | + return; |
| 180 | + } |
150 | 181 | let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
|
151 | 182 | if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
|
152 | 183 | // At least for integers, shifts are already handled by the CTFE
|
|
0 commit comments