use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use clippy_utils::expr_or_init;
+use clippy_utils::source::snippet;
use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_ast::ast;
-use rustc_attr::IntType;
+use rustc_errors::{Applicability, SuggestionStyle};
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{BinOpKind, Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, FloatTy, Ty};
+use rustc_target::abi::IntegerType;
use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
};
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
- let cast_from_ptr_size = def.repr().int.map_or(true, |ty| {
- matches!(
- ty,
- IntType::SignedInt(ast::IntTy::Isize) | IntType::UnsignedInt(ast::UintTy::Usize)
- )
- });
+ let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
(false, false) if from_nbits > to_nbits => "",
(true, false) if from_nbits > to_nbits => "",
);
return;
}
- format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}",)
+ format!("casting `{cast_from}` to `{cast_to}` may truncate the value{suffix}")
},
(ty::Float(_), true) => {
_ => return,
};
- span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg);
+ let snippet = snippet(cx, expr.span, "..");
+ let name_of_cast_from = snippet.split(" as").next().unwrap_or("..");
+ let suggestion = format!("{cast_to}::try_from({name_of_cast_from})");
+
+ span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg, |diag| {
+ diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_precision_loss)]` ...");
+ diag.span_suggestion_with_style(
+ expr.span,
+ "... or use `try_from` and handle the error accordingly",
+ suggestion,
+ Applicability::Unspecified,
+ // always show the suggestion in a separate line
+ SuggestionStyle::ShowAlways,
+ );
+ });
}