fix: unnecessary type cast causing a compile error (#16796)

In some cases, removing a cast that looks unnecessary can
still change type inference, even when the cast is to the
expected same type.

The lint now handles casts that flow through
intermediate expressions (e.g. calls, blocks, let
bindings, loops...). It also treats placeholder
generics (e.g. `::<_>`) as inference sensitive.

Added regression tests with similar behavior
as the original bug, some edge cases and
tests to check if false negative cases
weren't created.

Closes rust-lang/rust-clippy#16449

changelog: [`unnecessary_cast`]: fixed a suggestion to remove a cast
that can cause a compilation error if followed.
This commit is contained in:
llogiq
2026-04-04 20:12:58 +00:00
committed by GitHub
4 changed files with 1183 additions and 4 deletions
+347 -2
View File
@@ -8,10 +8,10 @@
use rustc_ast::{LitFloatType, LitIntType, LitKind};
use rustc_errors::Applicability;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::{Expr, ExprKind, FnRetTy, Lit, Node, Path, QPath, TyKind, UnOp};
use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, Lit, Node, Path, QPath, TyKind, UnOp};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::ty::adjustment::Adjust;
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
use rustc_middle::ty::{self, FloatTy, GenericArg, InferTy, Ty};
use rustc_span::Symbol;
use std::ops::ControlFlow;
@@ -173,6 +173,16 @@ fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
cx.tcx.get_diagnostic_name(def_id).is_some_and(|sym| ALLOWED_MACROS.contains(&sym)))
}
// Removing the cast here can change inference along the path to an outer
// method receiver, so avoid linting in that case.
if is_inference_sensitive_inner_expr(cx, cast_expr)
&& contains_unsuffixed_numeric_literal(cast_expr)
&& feeds_outer_method_receiver(cx, expr)
&& has_lint_blocking_context_on_receiver_path(cx, expr)
{
return false;
}
if let Some(id) = cast_expr.res_local_id()
&& !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span)
{
@@ -340,3 +350,338 @@ fn emit_lint(
applicability,
);
}
fn contains_unsuffixed_numeric_literal<'e>(expr: &'e Expr<'e>) -> bool {
for_each_expr_without_closures(expr, |e| {
if let Some(lit) = get_numeric_literal(e)
&& matches!(
lit.node,
LitKind::Int(_, LitIntType::Unsuffixed) | LitKind::Float(_, LitFloatType::Unsuffixed)
)
{
return ControlFlow::Break(());
}
ControlFlow::Continue(())
})
.is_some()
}
// Returns `true` for expressions whose resolved type or method depends on inference.
fn is_inference_sensitive_inner_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
match expr.kind {
ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) | ExprKind::Index(..) => cx
.typeck_results()
.type_dependent_def_id(expr.hir_id)
.and_then(|def_id| cx.tcx.opt_associated_item(def_id))
.is_some_and(|assoc| assoc.trait_container(cx.tcx).is_some()),
_ => false,
}
}
// Returns `true` if the function's output type contains a type parameter
// originating from the selected input.
fn output_depends_on_input_param(cx: &LateContext<'_>, def_id: rustc_hir::def_id::DefId, input_index: usize) -> bool {
let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
let Some(input_ty) = sig.inputs().get(input_index) else {
return false;
};
let output_ty = sig.output();
input_ty.walk().filter_map(GenericArg::as_type).any(|input_part| {
match input_part.kind() {
ty::Param(input_param) => output_ty
.walk()
.filter_map(GenericArg::as_type)
.any(|output_part| {
matches!(output_part.kind(), ty::Param(output_param) if output_param.index == input_param.index)
}),
_ => false,
}
})
}
// Returns `true` if the generic arguments include at least one explicit type or const
// argument and none of the provided generic arguments are placeholders like `::<_>`.
fn has_explicit_type_or_const_args(args: Option<&rustc_hir::GenericArgs<'_>>) -> bool {
let Some(args) = args else {
return false;
};
let mut has_explicit = false;
for arg in args.args {
match arg {
rustc_hir::GenericArg::Type(_) | rustc_hir::GenericArg::Const(_) => {
has_explicit = true;
},
rustc_hir::GenericArg::Infer(_) => return false,
rustc_hir::GenericArg::Lifetime(_) => {},
}
}
has_explicit
}
// Controls whether the receiver path walk is looking for an outer method
// receiver or for a context where linting should stop.
#[derive(Copy, Clone)]
enum ReceiverPathMode {
FindReceiver,
FindLintBlockingContext,
}
enum ReceiverPathResult {
Continue(HirId),
Stop(bool),
}
fn stop_if_lint_blocking_else_continue(parent_hir_id: HirId, mode: ReceiverPathMode) -> ReceiverPathResult {
if matches!(mode, ReceiverPathMode::FindLintBlockingContext) {
ReceiverPathResult::Stop(true)
} else {
ReceiverPathResult::Continue(parent_hir_id)
}
}
fn walk_receiver_path_method_call(
cx: &LateContext<'_>,
current_hir_id: HirId,
parent: &Expr<'_>,
segment: &rustc_hir::PathSegment<'_>,
receiver: &Expr<'_>,
args: &[Expr<'_>],
mode: ReceiverPathMode,
) -> ReceiverPathResult {
if receiver.hir_id == current_hir_id {
return if matches!(mode, ReceiverPathMode::FindLintBlockingContext) {
ReceiverPathResult::Continue(parent.hir_id)
} else {
ReceiverPathResult::Stop(true)
};
}
let Some(arg_index) = args.iter().position(|arg| arg.hir_id == current_hir_id) else {
return ReceiverPathResult::Stop(false);
};
let passthrough = !has_explicit_type_or_const_args(segment.args)
&& cx
.typeck_results()
.type_dependent_def_id(parent.hir_id)
.is_some_and(|def_id| output_depends_on_input_param(cx, def_id, arg_index + 1));
if matches!(mode, ReceiverPathMode::FindLintBlockingContext) {
if passthrough
|| args.iter().any(|arg| {
arg.hir_id != current_hir_id
&& get_numeric_literal(arg).is_none()
&& !cx.typeck_results().expr_ty(arg).is_primitive()
})
{
ReceiverPathResult::Stop(true)
} else {
ReceiverPathResult::Continue(parent.hir_id)
}
} else if passthrough {
ReceiverPathResult::Continue(parent.hir_id)
} else {
ReceiverPathResult::Stop(false)
}
}
fn walk_receiver_path_call(
cx: &LateContext<'_>,
current_hir_id: HirId,
parent: &Expr<'_>,
callee: &Expr<'_>,
args: &[Expr<'_>],
mode: ReceiverPathMode,
) -> ReceiverPathResult {
if callee.hir_id == current_hir_id {
return ReceiverPathResult::Continue(parent.hir_id);
}
let Some(arg_index) = args.iter().position(|arg| arg.hir_id == current_hir_id) else {
return ReceiverPathResult::Stop(false);
};
let passthrough = if let ExprKind::Path(qpath) = callee.kind
&& let Res::Def(DefKind::Fn, def_id) = cx.qpath_res(&qpath, callee.hir_id)
{
let has_explicit_args = match &qpath {
QPath::Resolved(_, path) => path
.segments
.last()
.is_some_and(|seg| has_explicit_type_or_const_args(seg.args)),
QPath::TypeRelative(_, segment) => has_explicit_type_or_const_args(segment.args),
};
!has_explicit_args && output_depends_on_input_param(cx, def_id, arg_index)
} else {
false
};
if matches!(mode, ReceiverPathMode::FindLintBlockingContext) {
ReceiverPathResult::Stop(passthrough)
} else if passthrough {
ReceiverPathResult::Continue(parent.hir_id)
} else {
ReceiverPathResult::Stop(false)
}
}
// Walk one step up the receiver path for the current mode.
fn walk_receiver_path_step(cx: &LateContext<'_>, current_hir_id: HirId, mode: ReceiverPathMode) -> ReceiverPathResult {
match cx.tcx.parent_hir_node(current_hir_id) {
Node::Expr(parent) => match parent.kind {
// Main case.
// The current node may be the receiver.
// Or it may flow through a passthrough method.
ExprKind::MethodCall(segment, receiver, args, _) => {
walk_receiver_path_method_call(cx, current_hir_id, parent, segment, receiver, args, mode)
},
// Regular calls only keep the path alive
// if the output still depends on this input.
ExprKind::Call(callee, args) => walk_receiver_path_call(cx, current_hir_id, parent, callee, args, mode),
// A sibling that is not primitive blocks the lint.
ExprKind::Binary(_, left, right) | ExprKind::Index(left, right, _)
if left.hir_id == current_hir_id || right.hir_id == current_hir_id =>
{
if matches!(mode, ReceiverPathMode::FindLintBlockingContext) {
let sibling = if left.hir_id == current_hir_id { right } else { left };
if get_numeric_literal(sibling).is_none() && !cx.typeck_results().expr_ty(sibling).is_primitive() {
ReceiverPathResult::Stop(true)
} else {
ReceiverPathResult::Continue(parent.hir_id)
}
} else {
ReceiverPathResult::Continue(parent.hir_id)
}
},
// These expressions don't block the lint, so we continue walking up the path.
ExprKind::Unary(_, inner)
| ExprKind::Cast(inner, _)
| ExprKind::AddrOf(_, _, inner)
| ExprKind::Field(inner, _)
| ExprKind::DropTemps(inner)
if inner.hir_id == current_hir_id =>
{
ReceiverPathResult::Continue(parent.hir_id)
},
// A block can forward its tail expression, so we keep walking through it.
ExprKind::Block(block, _)
if block.hir_id == current_hir_id || block.expr.is_some_and(|tail| tail.hir_id == current_hir_id) =>
{
ReceiverPathResult::Continue(parent.hir_id)
},
// Depending on the mode, either keep walking or block the lint.
ExprKind::Loop(block, ..) if block.hir_id == current_hir_id => {
stop_if_lint_blocking_else_continue(parent.hir_id, mode)
},
// Tuples and arrays wrap the current expression, so we continue walking up the path.
ExprKind::Tup(exprs) | ExprKind::Array(exprs) if exprs.iter().any(|e| e.hir_id == current_hir_id) => {
ReceiverPathResult::Continue(parent.hir_id)
},
// The expression is stored in a field. We continue walking up the path to see how the struct is used.
ExprKind::Struct(_, fields, _)
if fields
.iter()
.any(|field| field.hir_id == current_hir_id || field.expr.hir_id == current_hir_id) =>
{
ReceiverPathResult::Continue(parent.hir_id)
},
// Depending on the mode, either keep walking or block the lint.
ExprKind::If(cond, then_expr, else_expr)
if cond.hir_id == current_hir_id
|| then_expr.hir_id == current_hir_id
|| else_expr.is_some_and(|else_expr| else_expr.hir_id == current_hir_id) =>
{
stop_if_lint_blocking_else_continue(parent.hir_id, mode)
},
// Depending on the mode, either keep walking or block the lint.
ExprKind::Match(scrutinee, arms, _)
if scrutinee.hir_id == current_hir_id
|| arms
.iter()
.any(|arm| arm.hir_id == current_hir_id || arm.body.hir_id == current_hir_id) =>
{
stop_if_lint_blocking_else_continue(parent.hir_id, mode)
},
// Depending on the mode, either keep walking or block the lint.
ExprKind::Break(_, Some(inner)) if inner.hir_id == current_hir_id => {
stop_if_lint_blocking_else_continue(parent.hir_id, mode)
},
_ => ReceiverPathResult::Stop(false),
},
// These are structural HIR nodes. We just skip them and keep walking.
Node::ExprField(_) | Node::Block(_) | Node::Arm(_) | Node::Stmt(_) => {
ReceiverPathResult::Continue(cx.tcx.parent_hir_id(current_hir_id))
},
// Handle `let x = init; x` in the same block.
// Depending on the mode, either keep walking init or block the lint.
Node::LetStmt(local) => {
if let Some(block_hir_id) = let_init_to_block_hir_id(cx, local, current_hir_id) {
stop_if_lint_blocking_else_continue(block_hir_id, mode)
} else {
ReceiverPathResult::Stop(false)
}
},
_ => ReceiverPathResult::Stop(false),
}
}
// Returns `true` if `expr` eventually becomes the receiver of an outer method call.
fn feeds_outer_method_receiver(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let mut current_hir_id = expr.hir_id;
loop {
match walk_receiver_path_step(cx, current_hir_id, ReceiverPathMode::FindReceiver) {
ReceiverPathResult::Continue(next) => current_hir_id = next,
ReceiverPathResult::Stop(result) => return result,
}
}
}
// Returns `true` if the receiver path contains a context that should block the lint.
fn has_lint_blocking_context_on_receiver_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let mut current_hir_id = expr.hir_id;
loop {
match walk_receiver_path_step(cx, current_hir_id, ReceiverPathMode::FindLintBlockingContext) {
ReceiverPathResult::Continue(next) => current_hir_id = next,
ReceiverPathResult::Stop(result) => return result,
}
}
}
// If the initializer flows into the tail expression of the same block, returns that block HirId.
fn let_init_to_block_hir_id(
cx: &LateContext<'_>,
local: &rustc_hir::LetStmt<'_>,
current_hir_id: HirId,
) -> Option<HirId> {
let init = local.init?;
if init.hir_id != current_hir_id {
return None;
}
let stmt_hir_id = match cx.tcx.parent_hir_node(local.hir_id) {
Node::Stmt(stmt) => stmt.hir_id,
_ => return None,
};
let Node::Block(block) = cx.tcx.parent_hir_node(stmt_hir_id) else {
return None;
};
let tail = block.expr?;
let binding_hir_id = tail.res_local_id()?;
match local.pat.kind {
rustc_hir::PatKind::Binding(_, local_hir_id, ..) if local_hir_id == binding_hir_id => Some(block.hir_id),
_ => None,
}
}
+363
View File
@@ -284,6 +284,369 @@ mod fixable {
let _ = 5i32 as i64;
//~^ unnecessary_cast
}
mod issue_16449_support {
use std::marker::PhantomData;
use std::ops::{Add, Mul};
pub trait PowLike<Rhs> {
type Output;
fn pow_like(self, rhs: Rhs) -> Self::Output;
}
impl PowLike<f64> for f64 {
type Output = f64;
fn pow_like(self, rhs: f64) -> f64 {
self.powf(rhs)
}
}
impl PowLike<i32> for f64 {
type Output = f64;
fn pow_like(self, _: i32) -> f64 {
self
}
}
impl PowLike<u32> for f64 {
type Output = f32;
fn pow_like(self, _: u32) -> f32 {
self as f32
}
}
pub struct Mat<T>(pub PhantomData<T>);
pub fn mat<T>(_: &[[T; 1]; 1]) -> Mat<T> {
Mat(PhantomData)
}
pub struct Out<T>(pub PhantomData<T>);
impl Out<f64> {
pub fn view(self) {}
}
impl Out<f32> {
pub fn view(self) {}
}
impl Mul<&Mat<f64>> for f64 {
type Output = Out<f64>;
fn mul(self, _: &Mat<f64>) -> Self::Output {
Out(PhantomData)
}
}
impl Mul<&Mat<f32>> for f32 {
type Output = Out<f32>;
fn mul(self, _: &Mat<f32>) -> Self::Output {
Out(PhantomData)
}
}
pub fn id<T>(x: T) -> T {
x
}
pub fn id_with<T, U>(x: T, _: U) -> T {
x
}
pub struct Wrap<T> {
pub inner: T,
}
pub fn wrap<T>(inner: T) -> Wrap<T> {
Wrap { inner }
}
pub struct MethodWrap;
impl MethodWrap {
pub fn id<T>(&self, x: T) -> T {
x
}
pub fn id_with<T, U>(&self, x: T, _: U) -> T {
x
}
pub fn wrap<T>(&self, inner: T) -> Wrap<T> {
Wrap { inner }
}
}
pub struct X;
impl Add<i32> for X {
type Output = f64;
fn add(self, _: i32) -> f64 {
1.0
}
}
impl Add<u32> for X {
type Output = f32;
fn add(self, _: u32) -> f32 {
1.0
}
}
pub struct Y;
impl Add<i32> for Y {
type Output = f64;
fn add(self, _: i32) -> f64 {
1.0
}
}
pub trait PowLikeSingleImpl<Rhs> {
type Output;
fn pow_like_single_impl(self, rhs: Rhs) -> Self::Output;
}
impl PowLikeSingleImpl<i32> for f64 {
type Output = f64;
fn pow_like_single_impl(self, _: i32) -> f64 {
self
}
}
}
// Issue #16449: removing the cast still affects inference / impl selection,
// so these must not lint.
// Minimal reproduction of the original issue.
fn issue_16449_minimal_original_reproduction() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(1.0_f64.pow_like(2) as f64 * &a).view();
}
// Wrappers that preserve the inference sensitive path.
fn issue_16449_struct_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
Wrap {
inner: 1.0_f64.pow_like(2) as f64 * &a,
}
.inner
.view();
}
fn issue_16449_free_identity_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
id(1.0_f64.pow_like(2) as f64 * &a).view();
}
fn issue_16449_method_identity_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
s.id(1.0_f64.pow_like(2) as f64 * &a).view();
}
fn issue_16449_free_wrap_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
wrap(1.0_f64.pow_like(2) as f64 * &a).inner.view();
}
fn issue_16449_method_wrap_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
s.wrap(1.0_f64.pow_like(2) as f64 * &a).inner.view();
}
fn issue_16449_block_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
({ 1.0_f64.pow_like(2) as f64 * &a }).view();
}
#[allow(clippy::if_same_then_else)]
fn issue_16449_if_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(if true {
1.0_f64.pow_like(2) as f64 * &a
} else {
1.0_f64.pow_like(2) as f64 * &a
})
.view();
}
fn issue_16449_match_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(match 0 {
0 => 1.0_f64.pow_like(2) as f64 * &a,
_ => 1.0_f64.pow_like(2) as f64 * &a,
})
.view();
}
#[allow(clippy::let_and_return)]
fn issue_16449_let_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
({
let x = 1.0_f64.pow_like(2) as f64 * &a;
x
})
.view();
}
#[allow(clippy::never_loop)]
fn issue_16449_loop_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(loop {
break 1.0_f64.pow_like(2) as f64 * &a;
})
.view();
}
#[allow(clippy::double_parens)]
fn issue_16449_operator_reproduction() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
((X + 2) as f64 * &a).view();
}
// Placeholder generic arguments still leave inference active,
// so these must not lint.
fn issue_16449_placeholder_generics_do_not_lint() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
// placeholder call
id::<_>(1.0_f64.pow_like(2) as f64 * &a).view();
// placeholder method call
s.id::<_>(1.0_f64.pow_like(2) as f64 * &a).view();
// mixed placeholder call
id_with::<_, u8>(1.0_f64.pow_like(2) as f64 * &a, 0).view();
// mixed placeholder method call
s.id_with::<_, u8>(1.0_f64.pow_like(2) as f64 * &a, 0).view();
}
// These look similar, but inference no longer depends on the cast, so they should lint.
fn issue_16449_explicit_generics_still_lint() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
// explicit call
id::<Out<f64>>(1.0_f64.pow_like(2) * &a).view();
//~^ unnecessary_cast
// explicit method call
s.id::<Out<f64>>(1.0_f64.pow_like(2) * &a).view();
//~^ unnecessary_cast
// explicit free wrap
wrap::<Out<f64>>(1.0_f64.pow_like(2) * &a).inner.view();
//~^ unnecessary_cast
// explicit method wrap
s.wrap::<Out<f64>>(1.0_f64.pow_like(2) * &a).inner.view();
//~^ unnecessary_cast
}
// A nonprimitive method receiver alone should not suppress the lint.
fn issue_16449_nonprimitive_receiver_should_still_lint() {
use self::issue_16449_support::PowLikeSingleImpl;
struct Receiver;
impl Receiver {
fn take(&self, x: f64) -> f64 {
x
}
}
let receiver = Receiver;
let _ = receiver.take(1.0_f64.pow_like_single_impl(2)).abs();
//~^ unnecessary_cast
}
fn issue_16449_wrapper_still_lints() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
let _ = id(1.0_f64.powi(2)).abs();
//~^ unnecessary_cast
let _ = wrap(1.0_f64.powi(2)).inner.abs();
//~^ unnecessary_cast
let _ = s.id(1.0_f64.powi(2)).abs();
//~^ unnecessary_cast
let _ = s.wrap(1.0_f64.powi(2)).inner.abs();
//~^ unnecessary_cast
let _ = id(1.0_f64.powi(2) * &a);
//~^ unnecessary_cast
let _ = s.id(1.0_f64.powi(2) * &a);
//~^ unnecessary_cast
}
// To guarantee that good suggestions given before continue
// to be given even after the fix.
#[allow(clippy::double_parens)]
fn issue_16449_still_lints() {
use self::issue_16449_support::*;
const ONE: f64 = 1.0;
let one = 1.0_f64;
let _ = 1.0_f64.pow_like(0.5);
//~^ unnecessary_cast
let _ = 1.0_f64.pow_like(2);
//~^ unnecessary_cast
let _ = 1.0_f64.powi(2).abs();
//~^ unnecessary_cast
let _ = ((Y + 2)).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) + 1.0_f64).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) + ONE).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) + one).abs();
//~^ unnecessary_cast
}
}
fn issue16475() -> *const u8 {
+363
View File
@@ -284,6 +284,369 @@ fn issue_14640() {
let _ = 5i32 as i64 as i64;
//~^ unnecessary_cast
}
mod issue_16449_support {
use std::marker::PhantomData;
use std::ops::{Add, Mul};
pub trait PowLike<Rhs> {
type Output;
fn pow_like(self, rhs: Rhs) -> Self::Output;
}
impl PowLike<f64> for f64 {
type Output = f64;
fn pow_like(self, rhs: f64) -> f64 {
self.powf(rhs)
}
}
impl PowLike<i32> for f64 {
type Output = f64;
fn pow_like(self, _: i32) -> f64 {
self
}
}
impl PowLike<u32> for f64 {
type Output = f32;
fn pow_like(self, _: u32) -> f32 {
self as f32
}
}
pub struct Mat<T>(pub PhantomData<T>);
pub fn mat<T>(_: &[[T; 1]; 1]) -> Mat<T> {
Mat(PhantomData)
}
pub struct Out<T>(pub PhantomData<T>);
impl Out<f64> {
pub fn view(self) {}
}
impl Out<f32> {
pub fn view(self) {}
}
impl Mul<&Mat<f64>> for f64 {
type Output = Out<f64>;
fn mul(self, _: &Mat<f64>) -> Self::Output {
Out(PhantomData)
}
}
impl Mul<&Mat<f32>> for f32 {
type Output = Out<f32>;
fn mul(self, _: &Mat<f32>) -> Self::Output {
Out(PhantomData)
}
}
pub fn id<T>(x: T) -> T {
x
}
pub fn id_with<T, U>(x: T, _: U) -> T {
x
}
pub struct Wrap<T> {
pub inner: T,
}
pub fn wrap<T>(inner: T) -> Wrap<T> {
Wrap { inner }
}
pub struct MethodWrap;
impl MethodWrap {
pub fn id<T>(&self, x: T) -> T {
x
}
pub fn id_with<T, U>(&self, x: T, _: U) -> T {
x
}
pub fn wrap<T>(&self, inner: T) -> Wrap<T> {
Wrap { inner }
}
}
pub struct X;
impl Add<i32> for X {
type Output = f64;
fn add(self, _: i32) -> f64 {
1.0
}
}
impl Add<u32> for X {
type Output = f32;
fn add(self, _: u32) -> f32 {
1.0
}
}
pub struct Y;
impl Add<i32> for Y {
type Output = f64;
fn add(self, _: i32) -> f64 {
1.0
}
}
pub trait PowLikeSingleImpl<Rhs> {
type Output;
fn pow_like_single_impl(self, rhs: Rhs) -> Self::Output;
}
impl PowLikeSingleImpl<i32> for f64 {
type Output = f64;
fn pow_like_single_impl(self, _: i32) -> f64 {
self
}
}
}
// Issue #16449: removing the cast still affects inference / impl selection,
// so these must not lint.
// Minimal reproduction of the original issue.
fn issue_16449_minimal_original_reproduction() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(1.0_f64.pow_like(2) as f64 * &a).view();
}
// Wrappers that preserve the inference sensitive path.
fn issue_16449_struct_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
Wrap {
inner: 1.0_f64.pow_like(2) as f64 * &a,
}
.inner
.view();
}
fn issue_16449_free_identity_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
id(1.0_f64.pow_like(2) as f64 * &a).view();
}
fn issue_16449_method_identity_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
s.id(1.0_f64.pow_like(2) as f64 * &a).view();
}
fn issue_16449_free_wrap_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
wrap(1.0_f64.pow_like(2) as f64 * &a).inner.view();
}
fn issue_16449_method_wrap_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
s.wrap(1.0_f64.pow_like(2) as f64 * &a).inner.view();
}
fn issue_16449_block_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
({ 1.0_f64.pow_like(2) as f64 * &a }).view();
}
#[allow(clippy::if_same_then_else)]
fn issue_16449_if_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(if true {
1.0_f64.pow_like(2) as f64 * &a
} else {
1.0_f64.pow_like(2) as f64 * &a
})
.view();
}
fn issue_16449_match_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(match 0 {
0 => 1.0_f64.pow_like(2) as f64 * &a,
_ => 1.0_f64.pow_like(2) as f64 * &a,
})
.view();
}
#[allow(clippy::let_and_return)]
fn issue_16449_let_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
({
let x = 1.0_f64.pow_like(2) as f64 * &a;
x
})
.view();
}
#[allow(clippy::never_loop)]
fn issue_16449_loop_wrapper() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
(loop {
break 1.0_f64.pow_like(2) as f64 * &a;
})
.view();
}
#[allow(clippy::double_parens)]
fn issue_16449_operator_reproduction() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
((X + 2) as f64 * &a).view();
}
// Placeholder generic arguments still leave inference active,
// so these must not lint.
fn issue_16449_placeholder_generics_do_not_lint() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
// placeholder call
id::<_>(1.0_f64.pow_like(2) as f64 * &a).view();
// placeholder method call
s.id::<_>(1.0_f64.pow_like(2) as f64 * &a).view();
// mixed placeholder call
id_with::<_, u8>(1.0_f64.pow_like(2) as f64 * &a, 0).view();
// mixed placeholder method call
s.id_with::<_, u8>(1.0_f64.pow_like(2) as f64 * &a, 0).view();
}
// These look similar, but inference no longer depends on the cast, so they should lint.
fn issue_16449_explicit_generics_still_lint() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
// explicit call
id::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).view();
//~^ unnecessary_cast
// explicit method call
s.id::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).view();
//~^ unnecessary_cast
// explicit free wrap
wrap::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).inner.view();
//~^ unnecessary_cast
// explicit method wrap
s.wrap::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).inner.view();
//~^ unnecessary_cast
}
// A nonprimitive method receiver alone should not suppress the lint.
fn issue_16449_nonprimitive_receiver_should_still_lint() {
use self::issue_16449_support::PowLikeSingleImpl;
struct Receiver;
impl Receiver {
fn take(&self, x: f64) -> f64 {
x
}
}
let receiver = Receiver;
let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs();
//~^ unnecessary_cast
}
fn issue_16449_wrapper_still_lints() {
use self::issue_16449_support::*;
let a = mat(&[[1.0]]);
let s = MethodWrap;
let _ = id(1.0_f64.powi(2) as f64).abs();
//~^ unnecessary_cast
let _ = wrap(1.0_f64.powi(2) as f64).inner.abs();
//~^ unnecessary_cast
let _ = s.id(1.0_f64.powi(2) as f64).abs();
//~^ unnecessary_cast
let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs();
//~^ unnecessary_cast
let _ = id(1.0_f64.powi(2) as f64 * &a);
//~^ unnecessary_cast
let _ = s.id(1.0_f64.powi(2) as f64 * &a);
//~^ unnecessary_cast
}
// To guarantee that good suggestions given before continue
// to be given even after the fix.
#[allow(clippy::double_parens)]
fn issue_16449_still_lints() {
use self::issue_16449_support::*;
const ONE: f64 = 1.0;
let one = 1.0_f64;
let _ = 1.0_f64.pow_like(0.5) as f64;
//~^ unnecessary_cast
let _ = 1.0_f64.pow_like(2) as f64;
//~^ unnecessary_cast
let _ = (1.0_f64.powi(2) as f64).abs();
//~^ unnecessary_cast
let _ = ((Y + 2) as f64).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs();
//~^ unnecessary_cast
let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs();
//~^ unnecessary_cast
}
}
fn issue16475() -> *const u8 {
+110 -2
View File
@@ -277,11 +277,119 @@ error: casting to the same type is unnecessary (`i64` -> `i64`)
LL | let _ = 5i32 as i64 as i64;
| ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:563:24
|
LL | id::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).view();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:567:26
|
LL | s.id::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).view();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:571:26
|
LL | wrap::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).inner.view();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:575:28
|
LL | s.wrap::<Out<f64>>(1.0_f64.pow_like(2) as f64 * &a).inner.view();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:591:31
|
LL | let _ = receiver.take(1.0_f64.pow_like_single_impl(2) as f64).abs();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:601:20
|
LL | let _ = id(1.0_f64.powi(2) as f64).abs();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:604:22
|
LL | let _ = wrap(1.0_f64.powi(2) as f64).inner.abs();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:607:22
|
LL | let _ = s.id(1.0_f64.powi(2) as f64).abs();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:610:24
|
LL | let _ = s.wrap(1.0_f64.powi(2) as f64).inner.abs();
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:613:20
|
LL | let _ = id(1.0_f64.powi(2) as f64 * &a);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:616:22
|
LL | let _ = s.id(1.0_f64.powi(2) as f64 * &a);
| ^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:629:17
|
LL | let _ = 1.0_f64.pow_like(0.5) as f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(0.5)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:632:17
|
LL | let _ = 1.0_f64.pow_like(2) as f64;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:635:17
|
LL | let _ = (1.0_f64.powi(2) as f64).abs();
| ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.powi(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:638:17
|
LL | let _ = ((Y + 2) as f64).abs();
| ^^^^^^^^^^^^^^^^ help: try: `((Y + 2))`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:641:18
|
LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + 1.0_f64).abs();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:644:18
|
LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + ONE).abs();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)`
error: casting to the same type is unnecessary (`f64` -> `f64`)
--> tests/ui/unnecessary_cast.rs:647:18
|
LL | let _ = (1.0_f64.pow_like_single_impl(2) as f64 + one).abs();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `1.0_f64.pow_like_single_impl(2)`
error: casting raw pointers to the same type and constness is unnecessary (`*const *const u8` -> `*const *const u8`)
--> tests/ui/unnecessary_cast.rs:292:10
--> tests/ui/unnecessary_cast.rs:655:10
|
LL | *(&NONE as *const _ as *const _ as *const *const u8 as *const *const u8)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(&NONE as *const _ as *const _ as *const *const u8)`
error: aborting due to 47 previous errors
error: aborting due to 65 previous errors