diff --git a/README.md b/README.md index 3a957a7e4c9..ff875085da2 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code. [Jump to usage instructions](#usage) ##Lints -There are 129 lints included in this crate: +There are 130 lints included in this crate: name | default | meaning ---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ @@ -99,6 +99,7 @@ name [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_closure_call](https://github.com/Manishearth/rust-clippy/wiki#redundant_closure_call) | warn | Closures should not be called in the expression they are defined [redundant_pattern](https://github.com/Manishearth/rust-clippy/wiki#redundant_pattern) | warn | using `name @ _` in a pattern [regex_macro](https://github.com/Manishearth/rust-clippy/wiki#regex_macro) | warn | finds use of `regex!(_)`, suggests `Regex::new(_)` instead [result_unwrap_used](https://github.com/Manishearth/rust-clippy/wiki#result_unwrap_used) | allow | using `Result.unwrap()`, which might be better handled diff --git a/src/lib.rs b/src/lib.rs index 7436dada020..803a4c2345c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -272,6 +272,7 @@ pub fn plugin_registrar(reg: &mut Registry) { misc::TOPLEVEL_REF_ARG, misc::USED_UNDERSCORE_BINDING, misc_early::DUPLICATE_UNDERSCORE_ARGUMENT, + misc_early::REDUNDANT_CLOSURE_CALL, misc_early::UNNEEDED_FIELD_PATTERN, mut_reference::UNNECESSARY_MUT_PASSED, mutex_atomic::MUTEX_ATOMIC, diff --git a/src/misc_early.rs b/src/misc_early.rs index 604e6002103..89a0763e2e3 100644 --- a/src/misc_early.rs +++ b/src/misc_early.rs @@ -3,8 +3,7 @@ use std::collections::HashMap; use syntax::ast::*; use syntax::codemap::Span; use syntax::visit::FnKind; -use utils::{span_lint, span_help_and_lint}; - +use utils::{span_lint, span_help_and_lint, snippet, span_lint_and_then}; /// **What it does:** This lint checks for structure field patterns bound to wildcards. /// /// **Why is this bad?** Using `..` instead is shorter and leaves the focus on the fields that are actually bound. @@ -29,12 +28,24 @@ declare_lint! { "Function arguments having names which only differ by an underscore" } +/// **What it does:** This lint detects closures called in the same expression where they are defined. +/// +/// **Why is this bad?** It is unnecessarily adding to the expression's complexity. +/// +/// **Known problems:** None. +/// +/// **Example:** `(|| 42)()` +declare_lint! { + pub REDUNDANT_CLOSURE_CALL, Warn, + "Closures should not be called in the expression they are defined" +} + #[derive(Copy, Clone)] pub struct MiscEarly; impl LintPass for MiscEarly { fn get_lints(&self) -> LintArray { - lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT) + lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL) } } @@ -105,7 +116,7 @@ impl EarlyLintPass for MiscEarly { *correspondance, &format!("`{}` already exists, having another argument having almost the same \ name makes code comprehension and documentation more difficult", - arg_name[1..].to_owned())); + arg_name[1..].to_owned()));; } } else { registered_names.insert(arg_name, arg.pat.span); @@ -113,4 +124,43 @@ impl EarlyLintPass for MiscEarly { } } } + + fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) { + if let ExprKind::Call(ref paren, _) = expr.node { + if let ExprKind::Paren(ref closure) = paren.node { + if let ExprKind::Closure(_, ref decl, ref block) = closure.node { + span_lint_and_then(cx, + REDUNDANT_CLOSURE_CALL, + expr.span, + "Try not to call a closure in the expression where it is declared.", + |db| { + if decl.inputs.len() == 0 { + let hint = format!("{}", snippet(cx, block.span, "..")); + db.span_suggestion(expr.span, "Try doing something like: ", hint); + } + }); + } + } + } + } + + fn check_block(&mut self, cx: &EarlyContext, block: &Block) { + for w in block.stmts.windows(2) { + if_let_chain! {[ + let StmtKind::Decl(ref first, _) = w[0].node, + let DeclKind::Local(ref local) = first.node, + let Option::Some(ref t) = local.init, + let ExprKind::Closure(_,_,_) = t.node, + let PatKind::Ident(_,sp_ident,_) = local.pat.node, + let StmtKind::Semi(ref second,_) = w[1].node, + let ExprKind::Assign(_,ref call) = second.node, + let ExprKind::Call(ref closure,_) = call.node, + let ExprKind::Path(_,ref path) = closure.node + ], { + if sp_ident.node == (&path.segments[0]).identifier { + span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared"); + } + }} + } + } } diff --git a/tests/compile-fail/eta.rs b/tests/compile-fail/eta.rs index 46680f2b8d8..0e72efe654e 100644 --- a/tests/compile-fail/eta.rs +++ b/tests/compile-fail/eta.rs @@ -1,6 +1,6 @@ #![feature(plugin)] #![plugin(clippy)] -#![allow(unknown_lints, unused, no_effect)] +#![allow(unknown_lints, unused, no_effect, redundant_closure_call)] #![deny(redundant_closure)] fn main() { diff --git a/tests/compile-fail/redundant_closure_call.rs b/tests/compile-fail/redundant_closure_call.rs new file mode 100644 index 00000000000..73830ecc9f1 --- /dev/null +++ b/tests/compile-fail/redundant_closure_call.rs @@ -0,0 +1,25 @@ +#![feature(plugin)] +#![plugin(clippy)] + +#![deny(redundant_closure_call)] + +fn main() { + let a = (|| 42)(); + //~^ ERROR Try not to call a closure in the expression where it is declared. + //~| HELP Try doing something like: + //~| SUGGESTION let a = 42; + + let mut i = 1; + let k = (|m| m+1)(i); //~ERROR Try not to call a closure in the expression where it is declared. + + k = (|a,b| a*b)(1,5); //~ERROR Try not to call a closure in the expression where it is declared. + + let closure = || 32; + i = closure(); //~ERROR Closure called just once immediately after it was declared + + let closure = |i| i+1; + i = closure(3); //~ERROR Closure called just once immediately after it was declared + + i = closure(4); +} +