Skip to content

Commit 1c8b099

Browse files
committed
Fix suggestions in while_let_on_iterator for non-sized traits
1 parent 03ab7b8 commit 1c8b099

File tree

4 files changed

+101
-8
lines changed

4 files changed

+101
-8
lines changed

clippy_lints/src/loops/while_let_on_iterator.rs

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr};
1212
use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp};
1313
use rustc_lint::LateContext;
1414
use rustc_middle::hir::nested_filter::OnlyBodies;
15+
use rustc_middle::ty;
1516
use rustc_middle::ty::adjustment::Adjust;
1617
use rustc_span::Symbol;
1718
use rustc_span::symbol::sym;
@@ -43,26 +44,26 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
4344
};
4445

4546
// If the iterator is a field or the iterator is accessed after the loop is complete it needs to be
46-
// borrowed mutably. TODO: If the struct can be partially moved from and the struct isn't used
47+
// passed by reference. TODO: If the struct can be partially moved from and the struct isn't used
4748
// afterwards a mutable borrow of a field isn't necessary.
48-
let by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
49+
let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
50+
let iterator_by_ref = if cx.typeck_results().expr_ty(iter_expr).ref_mutability() == Some(Mutability::Mut)
4951
|| !iter_expr_struct.can_move
5052
|| !iter_expr_struct.fields.is_empty()
5153
|| needs_mutable_borrow(cx, &iter_expr_struct, expr)
5254
{
53-
".by_ref()"
55+
make_iterator_snippet(cx, iter_expr, iterator)
5456
} else {
55-
""
57+
iterator.to_string()
5658
};
5759

58-
let iterator = snippet_with_applicability(cx, iter_expr.span, "_", &mut applicability);
5960
span_lint_and_sugg(
6061
cx,
6162
WHILE_LET_ON_ITERATOR,
6263
expr.span.with_hi(let_expr.span.hi()),
6364
"this loop could be written as a `for` loop",
6465
"try",
65-
format!("{loop_label}for {loop_var} in {iterator}{by_ref}"),
66+
format!("{loop_label}for {loop_var} in {iterator_by_ref}"),
6667
applicability,
6768
);
6869
}
@@ -355,3 +356,21 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
355356
.is_break()
356357
}
357358
}
359+
360+
/// Constructs the transformed iterator expression for the suggestion.
361+
/// Returns `iterator.by_ref()` unless the iterator type is a reference to an unsized type,
362+
/// in which case it returns `&mut *iterator`.
363+
fn make_iterator_snippet<'tcx>(cx: &LateContext<'tcx>, iter_expr: &Expr<'tcx>, iterator: impl Into<String>) -> String {
364+
let iterator = iterator.into();
365+
let ty = cx.typeck_results().expr_ty(iter_expr);
366+
367+
// Check if the type is a reference
368+
if let ty::Ref(_, inner_ty, _) = ty.kind()
369+
&& !inner_ty.is_sized(cx.tcx, cx.typing_env())
370+
{
371+
return format!("&mut *{iterator}");
372+
}
373+
374+
// Default: use by_ref by appending to the iterator
375+
format!("{iterator}.by_ref()")
376+
}

tests/ui/while_let_on_iterator.fixed

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,37 @@ fn issue13123() {
492492
}
493493
}
494494

495+
fn issue16089() {
496+
trait CertainTrait: Iterator<Item = u8> {
497+
fn iter_over_self(&mut self) {
498+
let mut a = 0;
499+
for r in &mut *self {
500+
//~^ while_let_on_iterator
501+
a = r;
502+
}
503+
self.use_after_iter()
504+
}
505+
506+
fn use_after_iter(&mut self) {}
507+
}
508+
}
509+
510+
fn issue16089_sized_trait_not_reborrowed() {
511+
trait CertainTrait: Iterator<Item = u8> + Sized {
512+
fn iter_over_self(&mut self) {
513+
let mut a = 0;
514+
// Check that the suggestion is just "self", since the trait is sized.
515+
for r in self.by_ref() {
516+
//~^ while_let_on_iterator
517+
a = r;
518+
}
519+
self.use_after_iter()
520+
}
521+
522+
fn use_after_iter(&mut self) {}
523+
}
524+
}
525+
495526
fn main() {
496527
let mut it = 0..20;
497528
for _ in it {

tests/ui/while_let_on_iterator.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,37 @@ fn issue13123() {
492492
}
493493
}
494494

495+
fn issue16089() {
496+
trait CertainTrait: Iterator<Item = u8> {
497+
fn iter_over_self(&mut self) {
498+
let mut a = 0;
499+
while let Some(r) = self.next() {
500+
//~^ while_let_on_iterator
501+
a = r;
502+
}
503+
self.use_after_iter()
504+
}
505+
506+
fn use_after_iter(&mut self) {}
507+
}
508+
}
509+
510+
fn issue16089_sized_trait_not_reborrowed() {
511+
trait CertainTrait: Iterator<Item = u8> + Sized {
512+
fn iter_over_self(&mut self) {
513+
let mut a = 0;
514+
// Check that the suggestion is just "self", since the trait is sized.
515+
while let Some(r) = self.next() {
516+
//~^ while_let_on_iterator
517+
a = r;
518+
}
519+
self.use_after_iter()
520+
}
521+
522+
fn use_after_iter(&mut self) {}
523+
}
524+
}
525+
495526
fn main() {
496527
let mut it = 0..20;
497528
while let Some(..) = it.next() {

tests/ui/while_let_on_iterator.stderr

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,10 +164,22 @@ LL | 'label: while let Some(n) = it.next() {
164164
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it`
165165

166166
error: this loop could be written as a `for` loop
167-
--> tests/ui/while_let_on_iterator.rs:497:5
167+
--> tests/ui/while_let_on_iterator.rs:499:13
168+
|
169+
LL | while let Some(r) = self.next() {
170+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in &mut *self`
171+
172+
error: this loop could be written as a `for` loop
173+
--> tests/ui/while_let_on_iterator.rs:515:13
174+
|
175+
LL | while let Some(r) = self.next() {
176+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for r in self.by_ref()`
177+
178+
error: this loop could be written as a `for` loop
179+
--> tests/ui/while_let_on_iterator.rs:528:5
168180
|
169181
LL | while let Some(..) = it.next() {
170182
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
171183

172-
error: aborting due to 28 previous errors
184+
error: aborting due to 30 previous errors
173185

0 commit comments

Comments
 (0)