diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs index 62a192b7f770..77f0abc78026 100644 --- a/clippy_lints/src/methods/needless_collect.rs +++ b/clippy_lints/src/methods/needless_collect.rs @@ -35,6 +35,10 @@ pub(super) fn check<'tcx>( return; // don't lint if the iterator has side effects } + if collect_turbofish_is_fully_concrete(collect_expr) { + return; // don't lint if turbofish on collect maybe the only thing anchoring the type + } + match cx.tcx.parent_hir_node(collect_expr.hir_id) { Node::Expr(parent) => { check_collect_into_intoiterator(cx, parent, collect_expr, call_span, iter_expr); @@ -230,6 +234,41 @@ fn check_collect_into_intoiterator<'tcx>( } } +/// Returns `true` if `collect_expr`'s turbofish is fully concrete (has +/// generic arguments and none of them are inference placeholders) +fn collect_turbofish_is_fully_concrete(collect_expr: &Expr<'_>) -> bool { + if let ExprKind::MethodCall(segment, ..) = collect_expr.kind + && let Some(args) = segment.args + && let [a] = args.args + { + generic_arg_is_fully_concrete(a) + } else { + false + } +} + +fn generic_arg_is_fully_concrete(arg: &rustc_hir::GenericArg<'_>) -> bool { + match arg { + rustc_hir::GenericArg::Infer(_) => false, + rustc_hir::GenericArg::Type(ty) => ty_is_fully_concrete(ty.as_unambig_ty()), + rustc_hir::GenericArg::Const(ct) => !matches!(ct.as_unambig_ct().kind, rustc_hir::ConstArgKind::Infer(..)), + rustc_hir::GenericArg::Lifetime(_) => true, + } +} + +fn ty_is_fully_concrete(ty: &rustc_hir::Ty<'_>) -> bool { + match &ty.kind { + rustc_hir::TyKind::Infer(..) => false, + rustc_hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)) => path.segments.iter().all(|seg| { + seg.args + .is_none_or(|a| a.args.iter().all(generic_arg_is_fully_concrete)) + }), + rustc_hir::TyKind::Ref(_, mut_ty) => ty_is_fully_concrete(mut_ty.ty), + rustc_hir::TyKind::Slice(ty) | rustc_hir::TyKind::Array(ty, _) => ty_is_fully_concrete(ty), + rustc_hir::TyKind::Tup(tys) => tys.iter().all(ty_is_fully_concrete), + _ => true, + } +} /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool` fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool { cx.typeck_results().type_dependent_def_id(call_id).is_some_and(|id| { diff --git a/tests/ui/needless_collect.fixed b/tests/ui/needless_collect.fixed index ca25395e375b..a7df03ff3ef1 100644 --- a/tests/ui/needless_collect.fixed +++ b/tests/ui/needless_collect.fixed @@ -198,10 +198,6 @@ mod issue8055_regression { } } - #[expect( - clippy::needless_collect, - reason = "FIXME: proposed fix cannot determine type, see issue #17315" - )] fn foo() { Foo { inner: [].iter(), diff --git a/tests/ui/needless_collect.rs b/tests/ui/needless_collect.rs index 4fafa2e9adf6..b46c41068b2b 100644 --- a/tests/ui/needless_collect.rs +++ b/tests/ui/needless_collect.rs @@ -198,10 +198,6 @@ mod issue8055_regression { } } - #[expect( - clippy::needless_collect, - reason = "FIXME: proposed fix cannot determine type, see issue #17315" - )] fn foo() { Foo { inner: [].iter(), diff --git a/tests/ui/needless_collect.stderr b/tests/ui/needless_collect.stderr index 402a1fd19a7c..caaaee770545 100644 --- a/tests/ui/needless_collect.stderr +++ b/tests/ui/needless_collect.stderr @@ -122,7 +122,7 @@ LL | baz((0..10), (), ('a'..='z').collect::>()) | ^^^^^^^^^^^^^^^^^^^^ help: remove this call error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:224:26 + --> tests/ui/needless_collect.rs:220:26 | LL | let mut v = iter.collect::>(); | ^^^^^^^ @@ -139,7 +139,7 @@ LL ~ iter.chain([1]).map(|x| x + 1).collect() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:236:26 + --> tests/ui/needless_collect.rs:232:26 | LL | let mut v = iter.collect::>(); | ^^^^^^^ @@ -157,7 +157,7 @@ LL ~ iter.chain([1, 2]).map(|x| x + 1).collect() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:244:26 + --> tests/ui/needless_collect.rs:240:26 | LL | let mut v = iter.collect::>(); | ^^^^^^^ @@ -174,7 +174,7 @@ LL ~ iter.chain([1]).map(|x| x + 1).collect() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:264:27 + --> tests/ui/needless_collect.rs:260:27 | LL | let mut ll = iter.collect::>(); | ^^^^^^^ @@ -191,7 +191,7 @@ LL ~ iter.chain(s).map(|x| x + 1).collect() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:271:26 + --> tests/ui/needless_collect.rs:267:26 | LL | let mut v = iter.collect::>(); | ^^^^^^^ @@ -209,7 +209,7 @@ LL ~ [1, 2].into_iter().chain(iter).map(|x| x + 1).collect() | error: avoid using `collect()` when not needed - --> tests/ui/needless_collect.rs:282:26 + --> tests/ui/needless_collect.rs:278:26 | LL | let mut v = iter.collect::>(); | ^^^^^^^