Skip to content
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

add a new lint for repeat().take() that can be replaced with repeat_n() #13858

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

lapla-cogito
Copy link
Contributor

close #13271

changelog: [redundant_repeat_take]: new lint

@rustbot
Copy link
Collaborator

rustbot commented Dec 20, 2024

r? @dswij

rustbot has assigned @dswij.
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

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Dec 20, 2024
@lapla-cogito lapla-cogito force-pushed the repeatn branch 2 times, most recently from 55203fc to 33a5b21 Compare December 20, 2024 13:45
@@ -15,6 +15,7 @@
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::must_use_candidate,
clippy::redundant_repeat_take,
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I’m unsure whether I should add an allow rule like this, or if I should fix the part of the code that triggers the linting error, since currently there is only one part that is affected by this lint...

Copy link
Member

Choose a reason for hiding this comment

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

This lint looks useful to have to me so I think keeping it enabled is a good idea. The case also looks like it should be fairly easy to fix

///
/// ### Why is this bad?
///
/// Using `repeat_n()` is more concise and clearer.
Copy link
Member

Choose a reason for hiding this comment

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

May also be worth pointing out that it can be faster for types that are non-trivial to clone as it knows the repeat count and only needs to clone n-1 times

@@ -15,6 +15,7 @@
clippy::missing_errors_doc,
clippy::missing_panics_doc,
clippy::must_use_candidate,
clippy::redundant_repeat_take,
Copy link
Member

Choose a reason for hiding this comment

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

This lint looks useful to have to me so I think keeping it enabled is a good idea. The case also looks like it should be fairly easy to fix

&& let QPath::Resolved(_, path) = qpath
&& path.segments.len() == 1
&& path.segments[0].ident.name.as_str() == "repeat"
{
Copy link
Member

Choose a reason for hiding this comment

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

This should have an MSRV check as repeat_n was only very recently stabilized

Comment on lines 11 to 12
&& let ExprKind::Call(inner, repeat_args) = repeat_expr.kind
&& repeat_args.len() == 1
Copy link
Member

Choose a reason for hiding this comment

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

nit:

Suggested change
&& let ExprKind::Call(inner, repeat_args) = repeat_expr.kind
&& repeat_args.len() == 1
&& let ExprKind::Call(inner, [repeat_arg]) = repeat_expr.kind

Comment on lines 13 to 16
&& let ExprKind::Path(ref qpath) = inner.kind
&& let QPath::Resolved(_, path) = qpath
&& path.segments.len() == 1
&& path.segments[0].ident.name.as_str() == "repeat"
Copy link
Member

Choose a reason for hiding this comment

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

iter::repeat is a diagnostic item, so you should be able to match against its DefId rather than looking for exact paths. Something like

   && let Some(def_id) = fn_def_id(inner)
   && cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)

Copy link
Member

Choose a reason for hiding this comment

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

There should also be a check that the .take() call is the take from the Iterator trait and not from a user defined trait, with is_trait_method(cx, expr, sym::Iterator)

@@ -1,4 +1,4 @@
#![allow(non_local_definitions)]
#![allow(non_local_definitions, redundant_repeat_take)]
Copy link
Member

Choose a reason for hiding this comment

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

Should be clippy::redundant_repeat_take. The test file also has errors because of that

clippy_lints/src/methods/mod.rs Outdated Show resolved Hide resolved
Comment on lines 26 to 27
snippet(cx, repeat_args[0].span, ""),
snippet(cx, take_arg.span, "")
Copy link
Member

Choose a reason for hiding this comment

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

Would be good to have a test case where the element is a macro call, e.g. repeat(vec![1, 2]).take(2). I suspect this currently "doesn't work" (has macro expansion in the suggestion), so this may need to use snippet_with_context (with expr.span.ctxt() for the ctxt), but I have not tested it

snippet(cx, repeat_args[0].span, ""),
snippet(cx, take_arg.span, "")
),
Applicability::MachineApplicable,
Copy link
Member

@y21 y21 Dec 20, 2024

Choose a reason for hiding this comment

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

FWIW, this changes the expression's type so it can lead to compile errors in some cases where its type is unified with other branches, e.g.:

// From an external library the user doesn't get to control
mod external_lib {
  pub fn iter() -> std::iter::Take<std::iter::Repeat<&'static [u8]>> { todo!() }
}

let iter = match 1 {
  1 => external_lib::iter(),
  2 => std::iter::repeat([1,2].as_slice()).take(2) // can't really change this to repeat_n
};

so in those situations MachineApplicable could lead to issues. There have been a few false positive reports of the like in the past (#11761 comes to mind).

Adding !expr_use_ctxt(cx, expr).is_ty_unified to the chain could help avoid this I think. (edited, was thinking of the wrong util function before)

@lapla-cogito lapla-cogito force-pushed the repeatn branch 2 times, most recently from f3ac7b1 to 8e422ea Compare December 20, 2024 15:52
@lapla-cogito lapla-cogito changed the title add a lint for redundant repeat().take() add a new lint for repeat().take() that can be replaced with repeat_n() Dec 20, 2024
@lapla-cogito
Copy link
Contributor Author

@y21 Thank you for your thorough review! Looks like I was able to fix the reviewed items. Could you please take a look again?

Copy link
Contributor Author

@lapla-cogito lapla-cogito left a comment

Choose a reason for hiding this comment

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

I noticed this lint should suggest to use core::iter::repeat_n in a no_std environment. Therefore, I'll make a change.

@lapla-cogito lapla-cogito requested a review from y21 December 20, 2024 17:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

iter::repeat(copy_value).take(n) => repeat_n(copy_value, n)
4 participants