Skip to content

Conversation

@cuongleqq
Copy link

@cuongleqq cuongleqq commented Dec 12, 2025

Fixes #16061.

Extend never_loop to also lint iterator reduction methods (e.g. for_each, try_for_each, fold, try_fold, reduce, all, any) when the closure’s body diverges (return type !). Add a UI test never_loop_iterator_reduction to cover these cases.

Testing:

  • TESTNAME=never_loop_iterator_reduction cargo uitest

changelog: [never_loop]: lint diverging iterator reduction closures like for_each and fold

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 12, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 12, 2025

r? @Jarcho

rustbot has assigned @Jarcho.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Comment on lines 893 to 901
if let ExprKind::MethodCall(path, recv, args, _) = expr.kind
&& matches!(
path.ident.name,
sym::for_each | sym::try_for_each | sym::fold | sym::try_fold | sym::reduce | sym::all | sym::any
)
&& ty::get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)).is_some()
{
never_loop::check_iterator_reduction(cx, expr, recv, args);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be combined with the previous method check as something like

if let ExprKind::MethodCall(path, recv, args, _) = expr.kind {
  match (path.ident.name, args) {
    // methods here
  }
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a single MethodCall destructure as suggested, but I kept 2 if branches instead of a single match because some methods like for_each, all, any are handled by both lints. Match will short-circuit to only the first matching arm.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Still make it a single match. A match arm can do multiple things.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok! I switched it to a single match as suggested. For the overlapping methods, the match arm now calls both unused_enumerate_index and never_loop. Other arms handle the single-lint cases.

expr.span,
"this iterator reduction never loops (closure always diverges)",
|diag| {
let mut app = Applicability::Unspecified;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be WithPlaceholders

"this iterator reduction never loops (closure always diverges)",
|diag| {
let mut app = Applicability::Unspecified;
let recv_snip = make_iterator_snippet(cx, recv, &mut app);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just be snippet_with_context. We already know the receiver is an iterator.

path.ident.name,
sym::for_each | sym::try_for_each | sym::fold | sym::try_fold | sym::reduce | sym::all | sym::any
)
&& ty::get_iterator_item_ty(cx, cx.typeck_results().expr_ty(recv)).is_some()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be recv.ty_based_def(cx).assoc_fn_parent(cx).is_diag_item(cx, sym::Iterator).

@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 13, 2025
@rustbot
Copy link
Collaborator

rustbot commented Dec 13, 2025

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@rustbot rustbot added the S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) label Dec 13, 2025
@cuongleqq
Copy link
Author

I pushed an update to address all 4 comments

  • Combined the method check for both unused_enumerate_index and never_loop
  • Use a more direct check for Iterator trait
  • Use HasPlaceholders for the applicability
  • Use snippet_with_context instead of make_iterator_snippet

@rustbot ready

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties and removed S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) labels Dec 13, 2025
@cuongleqq
Copy link
Author

Addressed the requested changes:

  • Switched to a single match for the MethodCall handling

@rustbot ready

Comment on lines +82 to +98
// identify which argument is the closure based on the method kind
let Some(method_name) = (match expr.kind {
ExprKind::MethodCall(path, ..) => Some(path.ident.name),
_ => None,
}) else {
return;
};

let closure_arg = match method_name {
sym::for_each | sym::try_for_each | sym::reduce | sym::all | sym::any => args.first(),
sym::fold | sym::try_fold => args.get(1),
_ => None,
};

let Some(closure_arg) = closure_arg else {
return;
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be splitting this before getting to this function.

Comment on lines +894 to +897
unused_enumerate_index::check_method(cx, expr, recv, arg);
if is_iterator_method {
never_loop::check_iterator_reduction(cx, expr, recv, args);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you pull out the checks shared between the two lints to happen here. Both the check for ExprKind::Closure and the iterator check are the same for both of them.

Comment on lines +887 to +890
let is_iterator_method = cx
.ty_based_def(expr)
.assoc_fn_parent(cx)
.is_diag_item(cx, sym::Iterator);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be delayed until after the name check. Type checking and item identification is notably slower than matching on a symbol.

Defining a closure would be fine.

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels Dec 14, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

never_loop: Also check all known iterator reductions.

3 participants