[`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
[`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
[`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
+[`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
[`manual_memcpy`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_memcpy
[`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
[`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
mod loops;
mod macro_use;
mod main_recursion;
+mod manual_async_fn;
mod manual_non_exhaustive;
mod map_clone;
mod map_unit_fn;
&loops::WHILE_LET_ON_ITERATOR,
¯o_use::MACRO_USE_IMPORTS,
&main_recursion::MAIN_RECURSION,
+ &manual_async_fn::MANUAL_ASYNC_FN,
&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
&map_clone::MAP_CLONE,
&map_unit_fn::OPTION_MAP_UNIT_FN,
store.register_late_pass(|| box if_let_mutex::IfLetMutex);
store.register_late_pass(|| box match_on_vec_items::MatchOnVecItems);
store.register_early_pass(|| box manual_non_exhaustive::ManualNonExhaustive);
+ store.register_late_pass(|| box manual_async_fn::ManualAsyncFn);
store.register_group(true, "clippy::restriction", Some("clippy_restriction"), vec![
LintId::of(&arithmetic::FLOAT_ARITHMETIC),
LintId::of(&loops::WHILE_LET_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION),
+ LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&map_clone::MAP_CLONE),
LintId::of(&map_unit_fn::OPTION_MAP_UNIT_FN),
LintId::of(&loops::NEEDLESS_RANGE_LOOP),
LintId::of(&loops::WHILE_LET_ON_ITERATOR),
LintId::of(&main_recursion::MAIN_RECURSION),
+ LintId::of(&manual_async_fn::MANUAL_ASYNC_FN),
LintId::of(&manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
LintId::of(&map_clone::MAP_CLONE),
LintId::of(&matches::INFALLIBLE_DESTRUCTURING_MATCH),
--- /dev/null
+use crate::utils::paths::{FUTURE_CORE, FUTURE_FROM_GENERATOR, FUTURE_STD};
+use crate::utils::{match_function_call, match_path, snippet_block, snippet_opt, span_lint_and_then};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{
+ AsyncGeneratorKind, Block, Body, Expr, ExprKind, FnDecl, FnRetTy, GeneratorKind, GenericBound, HirId, IsAsync,
+ ItemKind, TraitRef, Ty, TyKind, TypeBindingKind,
+};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_span::Span;
+
+declare_clippy_lint! {
+ /// **What it does:** It checks for manual implementations of `async` functions.
+ ///
+ /// **Why is this bad?** It's more idiomatic to use the dedicated syntax.
+ ///
+ /// **Known problems:** None.
+ ///
+ /// **Example:**
+ ///
+ /// ```rust
+ /// use std::future::Future;
+ ///
+ /// fn foo() -> Future<Output = i32> { async { 42 } }
+ /// ```
+ /// Use instead:
+ /// ```rust
+ /// use std::future::Future;
+ ///
+ /// async fn foo() -> i32 { 42 }
+ /// ```
+ pub MANUAL_ASYNC_FN,
+ style,
+ "manual implementations of `async` functions can be simplified using the dedicated syntax"
+}
+
+declare_lint_pass!(ManualAsyncFn => [MANUAL_ASYNC_FN]);
+
+impl<'a, 'tcx> LateLintPass<'a, 'tcx> for ManualAsyncFn {
+ fn check_fn(
+ &mut self,
+ cx: &LateContext<'a, 'tcx>,
+ kind: FnKind<'tcx>,
+ decl: &'tcx FnDecl<'_>,
+ body: &'tcx Body<'_>,
+ span: Span,
+ _: HirId,
+ ) {
+ if_chain! {
+ if let Some(header) = kind.header();
+ if let IsAsync::NotAsync = header.asyncness;
+ // Check that this function returns `impl Future`
+ if let FnRetTy::Return(ret_ty) = decl.output;
+ if let Some(trait_ref) = future_trait_ref(cx, ret_ty);
+ if let Some(output) = future_output_ty(trait_ref);
+ // Check that the body of the function consists of one async block
+ if let ExprKind::Block(block, _) = body.value.kind;
+ if block.stmts.is_empty();
+ if let Some(closure_body) = desugared_async_block(cx, block);
+ then {
+ let header_span = span.with_hi(ret_ty.span.hi());
+
+ span_lint_and_then(
+ cx,
+ MANUAL_ASYNC_FN,
+ header_span,
+ "this function can be simplified using async syntax",
+ |diag| {
+ if_chain! {
+ if let Some(header_snip) = snippet_opt(cx, header_span);
+ if let Some(ret_pos) = header_snip.rfind("->");
+ if let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output);
+ then {
+ let help = format!("make the function `async` and {}", ret_sugg);
+ diag.span_suggestion(
+ header_span,
+ &help,
+ format!("async {}{}", &header_snip[..ret_pos], ret_snip),
+ Applicability::MachineApplicable
+ );
+
+ let body_snip = snippet_block(cx, closure_body.value.span, "..", Some(block.span));
+ diag.span_suggestion(
+ block.span,
+ "move the body of the async block to the enclosing function",
+ body_snip.to_string(),
+ Applicability::MachineApplicable
+ );
+ }
+ }
+ },
+ );
+ }
+ }
+ }
+}
+
+fn future_trait_ref<'tcx>(cx: &LateContext<'_, 'tcx>, ty: &'tcx Ty<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
+ if_chain! {
+ if let TyKind::Def(item_id, _) = ty.kind;
+ let item = cx.tcx.hir().item(item_id.id);
+ if let ItemKind::OpaqueTy(opaque) = &item.kind;
+ if opaque.bounds.len() == 1;
+ if let GenericBound::Trait(poly, _) = &opaque.bounds[0];
+ let path = poly.trait_ref.path;
+ if match_path(&path, &FUTURE_CORE) || match_path(&path, &FUTURE_STD);
+ then {
+ return Some(&poly.trait_ref);
+ }
+ }
+
+ None
+}
+
+fn future_output_ty<'tcx>(trait_ref: &'tcx TraitRef<'tcx>) -> Option<&'tcx Ty<'tcx>> {
+ if_chain! {
+ if let Some(segment) = trait_ref.path.segments.last();
+ if let Some(args) = segment.args;
+ if args.bindings.len() == 1;
+ let binding = &args.bindings[0];
+ if binding.ident.as_str() == "Output";
+ if let TypeBindingKind::Equality{ty: output} = binding.kind;
+ then {
+ return Some(output)
+ }
+ }
+
+ None
+}
+
+fn desugared_async_block<'tcx>(cx: &LateContext<'_, 'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
+ if_chain! {
+ if let Some(block_expr) = block.expr;
+ if let Some(args) = match_function_call(cx, block_expr, &FUTURE_FROM_GENERATOR);
+ if args.len() == 1;
+ if let Expr{kind: ExprKind::Closure(_, _, body_id, ..), ..} = args[0];
+ let closure_body = cx.tcx.hir().body(body_id);
+ if let Some(GeneratorKind::Async(AsyncGeneratorKind::Block)) = closure_body.generator_kind;
+ then {
+ return Some(closure_body);
+ }
+ }
+
+ None
+}
+
+fn suggested_ret(cx: &LateContext<'_, '_>, output: &Ty<'_>) -> Option<(&'static str, String)> {
+ match output.kind {
+ TyKind::Tup(tys) if tys.is_empty() => {
+ let sugg = "remove the return type";
+ Some((sugg, "".into()))
+ },
+ _ => {
+ let sugg = "return the output of the future directly";
+ snippet_opt(cx, output.span).map(|snip| (sugg, format!("-> {}", snip)))
+ },
+ }
+}
pub const FMT_ARGUMENTV1_NEW: [&str; 4] = ["core", "fmt", "ArgumentV1", "new"];
pub const FROM_FROM: [&str; 4] = ["core", "convert", "From", "from"];
pub const FROM_TRAIT: [&str; 3] = ["core", "convert", "From"];
+pub const FUTURE_CORE: [&str; 3] = ["core", "future", "Future"];
+pub const FUTURE_FROM_GENERATOR: [&str; 3] = ["core", "future", "from_generator"];
+pub const FUTURE_STD: [&str; 3] = ["std", "future", "Future"];
pub const HASH: [&str; 2] = ["hash", "Hash"];
pub const HASHMAP: [&str; 5] = ["std", "collections", "hash", "map", "HashMap"];
pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
deprecation: None,
module: "main_recursion",
},
+ Lint {
+ name: "manual_async_fn",
+ group: "style",
+ desc: "manual implementations of `async` functions can be simplified using the dedicated syntax",
+ deprecation: None,
+ module: "manual_async_fn",
+ },
Lint {
name: "manual_memcpy",
group: "perf",
self.private_future().await;
}
+ #[allow(clippy::manual_async_fn)]
pub fn public_send(&self) -> impl std::future::Future<Output = bool> {
async { false }
}
= note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:49:37
+ --> $DIR/future_not_send.rs:50:37
|
LL | async fn generic_future<T>(t: T) -> T
| ^ future returned by `generic_future` is not `Send`
|
note: future is not `Send` as this value is used across an await
- --> $DIR/future_not_send.rs:54:5
+ --> $DIR/future_not_send.rs:55:5
|
LL | let rt = &t;
| -- has type `&T` which is not `Send`
= note: `T` doesn't implement `std::marker::Sync`
error: future cannot be sent between threads safely
- --> $DIR/future_not_send.rs:65:34
+ --> $DIR/future_not_send.rs:66:34
|
LL | async fn unclear_future<T>(t: T) {}
| ^
--- /dev/null
+// run-rustfix
+// edition:2018
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+async fn fut() -> i32 { 42 }
+
+async fn empty_fut() {}
+
+async fn core_fut() -> i32 { 42 }
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S {}
+impl S {
+ async fn inh_fut() -> i32 { 42 }
+
+ async fn meth_fut(&self) -> i32 { 42 }
+
+ async fn empty_fut(&self) {}
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+// edition:2018
+#![warn(clippy::manual_async_fn)]
+#![allow(unused)]
+
+use std::future::Future;
+
+fn fut() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+fn empty_fut() -> impl Future<Output = ()> {
+ async {}
+}
+
+fn core_fut() -> impl core::future::Future<Output = i32> {
+ async move { 42 }
+}
+
+// should be ignored
+fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+}
+
+// should be ignored
+fn not_fut() -> i32 {
+ 42
+}
+
+// should be ignored
+async fn already_async() -> impl Future<Output = i32> {
+ async { 42 }
+}
+
+struct S {}
+impl S {
+ fn inh_fut() -> impl Future<Output = i32> {
+ async { 42 }
+ }
+
+ fn meth_fut(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+
+ fn empty_fut(&self) -> impl Future<Output = ()> {
+ async {}
+ }
+
+ // should be ignored
+ fn not_fut(&self) -> i32 {
+ 42
+ }
+
+ // should be ignored
+ fn has_other_stmts() -> impl core::future::Future<Output = i32> {
+ let _ = 42;
+ async move { 42 }
+ }
+
+ // should be ignored
+ async fn already_async(&self) -> impl Future<Output = i32> {
+ async { 42 }
+ }
+}
+
+fn main() {}
--- /dev/null
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:8:1
+ |
+LL | fn fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: `-D clippy::manual-async-fn` implied by `-D warnings`
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn fut() -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn fut() -> impl Future<Output = i32> { 42 }
+ | ^^^^^^
+
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:12:1
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut() {
+ | ^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut() -> impl Future<Output = ()> {}
+ | ^^
+
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:16:1
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn core_fut() -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn core_fut() -> impl core::future::Future<Output = i32> { 42 }
+ | ^^^^^^
+
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:38:5
+ |
+LL | fn inh_fut() -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn inh_fut() -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn inh_fut() -> impl Future<Output = i32> { 42 }
+ | ^^^^^^
+
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:42:5
+ |
+LL | fn meth_fut(&self) -> impl Future<Output = i32> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and return the output of the future directly
+ |
+LL | async fn meth_fut(&self) -> i32 {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn meth_fut(&self) -> impl Future<Output = i32> { 42 }
+ | ^^^^^^
+
+error: this function can be simplified using async syntax
+ --> $DIR/manual_async_fn.rs:46:5
+ |
+LL | fn empty_fut(&self) -> impl Future<Output = ()> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+help: make the function `async` and remove the return type
+ |
+LL | async fn empty_fut(&self) {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+help: move the body of the async block to the enclosing function
+ |
+LL | fn empty_fut(&self) -> impl Future<Output = ()> {}
+ | ^^
+
+error: aborting due to 6 previous errors
+