1 use rustc_errors::Applicability;
2 use rustc_hir::{Expr, ExprKind};
3 use rustc_lint::LateContext;
4 use rustc_middle::ty::{self, FloatTy, Ty};
6 use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg};
8 use super::{utils, CAST_LOSSLESS};
10 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
11 if !should_lint(cx, expr, cast_from, cast_to) {
15 // The suggestion is to use a function call, so if the original expression
16 // has parens on the outside, they are no longer needed.
17 let mut applicability = Applicability::MachineApplicable;
18 let opt = snippet_opt(cx, cast_op.span);
19 let sugg = opt.as_ref().map_or_else(
21 applicability = Applicability::HasPlaceholders;
25 if should_strip_parens(cast_op, snip) {
26 &snip[1..snip.len() - 1]
38 "casting `{}` to `{}` may become silently lossy if you later change the type",
42 format!("{}::from({})", cast_to, sugg),
47 fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool {
48 // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
49 if in_constant(cx, expr.hir_id) {
53 match (cast_from.is_integral(), cast_to.is_integral()) {
55 let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed();
56 let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
57 let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
58 !is_isize_or_usize(cast_from)
59 && !is_isize_or_usize(cast_to)
60 && from_nbits < to_nbits
61 && !cast_signed_to_unsigned
65 let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx);
66 let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() {
75 matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64))
80 fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool {
81 if let ExprKind::Binary(_, _, _) = cast_expr.kind {
82 if snip.starts_with('(') && snip.ends_with(')') {