diff options
| author | Jackson Lewis <jackson.lewis@agilebits.com> | 2021-05-14 16:45:18 -0700 |
|---|---|---|
| committer | Jackson Lewis <jackson.lewis@agilebits.com> | 2021-05-14 16:45:18 -0700 |
| commit | c6e0e843d279c0c4703e2b3326826423b791dee2 (patch) | |
| tree | fec2df5e6235173ab68af1c7c2b49bd16a18e2eb | |
| parent | 182a1853c3adfed34b084614c333d4945d5a24f2 (diff) | |
| download | rust-c6e0e843d279c0c4703e2b3326826423b791dee2.tar.gz rust-c6e0e843d279c0c4703e2b3326826423b791dee2.zip | |
Implement unnecessary-async and UI test
| -rw-r--r-- | clippy_lints/src/lib.rs | 4 | ||||
| -rw-r--r-- | clippy_lints/src/unnecessary_async.rs | 92 | ||||
| -rw-r--r-- | tests/ui/unnecessary_async.rs | 15 | ||||
| -rw-r--r-- | tests/ui/unnecessary_async.stderr | 13 |
4 files changed, 124 insertions, 0 deletions
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<Self::Map> { + 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 + |
