Skip to content

Commit 9d311b5

Browse files
initial fix
1 parent caad063 commit 9d311b5

File tree

4 files changed

+75
-2
lines changed

4 files changed

+75
-2
lines changed

clippy_lints/src/methods/iter_on_single_or_empty_collections.rs

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
1+
use std::iter::once;
2+
13
use clippy_utils::diagnostics::span_lint_and_sugg;
24
use clippy_utils::source::snippet;
35
use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core};
46

57
use rustc_errors::Applicability;
8+
use rustc_hir::def_id::DefId;
9+
use rustc_hir::hir_id::HirId;
610
use rustc_hir::LangItem::{OptionNone, OptionSome};
711
use rustc_hir::{Expr, ExprKind, Node};
812
use rustc_lint::LateContext;
@@ -25,7 +29,25 @@ impl IterType {
2529
}
2630
}
2731

28-
pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
32+
fn is_arg_ty_unified_in_fn<'tcx>(
33+
cx: &LateContext<'tcx>,
34+
fn_id: DefId,
35+
arg_id: HirId,
36+
args: impl Iterator<Item = &'tcx Expr<'tcx>> + Clone,
37+
) -> bool {
38+
let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity();
39+
let arg_id_in_args = args.clone().position(|e| e.hir_id == arg_id).unwrap();
40+
let arg_ty_in_args = fn_sig.input(arg_id_in_args);
41+
42+
cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| {
43+
clause
44+
.as_projection_clause()
45+
.and_then(|p| p.map_bound(|p| p.term.ty()).transpose())
46+
.is_some_and(|ty| ty == arg_ty_in_args)
47+
})
48+
}
49+
50+
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) {
2951
let item = match recv.kind {
3052
ExprKind::Array([]) => None,
3153
ExprKind::Array([e]) => Some(e),
@@ -43,6 +65,25 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re
4365
let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
4466
Some((Node::Expr(parent), child_id)) => match parent.kind {
4567
ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
68+
ExprKind::Call(
69+
Expr {
70+
kind: ExprKind::Path(path),
71+
hir_id,
72+
..
73+
},
74+
args,
75+
) => is_arg_ty_unified_in_fn(
76+
cx,
77+
cx.typeck_results().qpath_res(path, *hir_id).def_id(),
78+
expr.hir_id,
79+
args.iter(),
80+
),
81+
ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn(
82+
cx,
83+
cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(),
84+
expr.hir_id,
85+
once(recv).chain(args.iter()),
86+
),
4687
ExprKind::If(_, _, _)
4788
| ExprKind::Match(_, _, _)
4889
| ExprKind::Closure(_)

tests/ui/iter_on_empty_collections.fixed

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ fn array() {
2020
};
2121

2222
let _ = if false { ["test"].iter() } else { [].iter() };
23+
24+
let smth = Some(vec![1, 2, 3]);
25+
26+
// Don't trigger when the empty collection iter is relied upon for its concrete type
27+
// But do trigger if it is just an iterator, despite being an argument to a method
28+
for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain(std::iter::empty()) {
29+
println!("{i}");
30+
}
31+
32+
// Same as above, but for regular function calls
33+
for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) {
34+
println!("{i}");
35+
}
2336
}
2437

2538
macro_rules! in_macros {

tests/ui/iter_on_empty_collections.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,19 @@ fn array() {
2020
};
2121

2222
let _ = if false { ["test"].iter() } else { [].iter() };
23+
24+
let smth = Some(vec![1, 2, 3]);
25+
26+
// Don't trigger when the empty collection iter is relied upon for its concrete type
27+
// But do trigger if it is just an iterator, despite being an argument to a method
28+
for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) {
29+
println!("{i}");
30+
}
31+
32+
// Same as above, but for regular function calls
33+
for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) {
34+
println!("{i}");
35+
}
2336
}
2437

2538
macro_rules! in_macros {

tests/ui/iter_on_empty_collections.stderr

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,11 @@ error: `iter` call on an empty collection
3737
LL | assert_eq!(None.iter().next(), Option::<&i32>::None);
3838
| ^^^^^^^^^^^ help: try: `std::iter::empty()`
3939

40-
error: aborting due to 6 previous errors
40+
error: `iter` call on an empty collection
41+
--> tests/ui/iter_on_empty_collections.rs:28:66
42+
|
43+
LL | for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) {
44+
| ^^^^^^^^^ help: try: `std::iter::empty()`
45+
46+
error: aborting due to 7 previous errors
4147

0 commit comments

Comments
 (0)