Skip to content

Commit e421a0f

Browse files
Warn about calling a closure in the same expression where it's defined.
1 parent bdda6a2 commit e421a0f

File tree

5 files changed

+83
-6
lines changed

5 files changed

+83
-6
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ A collection of lints to catch common mistakes and improve your Rust code.
88
[Jump to usage instructions](#usage)
99

1010
##Lints
11-
There are 129 lints included in this crate:
11+
There are 130 lints included in this crate:
1212

1313
name | default | meaning
1414
---------------------------------------------------------------------------------------------------------------------|---------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
@@ -99,6 +99,7 @@ name
9999
[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
100100
[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
101101
[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`)
102+
[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
102103
[redundant_pattern](https://github.com/Manishearth/rust-clippy/wiki#redundant_pattern) | warn | using `name @ _` in a pattern
103104
[regex_macro](https://github.com/Manishearth/rust-clippy/wiki#regex_macro) | warn | finds use of `regex!(_)`, suggests `Regex::new(_)` instead
104105
[result_unwrap_used](https://github.com/Manishearth/rust-clippy/wiki#result_unwrap_used) | allow | using `Result.unwrap()`, which might be better handled

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ pub fn plugin_registrar(reg: &mut Registry) {
272272
misc::TOPLEVEL_REF_ARG,
273273
misc::USED_UNDERSCORE_BINDING,
274274
misc_early::DUPLICATE_UNDERSCORE_ARGUMENT,
275+
misc_early::REDUNDANT_CLOSURE_CALL,
275276
misc_early::UNNEEDED_FIELD_PATTERN,
276277
mut_reference::UNNECESSARY_MUT_PASSED,
277278
mutex_atomic::MUTEX_ATOMIC,

src/misc_early.rs

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::collections::HashMap;
33
use syntax::ast::*;
44
use syntax::codemap::Span;
55
use syntax::visit::FnKind;
6-
use utils::{span_lint, span_help_and_lint};
7-
6+
use utils::{span_lint, span_help_and_lint, snippet, span_lint_and_then};
87
/// **What it does:** This lint checks for structure field patterns bound to wildcards.
98
///
109
/// **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! {
2928
"Function arguments having names which only differ by an underscore"
3029
}
3130

31+
/// **What it does:** This lint detects closures called in the same expression where they are defined.
32+
///
33+
/// **Why is this bad?** It is unnecessarily adding to the expression's complexity.
34+
///
35+
/// **Known problems:** None.
36+
///
37+
/// **Example:** `(|| 42)()`
38+
declare_lint! {
39+
pub REDUNDANT_CLOSURE_CALL, Warn,
40+
"Closures should not be called in the expression they are defined"
41+
}
42+
3243
#[derive(Copy, Clone)]
3344
pub struct MiscEarly;
3445

3546
impl LintPass for MiscEarly {
3647
fn get_lints(&self) -> LintArray {
37-
lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT)
48+
lint_array!(UNNEEDED_FIELD_PATTERN, DUPLICATE_UNDERSCORE_ARGUMENT, REDUNDANT_CLOSURE_CALL)
3849
}
3950
}
4051

@@ -105,12 +116,51 @@ impl EarlyLintPass for MiscEarly {
105116
*correspondance,
106117
&format!("`{}` already exists, having another argument having almost the same \
107118
name makes code comprehension and documentation more difficult",
108-
arg_name[1..].to_owned()));
119+
arg_name[1..].to_owned()));;
109120
}
110121
} else {
111122
registered_names.insert(arg_name, arg.pat.span);
112123
}
113124
}
114125
}
115126
}
127+
128+
fn check_expr(&mut self, cx: &EarlyContext, expr: &Expr) {
129+
if let ExprKind::Call(ref paren, _) = expr.node {
130+
if let ExprKind::Paren(ref closure) = paren.node {
131+
if let ExprKind::Closure(_, ref decl, ref block) = closure.node {
132+
span_lint_and_then(cx,
133+
REDUNDANT_CLOSURE_CALL,
134+
expr.span,
135+
"Try not to call a closure in the expression where it is declared.",
136+
|db| {
137+
if decl.inputs.len() == 0 {
138+
let hint = format!("{}", snippet(cx, block.span, ".."));
139+
db.span_suggestion(expr.span, "Try doing something like: ", hint);
140+
}
141+
});
142+
}
143+
}
144+
}
145+
}
146+
147+
fn check_block(&mut self, cx: &EarlyContext, block: &Block) {
148+
for w in block.stmts.windows(2) {
149+
if_let_chain! {[
150+
let StmtKind::Decl(ref first, _) = w[0].node,
151+
let DeclKind::Local(ref local) = first.node,
152+
let Option::Some(ref t) = local.init,
153+
let ExprKind::Closure(_,_,_) = t.node,
154+
let PatKind::Ident(_,sp_ident,_) = local.pat.node,
155+
let StmtKind::Semi(ref second,_) = w[1].node,
156+
let ExprKind::Assign(_,ref call) = second.node,
157+
let ExprKind::Call(ref closure,_) = call.node,
158+
let ExprKind::Path(_,ref path) = closure.node
159+
], {
160+
if sp_ident.node == (&path.segments[0]).identifier {
161+
span_lint(cx, REDUNDANT_CLOSURE_CALL, second.span, "Closure called just once immediately after it was declared");
162+
}
163+
}}
164+
}
165+
}
116166
}

tests/compile-fail/eta.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![feature(plugin)]
22
#![plugin(clippy)]
3-
#![allow(unknown_lints, unused, no_effect)]
3+
#![allow(unknown_lints, unused, no_effect, redundant_closure_call)]
44
#![deny(redundant_closure)]
55

66
fn main() {
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#![feature(plugin)]
2+
#![plugin(clippy)]
3+
4+
#![deny(redundant_closure_call)]
5+
6+
fn main() {
7+
let a = (|| 42)();
8+
//~^ ERROR Try not to call a closure in the expression where it is declared.
9+
//~| HELP Try doing something like:
10+
//~| SUGGESTION let a = 42;
11+
12+
let mut i = 1;
13+
let k = (|m| m+1)(i); //~ERROR Try not to call a closure in the expression where it is declared.
14+
15+
k = (|a,b| a*b)(1,5); //~ERROR Try not to call a closure in the expression where it is declared.
16+
17+
let closure = || 32;
18+
i = closure(); //~ERROR Closure called just once immediately after it was declared
19+
20+
let closure = |i| i+1;
21+
i = closure(3); //~ERROR Closure called just once immediately after it was declared
22+
23+
i = closure(4);
24+
}
25+

0 commit comments

Comments
 (0)