Skip to content

Commit 12df638

Browse files
committed
Auto merge of rust-lang#5773 - giraffate:repeat_once, r=flip1995
Add a lint for `.repeat(1)` changelog: New lint `repeat_once` fix rust-lang#3028.
2 parents 4b87008 + b409103 commit 12df638

File tree

7 files changed

+167
-0
lines changed

7 files changed

+167
-0
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1617,6 +1617,7 @@ Released 2018-09-13
16171617
[`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
16181618
[`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
16191619
[`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
1620+
[`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
16201621
[`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
16211622
[`rest_pat_in_fully_bound_structs`]: https://rust-lang.github.io/rust-clippy/master/index.html#rest_pat_in_fully_bound_structs
16221623
[`result_map_or_into_option`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_map_or_into_option

clippy_lints/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ mod redundant_pub_crate;
281281
mod redundant_static_lifetimes;
282282
mod reference;
283283
mod regex;
284+
mod repeat_once;
284285
mod returns;
285286
mod serde_api;
286287
mod shadow;
@@ -764,6 +765,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
764765
&reference::REF_IN_DEREF,
765766
&regex::INVALID_REGEX,
766767
&regex::TRIVIAL_REGEX,
768+
&repeat_once::REPEAT_ONCE,
767769
&returns::NEEDLESS_RETURN,
768770
&returns::UNUSED_UNIT,
769771
&serde_api::SERDE_API_MISUSE,
@@ -1070,6 +1072,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10701072
store.register_late_pass(|| box macro_use::MacroUseImports::default());
10711073
store.register_late_pass(|| box map_identity::MapIdentity);
10721074
store.register_late_pass(|| box pattern_type_mismatch::PatternTypeMismatch);
1075+
store.register_late_pass(|| box repeat_once::RepeatOnce);
10731076

10741077
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10751078
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1393,6 +1396,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13931396
LintId::of(&reference::REF_IN_DEREF),
13941397
LintId::of(&regex::INVALID_REGEX),
13951398
LintId::of(&regex::TRIVIAL_REGEX),
1399+
LintId::of(&repeat_once::REPEAT_ONCE),
13961400
LintId::of(&returns::NEEDLESS_RETURN),
13971401
LintId::of(&returns::UNUSED_UNIT),
13981402
LintId::of(&serde_api::SERDE_API_MISUSE),
@@ -1602,6 +1606,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
16021606
LintId::of(&ranges::RANGE_ZIP_WITH_LEN),
16031607
LintId::of(&reference::DEREF_ADDROF),
16041608
LintId::of(&reference::REF_IN_DEREF),
1609+
LintId::of(&repeat_once::REPEAT_ONCE),
16051610
LintId::of(&swap::MANUAL_SWAP),
16061611
LintId::of(&temporary_assignment::TEMPORARY_ASSIGNMENT),
16071612
LintId::of(&transmute::CROSSPOINTER_TRANSMUTE),

clippy_lints/src/repeat_once.rs

+82
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
use crate::consts::{constant_context, Constant};
2+
use crate::utils::{in_macro, is_type_diagnostic_item, snippet, span_lint_and_sugg, walk_ptrs_ty};
3+
use if_chain::if_chain;
4+
use rustc_errors::Applicability;
5+
use rustc_hir::{Expr, ExprKind};
6+
use rustc_lint::{LateContext, LateLintPass};
7+
use rustc_session::{declare_lint_pass, declare_tool_lint};
8+
9+
declare_clippy_lint! {
10+
/// **What it does:** Checks for usage of `.repeat(1)` and suggest the following method for each types.
11+
/// - `.to_string()` for `str`
12+
/// - `.clone()` for `String`
13+
/// - `.to_vec()` for `slice`
14+
///
15+
/// **Why is this bad?** For example, `String.repeat(1)` is equivalent to `.clone()`. If cloning the string is the intention behind this, `clone()` should be used.
16+
///
17+
/// **Known problems:** None.
18+
///
19+
/// **Example:**
20+
///
21+
/// ```rust
22+
/// fn main() {
23+
/// let x = String::from("hello world").repeat(1);
24+
/// }
25+
/// ```
26+
/// Use instead:
27+
/// ```rust
28+
/// fn main() {
29+
/// let x = String::from("hello world").clone();
30+
/// }
31+
/// ```
32+
pub REPEAT_ONCE,
33+
complexity,
34+
"using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` "
35+
}
36+
37+
declare_lint_pass!(RepeatOnce => [REPEAT_ONCE]);
38+
39+
impl<'tcx> LateLintPass<'tcx> for RepeatOnce {
40+
fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'tcx Expr<'_>) {
41+
if_chain! {
42+
if let ExprKind::MethodCall(ref path, _, ref args, _) = expr.kind;
43+
if path.ident.name == sym!(repeat);
44+
if let Some(Constant::Int(1)) = constant_context(cx, cx.tables()).expr(&args[1]);
45+
if !in_macro(args[0].span);
46+
then {
47+
let ty = walk_ptrs_ty(cx.tables().expr_ty(&args[0]));
48+
if ty.is_str() {
49+
span_lint_and_sugg(
50+
cx,
51+
REPEAT_ONCE,
52+
expr.span,
53+
"calling `repeat(1)` on str",
54+
"consider using `.to_string()` instead",
55+
format!("{}.to_string()", snippet(cx, args[0].span, r#""...""#)),
56+
Applicability::MachineApplicable,
57+
);
58+
} else if ty.builtin_index().is_some() {
59+
span_lint_and_sugg(
60+
cx,
61+
REPEAT_ONCE,
62+
expr.span,
63+
"calling `repeat(1)` on slice",
64+
"consider using `.to_vec()` instead",
65+
format!("{}.to_vec()", snippet(cx, args[0].span, r#""...""#)),
66+
Applicability::MachineApplicable,
67+
);
68+
} else if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
69+
span_lint_and_sugg(
70+
cx,
71+
REPEAT_ONCE,
72+
expr.span,
73+
"calling `repeat(1)` on a string literal",
74+
"consider using `.clone()` instead",
75+
format!("{}.clone()", snippet(cx, args[0].span, r#""...""#)),
76+
Applicability::MachineApplicable,
77+
);
78+
}
79+
}
80+
}
81+
}
82+
}

src/lintlist/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -1886,6 +1886,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
18861886
deprecation: None,
18871887
module: "reference",
18881888
},
1889+
Lint {
1890+
name: "repeat_once",
1891+
group: "complexity",
1892+
desc: "using `.repeat(1)` instead of `String.clone()`, `str.to_string()` or `slice.to_vec()` ",
1893+
deprecation: None,
1894+
module: "repeat_once",
1895+
},
18891896
Lint {
18901897
name: "rest_pat_in_fully_bound_structs",
18911898
group: "restriction",

tests/ui/repeat_once.fixed

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
#![warn(clippy::repeat_once)]
3+
#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
4+
fn main() {
5+
const N: usize = 1;
6+
let s = "str";
7+
let string = "String".to_string();
8+
let slice = [1; 5];
9+
10+
let a = [1; 5].to_vec();
11+
let b = slice.to_vec();
12+
let c = "hello".to_string();
13+
let d = "hi".to_string();
14+
let e = s.to_string();
15+
let f = string.clone();
16+
}

tests/ui/repeat_once.rs

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-rustfix
2+
#![warn(clippy::repeat_once)]
3+
#[allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
4+
fn main() {
5+
const N: usize = 1;
6+
let s = "str";
7+
let string = "String".to_string();
8+
let slice = [1; 5];
9+
10+
let a = [1; 5].repeat(1);
11+
let b = slice.repeat(1);
12+
let c = "hello".repeat(N);
13+
let d = "hi".repeat(1);
14+
let e = s.repeat(1);
15+
let f = string.repeat(1);
16+
}

tests/ui/repeat_once.stderr

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
error: calling `repeat(1)` on slice
2+
--> $DIR/repeat_once.rs:10:13
3+
|
4+
LL | let a = [1; 5].repeat(1);
5+
| ^^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `[1; 5].to_vec()`
6+
|
7+
= note: `-D clippy::repeat-once` implied by `-D warnings`
8+
9+
error: calling `repeat(1)` on slice
10+
--> $DIR/repeat_once.rs:11:13
11+
|
12+
LL | let b = slice.repeat(1);
13+
| ^^^^^^^^^^^^^^^ help: consider using `.to_vec()` instead: `slice.to_vec()`
14+
15+
error: calling `repeat(1)` on str
16+
--> $DIR/repeat_once.rs:12:13
17+
|
18+
LL | let c = "hello".repeat(N);
19+
| ^^^^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hello".to_string()`
20+
21+
error: calling `repeat(1)` on str
22+
--> $DIR/repeat_once.rs:13:13
23+
|
24+
LL | let d = "hi".repeat(1);
25+
| ^^^^^^^^^^^^^^ help: consider using `.to_string()` instead: `"hi".to_string()`
26+
27+
error: calling `repeat(1)` on str
28+
--> $DIR/repeat_once.rs:14:13
29+
|
30+
LL | let e = s.repeat(1);
31+
| ^^^^^^^^^^^ help: consider using `.to_string()` instead: `s.to_string()`
32+
33+
error: calling `repeat(1)` on a string literal
34+
--> $DIR/repeat_once.rs:15:13
35+
|
36+
LL | let f = string.repeat(1);
37+
| ^^^^^^^^^^^^^^^^ help: consider using `.clone()` instead: `string.clone()`
38+
39+
error: aborting due to 6 previous errors
40+

0 commit comments

Comments
 (0)