add manual_repeat_n lint

This commit is contained in:
lapla-cogito 2024-12-20 22:29:06 +09:00
parent 25d319d1b2
commit 544f71f48d
No known key found for this signature in database
GPG key ID: B39C71D9F127FF9F
15 changed files with 188 additions and 19 deletions

View file

@ -420,6 +420,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
crate::methods::MANUAL_NEXT_BACK_INFO,
crate::methods::MANUAL_OK_OR_INFO,
crate::methods::MANUAL_REPEAT_N_INFO,
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
crate::methods::MANUAL_SPLIT_ONCE_INFO,
crate::methods::MANUAL_STR_REPEAT_INFO,

View file

@ -0,0 +1,43 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
use clippy_utils::msrvs::{self, Msrv};
use clippy_utils::source::{snippet, snippet_with_context};
use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core};
use rustc_errors::Applicability;
use rustc_hir::{Expr, ExprKind};
use rustc_lint::LateContext;
use rustc_span::sym;
use super::MANUAL_REPEAT_N;
pub(super) fn check<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'tcx>,
repeat_expr: &Expr<'_>,
take_arg: &Expr<'_>,
msrv: &Msrv,
) {
if msrv.meets(msrvs::REPEAT_N)
&& !expr.span.from_expansion()
&& is_trait_method(cx, expr, sym::Iterator)
&& let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind
&& let Some(def_id) = fn_def_id(cx, repeat_expr)
&& cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)
&& !expr_use_ctxt(cx, expr).is_ty_unified
&& let Some(std_or_core) = std_or_core(cx)
{
let mut app = Applicability::MachineApplicable;
span_lint_and_sugg(
cx,
MANUAL_REPEAT_N,
expr.span,
"this `repeat().take()` can be written more concisely",
"consider using `repeat_n()` instead",
format!(
"{std_or_core}::iter::repeat_n({}, {})",
snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0,
snippet(cx, take_arg.span, "..")
),
app,
);
}
}

View file

@ -58,6 +58,7 @@ mod manual_inspect;
mod manual_is_variant_and;
mod manual_next_back;
mod manual_ok_or;
mod manual_repeat_n;
mod manual_saturating_arithmetic;
mod manual_str_repeat;
mod manual_try_fold;
@ -4339,6 +4340,29 @@ declare_clippy_lint! {
"using `NonZero::new_unchecked()` in a `const` context"
}
declare_clippy_lint! {
/// ### What it does
///
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
///
/// ### Why is this bad?
///
/// Using `repeat_n()` is more concise and clearer. Also, `repeat_n()` is sometimes faster than `repeat().take()` when the type of the element is non-trivial to clone because the original value can be reused for the last `.next()` call rather than always cloning.
///
/// ### Example
/// ```no_run
/// let _ = std::iter::repeat(10).take(3);
/// ```
/// Use instead:
/// ```no_run
/// let _ = std::iter::repeat_n(10, 3);
/// ```
#[clippy::version = "1.86.0"]
pub MANUAL_REPEAT_N,
style,
"detect `repeat().take()` that can be replaced with `repeat_n()`"
}
pub struct Methods {
avoid_breaking_exported_api: bool,
msrv: Msrv,
@ -4506,6 +4530,7 @@ impl_lint_pass!(Methods => [
UNNECESSARY_MAP_OR,
DOUBLE_ENDED_ITERATOR_LAST,
USELESS_NONZERO_NEW_UNCHECKED,
MANUAL_REPEAT_N,
]);
/// Extracts a method call name, args, and `Span` of the method name.
@ -5176,6 +5201,7 @@ impl Methods {
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
("take", [arg]) => {
iter_out_of_bounds::check_take(cx, expr, recv, arg);
manual_repeat_n::check(cx, expr, recv, arg, &self.msrv);
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
iter_overeager_cloned::check(
cx,