Skip to content

Commit 9e919a5

Browse files
committed
Add empty closure lint
1 parent 100a24d commit 9e919a5

File tree

11 files changed

+86
-4
lines changed

11 files changed

+86
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1242,6 +1242,7 @@ Released 2018-09-13
12421242
[`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument
12431243
[`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec
12441244
[`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else
1245+
[`empty_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_closure
12451246
[`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum
12461247
[`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr
12471248
[`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
77

8-
[There are 362 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
8+
[There are 363 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
99

1010
We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you:
1111

clippy_lints/src/empty_closure.rs

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
use crate::utils::{match_def_path, paths, span_lint};
2+
use if_chain::if_chain;
3+
use rustc_hir::{Expr, ExprKind};
4+
use rustc_lint::{LateContext, LateLintPass};
5+
use rustc_session::{declare_lint_pass, declare_tool_lint};
6+
7+
declare_clippy_lint! {
8+
/// **What it does:** Checks for empty closure.
9+
///
10+
/// **Why is this bad?** Empty closure does nothing, it can be removed.
11+
///
12+
/// **Known problems:** None.
13+
///
14+
/// **Example:**
15+
///
16+
/// ```rust
17+
/// std::thread::spawn(|| {});
18+
/// ```
19+
pub EMPTY_CLOSURE,
20+
style,
21+
"closure with empty body"
22+
}
23+
24+
declare_lint_pass!(EmptyClosure => [EMPTY_CLOSURE]);
25+
26+
impl LateLintPass<'_, '_> for EmptyClosure {
27+
fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) {
28+
if_chain! {
29+
if let ExprKind::Call(ref path_expr, ref args) = expr.kind;
30+
if args.len() == 1;
31+
if let ExprKind::Path(ref qpath) = path_expr.kind;
32+
if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id();
33+
if match_def_path(cx, def_id, &paths::THREAD_SPAWN);
34+
if let ExprKind::Closure(_, _, body_id, ..) = args[0].kind;
35+
let body = cx.tcx.hir().body(body_id);
36+
if let ExprKind::Block(ref b, _) = body.value.kind;
37+
if b.stmts.is_empty() && b.expr.is_none();
38+
then {
39+
span_lint(
40+
cx,
41+
EMPTY_CLOSURE,
42+
args[0].span,
43+
"Empty closure may be removed",
44+
);
45+
}
46+
}
47+
}
48+
}

clippy_lints/src/lib.rs

+5
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ pub mod drop_bounds;
197197
pub mod drop_forget_ref;
198198
pub mod duration_subsec;
199199
pub mod else_if_without_else;
200+
pub mod empty_closure;
200201
pub mod empty_enum;
201202
pub mod entry;
202203
pub mod enum_clike;
@@ -522,6 +523,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
522523
&drop_forget_ref::FORGET_REF,
523524
&duration_subsec::DURATION_SUBSEC,
524525
&else_if_without_else::ELSE_IF_WITHOUT_ELSE,
526+
&empty_closure::EMPTY_CLOSURE,
525527
&empty_enum::EMPTY_ENUM,
526528
&entry::MAP_ENTRY,
527529
&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT,
@@ -1024,6 +1026,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
10241026
store.register_early_pass(|| box macro_use::MacroUseImports);
10251027
store.register_late_pass(|| box verbose_file_reads::VerboseFileReads);
10261028
store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default());
1029+
store.register_late_pass(|| box empty_closure::EmptyClosure);
10271030

10281031
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
10291032
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
@@ -1169,6 +1172,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
11691172
LintId::of(&drop_forget_ref::FORGET_COPY),
11701173
LintId::of(&drop_forget_ref::FORGET_REF),
11711174
LintId::of(&duration_subsec::DURATION_SUBSEC),
1175+
LintId::of(&empty_closure::EMPTY_CLOSURE),
11721176
LintId::of(&entry::MAP_ENTRY),
11731177
LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
11741178
LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
@@ -1403,6 +1407,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
14031407
LintId::of(&comparison_chain::COMPARISON_CHAIN),
14041408
LintId::of(&doc::MISSING_SAFETY_DOC),
14051409
LintId::of(&doc::NEEDLESS_DOCTEST_MAIN),
1410+
LintId::of(&empty_closure::EMPTY_CLOSURE),
14061411
LintId::of(&enum_variants::ENUM_VARIANT_NAMES),
14071412
LintId::of(&enum_variants::MODULE_INCEPTION),
14081413
LintId::of(&eq_op::OP_REF),

clippy_lints/src/utils/paths.rs

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ pub const STRING: [&str; 3] = ["alloc", "string", "String"];
116116
pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"];
117117
pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
118118
pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
119+
pub const THREAD_SPAWN: [&str; 3] = ["std", "thread", "spawn"];
119120
pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"];
120121
pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"];
121122
pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"];

src/lintlist/mod.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ pub use lint::Lint;
66
pub use lint::LINT_LEVELS;
77

88
// begin lint list, do not remove this comment, it’s used in `update_lints`
9-
pub const ALL_LINTS: [Lint; 362] = [
9+
pub const ALL_LINTS: [Lint; 363] = [
1010
Lint {
1111
name: "absurd_extreme_comparisons",
1212
group: "correctness",
@@ -427,6 +427,13 @@ pub const ALL_LINTS: [Lint; 362] = [
427427
deprecation: None,
428428
module: "else_if_without_else",
429429
},
430+
Lint {
431+
name: "empty_closure",
432+
group: "style",
433+
desc: "closure with empty body",
434+
deprecation: None,
435+
module: "empty_closure",
436+
},
430437
Lint {
431438
name: "empty_enum",
432439
group: "pedantic",

tests/ui/empty_closure.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#![warn(clippy::empty_closure)]
2+
3+
fn main() {
4+
// Lint
5+
std::thread::spawn(|| {});
6+
// No lint
7+
vec![0, 1, 2].iter().map(|_| {}).collect::<Vec<()>>();
8+
}

tests/ui/empty_closure.stderr

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
error: Empty closure may be removed
2+
--> $DIR/empty_closure.rs:5:24
3+
|
4+
LL | std::thread::spawn(|| {});
5+
| ^^^^^
6+
|
7+
= note: `-D clippy::empty-closure` implied by `-D warnings`
8+
9+
error: aborting due to previous error
10+

tests/ui/unused_unit.fixed

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ fn return_unit() { }
3535
#[allow(clippy::needless_return)]
3636
#[allow(clippy::never_loop)]
3737
#[allow(clippy::unit_cmp)]
38+
#[allow(clippy::empty_closure)]
3839
fn main() {
3940
let u = Unitter;
4041
assert_eq!(u.get_unit(|| {}, return_unit), u.into());

tests/ui/unused_unit.rs

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ fn return_unit() -> () { () }
3636
#[allow(clippy::needless_return)]
3737
#[allow(clippy::never_loop)]
3838
#[allow(clippy::unit_cmp)]
39+
#[allow(clippy::empty_closure)]
3940
fn main() {
4041
let u = Unitter;
4142
assert_eq!(u.get_unit(|| {}, return_unit), u.into());

tests/ui/unused_unit.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ LL | fn return_unit() -> () { () }
3737
| ^^ help: remove the final `()`
3838

3939
error: unneeded `()`
40-
--> $DIR/unused_unit.rs:44:14
40+
--> $DIR/unused_unit.rs:45:14
4141
|
4242
LL | break();
4343
| ^^ help: remove the `()`
4444

4545
error: unneeded `()`
46-
--> $DIR/unused_unit.rs:46:11
46+
--> $DIR/unused_unit.rs:47:11
4747
|
4848
LL | return();
4949
| ^^ help: remove the `()`

0 commit comments

Comments
 (0)