forked from rust-lang/rust
-
-
Notifications
You must be signed in to change notification settings - Fork 2
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#6873 - Y-Nak:refactor-casts-lint, r=flip1995
Refactor casts lint Ref: rust-lang#6724 Changes: 1. Separate the `casts` group from the `types` group. 2. Reorganize the lints of the `casts` group into their own modules. Notes: 1. I didn't `fix` rust-lang#6874 in order to maintain this PR as small as possible. --- changelog: none
- Loading branch information
Showing
17 changed files
with
1,166 additions
and
997 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 |
---|---|---|
@@ -0,0 +1,87 @@ | ||
use rustc_errors::Applicability; | ||
use rustc_hir::{Expr, ExprKind}; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::{self, FloatTy, Ty}; | ||
|
||
use crate::utils::{in_constant, is_isize_or_usize, snippet_opt, span_lint_and_sugg}; | ||
|
||
use super::{utils, CAST_LOSSLESS}; | ||
|
||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_op: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { | ||
if !should_lint(cx, expr, cast_from, cast_to) { | ||
return; | ||
} | ||
|
||
// The suggestion is to use a function call, so if the original expression | ||
// has parens on the outside, they are no longer needed. | ||
let mut applicability = Applicability::MachineApplicable; | ||
let opt = snippet_opt(cx, cast_op.span); | ||
let sugg = opt.as_ref().map_or_else( | ||
|| { | ||
applicability = Applicability::HasPlaceholders; | ||
".." | ||
}, | ||
|snip| { | ||
if should_strip_parens(cast_op, snip) { | ||
&snip[1..snip.len() - 1] | ||
} else { | ||
snip.as_str() | ||
} | ||
}, | ||
); | ||
|
||
span_lint_and_sugg( | ||
cx, | ||
CAST_LOSSLESS, | ||
expr.span, | ||
&format!( | ||
"casting `{}` to `{}` may become silently lossy if you later change the type", | ||
cast_from, cast_to | ||
), | ||
"try", | ||
format!("{}::from({})", cast_to, sugg), | ||
applicability, | ||
); | ||
} | ||
|
||
fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) -> bool { | ||
// Do not suggest using From in consts/statics until it is valid to do so (see #2267). | ||
if in_constant(cx, expr.hir_id) { | ||
return false; | ||
} | ||
|
||
match (cast_from.is_integral(), cast_to.is_integral()) { | ||
(true, true) => { | ||
let cast_signed_to_unsigned = cast_from.is_signed() && !cast_to.is_signed(); | ||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); | ||
let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx); | ||
!is_isize_or_usize(cast_from) | ||
&& !is_isize_or_usize(cast_to) | ||
&& from_nbits < to_nbits | ||
&& !cast_signed_to_unsigned | ||
}, | ||
|
||
(true, false) => { | ||
let from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); | ||
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { | ||
32 | ||
} else { | ||
64 | ||
}; | ||
from_nbits < to_nbits | ||
}, | ||
|
||
(_, _) => { | ||
matches!(cast_from.kind(), ty::Float(FloatTy::F32)) && matches!(cast_to.kind(), ty::Float(FloatTy::F64)) | ||
}, | ||
} | ||
} | ||
|
||
fn should_strip_parens(cast_expr: &Expr<'_>, snip: &str) -> bool { | ||
if let ExprKind::Binary(_, _, _) = cast_expr.kind { | ||
if snip.starts_with('(') && snip.ends_with(')') { | ||
return true; | ||
} | ||
} | ||
false | ||
} |
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 |
---|---|---|
@@ -0,0 +1,54 @@ | ||
use rustc_hir::Expr; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::{self, FloatTy, Ty}; | ||
|
||
use crate::utils::{is_isize_or_usize, span_lint}; | ||
|
||
use super::{utils, CAST_POSSIBLE_TRUNCATION}; | ||
|
||
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) { | ||
let msg = match (cast_from.is_integral(), cast_to.is_integral()) { | ||
(true, true) => { | ||
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, ""), | ||
(true, false) => ( | ||
to_nbits <= 32, | ||
if to_nbits == 32 { | ||
" on targets with 64-bit wide pointers" | ||
} else { | ||
"" | ||
}, | ||
), | ||
(false, true) => (from_nbits == 64, " on targets with 32-bit wide pointers"), | ||
}; | ||
|
||
if !should_lint { | ||
return; | ||
} | ||
|
||
format!( | ||
"casting `{}` to `{}` may truncate the value{}", | ||
cast_from, cast_to, suffix, | ||
) | ||
}, | ||
|
||
(false, true) => { | ||
format!("casting `{}` to `{}` may truncate the value", cast_from, cast_to) | ||
}, | ||
|
||
(_, _) => { | ||
if matches!(cast_from.kind(), &ty::Float(FloatTy::F64)) | ||
&& matches!(cast_to.kind(), &ty::Float(FloatTy::F32)) | ||
{ | ||
"casting `f64` to `f32` may truncate the value".to_string() | ||
} else { | ||
return; | ||
} | ||
}, | ||
}; | ||
|
||
span_lint(cx, CAST_POSSIBLE_TRUNCATION, expr.span, &msg); | ||
} |
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 |
---|---|---|
@@ -0,0 +1,44 @@ | ||
use rustc_hir::Expr; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::Ty; | ||
|
||
use crate::utils::{is_isize_or_usize, span_lint}; | ||
|
||
use super::{utils, CAST_POSSIBLE_WRAP}; | ||
|
||
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(); | ||
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 | ||
} else { | ||
arch_32_suffix | ||
}, | ||
), | ||
}; | ||
|
||
if should_lint { | ||
span_lint( | ||
cx, | ||
CAST_POSSIBLE_WRAP, | ||
expr.span, | ||
&format!( | ||
"casting `{}` to `{}` may wrap around the value{}", | ||
cast_from, cast_to, suffix, | ||
), | ||
); | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,51 @@ | ||
use rustc_hir::Expr; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::{self, FloatTy, Ty}; | ||
|
||
use crate::utils::{is_isize_or_usize, span_lint}; | ||
|
||
use super::{utils, CAST_PRECISION_LOSS}; | ||
|
||
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 from_nbits = utils::int_ty_to_nbits(cast_from, cx.tcx); | ||
let to_nbits = if let ty::Float(FloatTy::F32) = cast_to.kind() { | ||
32 | ||
} else { | ||
64 | ||
}; | ||
|
||
if !(is_isize_or_usize(cast_from) || from_nbits >= to_nbits) { | ||
return; | ||
} | ||
|
||
let cast_to_f64 = to_nbits == 64; | ||
let mantissa_nbits = if cast_to_f64 { 52 } else { 23 }; | ||
let arch_dependent = is_isize_or_usize(cast_from) && cast_to_f64; | ||
let arch_dependent_str = "on targets with 64-bit wide pointers "; | ||
let from_nbits_str = if arch_dependent { | ||
"64".to_owned() | ||
} else if is_isize_or_usize(cast_from) { | ||
"32 or 64".to_owned() | ||
} else { | ||
utils::int_ty_to_nbits(cast_from, cx.tcx).to_string() | ||
}; | ||
|
||
span_lint( | ||
cx, | ||
CAST_PRECISION_LOSS, | ||
expr.span, | ||
&format!( | ||
"casting `{0}` to `{1}` causes a loss of precision {2}(`{0}` is {3} bits wide, \ | ||
but `{1}`'s mantissa is only {4} bits wide)", | ||
cast_from, | ||
if cast_to_f64 { "f64" } else { "f32" }, | ||
if arch_dependent { arch_dependent_str } else { "" }, | ||
from_nbits_str, | ||
mantissa_nbits | ||
), | ||
); | ||
} |
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 |
---|---|---|
@@ -0,0 +1,81 @@ | ||
use rustc_hir::{Expr, ExprKind, GenericArg}; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty::{self, Ty}; | ||
use rustc_span::symbol::sym; | ||
use rustc_target::abi::LayoutOf; | ||
|
||
use if_chain::if_chain; | ||
|
||
use crate::utils::{is_hir_ty_cfg_dependant, span_lint}; | ||
|
||
use super::CAST_PTR_ALIGNMENT; | ||
|
||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
if let ExprKind::Cast(ref cast_expr, cast_to) = expr.kind { | ||
if is_hir_ty_cfg_dependant(cx, cast_to) { | ||
return; | ||
} | ||
let (cast_from, cast_to) = ( | ||
cx.typeck_results().expr_ty(cast_expr), | ||
cx.typeck_results().expr_ty(expr), | ||
); | ||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); | ||
} else if let ExprKind::MethodCall(method_path, _, args, _) = expr.kind { | ||
if_chain! { | ||
if method_path.ident.name == sym!(cast); | ||
if let Some(generic_args) = method_path.args; | ||
if let [GenericArg::Type(cast_to)] = generic_args.args; | ||
// There probably is no obvious reason to do this, just to be consistent with `as` cases. | ||
if !is_hir_ty_cfg_dependant(cx, cast_to); | ||
then { | ||
let (cast_from, cast_to) = | ||
(cx.typeck_results().expr_ty(&args[0]), cx.typeck_results().expr_ty(expr)); | ||
lint_cast_ptr_alignment(cx, expr, cast_from, cast_to); | ||
} | ||
} | ||
} | ||
} | ||
|
||
fn lint_cast_ptr_alignment<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) { | ||
if_chain! { | ||
if let ty::RawPtr(from_ptr_ty) = &cast_from.kind(); | ||
if let ty::RawPtr(to_ptr_ty) = &cast_to.kind(); | ||
if let Ok(from_layout) = cx.layout_of(from_ptr_ty.ty); | ||
if let Ok(to_layout) = cx.layout_of(to_ptr_ty.ty); | ||
if from_layout.align.abi < to_layout.align.abi; | ||
// with c_void, we inherently need to trust the user | ||
if !is_c_void(cx, from_ptr_ty.ty); | ||
// when casting from a ZST, we don't know enough to properly lint | ||
if !from_layout.is_zst(); | ||
then { | ||
span_lint( | ||
cx, | ||
CAST_PTR_ALIGNMENT, | ||
expr.span, | ||
&format!( | ||
"casting from `{}` to a more-strictly-aligned pointer (`{}`) ({} < {} bytes)", | ||
cast_from, | ||
cast_to, | ||
from_layout.align.abi.bytes(), | ||
to_layout.align.abi.bytes(), | ||
), | ||
); | ||
} | ||
} | ||
} | ||
|
||
/// Check if the given type is either `core::ffi::c_void` or | ||
/// one of the platform specific `libc::<platform>::c_void` of libc. | ||
fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool { | ||
if let ty::Adt(adt, _) = ty.kind() { | ||
let names = cx.get_def_path(adt.did); | ||
|
||
if names.is_empty() { | ||
return false; | ||
} | ||
if names[0] == sym::libc || names[0] == sym::core && *names.last().unwrap() == sym!(c_void) { | ||
return true; | ||
} | ||
} | ||
false | ||
} |
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 |
---|---|---|
@@ -0,0 +1,28 @@ | ||
use rustc_hir::{Expr, ExprKind, MutTy, Mutability, TyKind, UnOp}; | ||
use rustc_lint::LateContext; | ||
use rustc_middle::ty; | ||
|
||
use if_chain::if_chain; | ||
|
||
use crate::utils::span_lint; | ||
|
||
use super::CAST_REF_TO_MUT; | ||
|
||
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) { | ||
if_chain! { | ||
if let ExprKind::Unary(UnOp::Deref, e) = &expr.kind; | ||
if let ExprKind::Cast(e, t) = &e.kind; | ||
if let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind; | ||
if let ExprKind::Cast(e, t) = &e.kind; | ||
if let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind; | ||
if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind(); | ||
then { | ||
span_lint( | ||
cx, | ||
CAST_REF_TO_MUT, | ||
expr.span, | ||
"casting `&T` to `&mut T` may cause undefined behavior, consider instead using an `UnsafeCell`", | ||
); | ||
} | ||
} | ||
} |
Oops, something went wrong.