|
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::{match_type, 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 | 17 | const INTEGER_METHODS: &[&str] = &["saturating_div", "wrapping_div", "wrapping_rem", "wrapping_rem_euclid"];
|
22 | 18 |
|
@@ -86,6 +82,48 @@ 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 | + const SATURATING: &[&str] = &["core", "num", "saturating", "Saturating"]; |
| 93 | + const WRAPPING: &[&str] = &["core", "num", "wrapping", "Wrapping"]; |
| 94 | + let is_non_zero = |symbol: Option<Symbol>| { |
| 95 | + matches!( |
| 96 | + symbol, |
| 97 | + Some( |
| 98 | + sym::NonZeroI128 |
| 99 | + | sym::NonZeroI16 |
| 100 | + | sym::NonZeroI32 |
| 101 | + | sym::NonZeroI64 |
| 102 | + | sym::NonZeroI8 |
| 103 | + | sym::NonZeroU128 |
| 104 | + | sym::NonZeroU16 |
| 105 | + | sym::NonZeroU32 |
| 106 | + | sym::NonZeroU64 |
| 107 | + | sym::NonZeroU8 |
| 108 | + ) |
| 109 | + ) |
| 110 | + }; |
| 111 | + // If the RHS is NonZero*, then division or module by zero will never occur |
| 112 | + if is_non_zero(type_diagnostic_name(cx, rhs_ty)) && let hir::BinOpKind::Div | hir::BinOpKind::Rem = op.node { |
| 113 | + return true; |
| 114 | + } |
| 115 | + // For `Saturation` or `Wrapping` (RHS), all but division and module are allowed. |
| 116 | + let is_div_or_rem = matches!(op.node, hir::BinOpKind::Div | hir::BinOpKind::Rem); |
| 117 | + if (match_type(cx, rhs_ty, SATURATING) || match_type(cx, rhs_ty, WRAPPING)) && !is_div_or_rem { |
| 118 | + return true; |
| 119 | + } |
| 120 | + // For `Saturation` or `Wrapping` (LHS), everything is allowed |
| 121 | + if match_type(cx, lhs_ty, SATURATING) || match_type(cx, lhs_ty, WRAPPING) { |
| 122 | + return true; |
| 123 | + } |
| 124 | + false |
| 125 | + } |
| 126 | + |
89 | 127 | // For example, 8i32 or &i64::MAX.
|
90 | 128 | fn is_integral(ty: Ty<'_>) -> bool {
|
91 | 129 | ty.peel_refs().is_integral()
|
@@ -147,6 +185,9 @@ impl ArithmeticSideEffects {
|
147 | 185 | if self.has_allowed_binary(lhs_ty, rhs_ty) {
|
148 | 186 | return;
|
149 | 187 | }
|
| 188 | + if Self::has_specific_allowed_type_and_operation(cx, lhs_ty, op, rhs_ty) { |
| 189 | + return; |
| 190 | + } |
150 | 191 | let has_valid_op = if Self::is_integral(lhs_ty) && Self::is_integral(rhs_ty) {
|
151 | 192 | if let hir::BinOpKind::Shl | hir::BinOpKind::Shr = op.node {
|
152 | 193 | // At least for integers, shifts are already handled by the CTFE
|
|
0 commit comments