Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
[Jump to usage instructions](#usage)

##Lints
There are 70 lints included in this crate:
There are 71 lints included in this crate:

name | default | meaning
-------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Expand Down Expand Up @@ -53,6 +53,7 @@ name
[precedence](https://github.com/Manishearth/rust-clippy/wiki#precedence) | warn | catches operations where precedence may be unclear. See the wiki for a list of cases caught
[ptr_arg](https://github.com/Manishearth/rust-clippy/wiki#ptr_arg) | warn | fn arguments of the type `&Vec<...>` or `&String`, suggesting to use `&[...]` or `&str` instead, respectively
[range_step_by_zero](https://github.com/Manishearth/rust-clippy/wiki#range_step_by_zero) | warn | using Range::step_by(0), which produces an infinite iterator
[range_zip_with_len](https://github.com/Manishearth/rust-clippy/wiki#range_zip_with_len) | warn | zipping iterator with a range when enumerate() would do
[redundant_closure](https://github.com/Manishearth/rust-clippy/wiki#redundant_closure) | warn | using redundant closures, i.e. `|a| foo(a)` (which can be written as just `foo`)
[redundant_pattern](https://github.com/Manishearth/rust-clippy/wiki#redundant_pattern) | warn | using `name @ _` in a pattern
[result_unwrap_used](https://github.com/Manishearth/rust-clippy/wiki#result_unwrap_used) | allow | using `Result.unwrap()`, which might be better handled
Expand Down
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
precedence::PRECEDENCE,
ptr_arg::PTR_ARG,
ranges::RANGE_STEP_BY_ZERO,
ranges::RANGE_ZIP_WITH_LEN,
returns::LET_AND_RETURN,
returns::NEEDLESS_RETURN,
types::BOX_VEC,
Expand Down
37 changes: 34 additions & 3 deletions src/ranges.rs
Original file line number Diff line number Diff line change
@@ -1,33 +1,64 @@
use rustc::lint::*;
use rustc_front::hir::*;
use syntax::codemap::Spanned;
use utils::{match_type, is_integer_literal};
use utils::{is_integer_literal, match_type, snippet};

declare_lint! {
pub RANGE_STEP_BY_ZERO, Warn,
"using Range::step_by(0), which produces an infinite iterator"
}
declare_lint! {
pub RANGE_ZIP_WITH_LEN, Warn,
"zipping iterator with a range when enumerate() would do"
}

#[derive(Copy,Clone)]
pub struct StepByZero;

impl LintPass for StepByZero {
fn get_lints(&self) -> LintArray {
lint_array!(RANGE_STEP_BY_ZERO)
lint_array!(RANGE_STEP_BY_ZERO, RANGE_ZIP_WITH_LEN)
}
}

impl LateLintPass for StepByZero {
fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
if let ExprMethodCall(Spanned { node: ref name, .. }, _,
ref args) = expr.node {
// Only warn on literal ranges.
// Range with step_by(0).
if name.as_str() == "step_by" && args.len() == 2 &&
is_range(cx, &args[0]) && is_integer_literal(&args[1], 0) {
cx.span_lint(RANGE_STEP_BY_ZERO, expr.span,
"Range::step_by(0) produces an infinite iterator. \
Consider using `std::iter::repeat()` instead")
}

// x.iter().zip(0..x.len())
else if name.as_str() == "zip" && args.len() == 2 {
let iter = &args[0].node;
let zip_arg = &args[1].node;
if_let_chain! {
[
// .iter() call
let &ExprMethodCall( Spanned { node: ref iter_name, .. }, _, ref iter_args ) = iter,
iter_name.as_str() == "iter",
// range expression in .zip() call: 0..x.len()
let &ExprRange(Some(ref from), Some(ref to)) = zip_arg,
is_integer_literal(from, 0),
// .len() call
let ExprMethodCall(Spanned { node: ref len_name, .. }, _, ref len_args) = to.node,
len_name.as_str() == "len" && len_args.len() == 1,
// .iter() and .len() called on same Path
let ExprPath(_, Path { segments: ref iter_path, .. }) = iter_args[0].node,
let ExprPath(_, Path { segments: ref len_path, .. }) = len_args[0].node,
iter_path == len_path
], {
cx.span_lint(RANGE_ZIP_WITH_LEN, expr.span,
&format!("It is more idiomatic to use {}.iter().enumerate()",
snippet(cx, iter_args[0].span, "_")));
}
}
}
}
}
}
Expand Down
7 changes: 6 additions & 1 deletion tests/compile-fail/range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ impl NotARange {
fn step_by(&self, _: u32) {}
}

#[deny(range_step_by_zero)]
#[deny(range_step_by_zero, range_zip_with_len)]
fn main() {
(0..1).step_by(0); //~ERROR Range::step_by(0) produces an infinite iterator
// No warning for non-zero step
Expand All @@ -21,4 +21,9 @@ fn main() {
// No error, not a range.
let y = NotARange;
y.step_by(0);

let _v1 = vec![1,2,3];
let _v2 = vec![4,5];
let _x = _v1.iter().zip(0.._v1.len()); //~ERROR It is more idiomatic to use _v1.iter().enumerate()
let _y = _v1.iter().zip(0.._v2.len()); // No error
}