Move cast_lossless to its own module
This commit is contained in:
parent
c2cbcd3229
commit
b12d7515b1
2 changed files with 92 additions and 74 deletions
87
clippy_lints/src/casts/cast_lossless.rs
Normal file
87
clippy_lints/src/casts/cast_lossless.rs
Normal file
|
@ -0,0 +1,87 @@
|
|||
use rustc_errors::Applicability;
|
||||
use rustc_hir::{Expr, ExprKind};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{self, FloatTy, Ty};
|
||||
|
||||
use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg};
|
||||
|
||||
use super::{utils, CAST_LOSSLESS};
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
if !should_lint(cx, expr, cast_from, cast_to) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The suggestion is to use a function call, so if the original expression
|
||||
// has parens on the outside, they are no longer needed.
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let opt = snippet_opt(cx, cast_op.span);
|
||||
let sugg = opt.as_ref().map_or_else(
|
||||
|| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
".."
|
||||
},
|
||||
|snip| {
|
||||
if should_strip_parens(cast_op, snip) {
|
||||
&snip[1..snip.len() - 1]
|
||||
} else {
|
||||
snip.as_str()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_LOSSLESS,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting `{}` to `{}` may become silently lossy if you later change the type",
|
||||
cast_from, cast_to
|
||||
),
|
||||
"try",
|
||||
format!("{}::from({})", cast_to, sugg),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
|
||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
match (cast_from.is_integral(), cast_to.is_integral()) {
|
||||
(true, true) => {
|
||||
let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
|
||||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
|
||||
!is_isize_or_usize(cast_from)
|
||||
&& !is_isize_or_usize(cast_to)
|
||||
&& from_nbits < to_nbits
|
||||
&& !cast_signed_to_unsigned
|
||||
},
|
||||
|
||||
(true, false) => {
|
||||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
|
||||
32
|
||||
} else {
|
||||
64
|
||||
};
|
||||
from_nbits < to_nbits
|
||||
},
|
||||
|
||||
(_, _) => {
|
||||
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
|
||||
if let ExprKind::Binary(_, _, _) = cast_expr.kind {
|
||||
if snip.starts_with('(') && snip.ends_with(')') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
mod cast_lossless;
|
||||
mod cast_precision_loss;
|
||||
mod utils;
|
||||
|
||||
|
@ -18,9 +19,8 @@ use rustc_target::abi::LayoutOf;
|
|||
use crate::consts::{constant, Constant};
|
||||
use crate::utils::sugg::Sugg;
|
||||
use crate::utils::{
|
||||
in_constant, is_hir_ty_cfg_dependant, is_isize_or_usize, meets_msrv, method_chain_args,
|
||||
numeric_literal::NumericLiteral, sext, snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg,
|
||||
span_lint_and_then,
|
||||
is_hir_ty_cfg_dependant, is_isize_or_usize, meets_msrv, method_chain_args, numeric_literal::NumericLiteral, sext,
|
||||
snippet_opt, snippet_with_applicability, span_lint, span_lint_and_sugg, span_lint_and_then,
|
||||
};
|
||||
|
||||
use utils::int_ty_to_nbits;
|
||||
|
@ -254,52 +254,6 @@ declare_clippy_lint! {
|
|||
"casting a function pointer to a numeric type not wide enough to store the address"
|
||||
}
|
||||
|
||||
fn should_strip_parens(op: &Expr<'_>, snip: &str) -> bool {
|
||||
if let ExprKind::Binary(_, _, _) = op.kind {
|
||||
if snip.starts_with('(') && snip.ends_with(')') {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn span_lossless_lint(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267).
|
||||
if in_constant(cx, expr.hir_id) {
|
||||
return;
|
||||
}
|
||||
// The suggestion is to use a function call, so if the original expression
|
||||
// has parens on the outside, they are no longer needed.
|
||||
let mut applicability = Applicability::MachineApplicable;
|
||||
let opt = snippet_opt(cx, op.span);
|
||||
let sugg = opt.as_ref().map_or_else(
|
||||
|| {
|
||||
applicability = Applicability::HasPlaceholders;
|
||||
".."
|
||||
},
|
||||
|snip| {
|
||||
if should_strip_parens(op, snip) {
|
||||
&snip[1..snip.len() - 1]
|
||||
} else {
|
||||
snip.as_str()
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
CAST_LOSSLESS,
|
||||
expr.span,
|
||||
&format!(
|
||||
"casting `{}` to `{}` may become silently lossy if you later change the type",
|
||||
cast_from, cast_to
|
||||
),
|
||||
"try",
|
||||
format!("{}::from({})", cast_to, sugg),
|
||||
applicability,
|
||||
);
|
||||
}
|
||||
|
||||
enum ArchSuffix {
|
||||
_32,
|
||||
_64,
|
||||
|
@ -423,16 +377,6 @@ fn check_truncation_and_wrapping(cx: &LateContext<'_>, expr: &Expr<'_>, cast_fro
|
|||
}
|
||||
}
|
||||
|
||||
fn check_lossless(cx: &LateContext<'_>, expr: &Expr<'_>, op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
|
||||
let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
|
||||
let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = int_ty_to_nbits(cast_to, cx.tcx);
|
||||
if !is_isize_or_usize(cast_from) && !is_isize_or_usize(cast_to) && from_nbits < to_nbits && !cast_signed_to_unsigned
|
||||
{
|
||||
span_lossless_lint(cx, expr, op, cast_from, cast_to);
|
||||
}
|
||||
}
|
||||
|
||||
declare_lint_pass!(Casts => [
|
||||
CAST_PRECISION_LOSS,
|
||||
CAST_SIGN_LOSS,
|
||||
|
@ -584,18 +528,8 @@ fn lint_numeric_casts<'tcx>(
|
|||
cast_to: Ty<'tcx>,
|
||||
) {
|
||||
cast_precision_loss::check(cx, expr, cast_from, cast_to);
|
||||
cast_lossless::check(cx, expr, cast_expr, cast_from, cast_to);
|
||||
match (cast_from.is_integral(), cast_to.is_integral()) {
|
||||
(true, false) => {
|
||||
let from_nbits = int_ty_to_nbits(cast_from, cx.tcx);
|
||||
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
|
||||
32
|
||||
} else {
|
||||
64
|
||||
};
|
||||
if from_nbits < to_nbits {
|
||||
span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
span_lint(
|
||||
cx,
|
||||
|
@ -618,7 +552,6 @@ fn lint_numeric_casts<'tcx>(
|
|||
(true, true) => {
|
||||
check_loss_of_sign(cx, expr, cast_expr, cast_from, cast_to);
|
||||
check_truncation_and_wrapping(cx, expr, cast_from, cast_to);
|
||||
check_lossless(cx, expr, cast_expr, cast_from, cast_to);
|
||||
},
|
||||
(false, false) => {
|
||||
if let (&ty::Float(FloatTy::F64), &ty::Float(FloatTy::F32)) = (&cast_from.kind(), &cast_to.kind()) {
|
||||
|
@ -629,10 +562,8 @@ fn lint_numeric_casts<'tcx>(
|
|||
"casting `f64` to `f32` may truncate the value",
|
||||
);
|
||||
}
|
||||
if let (&ty::Float(FloatTy::F32), &ty::Float(FloatTy::F64)) = (&cast_from.kind(), &cast_to.kind()) {
|
||||
span_lossless_lint(cx, expr, cast_expr, cast_from, cast_to);
|
||||
}
|
||||
},
|
||||
(_, _) => {},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue