Skip to content

Commit

Permalink
Add linter for a single element for loop
Browse files Browse the repository at this point in the history
Signed-off-by: Patrick José Pereira <patrickelectric@gmail.com>
  • Loading branch information
patrickelectric committed Oct 4, 2020
1 parent 9408c68 commit 63ee923
Show file tree
Hide file tree
Showing 6 changed files with 78 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -1815,6 +1815,7 @@ Released 2018-09-13
[`single_char_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_pattern
[`single_char_push_str`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_char_push_str
[`single_component_path_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_component_path_imports
[`single_element_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_element_loop
[`single_match`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match
[`single_match_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#single_match_else
[`skip_while_next`]: https://rust-lang.github.io/rust-clippy/master/index.html#skip_while_next
Expand Down
3 changes: 3 additions & 0 deletions clippy_lints/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
&loops::NEEDLESS_RANGE_LOOP,
&loops::NEVER_LOOP,
&loops::SAME_ITEM_PUSH,
&loops::SINGLE_ELEMENT_LOOP,
&loops::WHILE_IMMUTABLE_CONDITION,
&loops::WHILE_LET_LOOP,
&loops::WHILE_LET_ON_ITERATOR,
Expand Down Expand Up @@ -1353,6 +1354,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::NEVER_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_IMMUTABLE_CONDITION),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
Expand Down Expand Up @@ -1563,6 +1565,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
LintId::of(&loops::FOR_KV_MAP),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::SAME_ITEM_PUSH),
LintId::of(&loops::SINGLE_ELEMENT_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION),
LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
Expand Down
48 changes: 48 additions & 0 deletions clippy_lints/src/loops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,31 @@ declare_clippy_lint! {
"the same item is pushed inside of a for loop"
}

declare_clippy_lint! {
/// **What it does:** Checks whether a for loop has a single element.
///
/// **Why is this bad?** There is no reason to have a loop of a
/// single element.
/// **Known problems:** None
///
/// **Example:**
/// ```rust
/// let item1 = 2;
/// for item in &[item1] {
/// println!("{}", item);
/// }
/// ```
/// could be written as
/// ```rust
/// let item1 = 2;
/// let item = &item1;
/// println!("{}", item);
/// ```
pub SINGLE_ELEMENT_LOOP,
style,
"there is no reason to have a single element loop"
}

declare_lint_pass!(Loops => [
MANUAL_MEMCPY,
NEEDLESS_RANGE_LOOP,
Expand All @@ -470,6 +495,7 @@ declare_lint_pass!(Loops => [
MUT_RANGE_BOUND,
WHILE_IMMUTABLE_CONDITION,
SAME_ITEM_PUSH,
SINGLE_ELEMENT_LOOP,
]);

impl<'tcx> LateLintPass<'tcx> for Loops {
Expand Down Expand Up @@ -774,6 +800,7 @@ fn check_for_loop<'tcx>(
check_for_loop_explicit_counter(cx, pat, arg, body, expr);
check_for_loop_over_map_kv(cx, pat, arg, body, expr);
check_for_mut_range_bound(cx, arg, body);
check_for_single_element_loop(cx, pat, arg, body, expr);
detect_manual_memcpy(cx, pat, arg, body, expr);
detect_same_item_push(cx, pat, arg, body, expr);
}
Expand Down Expand Up @@ -1684,6 +1711,27 @@ fn check_for_loop_over_map_kv<'tcx>(
}
}

fn check_for_single_element_loop<'tcx>(
cx: &LateContext<'tcx>,
_: &'tcx Pat<'_>,
arg: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
_: &'tcx Expr<'_>,
) {
if let ExprKind::AddrOf(BorrowKind::Ref, _, ref expr) = arg.kind {
if let ExprKind::Array(ref expr_list) = expr.kind {
if expr_list.len() == 1 {
span_lint(
cx,
SINGLE_ELEMENT_LOOP,
expr.span,
"do not iterate over a single element array",
);
}
}
}
}

struct MutatePairDelegate<'a, 'tcx> {
cx: &'a LateContext<'tcx>,
hir_id_low: Option<HirId>,
Expand Down
7 changes: 7 additions & 0 deletions src/lintlist/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2110,6 +2110,13 @@ pub static ref ALL_LINTS: Vec<Lint> = vec![
deprecation: None,
module: "single_component_path_imports",
},
Lint {
name: "single_element_loop",
group: "style",
desc: "there is no reason to have a single element loop",
deprecation: None,
module: "loops",
},
Lint {
name: "single_match",
group: "style",
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/single_element_loop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Tests from for_loop.rs that don't have suggestions

#[warn(clippy::single_element_loop)]
fn main() {
let item1 = 2;
for item in &[item1] {
println!("{}", item);
}
}
10 changes: 10 additions & 0 deletions tests/ui/single_element_loop.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: do not iterate over a single element array.
--> $DIR/single_element_loop.rs:6:18
|
LL | for item in &[item1] {
| ^^^^^^^
|
= note: `-D clippy::single-element-loop` implied by `-D warnings`

error: aborting due to previous error

0 comments on commit 63ee923

Please sign in to comment.