From c6e0e843d279c0c4703e2b3326826423b791dee2 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 16:45:18 -0700 Subject: [PATCH 1/3] Implement unnecessary-async and UI test --- clippy_lints/src/lib.rs | 4 ++ clippy_lints/src/unnecessary_async.rs | 92 +++++++++++++++++++++++++++ tests/ui/unnecessary_async.rs | 15 +++++ tests/ui/unnecessary_async.stderr | 13 ++++ 4 files changed, 124 insertions(+) create mode 100644 clippy_lints/src/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.rs create mode 100644 tests/ui/unnecessary_async.stderr diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 1a74f641554..e8b8b2e9117 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,6 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; +mod unnecessary_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -954,6 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, + unnecessary_async::UNNECESSARY_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1271,6 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); + store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1412,6 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), + LintId::of(unnecessary_async::UNNECESSARY_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unnecessary_async.rs new file mode 100644 index 00000000000..15a90ba42d2 --- /dev/null +++ b/clippy_lints/src/unnecessary_async.rs @@ -0,0 +1,92 @@ +use clippy_utils::diagnostics::span_lint_and_help; +use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, NestedVisitorMap, Visitor}; +use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, Item, ItemKind, YieldSource}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_middle::hir::map::Map; +use rustc_session::{declare_lint_pass, declare_tool_lint}; +use rustc_span::Span; + +declare_clippy_lint! { + /// **What it does:** Checks for functions that are declared `async` but have no `.await`s inside of them. + /// + /// **Why is this bad?** Async functions with no async code create overhead, both mentally and computationally. + /// Callers of async methods either need to be calling from an async function themselves or run it on an executor, both of which + /// causes runtime overhead and hassle for the caller. + /// + /// **Known problems:** None + /// + /// **Example:** + /// + /// ```rust + /// // Bad + /// async fn get_random_number() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = get_random_number(); + /// + /// // Good + /// fn get_random_number_improved() -> i64 { + /// 4 // Chosen by fair dice roll. Guaranteed to be random. + /// } + /// let number_future = async { get_random_number_improved() }; + /// ``` + pub UNNECESSARY_ASYNC, + pedantic, + "finds async functions with no await statements" +} + +declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); + +struct AsyncFnVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + found_await: bool, +} + +impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { + type Map = Map<'tcx>; + + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let ExprKind::Yield(_, YieldSource::Await { .. }) = ex.kind { + self.found_await = true; + } + walk_expr(self, ex); + } + + fn nested_visit_map(&mut self) -> NestedVisitorMap { + NestedVisitorMap::OnlyBodies(self.cx.tcx.hir()) + } +} + +impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { + fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { + if let ItemKind::Trait(..) = item.kind { + return; + } + } + fn check_fn( + &mut self, + cx: &LateContext<'tcx>, + fn_kind: FnKind<'tcx>, + fn_decl: &'tcx FnDecl<'tcx>, + body: &Body<'tcx>, + span: Span, + hir_id: HirId, + ) { + if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind { + if matches!(asyncness, IsAsync::Async) { + let mut visitor = AsyncFnVisitor { cx, found_await: false }; + walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id); + if !visitor.found_await { + span_lint_and_help( + cx, + UNNECESSARY_ASYNC, + span, + "unnecessary `async` for function with no await statements", + None, + "consider removing the `async` from this function", + ); + } + } + } + } +} diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unnecessary_async.rs new file mode 100644 index 00000000000..7d63083b13e --- /dev/null +++ b/tests/ui/unnecessary_async.rs @@ -0,0 +1,15 @@ +// edition:2018 +#![warn(clippy::unnecessary_async)] + +async fn foo() -> i32 { + 4 +} + +async fn bar() -> i32 { + foo().await +} + +fn main() { + foo(); + bar(); +} diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unnecessary_async.stderr new file mode 100644 index 00000000000..5542580e45d --- /dev/null +++ b/tests/ui/unnecessary_async.stderr @@ -0,0 +1,13 @@ +error: unnecessary `async` for function with no await statements + --> $DIR/unnecessary_async.rs:4:1 + | +LL | / async fn foo() -> i32 { +LL | | 4 +LL | | } + | |_^ + | + = note: `-D clippy::unnecessary-async` implied by `-D warnings` + = help: consider removing the `async` from this function + +error: aborting due to previous error + From 1d8f3b51e662bb66e2b696d0c13057d6ba89f979 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 17:07:30 -0700 Subject: [PATCH 2/3] Unnecessary -> Unused --- clippy_lints/src/lib.rs | 8 ++++---- .../src/{unnecessary_async.rs => unused_async.rs} | 10 +++++----- tests/ui/{unnecessary_async.rs => unused_async.rs} | 2 +- .../{unnecessary_async.stderr => unused_async.stderr} | 6 +++--- 4 files changed, 13 insertions(+), 13 deletions(-) rename clippy_lints/src/{unnecessary_async.rs => unused_async.rs} (91%) rename tests/ui/{unnecessary_async.rs => unused_async.rs} (78%) rename tests/ui/{unnecessary_async.stderr => unused_async.stderr} (50%) diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index e8b8b2e9117..52e64d6a48f 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,7 +360,7 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; -mod unnecessary_async; +mod unused_async; mod unnested_or_patterns; mod unsafe_removed_from_name; mod unused_io_amount; @@ -955,7 +955,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unnecessary_async::UNNECESSARY_ASYNC, + unused_async::UNUSED_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, @@ -1273,7 +1273,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: store.register_late_pass(|| box manual_map::ManualMap); store.register_late_pass(move || box if_then_some_else_none::IfThenSomeElseNone::new(msrv)); store.register_early_pass(|| box bool_assert_comparison::BoolAssertComparison); - store.register_late_pass(|| box unnecessary_async::UnnecessaryAsync); + store.register_late_pass(|| box unused_async::UnusedAsync); store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![ LintId::of(arithmetic::FLOAT_ARITHMETIC), @@ -1415,7 +1415,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unnecessary_async::UNNECESSARY_ASYNC), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), LintId::of(unused_self::UNUSED_SELF), diff --git a/clippy_lints/src/unnecessary_async.rs b/clippy_lints/src/unused_async.rs similarity index 91% rename from clippy_lints/src/unnecessary_async.rs rename to clippy_lints/src/unused_async.rs index 15a90ba42d2..18ee07d3a95 100644 --- a/clippy_lints/src/unnecessary_async.rs +++ b/clippy_lints/src/unused_async.rs @@ -30,12 +30,12 @@ declare_clippy_lint! { /// } /// let number_future = async { get_random_number_improved() }; /// ``` - pub UNNECESSARY_ASYNC, + pub UNUSED_ASYNC, pedantic, "finds async functions with no await statements" } -declare_lint_pass!(UnnecessaryAsync => [UNNECESSARY_ASYNC]); +declare_lint_pass!(UnusedAsync => [UNUSED_ASYNC]); struct AsyncFnVisitor<'a, 'tcx> { cx: &'a LateContext<'tcx>, @@ -57,7 +57,7 @@ impl<'a, 'tcx> Visitor<'tcx> for AsyncFnVisitor<'a, 'tcx> { } } -impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { +impl<'tcx> LateLintPass<'tcx> for UnusedAsync { fn check_item(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) { if let ItemKind::Trait(..) = item.kind { return; @@ -79,9 +79,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryAsync { if !visitor.found_await { span_lint_and_help( cx, - UNNECESSARY_ASYNC, + UNUSED_ASYNC, span, - "unnecessary `async` for function with no await statements", + "unused `async` for function with no await statements", None, "consider removing the `async` from this function", ); diff --git a/tests/ui/unnecessary_async.rs b/tests/ui/unused_async.rs similarity index 78% rename from tests/ui/unnecessary_async.rs rename to tests/ui/unused_async.rs index 7d63083b13e..4f4203f5fdb 100644 --- a/tests/ui/unnecessary_async.rs +++ b/tests/ui/unused_async.rs @@ -1,5 +1,5 @@ // edition:2018 -#![warn(clippy::unnecessary_async)] +#![warn(clippy::unused_async)] async fn foo() -> i32 { 4 diff --git a/tests/ui/unnecessary_async.stderr b/tests/ui/unused_async.stderr similarity index 50% rename from tests/ui/unnecessary_async.stderr rename to tests/ui/unused_async.stderr index 5542580e45d..8b834d205b1 100644 --- a/tests/ui/unnecessary_async.stderr +++ b/tests/ui/unused_async.stderr @@ -1,12 +1,12 @@ -error: unnecessary `async` for function with no await statements - --> $DIR/unnecessary_async.rs:4:1 +error: unused `async` for function with no await statements + --> $DIR/unused_async.rs:4:1 | LL | / async fn foo() -> i32 { LL | | 4 LL | | } | |_^ | - = note: `-D clippy::unnecessary-async` implied by `-D warnings` + = note: `-D clippy::unused-async` implied by `-D warnings` = help: consider removing the `async` from this function error: aborting due to previous error From 75ef9dc708e669bccbc18c3be4873c613058ef30 Mon Sep 17 00:00:00 2001 From: Jackson Lewis Date: Fri, 14 May 2021 17:12:25 -0700 Subject: [PATCH 3/3] update_lints --- CHANGELOG.md | 1 + clippy_lints/src/lib.rs | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 204d56e2a98..c922cc7e763 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2538,6 +2538,7 @@ Released 2018-09-13 [`unsound_collection_transmute`]: https://rust-lang.github.io/rust-clippy/master/index.html#unsound_collection_transmute [`unstable_as_mut_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_mut_slice [`unstable_as_slice`]: https://rust-lang.github.io/rust-clippy/master/index.html#unstable_as_slice +[`unused_async`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_async [`unused_collect`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_collect [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs index 52e64d6a48f..2f5642f24f2 100644 --- a/clippy_lints/src/lib.rs +++ b/clippy_lints/src/lib.rs @@ -360,9 +360,9 @@ mod unnamed_address; mod unnecessary_self_imports; mod unnecessary_sort_by; mod unnecessary_wraps; -mod unused_async; mod unnested_or_patterns; mod unsafe_removed_from_name; +mod unused_async; mod unused_io_amount; mod unused_self; mod unused_unit; @@ -955,12 +955,12 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: unit_types::UNIT_CMP, unnamed_address::FN_ADDRESS_COMPARISONS, unnamed_address::VTABLE_ADDRESS_COMPARISONS, - unused_async::UNUSED_ASYNC, unnecessary_self_imports::UNNECESSARY_SELF_IMPORTS, unnecessary_sort_by::UNNECESSARY_SORT_BY, unnecessary_wraps::UNNECESSARY_WRAPS, unnested_or_patterns::UNNESTED_OR_PATTERNS, unsafe_removed_from_name::UNSAFE_REMOVED_FROM_NAME, + unused_async::UNUSED_ASYNC, unused_io_amount::UNUSED_IO_AMOUNT, unused_self::UNUSED_SELF, unused_unit::UNUSED_UNIT, @@ -1415,9 +1415,9 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf: LintId::of(unicode::NON_ASCII_LITERAL), LintId::of(unicode::UNICODE_NOT_NFC), LintId::of(unit_types::LET_UNIT_VALUE), - LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unnecessary_wraps::UNNECESSARY_WRAPS), LintId::of(unnested_or_patterns::UNNESTED_OR_PATTERNS), + LintId::of(unused_async::UNUSED_ASYNC), LintId::of(unused_self::UNUSED_SELF), LintId::of(wildcard_imports::ENUM_GLOB_USE), LintId::of(wildcard_imports::WILDCARD_IMPORTS),