Skip to content

Commit dcd643a

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 45f7a60 commit dcd643a

6 files changed

+82
-2
lines changed

clippy_lints/src/methods/double_ended_iterator_last.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,22 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
4949
expr.span,
5050
"called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator",
5151
|diag| {
52+
let expr_ty = cx.typeck_results().expr_ty(expr);
53+
let droppable_elements = expr_ty.has_significant_drop(cx.tcx, cx.typing_env());
5254
diag.multipart_suggestion(
5355
"try",
5456
sugg,
5557
if dont_apply {
5658
Applicability::Unspecified
59+
} else if droppable_elements {
60+
Applicability::MaybeIncorrect
5761
} else {
5862
Applicability::MachineApplicable
5963
},
6064
);
65+
if droppable_elements {
66+
diag.note("this change will alter drop order which may be undesirable");
67+
}
6168
if dont_apply {
6269
diag.span_note(self_expr.span, "this must be made mutable to use `.next_back()`");
6370
}

tests/ui/double_ended_iterator_last.fixed

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

tests/ui/double_ended_iterator_last.rs

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

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:89:36
70+
|
71+
LL | println!("Last element is {}", v.last().unwrap().0);
72+
| ^^^^^^^^
73+
|
74+
= note: this change will alter drop order which may be undesirable
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

tests/ui/double_ended_iterator_last_unfixable.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,18 @@ fn main() {
66
let subindex = (index.by_ref().take(3), 42);
77
let _ = subindex.0.last(); //~ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
88
}
9+
10+
fn drop_order() {
11+
struct S(&'static str);
12+
impl std::ops::Drop for S {
13+
fn drop(&mut self) {
14+
println!("Dropping {}", self.0);
15+
}
16+
}
17+
18+
let v = vec![S("one"), S("two"), S("three")];
19+
let v = (v.into_iter(), 42);
20+
println!("Last element is {}", v.0.last().unwrap().0);
21+
//~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
22+
println!("Done");
23+
}

tests/ui/double_ended_iterator_last_unfixable.stderr

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,20 @@ LL | let _ = subindex.0.last();
1414
= note: `-D clippy::double-ended-iterator-last` implied by `-D warnings`
1515
= help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]`
1616

17-
error: aborting due to 1 previous error
17+
error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
18+
--> tests/ui/double_ended_iterator_last_unfixable.rs:20:36
19+
|
20+
LL | println!("Last element is {}", v.0.last().unwrap().0);
21+
| ^^^^------
22+
| |
23+
| help: try: `next_back()`
24+
|
25+
= note: this change will alter drop order which may be undesirable
26+
note: this must be made mutable to use `.next_back()`
27+
--> tests/ui/double_ended_iterator_last_unfixable.rs:20:36
28+
|
29+
LL | println!("Last element is {}", v.0.last().unwrap().0);
30+
| ^^^
31+
32+
error: aborting due to 2 previous errors
1833

0 commit comments

Comments
 (0)