Skip to content

Commit 0707fe8

Browse files
authored
add a new lint for repeat().take() that can be replaced with repeat_n() (rust-lang#13858)
close rust-lang#13271 changelog: add new `manual_repeat_n` lint
2 parents e692cd4 + 9a1bbe9 commit 0707fe8

16 files changed

+190
-22
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5768,6 +5768,7 @@ Released 2018-09-13
57685768
[`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
57695769
[`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
57705770
[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
5771+
[`manual_repeat_n`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_repeat_n
57715772
[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
57725773
[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate
57735774
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic

clippy_lints/src/declared_lints.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
420420
crate::methods::MANUAL_IS_VARIANT_AND_INFO,
421421
crate::methods::MANUAL_NEXT_BACK_INFO,
422422
crate::methods::MANUAL_OK_OR_INFO,
423+
crate::methods::MANUAL_REPEAT_N_INFO,
423424
crate::methods::MANUAL_SATURATING_ARITHMETIC_INFO,
424425
crate::methods::MANUAL_SPLIT_ONCE_INFO,
425426
crate::methods::MANUAL_STR_REPEAT_INFO,

clippy_lints/src/doc/lazy_continuation.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ pub(super) fn check(
5757
diag.span_suggestion_verbose(
5858
span.shrink_to_hi(),
5959
"indent this line",
60-
std::iter::repeat(" ").take(indent).join(""),
60+
std::iter::repeat_n(" ", indent).join(""),
6161
Applicability::MaybeIncorrect,
6262
);
6363
diag.help("if this is supposed to be its own paragraph, add a blank line");
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
use clippy_utils::diagnostics::span_lint_and_sugg;
2+
use clippy_utils::msrvs::{self, Msrv};
3+
use clippy_utils::source::{snippet, snippet_with_context};
4+
use clippy_utils::{expr_use_ctxt, fn_def_id, is_trait_method, std_or_core};
5+
use rustc_errors::Applicability;
6+
use rustc_hir::{Expr, ExprKind};
7+
use rustc_lint::LateContext;
8+
use rustc_span::sym;
9+
10+
use super::MANUAL_REPEAT_N;
11+
12+
pub(super) fn check<'tcx>(
13+
cx: &LateContext<'tcx>,
14+
expr: &'tcx Expr<'tcx>,
15+
repeat_expr: &Expr<'_>,
16+
take_arg: &Expr<'_>,
17+
msrv: &Msrv,
18+
) {
19+
if msrv.meets(msrvs::REPEAT_N)
20+
&& !expr.span.from_expansion()
21+
&& is_trait_method(cx, expr, sym::Iterator)
22+
&& let ExprKind::Call(_, [repeat_arg]) = repeat_expr.kind
23+
&& let Some(def_id) = fn_def_id(cx, repeat_expr)
24+
&& cx.tcx.is_diagnostic_item(sym::iter_repeat, def_id)
25+
&& !expr_use_ctxt(cx, expr).is_ty_unified
26+
&& let Some(std_or_core) = std_or_core(cx)
27+
{
28+
let mut app = Applicability::MachineApplicable;
29+
span_lint_and_sugg(
30+
cx,
31+
MANUAL_REPEAT_N,
32+
expr.span,
33+
"this `repeat().take()` can be written more concisely",
34+
"consider using `repeat_n()` instead",
35+
format!(
36+
"{std_or_core}::iter::repeat_n({}, {})",
37+
snippet_with_context(cx, repeat_arg.span, expr.span.ctxt(), "..", &mut app).0,
38+
snippet(cx, take_arg.span, "..")
39+
),
40+
app,
41+
);
42+
}
43+
}

clippy_lints/src/methods/mod.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ mod manual_inspect;
5858
mod manual_is_variant_and;
5959
mod manual_next_back;
6060
mod manual_ok_or;
61+
mod manual_repeat_n;
6162
mod manual_saturating_arithmetic;
6263
mod manual_str_repeat;
6364
mod manual_try_fold;
@@ -4339,6 +4340,29 @@ declare_clippy_lint! {
43394340
"using `NonZero::new_unchecked()` in a `const` context"
43404341
}
43414342

4343+
declare_clippy_lint! {
4344+
/// ### What it does
4345+
///
4346+
/// Checks for `repeat().take()` that can be replaced with `repeat_n()`.
4347+
///
4348+
/// ### Why is this bad?
4349+
///
4350+
/// 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.
4351+
///
4352+
/// ### Example
4353+
/// ```no_run
4354+
/// let _ = std::iter::repeat(10).take(3);
4355+
/// ```
4356+
/// Use instead:
4357+
/// ```no_run
4358+
/// let _ = std::iter::repeat_n(10, 3);
4359+
/// ```
4360+
#[clippy::version = "1.86.0"]
4361+
pub MANUAL_REPEAT_N,
4362+
style,
4363+
"detect `repeat().take()` that can be replaced with `repeat_n()`"
4364+
}
4365+
43424366
pub struct Methods {
43434367
avoid_breaking_exported_api: bool,
43444368
msrv: Msrv,
@@ -4506,6 +4530,7 @@ impl_lint_pass!(Methods => [
45064530
UNNECESSARY_MAP_OR,
45074531
DOUBLE_ENDED_ITERATOR_LAST,
45084532
USELESS_NONZERO_NEW_UNCHECKED,
4533+
MANUAL_REPEAT_N,
45094534
]);
45104535

45114536
/// Extracts a method call name, args, and `Span` of the method name.
@@ -5176,6 +5201,7 @@ impl Methods {
51765201
("step_by", [arg]) => iterator_step_by_zero::check(cx, expr, arg),
51775202
("take", [arg]) => {
51785203
iter_out_of_bounds::check_take(cx, expr, recv, arg);
5204+
manual_repeat_n::check(cx, expr, recv, arg, &self.msrv);
51795205
if let Some(("cloned", recv2, [], _span2, _)) = method_call(recv) {
51805206
iter_overeager_cloned::check(
51815207
cx,

clippy_utils/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ use core::mem;
8888
use core::ops::ControlFlow;
8989
use std::collections::hash_map::Entry;
9090
use std::hash::BuildHasherDefault;
91-
use std::iter::{once, repeat};
91+
use std::iter::{once, repeat_n};
9292
use std::sync::{Mutex, MutexGuard, OnceLock};
9393

9494
use itertools::Itertools;
@@ -3420,7 +3420,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
34203420
}))
34213421
.join("::")
34223422
} else {
3423-
repeat(String::from("super")).take(go_up_by).chain(path).join("::")
3423+
repeat_n(String::from("super"), go_up_by).chain(path).join("::")
34243424
}
34253425
}
34263426

tests/ui/from_iter_instead_of_collect.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![warn(clippy::from_iter_instead_of_collect)]
22
#![allow(unused_imports)]
3-
#![allow(clippy::useless_vec)]
3+
#![allow(clippy::useless_vec, clippy::manual_repeat_n)]
44

55
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
66

tests/ui/from_iter_instead_of_collect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![warn(clippy::from_iter_instead_of_collect)]
22
#![allow(unused_imports)]
3-
#![allow(clippy::useless_vec)]
3+
#![allow(clippy::useless_vec, clippy::manual_repeat_n)]
44

55
use std::collections::{BTreeMap, BTreeSet, HashMap, VecDeque};
66

tests/ui/manual_repeat_n.fixed

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![warn(clippy::manual_repeat_n)]
2+
3+
use std::iter::repeat;
4+
5+
fn main() {
6+
let _ = std::iter::repeat_n(10, 3);
7+
8+
let _ = std::iter::repeat_n(String::from("foo"), 4);
9+
10+
for value in std::iter::repeat_n(5, 3) {}
11+
12+
let _: Vec<_> = std::iter::repeat_n(String::from("bar"), 10).collect();
13+
14+
let _ = std::iter::repeat_n(vec![1, 2], 2);
15+
}
16+
17+
mod foo_lib {
18+
pub fn iter() -> std::iter::Take<std::iter::Repeat<&'static [u8]>> {
19+
todo!()
20+
}
21+
}
22+
23+
fn foo() {
24+
let _ = match 1 {
25+
1 => foo_lib::iter(),
26+
// Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`.
27+
2 => std::iter::repeat([1, 2].as_slice()).take(2),
28+
_ => todo!(),
29+
};
30+
}

tests/ui/manual_repeat_n.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![warn(clippy::manual_repeat_n)]
2+
3+
use std::iter::repeat;
4+
5+
fn main() {
6+
let _ = repeat(10).take(3);
7+
8+
let _ = repeat(String::from("foo")).take(4);
9+
10+
for value in std::iter::repeat(5).take(3) {}
11+
12+
let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect();
13+
14+
let _ = repeat(vec![1, 2]).take(2);
15+
}
16+
17+
mod foo_lib {
18+
pub fn iter() -> std::iter::Take<std::iter::Repeat<&'static [u8]>> {
19+
todo!()
20+
}
21+
}
22+
23+
fn foo() {
24+
let _ = match 1 {
25+
1 => foo_lib::iter(),
26+
// Shouldn't lint because `external_lib::iter` doesn't return `std::iter::RepeatN`.
27+
2 => std::iter::repeat([1, 2].as_slice()).take(2),
28+
_ => todo!(),
29+
};
30+
}

tests/ui/manual_repeat_n.stderr

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
error: this `repeat().take()` can be written more concisely
2+
--> tests/ui/manual_repeat_n.rs:6:13
3+
|
4+
LL | let _ = repeat(10).take(3);
5+
| ^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(10, 3)`
6+
|
7+
= note: `-D clippy::manual-repeat-n` implied by `-D warnings`
8+
= help: to override `-D warnings` add `#[allow(clippy::manual_repeat_n)]`
9+
10+
error: this `repeat().take()` can be written more concisely
11+
--> tests/ui/manual_repeat_n.rs:8:13
12+
|
13+
LL | let _ = repeat(String::from("foo")).take(4);
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("foo"), 4)`
15+
16+
error: this `repeat().take()` can be written more concisely
17+
--> tests/ui/manual_repeat_n.rs:10:18
18+
|
19+
LL | for value in std::iter::repeat(5).take(3) {}
20+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(5, 3)`
21+
22+
error: this `repeat().take()` can be written more concisely
23+
--> tests/ui/manual_repeat_n.rs:12:21
24+
|
25+
LL | let _: Vec<_> = std::iter::repeat(String::from("bar")).take(10).collect();
26+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(String::from("bar"), 10)`
27+
28+
error: this `repeat().take()` can be written more concisely
29+
--> tests/ui/manual_repeat_n.rs:14:13
30+
|
31+
LL | let _ = repeat(vec![1, 2]).take(2);
32+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `repeat_n()` instead: `std::iter::repeat_n(vec![1, 2], 2)`
33+
34+
error: aborting due to 5 previous errors
35+

tests/ui/manual_str_repeat.fixed

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(non_local_definitions)]
1+
#![allow(non_local_definitions, clippy::manual_repeat_n)]
22
#![warn(clippy::manual_str_repeat)]
33

44
use std::borrow::Cow;

tests/ui/manual_str_repeat.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#![allow(non_local_definitions)]
1+
#![allow(non_local_definitions, clippy::manual_repeat_n)]
22
#![warn(clippy::manual_str_repeat)]
33

44
use std::borrow::Cow;

tests/ui/slow_vector_initialization.fixed

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#![allow(clippy::useless_vec)]
1+
#![allow(clippy::useless_vec, clippy::manual_repeat_n)]
2+
23
use std::iter::repeat;
34
fn main() {
45
resize_vector();

tests/ui/slow_vector_initialization.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
#![allow(clippy::useless_vec)]
1+
#![allow(clippy::useless_vec, clippy::manual_repeat_n)]
2+
23
use std::iter::repeat;
34
fn main() {
45
resize_vector();

tests/ui/slow_vector_initialization.stderr

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: slow zero-filling initialization
2-
--> tests/ui/slow_vector_initialization.rs:13:20
2+
--> tests/ui/slow_vector_initialization.rs:14:20
33
|
44
LL | let mut vec1 = Vec::with_capacity(len);
55
| ____________________^
@@ -11,7 +11,7 @@ LL | | vec1.extend(repeat(0).take(len));
1111
= help: to override `-D warnings` add `#[allow(clippy::slow_vector_initialization)]`
1212

1313
error: slow zero-filling initialization
14-
--> tests/ui/slow_vector_initialization.rs:19:20
14+
--> tests/ui/slow_vector_initialization.rs:20:20
1515
|
1616
LL | let mut vec2 = Vec::with_capacity(len - 10);
1717
| ____________________^
@@ -20,7 +20,7 @@ LL | | vec2.extend(repeat(0).take(len - 10));
2020
| |_________________________________________^ help: consider replacing this with: `vec![0; len - 10]`
2121

2222
error: slow zero-filling initialization
23-
--> tests/ui/slow_vector_initialization.rs:27:20
23+
--> tests/ui/slow_vector_initialization.rs:28:20
2424
|
2525
LL | let mut vec4 = Vec::with_capacity(len);
2626
| ____________________^
@@ -29,7 +29,7 @@ LL | | vec4.extend(repeat(0).take(vec4.capacity()));
2929
| |________________________________________________^ help: consider replacing this with: `vec![0; len]`
3030

3131
error: slow zero-filling initialization
32-
--> tests/ui/slow_vector_initialization.rs:38:27
32+
--> tests/ui/slow_vector_initialization.rs:39:27
3333
|
3434
LL | let mut resized_vec = Vec::with_capacity(30);
3535
| ___________________________^
@@ -38,7 +38,7 @@ LL | | resized_vec.resize(30, 0);
3838
| |_____________________________^ help: consider replacing this with: `vec![0; 30]`
3939

4040
error: slow zero-filling initialization
41-
--> tests/ui/slow_vector_initialization.rs:42:26
41+
--> tests/ui/slow_vector_initialization.rs:43:26
4242
|
4343
LL | let mut extend_vec = Vec::with_capacity(30);
4444
| __________________________^
@@ -47,7 +47,7 @@ LL | | extend_vec.extend(repeat(0).take(30));
4747
| |_________________________________________^ help: consider replacing this with: `vec![0; 30]`
4848

4949
error: slow zero-filling initialization
50-
--> tests/ui/slow_vector_initialization.rs:50:20
50+
--> tests/ui/slow_vector_initialization.rs:51:20
5151
|
5252
LL | let mut vec1 = Vec::with_capacity(len);
5353
| ____________________^
@@ -56,7 +56,7 @@ LL | | vec1.resize(len, 0);
5656
| |_______________________^ help: consider replacing this with: `vec![0; len]`
5757

5858
error: slow zero-filling initialization
59-
--> tests/ui/slow_vector_initialization.rs:59:20
59+
--> tests/ui/slow_vector_initialization.rs:60:20
6060
|
6161
LL | let mut vec3 = Vec::with_capacity(len - 10);
6262
| ____________________^
@@ -65,7 +65,7 @@ LL | | vec3.resize(len - 10, 0);
6565
| |____________________________^ help: consider replacing this with: `vec![0; len - 10]`
6666

6767
error: slow zero-filling initialization
68-
--> tests/ui/slow_vector_initialization.rs:63:20
68+
--> tests/ui/slow_vector_initialization.rs:64:20
6969
|
7070
LL | let mut vec4 = Vec::with_capacity(len);
7171
| ____________________^
@@ -74,7 +74,7 @@ LL | | vec4.resize(vec4.capacity(), 0);
7474
| |___________________________________^ help: consider replacing this with: `vec![0; len]`
7575

7676
error: slow zero-filling initialization
77-
--> tests/ui/slow_vector_initialization.rs:68:12
77+
--> tests/ui/slow_vector_initialization.rs:69:12
7878
|
7979
LL | vec1 = Vec::with_capacity(10);
8080
| ____________^
@@ -83,7 +83,7 @@ LL | | vec1.resize(10, 0);
8383
| |______________________^ help: consider replacing this with: `vec![0; 10]`
8484

8585
error: slow zero-filling initialization
86-
--> tests/ui/slow_vector_initialization.rs:76:20
86+
--> tests/ui/slow_vector_initialization.rs:77:20
8787
|
8888
LL | let mut vec1 = Vec::new();
8989
| ____________________^
@@ -92,7 +92,7 @@ LL | | vec1.resize(len, 0);
9292
| |_______________________^ help: consider replacing this with: `vec![0; len]`
9393

9494
error: slow zero-filling initialization
95-
--> tests/ui/slow_vector_initialization.rs:81:20
95+
--> tests/ui/slow_vector_initialization.rs:82:20
9696
|
9797
LL | let mut vec3 = Vec::new();
9898
| ____________________^
@@ -101,7 +101,7 @@ LL | | vec3.resize(len - 10, 0);
101101
| |____________________________^ help: consider replacing this with: `vec![0; len - 10]`
102102

103103
error: slow zero-filling initialization
104-
--> tests/ui/slow_vector_initialization.rs:86:12
104+
--> tests/ui/slow_vector_initialization.rs:87:12
105105
|
106106
LL | vec1 = Vec::new();
107107
| ____________^
@@ -110,7 +110,7 @@ LL | | vec1.resize(10, 0);
110110
| |______________________^ help: consider replacing this with: `vec![0; 10]`
111111

112112
error: slow zero-filling initialization
113-
--> tests/ui/slow_vector_initialization.rs:90:12
113+
--> tests/ui/slow_vector_initialization.rs:91:12
114114
|
115115
LL | vec1 = vec![];
116116
| ____________^

0 commit comments

Comments
 (0)