-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Add iterator reduction coverage to never_loop #16222
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Add iterator reduction coverage to never_loop #16222
Conversation
clippy_lints/src/loops/mod.rs
Outdated
| 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); | ||
| } |
There was a problem hiding this comment.
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
}
}There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
There was a problem hiding this comment.
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.
clippy_lints/src/loops/never_loop.rs
Outdated
| expr.span, | ||
| "this iterator reduction never loops (closure always diverges)", | ||
| |diag| { | ||
| let mut app = Applicability::Unspecified; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should be WithPlaceholders
clippy_lints/src/loops/never_loop.rs
Outdated
| "this iterator reduction never loops (closure always diverges)", | ||
| |diag| { | ||
| let mut app = Applicability::Unspecified; | ||
| let recv_snip = make_iterator_snippet(cx, recv, &mut app); |
There was a problem hiding this comment.
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.
clippy_lints/src/loops/mod.rs
Outdated
| 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() |
There was a problem hiding this comment.
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).
|
Reminder, once the PR becomes ready for a review, use |
|
I pushed an update to address all 4 comments
@rustbot ready |
|
Addressed the requested changes:
@rustbot ready |
| // 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; | ||
| }; |
There was a problem hiding this comment.
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.
| unused_enumerate_index::check_method(cx, expr, recv, arg); | ||
| if is_iterator_method { | ||
| never_loop::check_iterator_reduction(cx, expr, recv, args); | ||
| } |
There was a problem hiding this comment.
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.
| let is_iterator_method = cx | ||
| .ty_based_def(expr) | ||
| .assoc_fn_parent(cx) | ||
| .is_diag_item(cx, sym::Iterator); |
There was a problem hiding this comment.
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.
Fixes #16061.
Extend
never_loopto 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 testnever_loop_iterator_reductionto cover these cases.Testing:
changelog: [
never_loop]: lint diverging iterator reduction closures likefor_eachandfold