11use clippy_utils:: diagnostics:: span_lint_and_help;
2- use clippy_utils:: ty:: { is_must_use_ty, match_type} ;
2+ use clippy_utils:: ty:: { implements_trait , is_must_use_ty, match_type} ;
33use clippy_utils:: { is_must_use_func_call, paths} ;
44use rustc_hir:: { Local , PatKind } ;
55use rustc_lint:: { LateContext , LateLintPass } ;
@@ -28,7 +28,7 @@ declare_clippy_lint! {
2828 #[ clippy:: version = "1.42.0" ]
2929 pub LET_UNDERSCORE_MUST_USE ,
3030 restriction,
31- "non-binding let on a `#[must_use]` expression"
31+ "non-binding ` let` on a `#[must_use]` expression"
3232}
3333
3434declare_clippy_lint ! {
@@ -56,10 +56,41 @@ declare_clippy_lint! {
5656 #[ clippy:: version = "1.43.0" ]
5757 pub LET_UNDERSCORE_LOCK ,
5858 correctness,
59- "non-binding let on a synchronization lock"
59+ "non-binding ` let` on a synchronization lock"
6060}
6161
62- declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_MUST_USE , LET_UNDERSCORE_LOCK ] ) ;
62+ declare_clippy_lint ! {
63+ /// ### What it does
64+ /// Checks for `let _ = <expr>` where the resulting type of expr implements `Future`
65+ ///
66+ /// ### Why is this bad?
67+ /// Futures must be polled for work to be done. The original intention was most likely to await the future
68+ /// and ignore the resulting value.
69+ ///
70+ /// ### Example
71+ /// ```rust
72+ /// async fn foo() -> Result<(), ()> {
73+ /// Ok(())
74+ /// }
75+ /// let _ = foo();
76+ /// ```
77+ ///
78+ /// Use instead:
79+ /// ```rust
80+ /// # async fn context() {
81+ /// async fn foo() -> Result<(), ()> {
82+ /// Ok(())
83+ /// }
84+ /// let _ = foo().await;
85+ /// # }
86+ /// ```
87+ #[ clippy:: version = "1.66" ]
88+ pub LET_UNDERSCORE_FUTURE ,
89+ suspicious,
90+ "non-binding `let` on a future"
91+ }
92+
93+ declare_lint_pass ! ( LetUnderscore => [ LET_UNDERSCORE_MUST_USE , LET_UNDERSCORE_LOCK , LET_UNDERSCORE_FUTURE ] ) ;
6394
6495const SYNC_GUARD_PATHS : [ & [ & str ] ; 3 ] = [
6596 & paths:: PARKING_LOT_MUTEX_GUARD ,
@@ -83,17 +114,27 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
83114 cx,
84115 LET_UNDERSCORE_LOCK ,
85116 local. span ,
86- "non-binding let on a synchronization lock" ,
117+ "non-binding ` let` on a synchronization lock" ,
87118 None ,
88119 "consider using an underscore-prefixed named \
89120 binding or dropping explicitly with `std::mem::drop`",
90121 ) ;
122+ } else if let Some ( future_trait_def_id) = cx. tcx . lang_items ( ) . future_trait ( )
123+ && implements_trait ( cx, cx. typeck_results ( ) . expr_ty ( init) , future_trait_def_id, & [ ] ) {
124+ span_lint_and_help (
125+ cx,
126+ LET_UNDERSCORE_FUTURE ,
127+ local. span ,
128+ "non-binding `let` on a future" ,
129+ None ,
130+ "consider awaiting the future or dropping explicitly with `std::mem::drop`"
131+ ) ;
91132 } else if is_must_use_ty ( cx, cx. typeck_results ( ) . expr_ty ( init) ) {
92133 span_lint_and_help (
93134 cx,
94135 LET_UNDERSCORE_MUST_USE ,
95136 local. span ,
96- "non-binding let on an expression with `#[must_use]` type" ,
137+ "non-binding ` let` on an expression with `#[must_use]` type" ,
97138 None ,
98139 "consider explicitly using expression value" ,
99140 ) ;
@@ -102,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
102143 cx,
103144 LET_UNDERSCORE_MUST_USE ,
104145 local. span ,
105- "non-binding let on a result of a `#[must_use]` function" ,
146+ "non-binding ` let` on a result of a `#[must_use]` function" ,
106147 None ,
107148 "consider explicitly using function result" ,
108149 ) ;
0 commit comments