forked from rust-lang/rust-clippy
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Auto merge of rust-lang#10564 - asquared31415:cast_doesnt_wrap, r=gir…
…affate make cast_possible_wrap work correctly for 16 bit {u,i}size These changes make `cast_possible_wrap` aware of the different pointer widths and fixes the implementation to print the correct pointer widths. Fixes rust-lang#9337 changelog: `cast_possible_wrap` does not lint on `u8 as isize` or `usize as i8`, since these can never wrap. `cast_possible_wrap` now properly considers 16 bit pointer size and prints the correct bit widths.
- Loading branch information
Showing
4 changed files
with
187 additions
and
40 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,41 +1,89 @@ | ||
use clippy_utils::diagnostics::span_lint; | ||
use clippy_utils::ty::is_isize_or_usize; | ||
use rustc_hir::Expr; | ||
use rustc_lint::LateContext; | ||
use rustc_lint::{LateContext, LintContext}; | ||
use rustc_middle::ty::Ty; | ||
|
||
use super::{utils, CAST_POSSIBLE_WRAP}; | ||
|
||
// this should be kept in sync with the allowed bit widths of `usize` and `isize` | ||
const ALLOWED_POINTER_SIZES: [u64; 3] = [16, 32, 64]; | ||
|
||
// whether the lint should be emitted, and the required pointer size, if it matters | ||
#[derive(Copy, Clone, Debug, PartialEq, Eq)] | ||
enum EmitState { | ||
NoLint, | ||
LintAlways, | ||
LintOnPtrSize(u64), | ||
} | ||
|
||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { | ||
if !(cast_from.is_integral() && cast_to.is_integral()) { | ||
return; | ||
} | ||
|
||
let arch_64_suffix = " on targets with 64-bit wide pointers"; | ||
let arch_32_suffix = " on targets with 32-bit wide pointers"; | ||
let cast_unsigned_to_signed = !cast_from.is_signed() && cast_to.is_signed(); | ||
// emit a lint if a cast is: | ||
// 1. unsigned to signed | ||
// and | ||
// 2. either: | ||
// 2a. between two types of constant size that are always the same size | ||
// 2b. between one target-dependent size and one constant size integer, | ||
// and the constant integer is in the allowed set of target dependent sizes | ||
// (the ptr size could be chosen to be the same as the constant size) | ||
|
||
if cast_from.is_signed() || !cast_to.is_signed() { | ||
return; | ||
} | ||
|
||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); | ||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); | ||
|
||
let (should_lint, suffix) = match (is_isize_or_usize(cast_from), is_isize_or_usize(cast_to)) { | ||
(true, true) | (false, false) => (to_nbits == from_nbits && cast_unsigned_to_signed, ""), | ||
(true, false) => (to_nbits <= 32 && cast_unsigned_to_signed, arch_32_suffix), | ||
(false, true) => ( | ||
cast_unsigned_to_signed, | ||
if from_nbits == 64 { | ||
arch_64_suffix | ||
let should_lint = match (cast_from.is_ptr_sized_integral(), cast_to.is_ptr_sized_integral()) { | ||
(true, true) => { | ||
// casts between two ptr sized integers are trivially always the same size | ||
// so do not depend on any specific pointer size to be the same | ||
EmitState::LintAlways | ||
}, | ||
(true, false) => { | ||
// the first type is `usize` and the second is a constant sized signed integer | ||
if ALLOWED_POINTER_SIZES.contains(&to_nbits) { | ||
EmitState::LintOnPtrSize(to_nbits) | ||
} else { | ||
EmitState::NoLint | ||
} | ||
}, | ||
(false, true) => { | ||
// the first type is a constant sized unsigned integer, and the second is `isize` | ||
if ALLOWED_POINTER_SIZES.contains(&from_nbits) { | ||
EmitState::LintOnPtrSize(from_nbits) | ||
} else { | ||
EmitState::NoLint | ||
} | ||
}, | ||
(false, false) => { | ||
// the types are both a constant known size | ||
// and do not depend on any specific pointer size to be the same | ||
if from_nbits == to_nbits { | ||
EmitState::LintAlways | ||
} else { | ||
arch_32_suffix | ||
}, | ||
EmitState::NoLint | ||
} | ||
}, | ||
}; | ||
|
||
let message = match should_lint { | ||
EmitState::NoLint => return, | ||
EmitState::LintAlways => format!("casting `{cast_from}` to `{cast_to}` may wrap around the value"), | ||
EmitState::LintOnPtrSize(ptr_size) => format!( | ||
"casting `{cast_from}` to `{cast_to}` may wrap around the value on targets with {ptr_size}-bit wide pointers", | ||
), | ||
}; | ||
|
||
if should_lint { | ||
span_lint( | ||
cx, | ||
CAST_POSSIBLE_WRAP, | ||
expr.span, | ||
&format!("casting `{cast_from}` to `{cast_to}` may wrap around the value{suffix}",), | ||
); | ||
} | ||
cx.struct_span_lint(CAST_POSSIBLE_WRAP, expr.span, message, |diag| { | ||
if let EmitState::LintOnPtrSize(16) = should_lint { | ||
diag | ||
.note("`usize` and `isize` may be as small as 16 bits on some platforms") | ||
.note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types") | ||
} else { | ||
diag | ||
} | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters