1
1
use 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} ;
3
3
use clippy_utils:: { is_must_use_func_call, paths} ;
4
4
use rustc_hir:: { Local , PatKind } ;
5
5
use rustc_lint:: { LateContext , LateLintPass } ;
@@ -28,7 +28,7 @@ declare_clippy_lint! {
28
28
#[ clippy:: version = "1.42.0" ]
29
29
pub LET_UNDERSCORE_MUST_USE ,
30
30
restriction,
31
- "non-binding let on a `#[must_use]` expression"
31
+ "non-binding ` let` on a `#[must_use]` expression"
32
32
}
33
33
34
34
declare_clippy_lint ! {
@@ -56,10 +56,41 @@ declare_clippy_lint! {
56
56
#[ clippy:: version = "1.43.0" ]
57
57
pub LET_UNDERSCORE_LOCK ,
58
58
correctness,
59
- "non-binding let on a synchronization lock"
59
+ "non-binding ` let` on a synchronization lock"
60
60
}
61
61
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 ] ) ;
63
94
64
95
const SYNC_GUARD_PATHS : [ & [ & str ] ; 3 ] = [
65
96
& paths:: PARKING_LOT_MUTEX_GUARD ,
@@ -83,17 +114,27 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
83
114
cx,
84
115
LET_UNDERSCORE_LOCK ,
85
116
local. span ,
86
- "non-binding let on a synchronization lock" ,
117
+ "non-binding ` let` on a synchronization lock" ,
87
118
None ,
88
119
"consider using an underscore-prefixed named \
89
120
binding or dropping explicitly with `std::mem::drop`",
90
121
) ;
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
+ ) ;
91
132
} else if is_must_use_ty ( cx, cx. typeck_results ( ) . expr_ty ( init) ) {
92
133
span_lint_and_help (
93
134
cx,
94
135
LET_UNDERSCORE_MUST_USE ,
95
136
local. span ,
96
- "non-binding let on an expression with `#[must_use]` type" ,
137
+ "non-binding ` let` on an expression with `#[must_use]` type" ,
97
138
None ,
98
139
"consider explicitly using expression value" ,
99
140
) ;
@@ -102,7 +143,7 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
102
143
cx,
103
144
LET_UNDERSCORE_MUST_USE ,
104
145
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" ,
106
147
None ,
107
148
"consider explicitly using function result" ,
108
149
) ;
0 commit comments