about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicky Lim <nickylim.p@gmail.com>2023-05-05 00:29:11 +0700
committerNicky Lim <nickylim.p@gmail.com>2023-06-11 18:27:26 +0800
commit6033895ec20bc6def7eee32eb3532faa8b1c67d5 (patch)
treeb94492ca44c1976d8811e39efb207b39c456a2f5
parent34d64ab7a21f704adb496ee2749242fc3511f0f8 (diff)
downloadrust-6033895ec20bc6def7eee32eb3532faa8b1c67d5.tar.gz
rust-6033895ec20bc6def7eee32eb3532faa8b1c67d5.zip
Add subdiagnostic and suggestion for overflowing bin hex with sign bits
-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
3 files changed, 63 insertions, 4 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,
+        },
     )
 }