Skip to content

Commit 33c9b51

Browse files
committed
double_ended_iterator_last: note when drop order is changed
`iter.last()` will drop all elements of `iter` in order, while `iter.next_back()` will drop the non-last elements of `iter` when `iter` goes out of scope since `.next_back()` does not consume its argument. When the transformation proposed by `double_ended_iterator_last` would concern an iterator whose element type has a significant drop, a note is added to warn about the possible drop order change, and the suggestion is switched from `MachineApplicable` to `MaybeIncorrect`.
1 parent 19fd79d commit 33c9b51

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

clippy_lints/src/methods/double_ended_iterator_last.rs

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,20 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
4747
expr.span,
4848
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
4949
|diag| {
50-
diag.multipart_suggestion("try", sugg, Applicability::MachineApplicable);
50+
let expr_ty = cx.typeck_results().expr_ty(expr);
51+
let droppable_elements = expr_ty.has_significant_drop(cx.tcx, cx.typing_env());
52+
diag.multipart_suggestion(
53+
"try",
54+
sugg,
55+
if droppable_elements {
56+
Applicability::MaybeIncorrect
57+
} else {
58+
Applicability::MachineApplicable
59+
},
60+
);
61+
if droppable_elements {
62+
diag.note("this might cause unretrieved elements to be dropped after the retrieved one");
63+
}
5164
},
5265
);
5366
}

tests/ui/double_ended_iterator_last.fixed

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,18 @@ fn issue_14139() {
7979
let (mut subindex, _) = (index.by_ref().take(3), 42);
8080
let _ = subindex.next_back(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
8181
}
82+
83+
fn drop_order() {
84+
struct S(&'static str);
85+
impl std::ops::Drop for S {
86+
fn drop(&mut self) {
87+
println!("Dropping {}", self.0);
88+
}
89+
}
90+
91+
let v = vec![S("one"), S("two"), S("three")];
92+
let mut v = v.into_iter();
93+
println!("Last element is {}", v.next_back().unwrap().0);
94+
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
95+
println!("Done");
96+
}

tests/ui/double_ended_iterator_last.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,3 +79,18 @@ fn issue_14139() {
7979
let (subindex, _) = (index.by_ref().take(3), 42);
8080
let _ = subindex.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
8181
}
82+
83+
fn drop_order() {
84+
struct S(&'static str);
85+
impl std::ops::Drop for S {
86+
fn drop(&mut self) {
87+
println!("Dropping {}", self.0);
88+
}
89+
}
90+
91+
let v = vec![S("one"), S("two"), S("three")];
92+
let v = v.into_iter();
93+
println!("Last element is {}", v.last().unwrap().0);
94+
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
95+
println!("Done");
96+
}

tests/ui/double_ended_iterator_last.stderr

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,18 @@ LL ~ let (mut subindex, _) = (index.by_ref().take(3), 42);
6565
LL ~ let _ = subindex.next_back();
6666
|
6767

68-
error: aborting due to 7 previous errors
68+
error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
69+
--> tests/ui/double_ended_iterator_last.rs:93:36
70+
|
71+
LL | println!("Last element is {}", v.last().unwrap().0);
72+
| ^^^^^^^^
73+
|
74+
= note: this might cause unretrieved elements to be dropped after the retrieved one
75+
help: try
76+
|
77+
LL ~ let mut v = v.into_iter();
78+
LL ~ println!("Last element is {}", v.next_back().unwrap().0);
79+
|
80+
81+
error: aborting due to 8 previous errors
6982

0 commit comments

Comments
 (0)