Skip to content

Commit d38b905

Browse files
committed
fix: unnecessary_cast suggests extra brackets when in macro
1 parent aeb6ac9 commit d38b905

File tree

4 files changed

+91
-16
lines changed

4 files changed

+91
-16
lines changed

clippy_lints/src/casts/unnecessary_cast.rs

+42-13
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use rustc_errors::Applicability;
88
use rustc_hir::def::{DefKind, Res};
99
use rustc_hir::{Expr, ExprKind, Lit, Node, Path, QPath, TyKind, UnOp};
1010
use rustc_lint::{LateContext, LintContext};
11+
use rustc_middle::ty::adjustment::Adjust;
1112
use rustc_middle::ty::{self, FloatTy, InferTy, Ty};
13+
use rustc_span::{Symbol, sym};
1214
use std::ops::ControlFlow;
1315

1416
use super::UNNECESSARY_CAST;
@@ -142,6 +144,33 @@ pub(super) fn check<'tcx>(
142144
}
143145

144146
if cast_from.kind() == cast_to.kind() && !expr.span.in_external_macro(cx.sess().source_map()) {
147+
enum MaybeParenOrBlock {
148+
Paren,
149+
Block,
150+
Nothing,
151+
}
152+
153+
fn is_borrow_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
154+
matches!(expr.kind, ExprKind::AddrOf(..))
155+
|| cx
156+
.typeck_results()
157+
.expr_adjustments(expr)
158+
.iter()
159+
.any(|adj| matches!(adj.kind, Adjust::Borrow(_)))
160+
}
161+
162+
fn is_in_allowed_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
163+
const ALLOWED_MACROS: &[Symbol] = &[
164+
sym::format_args_macro,
165+
sym::assert_eq_macro,
166+
sym::debug_assert_eq_macro,
167+
sym::assert_ne_macro,
168+
sym::debug_assert_ne_macro,
169+
];
170+
matches!(expr.span.ctxt().outer_expn_data().macro_def_id, Some(def_id) if
171+
ALLOWED_MACROS.iter().any(|&sym| cx.tcx.is_diagnostic_item(sym, def_id)))
172+
}
173+
145174
if let Some(id) = path_to_local(cast_expr)
146175
&& !cx.tcx.hir_span(id).eq_ctxt(cast_expr.span)
147176
{
@@ -150,26 +179,26 @@ pub(super) fn check<'tcx>(
150179
return false;
151180
}
152181

153-
// If the whole cast expression is a unary expression (`(*x as T)`) or an addressof
154-
// expression (`(&x as T)`), then not surrounding the suggestion into a block risks us
155-
// changing the precedence of operators if the cast expression is followed by an operation
156-
// with higher precedence than the unary operator (`(*x as T).foo()` would become
157-
// `*x.foo()`, which changes what the `*` applies on).
158-
// The same is true if the expression encompassing the cast expression is a unary
159-
// expression or an addressof expression.
160-
let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))
161-
|| get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
182+
// Changing `&(x as i32)` to `&x` would change the meaning of the code because the previous creates
183+
// a reference to the temporary while the latter creates a reference to the original value.
184+
let surrounding = match cx.tcx.parent_hir_node(expr.hir_id) {
185+
Node::Expr(parent) if !is_in_allowed_macro(cx, parent) && is_borrow_expr(cx, parent) => {
186+
MaybeParenOrBlock::Block
187+
},
188+
Node::Expr(parent) if cast_expr.precedence() < parent.precedence() => MaybeParenOrBlock::Paren,
189+
_ => MaybeParenOrBlock::Nothing,
190+
};
162191

163192
span_lint_and_sugg(
164193
cx,
165194
UNNECESSARY_CAST,
166195
expr.span,
167196
format!("casting to the same type is unnecessary (`{cast_from}` -> `{cast_to}`)"),
168197
"try",
169-
if needs_block {
170-
format!("{{ {cast_str} }}")
171-
} else {
172-
cast_str
198+
match surrounding {
199+
MaybeParenOrBlock::Paren => format!("({cast_str})"),
200+
MaybeParenOrBlock::Block => format!("{{ {cast_str} }}"),
201+
MaybeParenOrBlock::Nothing => cast_str,
173202
},
174203
Applicability::MachineApplicable,
175204
);

tests/ui/unnecessary_cast.fixed

+15-1
Original file line numberDiff line numberDiff line change
@@ -266,7 +266,21 @@ mod fixable {
266266
// Issue #11968: The suggestion for this lint removes the parentheses and leave the code as
267267
// `*x.pow(2)` which tries to dereference the return value rather than `x`.
268268
fn issue_11968(x: &usize) -> usize {
269-
{ *x }.pow(2)
269+
(*x).pow(2)
270+
//~^ unnecessary_cast
271+
}
272+
273+
#[allow(clippy::cast_lossless)]
274+
fn issue_14640() {
275+
let x = 5usize;
276+
let vec: Vec<u64> = vec![1, 2, 3, 4, 5];
277+
assert_eq!(vec.len(), x);
278+
//~^ unnecessary_cast
279+
280+
let _ = (5i32 as i64).abs();
281+
//~^ unnecessary_cast
282+
283+
let _ = 5i32 as i64;
270284
//~^ unnecessary_cast
271285
}
272286
}

tests/ui/unnecessary_cast.rs

+14
Original file line numberDiff line numberDiff line change
@@ -269,4 +269,18 @@ mod fixable {
269269
(*x as usize).pow(2)
270270
//~^ unnecessary_cast
271271
}
272+
273+
#[allow(clippy::cast_lossless)]
274+
fn issue_14640() {
275+
let x = 5usize;
276+
let vec: Vec<u64> = vec![1, 2, 3, 4, 5];
277+
assert_eq!(vec.len(), x as usize);
278+
//~^ unnecessary_cast
279+
280+
let _ = (5i32 as i64 as i64).abs();
281+
//~^ unnecessary_cast
282+
283+
let _ = 5i32 as i64 as i64;
284+
//~^ unnecessary_cast
285+
}
272286
}

tests/ui/unnecessary_cast.stderr

+20-2
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,25 @@ error: casting to the same type is unnecessary (`usize` -> `usize`)
245245
--> tests/ui/unnecessary_cast.rs:269:9
246246
|
247247
LL | (*x as usize).pow(2)
248-
| ^^^^^^^^^^^^^ help: try: `{ *x }`
248+
| ^^^^^^^^^^^^^ help: try: `(*x)`
249249

250-
error: aborting due to 41 previous errors
250+
error: casting to the same type is unnecessary (`usize` -> `usize`)
251+
--> tests/ui/unnecessary_cast.rs:277:31
252+
|
253+
LL | assert_eq!(vec.len(), x as usize);
254+
| ^^^^^^^^^^ help: try: `x`
255+
256+
error: casting to the same type is unnecessary (`i64` -> `i64`)
257+
--> tests/ui/unnecessary_cast.rs:280:17
258+
|
259+
LL | let _ = (5i32 as i64 as i64).abs();
260+
| ^^^^^^^^^^^^^^^^^^^^ help: try: `(5i32 as i64)`
261+
262+
error: casting to the same type is unnecessary (`i64` -> `i64`)
263+
--> tests/ui/unnecessary_cast.rs:283:17
264+
|
265+
LL | let _ = 5i32 as i64 as i64;
266+
| ^^^^^^^^^^^^^^^^^^ help: try: `5i32 as i64`
267+
268+
error: aborting due to 44 previous errors
251269

0 commit comments

Comments
 (0)