about summary refs log tree commit diff
diff options
context:
space:
mode:
authory21 <30553356+y21@users.noreply.github.com>2023-07-03 01:19:09 +0200
committery21 <30553356+y21@users.noreply.github.com>2023-08-14 16:28:04 +0200
commit2820d980cb3495768babdaeeabd8aa44c3f1aaa8 (patch)
treea0cc2b0bf64b4e545053c1885159cadbdb8e6989
parent34348f72f40b070a1a5fe5eac829e41cbd5f98e1 (diff)
downloadrust-2820d980cb3495768babdaeeabd8aa44c3f1aaa8.tar.gz
rust-2820d980cb3495768babdaeeabd8aa44c3f1aaa8.zip
[`useless_conversion`]: fix FP in macro and add test
-rw-r--r--clippy_lints/src/useless_conversion.rs15
-rw-r--r--tests/ui/crashes/ice-11065.rs19
2 files changed, 33 insertions, 1 deletions
diff --git a/clippy_lints/src/useless_conversion.rs b/clippy_lints/src/useless_conversion.rs
index 3be4fb7cc32..ec8af2b8362 100644
--- a/clippy_lints/src/useless_conversion.rs
+++ b/clippy_lints/src/useless_conversion.rs
@@ -5,6 +5,7 @@ use clippy_utils::ty::{is_copy, is_type_diagnostic_item, same_type_and_consts};
 use clippy_utils::{get_parent_expr, is_trait_method, is_ty_alias, match_def_path, path_to_local, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{BindingAnnotation, Expr, ExprKind, HirId, MatchSource, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -101,6 +102,17 @@ fn into_iter_deep_call<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -
     (expr, depth)
 }
 
+/// Checks if the given `expr` is an argument of a macro invocation.
+/// This is a slow-ish operation, so consider calling this late
+/// to avoid slowing down the lint in the happy path when not emitting a warning
+fn is_macro_argument(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    if let Some(parent) = get_parent_expr(cx, expr) {
+        parent.span.from_expansion() || is_macro_argument(cx, parent)
+    } else {
+        false
+    }
+}
+
 #[expect(clippy::too_many_lines)]
 impl<'tcx> LateLintPass<'tcx> for UselessConversion {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
@@ -155,7 +167,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                                     && let Some(did) = cx.qpath_res(qpath, recv.hir_id).opt_def_id()
                                     // make sure that the path indeed points to a fn-like item, so that
                                     // `fn_sig` does not ICE. (see #11065)
-                                    && cx.tcx.opt_def_kind(did).is_some_and(|k| k.is_fn_like()) =>
+                                    && cx.tcx.opt_def_kind(did).is_some_and(DefKind::is_fn_like) =>
                             {
                                     Some((did, args, MethodOrFunction::Function))
                             }
@@ -173,6 +185,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             && let Some(&into_iter_param) = sig.inputs().get(kind.param_pos(arg_pos))
                             && let ty::Param(param) = into_iter_param.kind()
                             && let Some(span) = into_iter_bound(cx, parent_fn_did, into_iter_did, param.index)
+                            && !is_macro_argument(cx, e)
                         {
                             // Get the "innermost" `.into_iter()` call, e.g. given this expression:
                             // `foo.into_iter().into_iter()`
diff --git a/tests/ui/crashes/ice-11065.rs b/tests/ui/crashes/ice-11065.rs
new file mode 100644
index 00000000000..f5cf6b1cd77
--- /dev/null
+++ b/tests/ui/crashes/ice-11065.rs
@@ -0,0 +1,19 @@
+#![warn(clippy::useless_conversion)]
+
+use std::iter::FromIterator;
+use std::option::IntoIter as OptionIter;
+
+fn eq<T: Eq>(a: T, b: T) -> bool {
+    a == b
+}
+
+macro_rules! tests {
+    ($($expr:expr, $ty:ty, ($($test:expr),*);)+) => (pub fn main() {$({
+        const C: $ty = $expr;
+        assert!(eq(C($($test),*), $expr($($test),*)));
+    })+})
+}
+
+tests! {
+    FromIterator::from_iter, fn(OptionIter<i32>) -> Vec<i32>, (Some(5).into_iter());
+}