diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 5e13ee7b8a41d..c66fcdec7164c 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -425,6 +425,7 @@ lint_overflowing_bin_hex = literal out of range for `{$ty}` .negative_becomes_note = and the value `-{$lit}` will become `{$actually}{$ty}` .positive_note = the literal `{$lit}` (decimal `{$dec}`) does not fit into the type `{$ty}` and will become `{$actually}{$ty}` .suggestion = consider using the type `{$suggestion_ty}` instead + .sign_bit_suggestion = to use as a negative number (decimal `{$negative_val}`), consider using the type `{$uint_ty}` for the literal and cast it to `{$int_ty}` .help = consider using the type `{$suggestion_ty}` instead lint_overflowing_int = literal out of range for `{$ty}` diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index e990c771bdf50..89fa5713b73ef 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -1342,6 +1342,8 @@ pub struct OverflowingBinHex<'a> { pub sign: OverflowingBinHexSign, #[subdiagnostic] pub sub: Option>, + #[subdiagnostic] + pub sign_bit_sub: Option>, } pub enum OverflowingBinHexSign { @@ -1386,6 +1388,21 @@ pub enum OverflowingBinHexSub<'a> { Help { suggestion_ty: &'a str }, } +#[derive(Subdiagnostic)] +#[suggestion( + lint_sign_bit_suggestion, + code = "{lit_no_suffix}{uint_ty} as {int_ty}", + applicability = "maybe-incorrect" +)] +pub struct OverflowingBinHexSignBitSub<'a> { + #[primary_span] + pub span: Span, + pub lit_no_suffix: &'a str, + pub negative_val: String, + pub uint_ty: &'a str, + pub int_ty: &'a str, +} + #[derive(LintDiagnostic)] #[diag(lint_overflowing_int)] #[note] diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index 264a59c5585a0..ec9e7c7fdae83 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -3,9 +3,10 @@ use crate::{ lints::{ AtomicOrderingFence, AtomicOrderingLoad, AtomicOrderingStore, ImproperCTypes, InvalidAtomicOrderingDiag, InvalidNanComparisons, InvalidNanComparisonsSuggestion, - OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSub, - OverflowingInt, OverflowingIntHelp, OverflowingLiteral, OverflowingUInt, - RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, VariantSizeDifferencesDiag, + OnlyCastu8ToChar, OverflowingBinHex, OverflowingBinHexSign, OverflowingBinHexSignBitSub, + OverflowingBinHexSub, OverflowingInt, OverflowingIntHelp, OverflowingLiteral, + OverflowingUInt, RangeEndpointOutOfRange, UnusedComparisons, UseInclusiveRange, + VariantSizeDifferencesDiag, }, }; use crate::{LateContext, LateLintPass, LintContext}; @@ -297,10 +298,50 @@ fn report_bin_hex_error( } }, ); + let sign_bit_sub = (!negative) + .then(|| { + let ty::Int(int_ty) = cx.typeck_results().node_type(expr.hir_id).kind() else { + return None; + }; + + let Some(bit_width) = int_ty.bit_width() else { + return None; // isize case + }; + + // Skip if sign bit is not set + if (val & (1 << (bit_width - 1))) == 0 { + return None; + } + + let lit_no_suffix = + if let Some(pos) = repr_str.chars().position(|c| c == 'i' || c == 'u') { + repr_str.split_at(pos).0 + } else { + &repr_str + }; + + Some(OverflowingBinHexSignBitSub { + span: expr.span, + lit_no_suffix, + negative_val: actually.clone(), + int_ty: int_ty.name_str(), + uint_ty: int_ty.to_unsigned().name_str(), + }) + }) + .flatten(); + cx.emit_spanned_lint( OVERFLOWING_LITERALS, expr.span, - OverflowingBinHex { ty: t, lit: repr_str.clone(), dec: val, actually, sign, sub }, + OverflowingBinHex { + ty: t, + lit: repr_str.clone(), + dec: val, + actually, + sign, + sub, + sign_bit_sub, + }, ) } diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index 62cb1f7f4aa37..e7c90dcc81bb2 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -16,17 +16,33 @@ warning: literal out of range for `i8` --> $DIR/type-overflow.rs:10:16 | LL | let fail = 0b1000_0001i8; - | ^^^^^^^^^^^^^ help: consider using the type `u8` instead: `0b1000_0001u8` + | ^^^^^^^^^^^^^ | = note: the literal `0b1000_0001i8` (decimal `129`) does not fit into the type `i8` and will become `-127i8` +help: consider using the type `u8` instead + | +LL | let fail = 0b1000_0001u8; + | ~~~~~~~~~~~~~ +help: to use as a negative number (decimal `-127`), consider using the type `u8` for the literal and cast it to `i8` + | +LL | let fail = 0b1000_0001u8 as i8; + | ~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `i64` --> $DIR/type-overflow.rs:12:16 | LL | let fail = 0x8000_0000_0000_0000i64; - | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the type `u64` instead: `0x8000_0000_0000_0000u64` + | ^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8000_0000_0000_0000i64` (decimal `9223372036854775808`) does not fit into the type `i64` and will become `-9223372036854775808i64` +help: consider using the type `u64` instead + | +LL | let fail = 0x8000_0000_0000_0000u64; + | ~~~~~~~~~~~~~~~~~~~~~~~~ +help: to use as a negative number (decimal `-9223372036854775808`), consider using the type `u64` for the literal and cast it to `i64` + | +LL | let fail = 0x8000_0000_0000_0000u64 as i64; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `u32` --> $DIR/type-overflow.rs:14:16 @@ -44,6 +60,10 @@ LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i128` and will become `-170141183460469231731687303715884105728i128` = help: consider using the type `u128` instead +help: to use as a negative number (decimal `-170141183460469231731687303715884105728`), consider using the type `u128` for the literal and cast it to `i128` + | +LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000u128 as i128; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `i32` --> $DIR/type-overflow.rs:19:16 @@ -53,6 +73,10 @@ LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; | = note: the literal `0x8FFF_FFFF_FFFF_FFFE` (decimal `10376293541461622782`) does not fit into the type `i32` and will become `-2i32` = help: consider using the type `i128` instead +help: to use as a negative number (decimal `-2`), consider using the type `u32` for the literal and cast it to `i32` + | +LL | let fail = 0x8FFF_FFFF_FFFF_FFFEu32 as i32; + | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `i8` --> $DIR/type-overflow.rs:21:17