Skip to content

Commit

Permalink
Rollup merge of rust-lang#111212 - nicklimmm:issue-107896-fix, r=pnkf…
Browse files Browse the repository at this point in the history
…elix

Add casting suggestion when assigning negative 2's complement bin or hex literal to a size compatible signed integer

Fixes rust-lang#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">
  • Loading branch information
matthiaskrgr authored Jun 15, 2023
2 parents 4996b56 + 2113769 commit f530016
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 6 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_lint/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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}`
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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]
Expand Down
49 changes: 45 additions & 4 deletions compiler/rustc_lint/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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,
},
)
}

Expand Down
28 changes: 26 additions & 2 deletions tests/ui/lint/type-overflow.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand Down

0 comments on commit f530016

Please sign in to comment.