Skip to content

Commit 62b4964

Browse files
Add suggestion to cast_sign_loss and cast_possible_wrap using the cast_{un,}signed() methods
1 parent 94b7035 commit 62b4964

File tree

6 files changed

+131
-48
lines changed

6 files changed

+131
-48
lines changed

clippy_lints/src/casts/cast_possible_wrap.rs

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
use clippy_utils::diagnostics::span_lint_and_then;
2+
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::sugg::Sugg;
4+
use rustc_errors::Applicability;
25
use rustc_hir::Expr;
36
use rustc_lint::LateContext;
47
use rustc_middle::ty::Ty;
@@ -16,7 +19,14 @@ enum EmitState {
1619
LintOnPtrSize(u64),
1720
}
1821

19-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
22+
pub(super) fn check(
23+
cx: &LateContext<'_>,
24+
expr: &Expr<'_>,
25+
cast_op: &Expr<'_>,
26+
cast_from: Ty<'_>,
27+
cast_to: Ty<'_>,
28+
msrv: Msrv,
29+
) {
2030
let (Some(from_nbits), Some(to_nbits)) = (
2131
utils::int_ty_to_nbits(cx.tcx, cast_from),
2232
utils::int_ty_to_nbits(cx.tcx, cast_to),
@@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
8595
.note("`usize` and `isize` may be as small as 16 bits on some platforms")
8696
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types");
8797
}
98+
99+
if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST)
100+
&& let Some(cast) = utils::is_signedness_cast(cast_from, cast_to)
101+
{
102+
let method = match cast {
103+
utils::CastTo::Signed => "cast_signed()",
104+
utils::CastTo::Unsigned => "cast_unsigned()",
105+
};
106+
let mut app = Applicability::MaybeIncorrect;
107+
let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app);
108+
109+
diag.span_suggestion(
110+
expr.span,
111+
format!("if this is intentional, consider using `{method}` instead"),
112+
format!("{}.{method}", sugg.maybe_paren()),
113+
app,
114+
);
115+
}
88116
});
89117
}

clippy_lints/src/casts/cast_sign_loss.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ use std::convert::Infallible;
22
use std::ops::ControlFlow;
33

44
use clippy_utils::consts::{ConstEvalCtxt, Constant};
5-
use clippy_utils::diagnostics::span_lint;
5+
use clippy_utils::diagnostics::span_lint_and_then;
6+
use clippy_utils::msrvs::{self, Msrv};
7+
use clippy_utils::sugg::Sugg;
68
use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
79
use clippy_utils::{method_chain_args, sext, sym};
10+
use rustc_errors::Applicability;
811
use rustc_hir::{BinOpKind, Expr, ExprKind};
912
use rustc_lint::LateContext;
1013
use rustc_middle::ty::{self, Ty};
1114
use rustc_span::Symbol;
1215

13-
use super::CAST_SIGN_LOSS;
16+
use super::{CAST_SIGN_LOSS, utils};
1417

1518
/// A list of methods that can never return a negative value.
1619
/// Includes methods that panic rather than returning a negative value.
@@ -42,13 +45,33 @@ pub(super) fn check<'cx>(
4245
cast_op: &Expr<'_>,
4346
cast_from: Ty<'cx>,
4447
cast_to: Ty<'_>,
48+
msrv: Msrv,
4549
) {
4650
if should_lint(cx, cast_op, cast_from, cast_to) {
47-
span_lint(
51+
span_lint_and_then(
4852
cx,
4953
CAST_SIGN_LOSS,
5054
expr.span,
5155
format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"),
56+
|diag| {
57+
if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST)
58+
&& let Some(cast) = utils::is_signedness_cast(cast_from, cast_to)
59+
{
60+
let method = match cast {
61+
utils::CastTo::Signed => "cast_signed()",
62+
utils::CastTo::Unsigned => "cast_unsigned()",
63+
};
64+
let mut app = Applicability::MaybeIncorrect;
65+
let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app);
66+
67+
diag.span_suggestion(
68+
expr.span,
69+
format!("if this is intentional, consider using `{method}` instead"),
70+
format!("{}.{method}", sugg.maybe_paren()),
71+
app,
72+
);
73+
}
74+
},
5275
);
5376
}
5477
}

clippy_lints/src/casts/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -887,9 +887,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
887887
if cast_to.is_numeric() {
888888
cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
889889
if cast_from.is_numeric() {
890-
cast_possible_wrap::check(cx, expr, cast_from, cast_to);
890+
cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
891891
cast_precision_loss::check(cx, expr, cast_from, cast_to);
892-
cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to);
892+
cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
893893
cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
894894
cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to);
895895
}

clippy_lints/src/casts/utils.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,35 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 {
6060
neg_bits.max(pos_bits).into()
6161
}
6262
}
63+
64+
pub(super) enum CastTo {
65+
Signed,
66+
Unsigned,
67+
}
68+
/// Returns `Some` if the type cast is between 2 integral types that differ
69+
/// only in signedness, otherwise `None`. The value of `Some` is which
70+
/// signedness is casted to.
71+
pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option<CastTo> {
72+
if !cast_from.is_integral() || !cast_to.is_integral() {
73+
return None;
74+
}
75+
if cast_from.is_signed() == cast_to.is_signed() {
76+
return None;
77+
}
78+
if as_uint_ty(cast_from) != as_uint_ty(cast_to) {
79+
return None;
80+
}
81+
82+
if cast_to.is_signed() {
83+
Some(CastTo::Signed)
84+
} else {
85+
Some(CastTo::Unsigned)
86+
}
87+
}
88+
fn as_uint_ty(ty: Ty<'_>) -> Option<UintTy> {
89+
match ty.kind() {
90+
ty::Uint(uint_ty) => Some(*uint_ty),
91+
ty::Int(int_ty) => Some(int_ty.to_unsigned()),
92+
_ => None,
93+
}
94+
}

clippy_utils/src/msrvs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ macro_rules! msrv_aliases {
2424
// names may refer to stabilized feature flags or library items
2525
msrv_aliases! {
2626
1,88,0 { LET_CHAINS }
27-
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF }
27+
1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
2828
1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
2929
1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
3030
1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }

0 commit comments

Comments
 (0)