|
1 | 1 | use clippy_utils::consts::{constant, Constant};
|
2 | 2 | use clippy_utils::diagnostics::span_lint;
|
3 | 3 | use clippy_utils::expr_or_init;
|
4 |
| -use clippy_utils::ty::is_isize_or_usize; |
| 4 | +use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize}; |
| 5 | +use rustc_ast::ast; |
| 6 | +use rustc_attr::IntType; |
| 7 | +use rustc_hir::def::{DefKind, Res}; |
5 | 8 | use rustc_hir::{BinOpKind, Expr, ExprKind};
|
6 | 9 | use rustc_lint::LateContext;
|
7 | 10 | use rustc_middle::ty::{self, FloatTy, Ty};
|
8 | 11 |
|
9 |
| -use super::{utils, CAST_POSSIBLE_TRUNCATION}; |
| 12 | +use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION}; |
10 | 13 |
|
11 | 14 | fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
|
12 | 15 | if let Some((Constant::Int(c), _)) = constant(cx, cx.typeck_results(), expr) {
|
@@ -75,8 +78,8 @@ fn apply_reductions(cx: &LateContext<'_>, nbits: u64, expr: &Expr<'_>, signed: b
|
75 | 78 | }
|
76 | 79 |
|
77 | 80 | pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
78 |
| - let msg = match (cast_from.is_integral(), cast_to.is_integral()) { |
79 |
| - (true, true) => { |
| 81 | + let msg = match (cast_from.kind(), cast_to.is_integral()) { |
| 82 | + (ty::Int(_) | ty::Uint(_), true) => { |
80 | 83 | let from_nbits = apply_reductions(
|
81 | 84 | cx,
|
82 | 85 | utils::int_ty_to_nbits(cast_from, cx.tcx),
|
@@ -108,19 +111,60 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
|
108 | 111 | )
|
109 | 112 | },
|
110 | 113 |
|
111 |
| - (false, true) => { |
112 |
| - format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) |
113 |
| - }, |
114 |
| - |
115 |
| - (_, _) => { |
116 |
| - if matches!(cast_from.kind(), &ty::Float(FloatTy::F64)) |
117 |
| - && matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) |
| 114 | + (ty::Adt(def, _), true) if def.is_enum() => { |
| 115 | + let (from_nbits, variant) = if let ExprKind::Path(p) = &cast_expr.kind |
| 116 | + && let Res::Def(DefKind::Ctor(..), id) = cx.qpath_res(p, cast_expr.hir_id) |
118 | 117 | {
|
119 |
| - "casting `f64` to `f32` may truncate the value".to_string() |
| 118 | + let i = def.variant_index_with_ctor_id(id); |
| 119 | + let variant = &def.variants[i]; |
| 120 | + let nbits = utils::enum_value_nbits(get_discriminant_value(cx.tcx, def, i)); |
| 121 | + (nbits, Some(variant)) |
120 | 122 | } else {
|
| 123 | + (utils::enum_ty_to_nbits(def, cx.tcx), None) |
| 124 | + }; |
| 125 | + let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); |
| 126 | + |
| 127 | + let cast_from_ptr_size = def.repr.int.map_or(true, |ty| { |
| 128 | + matches!( |
| 129 | + ty, |
| 130 | + IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize) |
| 131 | + ) |
| 132 | + }); |
| 133 | + let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) { |
| 134 | + (false, false) if from_nbits > to_nbits => "", |
| 135 | + (true, false) if from_nbits > to_nbits => "", |
| 136 | + (false, true) if from_nbits > 64 => "", |
| 137 | + (false, true) if from_nbits > 32 => " on targets with 32-bit wide pointers", |
| 138 | + _ => return, |
| 139 | + }; |
| 140 | + |
| 141 | + if let Some(variant) = variant { |
| 142 | + span_lint( |
| 143 | + cx, |
| 144 | + CAST_ENUM_TRUNCATION, |
| 145 | + expr.span, |
| 146 | + &format!( |
| 147 | + "casting `{}::{}` to `{}` will truncate the value{}", |
| 148 | + cast_from, variant.name, cast_to, suffix, |
| 149 | + ), |
| 150 | + ); |
121 | 151 | return;
|
122 | 152 | }
|
| 153 | + format!( |
| 154 | + "casting `{}` to `{}` may truncate the value{}", |
| 155 | + cast_from, cast_to, suffix, |
| 156 | + ) |
123 | 157 | },
|
| 158 | + |
| 159 | + (ty::Float(_), true) => { |
| 160 | + format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) |
| 161 | + }, |
| 162 | + |
| 163 | + (ty::Float(FloatTy::F64), false) if matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) => { |
| 164 | + "casting `f64` to `f32` may truncate the value".to_string() |
| 165 | + }, |
| 166 | + |
| 167 | + _ => return, |
124 | 168 | };
|
125 | 169 |
|
126 | 170 | span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
|
|
0 commit comments