Skip to content

Commit 0165aa1

Browse files
committed
[useless_conversion]: fix FP in macro and add test
1 parent c01bec6 commit 0165aa1

File tree

2 files changed

+33
-1
lines changed

2 files changed

+33
-1
lines changed

clippy_lints/src/useless_conversion.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
66
use clippy_utils::{get_parent_expr, is_trait_method, match_def_path, path_to_local, paths};
77
use if_chain::if_chain;
88
use rustc_errors::Applicability;
9+
use rustc_hir::def::DefKind;
910
use rustc_hir::def_id::DefId;
1011
use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
1112
use rustc_lint::{LateContext, LateLintPass};
@@ -102,6 +103,17 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -
102103
(expr, depth)
103104
}
104105

106+
/// Checks if the given `expr` is an argument of a macro invocation.
107+
/// This is a slow-ish operation, so consider calling this late
108+
/// to avoid slowing down the lint in the happy path when not emitting a warning
109+
fn is_macro_argument(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
110+
if let Some(parent) = get_parent_expr(cx, expr) {
111+
parent.span.from_expansion() || is_macro_argument(cx, parent)
112+
} else {
113+
false
114+
}
115+
}
116+
105117
#[expect(clippy::too_many_lines)]
106118
impl<'tcx> LateLintPass<'tcx> for UselessConversion {
107119
fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
@@ -156,7 +168,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
156168
&& let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
157169
// make sure that the path indeed points to a fn-like item, so that
158170
// `fn_sig` does not ICE. (see #11065)
159-
&& cx.tcx.opt_def_kind(did).is_some_and(|k| k.is_fn_like()) =>
171+
&& cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
160172
{
161173
Some((did, args, MethodOrFunction::Function))
162174
}
@@ -174,6 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
174186
&& let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
175187
&& let ty::Param(param) = into_iter_param.kind()
176188
&& let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
189+
&& !is_macro_argument(cx, e)
177190
{
178191
// Get the "innermost" `.into_iter()` call, e.g. given this expression:
179192
// `foo.into_iter().into_iter()`

tests/ui/crashes/ice-11065.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#![warn(clippy::useless_conversion)]
2+
3+
use std::iter::FromIterator;
4+
use std::option::IntoIter as OptionIter;
5+
6+
fn eq<T: Eq>(a: T, b: T) -> bool {
7+
a == b
8+
}
9+
10+
macro_rules! tests {
11+
($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
12+
const C: $ty = $expr;
13+
assert!(eq(C($($test),*), $expr($($test),*)));
14+
})+})
15+
}
16+
17+
tests! {
18+
FromIterator::from_iter, fn(OptionIter<i32>) -> Vec<i32>, (Some(5).into_iter());
19+
}

0 commit comments

Comments
 (0)