From be2369708a0fd53fc1ed80cbcca25f24e12a781d Mon Sep 17 00:00:00 2001 From: spore Date: Wed, 8 Jan 2025 18:31:29 +0800 Subject: [PATCH 1/7] Detect overflow when the literal is larger than i128::MAX --- compiler/rustc_lint/src/types/literal.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 83942918e3b58..c3e558a3e20a9 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -206,12 +206,15 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()), ty::Int(int) => { - let signed = Integer::fit_signed(val as i128); let unsigned = Integer::fit_unsigned(val); - Some(if Some(unsigned.size().bits()) == int.bit_width() { - unsigned.uint_ty_str() + Some(if let Ok(signed) = i128::try_from(val).map(Integer::fit_signed) { + if Some(unsigned.size().bits()) == int.bit_width() { + unsigned.uint_ty_str() + } else { + signed.int_ty_str() + } } else { - signed.int_ty_str() + unsigned.uint_ty_str() }) } _ => None, From 330be171440eb4efe479c5b1fcfd079153c5159d Mon Sep 17 00:00:00 2001 From: spore Date: Wed, 8 Jan 2025 19:00:10 +0800 Subject: [PATCH 2/7] Detect overflow when the literal is negative --- compiler/rustc_lint/src/types/literal.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index c3e558a3e20a9..13eca89fba67b 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -204,7 +204,13 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static match t.kind() { ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), - ty::Int(_) if negative => Some(Integer::fit_signed(-(val as i128)).int_ty_str()), + ty::Int(_) if negative => { + if val > i128::MAX as u128 + 1 { + None + } else { + Some(Integer::fit_signed(val.wrapping_neg() as i128).int_ty_str()) + } + } ty::Int(int) => { let unsigned = Integer::fit_unsigned(val); Some(if let Ok(signed) = i128::try_from(val).map(Integer::fit_signed) { From 1517a41c57b328b2e89641d79b1272e7004f7514 Mon Sep 17 00:00:00 2001 From: spore Date: Wed, 8 Jan 2025 19:00:35 +0800 Subject: [PATCH 3/7] Add test cases and test for `HELP` --- tests/ui/lint/type-overflow.rs | 26 +++++++++++++++ tests/ui/lint/type-overflow.stderr | 51 ++++++++++++++++++++++++++---- 2 files changed, 70 insertions(+), 7 deletions(-) diff --git a/tests/ui/lint/type-overflow.rs b/tests/ui/lint/type-overflow.rs index 7239e1c983764..0848d676e237c 100644 --- a/tests/ui/lint/type-overflow.rs +++ b/tests/ui/lint/type-overflow.rs @@ -3,20 +3,46 @@ fn main() { let error = 255i8; //~WARNING literal out of range for `i8` + //~^ HELP consider using the type `u8` instead let ok = 0b1000_0001; // should be ok -> i32 let ok = 0b0111_1111i8; // should be ok -> 127i8 let fail = 0b1000_0001i8; //~WARNING literal out of range for `i8` + //~^ HELP consider using the type `u8` instead + //~| HELP consider using the type `u8` for the literal and cast it to `i8` let fail = 0x8000_0000_0000_0000i64; //~WARNING literal out of range for `i64` + //~^ HELP consider using the type `u64` instead + //~| HELP consider using the type `u64` for the literal and cast it to `i64` let fail = 0x1_FFFF_FFFFu32; //~WARNING literal out of range for `u32` + //~^ HELP consider using the type `u64` instead let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; //~^ WARNING literal out of range for `i128` + //~| HELP consider using the type `u128` instead + //~| HELP consider using the type `u128` for the literal and cast it to `i128` + + let fail = 0x8000_0000_0000_0000_0000_0000_0000_0000; + //~^ WARNING literal out of range for `i32` + //~| HELP consider using the type `u128` instead + + let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; + //~^ WARNING literal out of range for `i32` + //~| HELP consider using the type `i128` instead + + let fail = -0x8000_0000_0000_0000_0000_0000_0000_0001i128; + //~^ WARNING literal out of range for `i128` + + let fail = 340282366920938463463374607431768211455i8; + //~^ WARNING literal out of range for `i8` + //~| HELP consider using the type `u128` instead let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for `i32` + //~| HELP consider using the type `i128` instead + //~| HELP let fail = -0b1111_1111i8; //~WARNING literal out of range for `i8` + //~| HELP consider using the type `i16` instead } diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index e7c90dcc81bb2..dbbe81e5c182d 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -13,7 +13,7 @@ LL | #![warn(overflowing_literals)] | ^^^^^^^^^^^^^^^^^^^^ warning: literal out of range for `i8` - --> $DIR/type-overflow.rs:10:16 + --> $DIR/type-overflow.rs:11:16 | LL | let fail = 0b1000_0001i8; | ^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | let fail = 0b1000_0001u8 as i8; | ~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `i64` - --> $DIR/type-overflow.rs:12:16 + --> $DIR/type-overflow.rs:15:16 | LL | let fail = 0x8000_0000_0000_0000i64; | ^^^^^^^^^^^^^^^^^^^^^^^^ @@ -45,7 +45,7 @@ LL | let fail = 0x8000_0000_0000_0000u64 as i64; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `u32` - --> $DIR/type-overflow.rs:14:16 + --> $DIR/type-overflow.rs:19:16 | LL | let fail = 0x1_FFFF_FFFFu32; | ^^^^^^^^^^^^^^^^ help: consider using the type `u64` instead: `0x1_FFFF_FFFFu64` @@ -53,7 +53,7 @@ LL | let fail = 0x1_FFFF_FFFFu32; = note: the literal `0x1_FFFF_FFFFu32` (decimal `8589934591`) does not fit into the type `u32` and will become `4294967295u32` warning: literal out of range for `i128` - --> $DIR/type-overflow.rs:16:22 + --> $DIR/type-overflow.rs:22:22 | LL | let fail: i128 = 0x8000_0000_0000_0000_0000_0000_0000_0000; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -66,7 +66,44 @@ 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 + --> $DIR/type-overflow.rs:27:16 + | +LL | let fail = 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 `i32` and will become `0i32` + = help: consider using the type `u128` instead + +warning: literal out of range for `i32` + --> $DIR/type-overflow.rs:31:17 + | +LL | let fail = -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 `i32` + = note: and the value `-0x8000_0000_0000_0000_0000_0000_0000_0000` will become `0i32` + = help: consider using the type `i128` instead + +warning: literal out of range for `i128` + --> $DIR/type-overflow.rs:35:17 + | +LL | let fail = -0x8000_0000_0000_0000_0000_0000_0000_0001i128; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0001i128` (decimal `170141183460469231731687303715884105729`) does not fit into the type `i128` + = note: and the value `-0x8000_0000_0000_0000_0000_0000_0000_0001i128` will become `170141183460469231731687303715884105727i128` + +warning: literal out of range for `i8` + --> $DIR/type-overflow.rs:38:16 + | +LL | let fail = 340282366920938463463374607431768211455i8; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: the literal `340282366920938463463374607431768211455i8` does not fit into the type `i8` whose range is `-128..=127` + = help: consider using the type `u128` instead + +warning: literal out of range for `i32` + --> $DIR/type-overflow.rs:42:16 | LL | let fail = 0x8FFF_FFFF_FFFF_FFFE; | ^^^^^^^^^^^^^^^^^^^^^ @@ -79,7 +116,7 @@ LL | let fail = 0x8FFF_FFFF_FFFF_FFFEu32 as i32; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ warning: literal out of range for `i8` - --> $DIR/type-overflow.rs:21:17 + --> $DIR/type-overflow.rs:46:17 | LL | let fail = -0b1111_1111i8; | ^^^^^^^^^^^^^ help: consider using the type `i16` instead: `0b1111_1111i16` @@ -87,5 +124,5 @@ LL | let fail = -0b1111_1111i8; = note: the literal `0b1111_1111i8` (decimal `255`) does not fit into the type `i8` = note: and the value `-0b1111_1111i8` will become `1i8` -warning: 7 warnings emitted +warning: 11 warnings emitted From c04e65dbc5f936fdd4dc53ba7b7a058165aad157 Mon Sep 17 00:00:00 2001 From: spore Date: Thu, 9 Jan 2025 02:02:40 +0800 Subject: [PATCH 4/7] Extract integer conversion into a function --- compiler/rustc_lint/src/types/literal.rs | 38 ++++++++++++++---------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 13eca89fba67b..4faf345bcd58f 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -204,29 +204,35 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static match t.kind() { ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), - ty::Int(_) if negative => { - if val > i128::MAX as u128 + 1 { - None - } else { - Some(Integer::fit_signed(val.wrapping_neg() as i128).int_ty_str()) - } - } ty::Int(int) => { - let unsigned = Integer::fit_unsigned(val); - Some(if let Ok(signed) = i128::try_from(val).map(Integer::fit_signed) { - if Some(unsigned.size().bits()) == int.bit_width() { - unsigned.uint_ty_str() - } else { - signed.int_ty_str() - } + let signed = literal_to_i128(val, negative).map(|v| Integer::fit_signed(v)); + if negative { + signed.map(Integer::int_ty_str) } else { - unsigned.uint_ty_str() - }) + let unsigned = Integer::fit_unsigned(val); + Some(if let Some(signed) = signed { + if Some(unsigned.size().bits()) == int.bit_width() { + unsigned.uint_ty_str() + } else { + signed.int_ty_str() + } + } else { + unsigned.uint_ty_str() + }) + } } _ => None, } } +fn literal_to_i128(val: u128, negative: bool) -> Option { + if negative { + (val <= i128::MAX as u128 + 1).then(|| val.wrapping_neg() as i128) + } else { + val.try_into().ok() + } +} + fn lint_int_literal<'tcx>( cx: &LateContext<'tcx>, type_limits: &TypeLimits, From 4a857557563a06a70c1c7533cf4220f42b37198a Mon Sep 17 00:00:00 2001 From: spore Date: Sat, 11 Jan 2025 13:05:15 +0800 Subject: [PATCH 5/7] Minor simplification Apply eta-reduction on map to simplify code and make the style more consistent --- compiler/rustc_lint/src/types/literal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index 4faf345bcd58f..f7a59437bbc27 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -205,7 +205,7 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), ty::Int(int) => { - let signed = literal_to_i128(val, negative).map(|v| Integer::fit_signed(v)); + let signed = literal_to_i128(val, negative).map(Integer::fit_signed); if negative { signed.map(Integer::int_ty_str) } else { From f52724c917216a8c9126479b0e0c097afe447593 Mon Sep 17 00:00:00 2001 From: spore Date: Sun, 12 Jan 2025 19:50:01 +0800 Subject: [PATCH 6/7] Add comment on case to mark the original issue --- tests/ui/lint/type-overflow.rs | 2 +- tests/ui/lint/type-overflow.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/lint/type-overflow.rs b/tests/ui/lint/type-overflow.rs index 0848d676e237c..102d042773b99 100644 --- a/tests/ui/lint/type-overflow.rs +++ b/tests/ui/lint/type-overflow.rs @@ -28,7 +28,7 @@ fn main() { //~^ WARNING literal out of range for `i32` //~| HELP consider using the type `u128` instead - let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; + let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; // issue #131849 //~^ WARNING literal out of range for `i32` //~| HELP consider using the type `i128` instead diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index dbbe81e5c182d..720044c68ebda 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -77,7 +77,7 @@ LL | let fail = 0x8000_0000_0000_0000_0000_0000_0000_0000; warning: literal out of range for `i32` --> $DIR/type-overflow.rs:31:17 | -LL | let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; +LL | let fail = -0x8000_0000_0000_0000_0000_0000_0000_0000; // issue #131849 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: the literal `0x8000_0000_0000_0000_0000_0000_0000_0000` (decimal `170141183460469231731687303715884105728`) does not fit into the type `i32` From 74e2e8b59834aede7aafee57e00ede07ae13739b Mon Sep 17 00:00:00 2001 From: spore Date: Sun, 12 Jan 2025 20:18:53 +0800 Subject: [PATCH 7/7] Suggest the smallest fitting type instead Changes the behavior of the `overflowing_literals` suggestion so that it always suggest the smallest type regardless of the original type size. --- compiler/rustc_lint/src/types/literal.rs | 4 ++-- tests/ui/lint/type-overflow.rs | 2 +- tests/ui/lint/type-overflow.stderr | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_lint/src/types/literal.rs b/compiler/rustc_lint/src/types/literal.rs index f7a59437bbc27..4b5163522f866 100644 --- a/compiler/rustc_lint/src/types/literal.rs +++ b/compiler/rustc_lint/src/types/literal.rs @@ -204,14 +204,14 @@ fn get_type_suggestion(t: Ty<'_>, val: u128, negative: bool) -> Option<&'static match t.kind() { ty::Uint(ty::UintTy::Usize) | ty::Int(ty::IntTy::Isize) => None, ty::Uint(_) => Some(Integer::fit_unsigned(val).uint_ty_str()), - ty::Int(int) => { + ty::Int(_) => { let signed = literal_to_i128(val, negative).map(Integer::fit_signed); if negative { signed.map(Integer::int_ty_str) } else { let unsigned = Integer::fit_unsigned(val); Some(if let Some(signed) = signed { - if Some(unsigned.size().bits()) == int.bit_width() { + if unsigned.size() < signed.size() { unsigned.uint_ty_str() } else { signed.int_ty_str() diff --git a/tests/ui/lint/type-overflow.rs b/tests/ui/lint/type-overflow.rs index 102d042773b99..1e74a8925f621 100644 --- a/tests/ui/lint/type-overflow.rs +++ b/tests/ui/lint/type-overflow.rs @@ -40,7 +40,7 @@ fn main() { //~| HELP consider using the type `u128` instead let fail = 0x8FFF_FFFF_FFFF_FFFE; //~WARNING literal out of range for `i32` - //~| HELP consider using the type `i128` instead + //~| HELP consider using the type `u64` instead //~| HELP let fail = -0b1111_1111i8; //~WARNING literal out of range for `i8` diff --git a/tests/ui/lint/type-overflow.stderr b/tests/ui/lint/type-overflow.stderr index 720044c68ebda..9fdb05ed1c036 100644 --- a/tests/ui/lint/type-overflow.stderr +++ b/tests/ui/lint/type-overflow.stderr @@ -109,7 +109,7 @@ 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: consider using the type `u64` 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;