Add needless_type_cast lint
This commit is contained in:
parent
0cf51b1b73
commit
1232c81a3e
9 changed files with 760 additions and 2 deletions
|
|
@ -6688,6 +6688,7 @@ Released 2018-09-13
|
|||
[`needless_return`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return
|
||||
[`needless_return_with_question_mark`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_return_with_question_mark
|
||||
[`needless_splitn`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_splitn
|
||||
[`needless_type_cast`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_type_cast
|
||||
[`needless_update`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_update
|
||||
[`neg_cmp_op_on_partial_ord`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_cmp_op_on_partial_ord
|
||||
[`neg_multiply`]: https://rust-lang.github.io/rust-clippy/master/index.html#neg_multiply
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
|
||||
|
||||
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
[There are over 800 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
|
||||
You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
A collection of lints to catch common mistakes and improve your
|
||||
[Rust](https://github.com/rust-lang/rust) code.
|
||||
|
||||
[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
[There are over 800 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
|
||||
|
||||
Lints are divided into categories, each with a default [lint
|
||||
level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ mod fn_to_numeric_cast;
|
|||
mod fn_to_numeric_cast_any;
|
||||
mod fn_to_numeric_cast_with_truncation;
|
||||
mod manual_dangling_ptr;
|
||||
mod needless_type_cast;
|
||||
mod ptr_as_ptr;
|
||||
mod ptr_cast_constness;
|
||||
mod ref_as_ptr;
|
||||
|
|
@ -813,6 +814,32 @@ declare_clippy_lint! {
|
|||
"casting a primitive method pointer to any integer type"
|
||||
}
|
||||
|
||||
declare_clippy_lint! {
|
||||
/// ### What it does
|
||||
/// Checks for bindings (constants, statics, or let bindings) that are defined
|
||||
/// with one numeric type but are consistently cast to a different type in all usages.
|
||||
///
|
||||
/// ### Why is this bad?
|
||||
/// If a binding is always cast to a different type when used, it would be clearer
|
||||
/// and more efficient to define it with the target type from the start.
|
||||
///
|
||||
/// ### Example
|
||||
/// ```no_run
|
||||
/// const SIZE: u16 = 15;
|
||||
/// let arr: [u8; SIZE as usize] = [0; SIZE as usize];
|
||||
/// ```
|
||||
///
|
||||
/// Use instead:
|
||||
/// ```no_run
|
||||
/// const SIZE: usize = 15;
|
||||
/// let arr: [u8; SIZE] = [0; SIZE];
|
||||
/// ```
|
||||
#[clippy::version = "1.93.0"]
|
||||
pub NEEDLESS_TYPE_CAST,
|
||||
pedantic,
|
||||
"binding defined with one type but always cast to another"
|
||||
}
|
||||
|
||||
pub struct Casts {
|
||||
msrv: Msrv,
|
||||
}
|
||||
|
|
@ -851,6 +878,7 @@ impl_lint_pass!(Casts => [
|
|||
AS_POINTER_UNDERSCORE,
|
||||
MANUAL_DANGLING_PTR,
|
||||
CONFUSING_METHOD_TO_NUMERIC_CAST,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for Casts {
|
||||
|
|
@ -920,4 +948,8 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
|
|||
cast_slice_different_sizes::check(cx, expr, self.msrv);
|
||||
ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
|
||||
}
|
||||
|
||||
fn check_body(&mut self, cx: &LateContext<'tcx>, body: &rustc_hir::Body<'tcx>) {
|
||||
needless_type_cast::check(cx, body);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
289
clippy_lints/src/casts/needless_type_cast.rs
Normal file
289
clippy_lints/src/casts/needless_type_cast.rs
Normal file
|
|
@ -0,0 +1,289 @@
|
|||
use clippy_utils::diagnostics::span_lint_and_sugg;
|
||||
use clippy_utils::visitors::{Descend, for_each_expr, for_each_expr_without_closures};
|
||||
use core::ops::ControlFlow;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::{BlockCheckMode, Body, Expr, ExprKind, HirId, LetStmt, PatKind, StmtKind, UnsafeSource};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::{Ty, TypeVisitableExt};
|
||||
use rustc_span::Span;
|
||||
|
||||
use super::NEEDLESS_TYPE_CAST;
|
||||
|
||||
struct BindingInfo<'a> {
|
||||
source_ty: Ty<'a>,
|
||||
ty_span: Span,
|
||||
}
|
||||
|
||||
struct UsageInfo<'a> {
|
||||
cast_to: Option<Ty<'a>>,
|
||||
in_generic_context: bool,
|
||||
}
|
||||
|
||||
pub(super) fn check<'a>(cx: &LateContext<'a>, body: &Body<'a>) {
|
||||
let mut bindings: FxHashMap<HirId, BindingInfo<'a>> = FxHashMap::default();
|
||||
|
||||
for_each_expr_without_closures(body.value, |expr| {
|
||||
match expr.kind {
|
||||
ExprKind::Block(block, _) => {
|
||||
for stmt in block.stmts {
|
||||
if let StmtKind::Let(let_stmt) = stmt.kind {
|
||||
collect_binding_from_local(cx, let_stmt, &mut bindings);
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::Let(let_expr) => {
|
||||
collect_binding_from_let(cx, let_expr, &mut bindings);
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
ControlFlow::<()>::Continue(())
|
||||
});
|
||||
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
let mut binding_vec: Vec<_> = bindings.into_iter().collect();
|
||||
binding_vec.sort_by_key(|(_, info)| info.ty_span.lo());
|
||||
|
||||
for (hir_id, binding_info) in binding_vec {
|
||||
check_binding_usages(cx, body, hir_id, &binding_info);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_binding_from_let<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
let_expr: &rustc_hir::LetExpr<'a>,
|
||||
bindings: &mut FxHashMap<HirId, BindingInfo<'a>>,
|
||||
) {
|
||||
if let_expr.ty.is_none()
|
||||
|| let_expr.span.from_expansion()
|
||||
|| has_generic_return_type(cx, let_expr.init)
|
||||
|| contains_unsafe(let_expr.init)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let PatKind::Binding(_, hir_id, _, _) = let_expr.pat.kind
|
||||
&& let Some(ty_hir) = let_expr.ty
|
||||
{
|
||||
let ty = cx.typeck_results().pat_ty(let_expr.pat);
|
||||
if ty.is_numeric() {
|
||||
bindings.insert(
|
||||
hir_id,
|
||||
BindingInfo {
|
||||
source_ty: ty,
|
||||
ty_span: ty_hir.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_binding_from_local<'a>(
|
||||
cx: &LateContext<'a>,
|
||||
let_stmt: &LetStmt<'a>,
|
||||
bindings: &mut FxHashMap<HirId, BindingInfo<'a>>,
|
||||
) {
|
||||
if let_stmt.ty.is_none()
|
||||
|| let_stmt.span.from_expansion()
|
||||
|| let_stmt
|
||||
.init
|
||||
.is_some_and(|init| has_generic_return_type(cx, init) || contains_unsafe(init))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if let PatKind::Binding(_, hir_id, _, _) = let_stmt.pat.kind
|
||||
&& let Some(ty_hir) = let_stmt.ty
|
||||
{
|
||||
let ty = cx.typeck_results().pat_ty(let_stmt.pat);
|
||||
if ty.is_numeric() {
|
||||
bindings.insert(
|
||||
hir_id,
|
||||
BindingInfo {
|
||||
source_ty: ty,
|
||||
ty_span: ty_hir.span,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn contains_unsafe(expr: &Expr<'_>) -> bool {
|
||||
for_each_expr_without_closures(expr, |e| {
|
||||
if let ExprKind::Block(block, _) = e.kind
|
||||
&& let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules
|
||||
{
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
ControlFlow::Continue(())
|
||||
})
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn has_generic_return_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
|
||||
match &expr.kind {
|
||||
ExprKind::Block(block, _) => {
|
||||
if let Some(tail_expr) = block.expr {
|
||||
return has_generic_return_type(cx, tail_expr);
|
||||
}
|
||||
false
|
||||
},
|
||||
ExprKind::If(_, then_block, else_expr) => {
|
||||
has_generic_return_type(cx, then_block) || else_expr.is_some_and(|e| has_generic_return_type(cx, e))
|
||||
},
|
||||
ExprKind::Match(_, arms, _) => arms.iter().any(|arm| has_generic_return_type(cx, arm.body)),
|
||||
ExprKind::Loop(block, label, ..) => for_each_expr_without_closures(*block, |e| {
|
||||
match e.kind {
|
||||
ExprKind::Loop(..) => {
|
||||
// Unlabeled breaks inside nested loops target the inner loop, not ours
|
||||
return ControlFlow::Continue(Descend::No);
|
||||
},
|
||||
ExprKind::Break(dest, Some(break_expr)) => {
|
||||
let targets_this_loop =
|
||||
dest.label.is_none() || dest.label.map(|l| l.ident) == label.map(|l| l.ident);
|
||||
if targets_this_loop && has_generic_return_type(cx, break_expr) {
|
||||
return ControlFlow::Break(());
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
ControlFlow::Continue(Descend::Yes)
|
||||
})
|
||||
.is_some(),
|
||||
ExprKind::MethodCall(..) => {
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
|
||||
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||
let ret_ty = sig.output().skip_binder();
|
||||
return ret_ty.has_param();
|
||||
}
|
||||
false
|
||||
},
|
||||
ExprKind::Call(callee, _) => {
|
||||
if let ExprKind::Path(qpath) = &callee.kind {
|
||||
let res = cx.qpath_res(qpath, callee.hir_id);
|
||||
if let Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) = res {
|
||||
let sig = cx.tcx.fn_sig(def_id).instantiate_identity();
|
||||
let ret_ty = sig.output().skip_binder();
|
||||
return ret_ty.has_param();
|
||||
}
|
||||
}
|
||||
false
|
||||
},
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_generic_res(cx: &LateContext<'_>, res: Res) -> bool {
|
||||
let has_type_params = |def_id| {
|
||||
cx.tcx
|
||||
.generics_of(def_id)
|
||||
.own_params
|
||||
.iter()
|
||||
.any(|p| p.kind.is_ty_or_const())
|
||||
};
|
||||
match res {
|
||||
Res::Def(DefKind::Fn | DefKind::AssocFn, def_id) => has_type_params(def_id),
|
||||
// Ctor → Variant → ADT: constructor's parent is variant, variant's parent is the ADT
|
||||
Res::Def(DefKind::Ctor(..), def_id) => has_type_params(cx.tcx.parent(cx.tcx.parent(def_id))),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_cast_in_generic_context<'a>(cx: &LateContext<'a>, cast_expr: &Expr<'a>) -> bool {
|
||||
let mut current_id = cast_expr.hir_id;
|
||||
|
||||
loop {
|
||||
let parent_id = cx.tcx.parent_hir_id(current_id);
|
||||
if parent_id == current_id {
|
||||
return false;
|
||||
}
|
||||
|
||||
let parent = cx.tcx.hir_node(parent_id);
|
||||
|
||||
match parent {
|
||||
rustc_hir::Node::Expr(parent_expr) => {
|
||||
match &parent_expr.kind {
|
||||
ExprKind::Closure(_) => return false,
|
||||
ExprKind::Call(callee, _) => {
|
||||
if let ExprKind::Path(qpath) = &callee.kind {
|
||||
let res = cx.qpath_res(qpath, callee.hir_id);
|
||||
if is_generic_res(cx, res) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
},
|
||||
ExprKind::MethodCall(..) => {
|
||||
if let Some(def_id) = cx.typeck_results().type_dependent_def_id(parent_expr.hir_id)
|
||||
&& cx
|
||||
.tcx
|
||||
.generics_of(def_id)
|
||||
.own_params
|
||||
.iter()
|
||||
.any(|p| p.kind.is_ty_or_const())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
current_id = parent_id;
|
||||
},
|
||||
_ => return false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_binding_usages<'a>(cx: &LateContext<'a>, body: &Body<'a>, hir_id: HirId, binding_info: &BindingInfo<'a>) {
|
||||
let mut usages = Vec::new();
|
||||
|
||||
for_each_expr(cx, body.value, |expr| {
|
||||
if let ExprKind::Path(ref qpath) = expr.kind
|
||||
&& !expr.span.from_expansion()
|
||||
&& let Res::Local(id) = cx.qpath_res(qpath, expr.hir_id)
|
||||
&& id == hir_id
|
||||
{
|
||||
let parent_id = cx.tcx.parent_hir_id(expr.hir_id);
|
||||
let parent = cx.tcx.hir_node(parent_id);
|
||||
|
||||
let usage = if let rustc_hir::Node::Expr(parent_expr) = parent
|
||||
&& let ExprKind::Cast(..) = parent_expr.kind
|
||||
&& !parent_expr.span.from_expansion()
|
||||
{
|
||||
UsageInfo {
|
||||
cast_to: Some(cx.typeck_results().expr_ty(parent_expr)),
|
||||
in_generic_context: is_cast_in_generic_context(cx, parent_expr),
|
||||
}
|
||||
} else {
|
||||
UsageInfo {
|
||||
cast_to: None,
|
||||
in_generic_context: false,
|
||||
}
|
||||
};
|
||||
usages.push(usage);
|
||||
}
|
||||
ControlFlow::<()>::Continue(())
|
||||
});
|
||||
|
||||
let Some(first_target) = usages
|
||||
.first()
|
||||
.and_then(|u| u.cast_to)
|
||||
.filter(|&t| t != binding_info.source_ty)
|
||||
.filter(|&t| usages.iter().all(|u| u.cast_to == Some(t) && !u.in_generic_context))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
span_lint_and_sugg(
|
||||
cx,
|
||||
NEEDLESS_TYPE_CAST,
|
||||
binding_info.ty_span,
|
||||
format!(
|
||||
"this binding is defined as `{}` but is always cast to `{}`",
|
||||
binding_info.source_ty, first_target
|
||||
),
|
||||
"consider defining it as",
|
||||
first_target.to_string(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
|
|
@ -70,6 +70,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
|
|||
crate::casts::FN_TO_NUMERIC_CAST_ANY_INFO,
|
||||
crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
|
||||
crate::casts::MANUAL_DANGLING_PTR_INFO,
|
||||
crate::casts::NEEDLESS_TYPE_CAST_INFO,
|
||||
crate::casts::PTR_AS_PTR_INFO,
|
||||
crate::casts::PTR_CAST_CONSTNESS_INFO,
|
||||
crate::casts::REF_AS_PTR_INFO,
|
||||
|
|
|
|||
182
tests/ui/needless_type_cast.fixed
Normal file
182
tests/ui/needless_type_cast.fixed
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
#![warn(clippy::needless_type_cast)]
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)]
|
||||
|
||||
fn takes_i32(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn generic<T>(x: T) -> T {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = a as i32 + 5;
|
||||
let _ = a as i32 * 2;
|
||||
|
||||
let b: u16 = 20;
|
||||
let _ = b;
|
||||
let _ = b as u32;
|
||||
|
||||
let c: u8 = 5;
|
||||
let _ = c as u16;
|
||||
let _ = c as u32;
|
||||
|
||||
let d: i32 = 100;
|
||||
let _ = d + 1;
|
||||
|
||||
let e = 42u8;
|
||||
let _ = e as i64;
|
||||
let _ = e as i64 + 10;
|
||||
|
||||
let f: usize = 1;
|
||||
//~^ needless_type_cast
|
||||
let _ = f as usize;
|
||||
}
|
||||
|
||||
fn test_function_call() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = takes_i32(a as i32);
|
||||
let _ = takes_i32(a as i32);
|
||||
}
|
||||
|
||||
fn test_generic_call() {
|
||||
let a: u8 = 10;
|
||||
let _ = generic(a as i32);
|
||||
let _ = generic(a as i32);
|
||||
}
|
||||
|
||||
fn test_method_on_cast() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = (a as i32).checked_add(5);
|
||||
let _ = (a as i32).saturating_mul(2);
|
||||
}
|
||||
|
||||
fn test_iterator_sum() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let arr = [a as i32, a as i32];
|
||||
let _: i32 = arr.iter().copied().sum();
|
||||
}
|
||||
|
||||
fn test_closure() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum();
|
||||
}
|
||||
|
||||
fn test_struct_field() {
|
||||
struct S {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = S {
|
||||
x: a as i32,
|
||||
y: a as i32,
|
||||
};
|
||||
}
|
||||
|
||||
fn test_option() {
|
||||
let a: u8 = 10;
|
||||
let _: Option<i32> = Some(a as i32);
|
||||
let _: Option<i32> = Some(a as i32);
|
||||
}
|
||||
|
||||
fn test_mixed_context() {
|
||||
let a: u8 = 10;
|
||||
let _ = takes_i32(a as i32);
|
||||
let _ = generic(a as i32);
|
||||
}
|
||||
|
||||
fn test_nested_block() {
|
||||
if true {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = a as i32 + 1;
|
||||
let _ = a as i32 * 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn test_match_expr() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = match 1 {
|
||||
1 => a as i32,
|
||||
_ => a as i32,
|
||||
};
|
||||
}
|
||||
|
||||
fn test_return_expr() -> i32 {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
a as i32
|
||||
}
|
||||
|
||||
fn test_closure_always_cast() {
|
||||
let a: i32 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||
let _ = a as i32;
|
||||
}
|
||||
|
||||
fn test_closure_mixed_usage() {
|
||||
let a: u8 = 10;
|
||||
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||
let _ = a + 1;
|
||||
}
|
||||
|
||||
fn test_nested_generic_call() {
|
||||
let a: u8 = 10;
|
||||
let _ = generic(takes_i32(a as i32));
|
||||
let _ = generic(takes_i32(a as i32));
|
||||
}
|
||||
|
||||
fn test_generic_initializer() {
|
||||
// Should not lint: changing type would affect what generic() returns
|
||||
let a: u8 = generic(10u8);
|
||||
let _ = a as i32;
|
||||
let _ = a as i32;
|
||||
}
|
||||
|
||||
fn test_unsafe_transmute() {
|
||||
// Should not lint: initializer contains unsafe block
|
||||
#[allow(clippy::useless_transmute)]
|
||||
let x: u32 = unsafe { std::mem::transmute(0u32) };
|
||||
let _ = x as u64;
|
||||
}
|
||||
|
||||
fn test_if_with_generic() {
|
||||
// Should not lint: one branch has generic return type
|
||||
let x: u8 = if true { generic(1) } else { 2 };
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_match_with_generic() {
|
||||
// Should not lint: one branch has generic return type
|
||||
let x: u8 = match 1 {
|
||||
1 => generic(1),
|
||||
_ => 2,
|
||||
};
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_default() {
|
||||
// Should not lint: Default::default() has generic return type
|
||||
let x: u8 = Default::default();
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_loop_with_generic() {
|
||||
// Should not lint: loop break has generic return type
|
||||
#[allow(clippy::never_loop)]
|
||||
let x: u8 = loop {
|
||||
break generic(1);
|
||||
};
|
||||
let _ = x as i32;
|
||||
}
|
||||
182
tests/ui/needless_type_cast.rs
Normal file
182
tests/ui/needless_type_cast.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
#![warn(clippy::needless_type_cast)]
|
||||
#![allow(clippy::no_effect, clippy::unnecessary_cast, unused)]
|
||||
|
||||
fn takes_i32(x: i32) -> i32 {
|
||||
x
|
||||
}
|
||||
|
||||
fn generic<T>(x: T) -> T {
|
||||
x
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = a as i32 + 5;
|
||||
let _ = a as i32 * 2;
|
||||
|
||||
let b: u16 = 20;
|
||||
let _ = b;
|
||||
let _ = b as u32;
|
||||
|
||||
let c: u8 = 5;
|
||||
let _ = c as u16;
|
||||
let _ = c as u32;
|
||||
|
||||
let d: i32 = 100;
|
||||
let _ = d + 1;
|
||||
|
||||
let e = 42u8;
|
||||
let _ = e as i64;
|
||||
let _ = e as i64 + 10;
|
||||
|
||||
let f: u8 = 1;
|
||||
//~^ needless_type_cast
|
||||
let _ = f as usize;
|
||||
}
|
||||
|
||||
fn test_function_call() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = takes_i32(a as i32);
|
||||
let _ = takes_i32(a as i32);
|
||||
}
|
||||
|
||||
fn test_generic_call() {
|
||||
let a: u8 = 10;
|
||||
let _ = generic(a as i32);
|
||||
let _ = generic(a as i32);
|
||||
}
|
||||
|
||||
fn test_method_on_cast() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = (a as i32).checked_add(5);
|
||||
let _ = (a as i32).saturating_mul(2);
|
||||
}
|
||||
|
||||
fn test_iterator_sum() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let arr = [a as i32, a as i32];
|
||||
let _: i32 = arr.iter().copied().sum();
|
||||
}
|
||||
|
||||
fn test_closure() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _: i32 = [1i32, 2].iter().map(|x| x + a as i32).sum();
|
||||
}
|
||||
|
||||
fn test_struct_field() {
|
||||
struct S {
|
||||
x: i32,
|
||||
y: i32,
|
||||
}
|
||||
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = S {
|
||||
x: a as i32,
|
||||
y: a as i32,
|
||||
};
|
||||
}
|
||||
|
||||
fn test_option() {
|
||||
let a: u8 = 10;
|
||||
let _: Option<i32> = Some(a as i32);
|
||||
let _: Option<i32> = Some(a as i32);
|
||||
}
|
||||
|
||||
fn test_mixed_context() {
|
||||
let a: u8 = 10;
|
||||
let _ = takes_i32(a as i32);
|
||||
let _ = generic(a as i32);
|
||||
}
|
||||
|
||||
fn test_nested_block() {
|
||||
if true {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = a as i32 + 1;
|
||||
let _ = a as i32 * 2;
|
||||
}
|
||||
}
|
||||
|
||||
fn test_match_expr() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = match 1 {
|
||||
1 => a as i32,
|
||||
_ => a as i32,
|
||||
};
|
||||
}
|
||||
|
||||
fn test_return_expr() -> i32 {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
a as i32
|
||||
}
|
||||
|
||||
fn test_closure_always_cast() {
|
||||
let a: u8 = 10;
|
||||
//~^ needless_type_cast
|
||||
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||
let _ = a as i32;
|
||||
}
|
||||
|
||||
fn test_closure_mixed_usage() {
|
||||
let a: u8 = 10;
|
||||
let _ = [1, 2].iter().map(|_| a as i32).sum::<i32>();
|
||||
let _ = a + 1;
|
||||
}
|
||||
|
||||
fn test_nested_generic_call() {
|
||||
let a: u8 = 10;
|
||||
let _ = generic(takes_i32(a as i32));
|
||||
let _ = generic(takes_i32(a as i32));
|
||||
}
|
||||
|
||||
fn test_generic_initializer() {
|
||||
// Should not lint: changing type would affect what generic() returns
|
||||
let a: u8 = generic(10u8);
|
||||
let _ = a as i32;
|
||||
let _ = a as i32;
|
||||
}
|
||||
|
||||
fn test_unsafe_transmute() {
|
||||
// Should not lint: initializer contains unsafe block
|
||||
#[allow(clippy::useless_transmute)]
|
||||
let x: u32 = unsafe { std::mem::transmute(0u32) };
|
||||
let _ = x as u64;
|
||||
}
|
||||
|
||||
fn test_if_with_generic() {
|
||||
// Should not lint: one branch has generic return type
|
||||
let x: u8 = if true { generic(1) } else { 2 };
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_match_with_generic() {
|
||||
// Should not lint: one branch has generic return type
|
||||
let x: u8 = match 1 {
|
||||
1 => generic(1),
|
||||
_ => 2,
|
||||
};
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_default() {
|
||||
// Should not lint: Default::default() has generic return type
|
||||
let x: u8 = Default::default();
|
||||
let _ = x as i32;
|
||||
}
|
||||
|
||||
fn test_loop_with_generic() {
|
||||
// Should not lint: loop break has generic return type
|
||||
#[allow(clippy::never_loop)]
|
||||
let x: u8 = loop {
|
||||
break generic(1);
|
||||
};
|
||||
let _ = x as i32;
|
||||
}
|
||||
71
tests/ui/needless_type_cast.stderr
Normal file
71
tests/ui/needless_type_cast.stderr
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:13:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
|
||||
= note: `-D clippy::needless-type-cast` implied by `-D warnings`
|
||||
= help: to override `-D warnings` add `#[allow(clippy::needless_type_cast)]`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `usize`
|
||||
--> tests/ui/needless_type_cast.rs:33:12
|
||||
|
|
||||
LL | let f: u8 = 1;
|
||||
| ^^ help: consider defining it as: `usize`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:39:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:52:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:59:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:66:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:77:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:99:16
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:107:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:116:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: this binding is defined as `u8` but is always cast to `i32`
|
||||
--> tests/ui/needless_type_cast.rs:122:12
|
||||
|
|
||||
LL | let a: u8 = 10;
|
||||
| ^^ help: consider defining it as: `i32`
|
||||
|
||||
error: aborting due to 11 previous errors
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue