diff --git a/CHANGELOG.md b/CHANGELOG.md index 81a1e276a880..db81b1b86e0b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1242,6 +1242,7 @@ Released 2018-09-13 [`duplicate_underscore_argument`]: https://rust-lang.github.io/rust-clippy/master/index.html#duplicate_underscore_argument [`duration_subsec`]: https://rust-lang.github.io/rust-clippy/master/index.html#duration_subsec [`else_if_without_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#else_if_without_else +[`empty_closure`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_closure [`empty_enum`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum [`empty_line_after_outer_attr`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_line_after_outer_attr [`empty_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#empty_loop diff --git a/README.md b/README.md index ebdab4c6a1a0..bd29bfa1d774 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code. -[There are 362 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) +[There are 363 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html) We have a bunch of lint categories to allow you to choose how much Clippy is supposed to ~~annoy~~ help you: diff --git a/clippy_lints/src/empty_closure.rs b/clippy_lints/src/empty_closure.rs new file mode 100644 index 000000000000..ee29fd32a9ac --- /dev/null +++ b/clippy_lints/src/empty_closure.rs @@ -0,0 +1,48 @@ +use crate::utils::{match_def_path, paths, span_lint}; +use if_chain::if_chain; +use rustc_hir::{Expr, ExprKind}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::{declare_lint_pass, declare_tool_lint}; + +declare_clippy_lint! { + /// **What it does:** Checks for empty closure when calling `std::thread::spawn`. + /// + /// **Why is this bad?** Empty closure does nothing, it can be removed. + /// + /// **Known problems:** None. + /// + /// **Example:** + /// + /// ```rust + /// std::thread::spawn(|| {}); + /// ``` + pub EMPTY_CLOSURE, + style, + "closure with empty body" +} + +declare_lint_pass!(EmptyClosure => [EMPTY_CLOSURE]); + +impl LateLintPass<'_, '_> for EmptyClosure { + fn check_expr(&mut self, cx: &LateContext<'_, '_>, expr: &'_ Expr<'_>) { + if_chain! { + if let ExprKind::Call(ref path_expr, ref args) = expr.kind; + if args.len() == 1; + if let ExprKind::Path(ref qpath) = path_expr.kind; + if let Some(def_id) = cx.tables.qpath_res(qpath, path_expr.hir_id).opt_def_id(); + if match_def_path(cx, def_id, &paths::THREAD_SPAWN); + if let ExprKind::Closure(_, _, body_id, ..) = args[0].kind; + let body = cx.tcx.hir().body(body_id); + if let ExprKind::Block(ref b, _) = body.value.kind; + if b.stmts.is_empty() && b.expr.is_none(); + then { + span_lint( + cx, + EMPTY_CLOSURE, + args[0].span, + "Empty closure may be removed", + ); + } + } + } +} diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 140500afe0f9..0e3e3d32e48e 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -199,6 +199,7 @@ pub mod drop_bounds; pub mod drop_forget_ref; pub mod duration_subsec; pub mod else_if_without_else; +pub mod empty_closure; pub mod empty_enum; pub mod entry; pub mod enum_clike; @@ -524,6 +525,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: &drop_forget_ref::FORGET_REF, &duration_subsec::DURATION_SUBSEC, &else_if_without_else::ELSE_IF_WITHOUT_ELSE, + &empty_closure::EMPTY_CLOSURE, &empty_enum::EMPTY_ENUM, &entry::MAP_ENTRY, &enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT, @@ -1026,6 +1028,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_early_pass(|| box macro_use::MacroUseImports); store.register_late_pass(|| box verbose_file_reads::VerboseFileReads); store.register_late_pass(|| box redundant_pub_crate::RedundantPubCrate::default()); + store.register_late_pass(|| box empty_closure::EmptyClosure); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(&arithmetic::FLOAT_ARITHMETIC), @@ -1172,6 +1175,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&drop_forget_ref::FORGET_COPY), LintId::of(&drop_forget_ref::FORGET_REF), LintId::of(&duration_subsec::DURATION_SUBSEC), + LintId::of(&empty_closure::EMPTY_CLOSURE), LintId::of(&entry::MAP_ENTRY), LintId::of(&enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), @@ -1404,6 +1408,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(&comparison_chain::COMPARISON_CHAIN), LintId::of(&doc::MISSING_SAFETY_DOC), LintId::of(&doc::NEEDLESS_DOCTEST_MAIN), + LintId::of(&empty_closure::EMPTY_CLOSURE), LintId::of(&enum_variants::ENUM_VARIANT_NAMES), LintId::of(&enum_variants::MODULE_INCEPTION), LintId::of(&eq_op::OP_REF), diff --git a/clippy_lints/src/utils/paths.rs b/clippy_lints/src/utils/paths.rs index 6cb1f694fd5e..754d59c7af08 100644 --- a/clippy_lints/src/utils/paths.rs +++ b/clippy_lints/src/utils/paths.rs @@ -116,6 +116,7 @@ pub const STRING: [&str; 3] = ["alloc", "string", "String"]; pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_str"]; pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"]; pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"]; +pub const THREAD_SPAWN: [&str; 3] = ["std", "thread", "spawn"]; pub const TO_OWNED: [&str; 3] = ["alloc", "borrow", "ToOwned"]; pub const TO_OWNED_METHOD: [&str; 4] = ["alloc", "borrow", "ToOwned", "to_owned"]; pub const TO_STRING: [&str; 3] = ["alloc", "string", "ToString"]; diff --git a/src/lintlist/mod.rs b/src/lintlist/mod.rs index 2888741cb454..2dc0840111f2 100644 --- a/src/lintlist/mod.rs +++ b/src/lintlist/mod.rs @@ -6,7 +6,7 @@ pub use lint::Lint; pub use lint::LINT_LEVELS; // begin lint list, do not remove this comment, it’s used in `update_lints` -pub const ALL_LINTS: [Lint; 362] = [ +pub const ALL_LINTS: [Lint; 363] = [ Lint { name: "absurd_extreme_comparisons", group: "correctness", @@ -427,6 +427,13 @@ pub const ALL_LINTS: [Lint; 362] = [ deprecation: None, module: "else_if_without_else", }, + Lint { + name: "empty_closure", + group: "style", + desc: "closure with empty body", + deprecation: None, + module: "empty_closure", + }, Lint { name: "empty_enum", group: "pedantic", diff --git a/tests/ui/empty_closure.rs b/tests/ui/empty_closure.rs new file mode 100644 index 000000000000..08435a917bd9 --- /dev/null +++ b/tests/ui/empty_closure.rs @@ -0,0 +1,8 @@ +#![warn(clippy::empty_closure)] + +fn main() { + // Lint + std::thread::spawn(|| {}); + // No lint + vec![0, 1, 2].iter().map(|_| {}).collect::>(); +} diff --git a/tests/ui/empty_closure.stderr b/tests/ui/empty_closure.stderr new file mode 100644 index 000000000000..a4bf1eaab779 --- /dev/null +++ b/tests/ui/empty_closure.stderr @@ -0,0 +1,10 @@ +error: Empty closure may be removed + --> $DIR/empty_closure.rs:5:24 + | +LL | std::thread::spawn(|| {}); + | ^^^^^ + | + = note: `-D clippy::empty-closure` implied by `-D warnings` + +error: aborting due to previous error + diff --git a/tests/ui/unused_unit.fixed b/tests/ui/unused_unit.fixed index 3f63624720f7..c4c57824ae6a 100644 --- a/tests/ui/unused_unit.fixed +++ b/tests/ui/unused_unit.fixed @@ -35,6 +35,7 @@ fn return_unit() { } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] #[allow(clippy::unit_cmp)] +#[allow(clippy::empty_closure)] fn main() { let u = Unitter; assert_eq!(u.get_unit(|| {}, return_unit), u.into()); diff --git a/tests/ui/unused_unit.rs b/tests/ui/unused_unit.rs index 8fc072ebd69f..8a05a05db430 100644 --- a/tests/ui/unused_unit.rs +++ b/tests/ui/unused_unit.rs @@ -36,6 +36,7 @@ fn return_unit() -> () { () } #[allow(clippy::needless_return)] #[allow(clippy::never_loop)] #[allow(clippy::unit_cmp)] +#[allow(clippy::empty_closure)] fn main() { let u = Unitter; assert_eq!(u.get_unit(|| {}, return_unit), u.into()); diff --git a/tests/ui/unused_unit.stderr b/tests/ui/unused_unit.stderr index a013d2b3495b..3b6df51b2d2e 100644 --- a/tests/ui/unused_unit.stderr +++ b/tests/ui/unused_unit.stderr @@ -37,13 +37,13 @@ LL | fn return_unit() -> () { () } | ^^ help: remove the final `()` error: unneeded `()` - --> $DIR/unused_unit.rs:44:14 + --> $DIR/unused_unit.rs:45:14 | LL | break(); | ^^ help: remove the `()` error: unneeded `()` - --> $DIR/unused_unit.rs:46:11 + --> $DIR/unused_unit.rs:47:11 | LL | return(); | ^^ help: remove the `()`