Skip to content

Commit c2cbcd3

Browse files
committed
Move cast_precision_loss to its own module
1 parent 360f065 commit c2cbcd3

File tree

3 files changed

+83
-55
lines changed

3 files changed

+83
-55
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
use rustc_hir::Expr;
2+
use rustc_lint::LateContext;
3+
use rustc_middle::ty::{self, FloatTy, Ty};
4+
5+
use crate::utils::{is_isize_or_usize, span_lint};
6+
7+
use super::{utils, CAST_PRECISION_LOSS};
8+
9+
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
10+
if !cast_from.is_integral() || cast_to.is_integral() {
11+
return;
12+
}
13+
14+
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
15+
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
16+
32
17+
} else {
18+
64
19+
};
20+
21+
if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) {
22+
return;
23+
}
24+
25+
let cast_to_f64 = to_nbits == 64;
26+
let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
27+
let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
28+
let arch_dependent_str = "on targets with 64-bit wide pointers ";
29+
let from_nbits_str = if arch_dependent {
30+
"64".to_owned()
31+
} else if is_isize_or_usize(cast_from) {
32+
"32 or 64".to_owned()
33+
} else {
34+
utils::int_ty_to_nbits(cast_from, cx.tcx).to_string()
35+
};
36+
37+
span_lint(
38+
cx,
39+
CAST_PRECISION_LOSS,
40+
expr.span,
41+
&format!(
42+
"casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \
43+
but `{1}`'s mantissa is only {4} bits wide)",
44+
cast_from,
45+
if cast_to_f64 { "f64" } else { "f32" },
46+
if arch_dependent { arch_dependent_str } else { "" },
47+
from_nbits_str,
48+
mantissa_nbits
49+
),
50+
);
51+
}

clippy_lints/src/casts/mod.rs

Lines changed: 7 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
mod cast_precision_loss;
2+
mod utils;
3+
14
use std::borrow::Cow;
25

36
use if_chain::if_chain;
@@ -6,7 +9,7 @@ use rustc_errors::Applicability;
69
use rustc_hir::{Expr, ExprKind, GenericArg, Lit, MutTy, Mutability, TyKind, UnOp};
710
use rustc_lint::{LateContext, LateLintPass, LintContext};
811
use rustc_middle::lint::in_external_macro;
9-
use rustc_middle::ty::{self, FloatTy, InferTy, IntTy, Ty, TyCtxt, TypeAndMut, UintTy};
12+
use rustc_middle::ty::{self, FloatTy, InferTy, Ty, TypeAndMut, UintTy};
1013
use rustc_semver::RustcVersion;
1114
use rustc_session::{declare_lint_pass, declare_tool_lint, impl_lint_pass};
1215
use rustc_span::symbol::sym;
@@ -20,6 +23,8 @@ use crate::utils::{
2023
span_lint_and_then,
2124
};
2225

26+
use utils::int_ty_to_nbits;
27+
2328
declare_clippy_lint! {
2429
/// **What it does:** Checks for casts from any numerical to a float type where
2530
/// the receiving type cannot store all values from the original type without
@@ -249,57 +254,6 @@ declare_clippy_lint! {
249254
"casting a function pointer to a numeric type not wide enough to store the address"
250255
}
251256

252-
/// Returns the size in bits of an integral type.
253-
/// Will return 0 if the type is not an int or uint variant
254-
fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
255-
match typ.kind() {
256-
ty::Int(i) => match i {
257-
IntTy::Isize => tcx.data_layout.pointer_size.bits(),
258-
IntTy::I8 => 8,
259-
IntTy::I16 => 16,
260-
IntTy::I32 => 32,
261-
IntTy::I64 => 64,
262-
IntTy::I128 => 128,
263-
},
264-
ty::Uint(i) => match i {
265-
UintTy::Usize => tcx.data_layout.pointer_size.bits(),
266-
UintTy::U8 => 8,
267-
UintTy::U16 => 16,
268-
UintTy::U32 => 32,
269-
UintTy::U64 => 64,
270-
UintTy::U128 => 128,
271-
},
272-
_ => 0,
273-
}
274-
}
275-
276-
fn span_precision_loss_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to_f64: bool) {
277-
let mantissa_nbits = if cast_to_f64 { 52 } else { 23 };
278-
let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64;
279-
let arch_dependent_str = "on targets with 64-bit wide pointers ";
280-
let from_nbits_str = if arch_dependent {
281-
"64".to_owned()
282-
} else if is_isize_or_usize(cast_from) {
283-
"32 or 64".to_owned()
284-
} else {
285-
int_ty_to_nbits(cast_from, cx.tcx).to_string()
286-
};
287-
span_lint(
288-
cx,
289-
CAST_PRECISION_LOSS,
290-
expr.span,
291-
&format!(
292-
"casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \
293-
but `{1}`'s mantissa is only {4} bits wide)",
294-
cast_from,
295-
if cast_to_f64 { "f64" } else { "f32" },
296-
if arch_dependent { arch_dependent_str } else { "" },
297-
from_nbits_str,
298-
mantissa_nbits
299-
),
300-
);
301-
}
302-
303257
fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool {
304258
if let ExprKind::Binary(_, _, _) = op.kind {
305259
if snip.starts_with('(') && snip.ends_with(')') {
@@ -629,6 +583,7 @@ fn lint_numeric_casts<'tcx>(
629583
cast_from: Ty<'tcx>,
630584
cast_to: Ty<'tcx>,
631585
) {
586+
cast_precision_loss::check(cx, expr, cast_from, cast_to);
632587
match (cast_from.is_integral(), cast_to.is_integral()) {
633588
(true, false) => {
634589
let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
@@ -637,9 +592,6 @@ fn lint_numeric_casts<'tcx>(
637592
} else {
638593
64
639594
};
640-
if is_isize_or_usize(cast_from) || from_nbits >= to_nbits {
641-
span_precision_loss_lint(cx, expr, cast_from, to_nbits == 64);
642-
}
643595
if from_nbits < to_nbits {
644596
span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
645597
}

clippy_lints/src/casts/utils.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
2+
3+
/// Returns the size in bits of an integral type.
4+
/// Will return 0 if the type is not an int or uint variant
5+
pub(super) fn int_ty_to_nbits(typ: Ty<'_>, tcx: TyCtxt<'_>) -> u64 {
6+
match typ.kind() {
7+
ty::Int(i) => match i {
8+
IntTy::Isize => tcx.data_layout.pointer_size.bits(),
9+
IntTy::I8 => 8,
10+
IntTy::I16 => 16,
11+
IntTy::I32 => 32,
12+
IntTy::I64 => 64,
13+
IntTy::I128 => 128,
14+
},
15+
ty::Uint(i) => match i {
16+
UintTy::Usize => tcx.data_layout.pointer_size.bits(),
17+
UintTy::U8 => 8,
18+
UintTy::U16 => 16,
19+
UintTy::U32 => 32,
20+
UintTy::U64 => 64,
21+
UintTy::U128 => 128,
22+
},
23+
_ => 0,
24+
}
25+
}

0 commit comments

Comments
 (0)