about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMatthias Krüger <matthias.krueger@famsik.de>2023-06-15 17:52:35 +0200
committerGitHub <noreply@github.com>2023-06-15 17:52:35 +0200
commitf530016f50799aba1a39df2fb881324e6e9193aa (patch)
tree58e9b92529ff3ecc2491630730dad51483ef8b9d
parent4996b56ba9647d149265d03dcbd9ab837af3a1bb (diff)
parent211376927d02a032987d61a445648e7c1e6cc4ca (diff)
Rollup merge of #111212 - nicklimmm:issue-107896-fix, r=pnkfelix
Add casting suggestion when assigning negative 2's complement bin or hex literal to a size compatible signed integer

Fixes #107896

The issue stated the case for `iX::MIN` variants. This PR extends the cases for other negative values (in the 2's complement).

Leveraged sign bits to detect such cases.

Example cases:
- <img width="845" alt="image" src="https://user-images.githubusercontent.com/65026286/236289682-19859f59-a9c5-48c5-b15f-78a935fbfcec.png">
- <img width="831" alt="image" src="https://user-images.githubusercontent.com/65026286/236289805-5b16488d-9138-4363-a1b6-a5c027c50aba.png">
- <img width="912" alt="image" src="https://user-images.githubusercontent.com/65026286/236290065-685a9777-034b-4def-83a8-cc4e20b1ed0c.png">
-rw-r--r--compiler/rustc_lint/messages.ftl1
-rw-r--r--compiler/rustc_lint/src/lints.rs17
-rw-r--r--compiler/rustc_lint/src/types.rs49
-rw-r--r--tests/ui/lint/type-overflow.stderr28
4 files changed, 89 insertions, 6 deletions
diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl
index 5e13ee7b8a4..c66fcdec716 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 e990c771bdf..89fa5713b73 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<OverflowingBinHexSub<'a>>,
+    #[subdiagnostic]
+    pub sign_bit_sub: Option<OverflowingBinHexSignBitSub<'a>>,
 }
 
 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 264a59c5585..ec9e7c7fdae 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 62cb1f7f4aa..e7c90dcc81b 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