Skip to content

Commit d95c2ba

Browse files
Add linter for a single element for loop
Signed-off-by: Patrick José Pereira <[email protected]>
1 parent 9408c68 commit d95c2ba

File tree

6 files changed

+78
-0
lines changed

6 files changed

+78
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1815,6 +1815,7 @@ Released 2018-09-13
18151815
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
18161816
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
18171817
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
1818+
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
18181819
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
18191820
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
18201821
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next

clippy_lints/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -627,6 +627,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
627627
&loops::NEEDLESS_RANGE_LOOP,
628628
&loops::NEVER_LOOP,
629629
&loops::SAME_ITEM_PUSH,
630+
&loops::SINGLE_ELEMENT_LOOP,
630631
&loops::WHILE_IMMUTABLE_CONDITION,
631632
&loops::WHILE_LET_LOOP,
632633
&loops::WHILE_LET_ON_ITERATOR,
@@ -1353,6 +1354,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
13531354
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
13541355
LintId::of(&loops::NEVER_LOOP),
13551356
LintId::of(&loops::SAME_ITEM_PUSH),
1357+
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
13561358
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
13571359
LintId::of(&loops::WHILE_LET_LOOP),
13581360
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
@@ -1563,6 +1565,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
15631565
LintId::of(&loops::FOR_KV_MAP),
15641566
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
15651567
LintId::of(&loops::SAME_ITEM_PUSH),
1568+
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
15661569
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
15671570
LintId::of(&main_recursion::MAIN_RECURSION),
15681571
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),

clippy_lints/src/loops.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,6 +453,31 @@ declare_clippy_lint! {
453453
"the same item is pushed inside of a for loop"
454454
}
455455

456+
declare_clippy_lint! {
457+
/// **What it does:** Checks whether a for loop has a single element.
458+
///
459+
/// **Why is this bad?** There is no reason to have a loop of a
460+
/// single element.
461+
/// **Known problems:** None
462+
///
463+
/// **Example:**
464+
/// ```rust
465+
/// let item1 = 2;
466+
/// for item in &[item1] {
467+
/// println!("{}", item);
468+
/// }
469+
/// ```
470+
/// could be written as
471+
/// ```rust
472+
/// let item1 = 2;
473+
/// let item = &item1;
474+
/// println!("{}", item);
475+
/// ```
476+
pub SINGLE_ELEMENT_LOOP,
477+
style,
478+
"there is no reason to have a single element loop"
479+
}
480+
456481
declare_lint_pass!(Loops => [
457482
MANUAL_MEMCPY,
458483
NEEDLESS_RANGE_LOOP,
@@ -470,6 +495,7 @@ declare_lint_pass!(Loops => [
470495
MUT_RANGE_BOUND,
471496
WHILE_IMMUTABLE_CONDITION,
472497
SAME_ITEM_PUSH,
498+
SINGLE_ELEMENT_LOOP,
473499
]);
474500

475501
impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -774,6 +800,7 @@ fn check_for_loop<'tcx>(
774800
check_for_loop_explicit_counter(cx, pat, arg, body, expr);
775801
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
776802
check_for_mut_range_bound(cx, arg, body);
803+
check_for_single_element_loop(cx, pat, arg, body, expr);
777804
detect_manual_memcpy(cx, pat, arg, body, expr);
778805
detect_same_item_push(cx, pat, arg, body, expr);
779806
}
@@ -1684,6 +1711,27 @@ fn check_for_loop_over_map_kv<'tcx>(
16841711
}
16851712
}
16861713

1714+
fn check_for_single_element_loop<'tcx>(
1715+
cx: &LateContext<'tcx>,
1716+
_: &'tcx Pat<'_>,
1717+
arg: &'tcx Expr<'_>,
1718+
_: &'tcx Expr<'_>,
1719+
_: &'tcx Expr<'_>,
1720+
) {
1721+
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = arg.kind {
1722+
if let ExprKind::Array(ref expr_list) = expr.kind {
1723+
if expr_list.len() == 1 {
1724+
span_lint(
1725+
cx,
1726+
SINGLE_ELEMENT_LOOP,
1727+
expr.span,
1728+
"Do not iterate over a single element array.",
1729+
);
1730+
}
1731+
}
1732+
}
1733+
}
1734+
16871735
struct MutatePairDelegate<'a, 'tcx> {
16881736
cx: &'a LateContext<'tcx>,
16891737
hir_id_low: Option<HirId>,

src/lintlist/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2110,6 +2110,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
21102110
deprecation: None,
21112111
module: "single_component_path_imports",
21122112
},
2113+
Lint {
2114+
name: "single_element_loop",
2115+
group: "style",
2116+
desc: "there is no reason to have a single element loop",
2117+
deprecation: None,
2118+
module: "loops",
2119+
},
21132120
Lint {
21142121
name: "single_match",
21152122
group: "style",

tests/ui/single_element_loop.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
// Tests from for_loop.rs that don't have suggestions
2+
3+
#[warn(clippy::single_element_loop)]
4+
fn main() {
5+
let item1 = 2;
6+
for item in &[item1] {
7+
println!("{}", item);
8+
}
9+
}

tests/ui/single_element_loop.stderr

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: Do not iterate over a single element array.
2+
--> $DIR/single_element_loop.rs:6:18
3+
|
4+
LL | for item in &[item1] {
5+
| ^^^^^^^
6+
|
7+
= note: `-D clippy::single-element-loop` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

0 commit comments

Comments
 (0)