1- use std:: ops:: ControlFlow ;
1+ use std:: { fmt :: Display , ops:: ControlFlow } ;
22
33use crate :: { is_scoped_ty, method_call} ;
44use clippy_utils:: {
55 diagnostics:: span_lint_and_help,
6- get_enclosing_block, get_parent_expr, path_to_local_id ,
6+ get_enclosing_block, get_parent_expr,
77 paths:: { PathNS , lookup_path_str} ,
8+ res:: MaybeResPath ,
89 ty:: implements_trait,
910 visitors:: for_each_expr,
1011} ;
@@ -16,11 +17,14 @@ use rustc_middle::ty::Ty;
1617dylint_linting:: declare_late_lint! {
1718 /// ### What it does
1819 ///
19- /// Makes sure that the user immediately binds `Scoped<Value>::get` results.
20+ /// Makes sure that the user immediately binds `Scoped<Value>::get` and
21+ /// `Scoped<Value>::take` results.
2022 ///
2123 /// ### Why is this bad?
2224 ///
23- /// TODO: Write an explanation of why this is bad.
25+ /// To avoid odd bugs with the garbage collector when dealing with scoped
26+ /// values, it is important that `Scoped<Value>::get` and
27+ /// `Scoped<Value>::take` results are immediately bound.
2428 ///
2529 /// ### Example
2630 ///
@@ -48,13 +52,13 @@ dylint_linting::declare_late_lint! {
4852 /// `Value`s immediately.
4953 pub IMMEDIATELY_BIND_SCOPED ,
5054 Deny ,
51- "the result of `Scoped<Value>::get` should be immediately bound"
55+ "the result of `Scoped<Value>::get` or `Scoped<Value>::take` should be immediately bound"
5256}
5357
5458impl < ' tcx > LateLintPass < ' tcx > for ImmediatelyBindScoped {
5559 fn check_expr ( & mut self , cx : & LateContext < ' tcx > , expr : & ' tcx Expr < ' tcx > ) {
56- // First we check if we have found a `Scoped<Value>::get` call
57- if is_scoped_get_method_call ( cx, expr) {
60+ // First we check if we have found a `Scoped<Value>::get` or `Scoped<Value>::take` call
61+ if let Some ( scoped_method ) = is_scoped_get_or_take_method_call ( cx, expr) {
5862 // Which is followed by a trait method call to `bind` in which case
5963 // it is all done properly and we can exit out of the lint.
6064 if let Some ( parent) = get_parent_expr ( cx, expr)
@@ -97,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for ImmediatelyBindScoped {
97101 // of the value, breaking when found and optionally marking
98102 // it as valid.
99103 if for_each_expr ( cx, stmt_expr, |expr_in_stmt| {
100- if path_to_local_id ( expr_in_stmt, local_hir_id) {
104+ if expr_in_stmt. res_local_id ( ) == Some ( local_hir_id) {
101105 if is_valid_use_of_unbound_value ( cx, expr_in_stmt, local_hir_id) {
102106 found_valid_next_use = true ;
103107 }
@@ -117,7 +121,10 @@ impl<'tcx> LateLintPass<'tcx> for ImmediatelyBindScoped {
117121 cx,
118122 IMMEDIATELY_BIND_SCOPED ,
119123 expr. span ,
120- "the result of `Scoped<Value>::get` should be immediately bound" ,
124+ format ! (
125+ "the result of `Scoped<Value>::{}` should be immediately bound" ,
126+ scoped_method
127+ ) ,
121128 None ,
122129 "immediately bind the value" ,
123130 ) ;
@@ -142,7 +149,8 @@ fn get_assigned_local(cx: &LateContext<'_>, expr: &Expr) -> Option<HirId> {
142149 }
143150}
144151
145- /// Check if a use of an unbound value is valid (binding or function argument)
152+ /// Check if a use of an unbound value is valid, this is either by being a
153+ /// binding or function argument, or in the return position of a function.
146154fn is_valid_use_of_unbound_value ( cx : & LateContext < ' _ > , expr : & Expr , hir_id : HirId ) -> bool {
147155 // Check if we're in a method call and if so, check if it's a bind call
148156 if let Some ( parent) = get_parent_expr ( cx, expr)
@@ -157,12 +165,12 @@ fn is_valid_use_of_unbound_value(cx: &LateContext<'_>, expr: &Expr, hir_id: HirI
157165 }
158166
159167 // If this is the local being used as a function argument, it's valid
160- if path_to_local_id ( expr, hir_id) && is_in_argument_position ( cx, expr) {
168+ if expr. res_local_id ( ) == Some ( hir_id) && is_in_argument_position ( cx, expr) {
161169 return true ;
162170 }
163171
164172 // If this is the self value of a method call, it's valid
165- if path_to_local_id ( expr, hir_id) && is_in_self_position ( cx, expr) {
173+ if expr. res_local_id ( ) == Some ( hir_id) && is_in_self_position ( cx, expr) {
166174 return true ;
167175 }
168176
@@ -174,16 +182,12 @@ fn is_in_self_position(cx: &LateContext<'_>, expr: &Expr) -> bool {
174182
175183 // Walk up the parent chain to see if we're in a method call
176184 while let Some ( parent) = get_parent_expr ( cx, current_expr) {
177- match parent. kind {
178- // If we find a method call where our expression is in the receiver position
179- ExprKind :: MethodCall ( _, receiver, args, _) => {
180- if receiver. hir_id == current_expr. hir_id {
181- return true ;
182- }
185+ // If we find a method call where our expression is in the receiver position
186+ if let ExprKind :: MethodCall ( _, receiver, _, _) = parent. kind
187+ && receiver. hir_id == current_expr. hir_id {
188+ return true ;
183189 }
184- // Continue walking up for other expression types
185- _ => { }
186- }
190+ // Else continue walking up for other expression types
187191 current_expr = parent;
188192 }
189193
@@ -220,16 +224,33 @@ fn is_in_argument_position(cx: &LateContext<'_>, expr: &Expr) -> bool {
220224 false
221225}
222226
223- fn is_scoped_get_method_call ( cx : & LateContext < ' _ > , expr : & Expr ) -> bool {
227+ enum ScopedMethod {
228+ Get ,
229+ Take ,
230+ }
231+
232+ impl Display for ScopedMethod {
233+ fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
234+ match self {
235+ ScopedMethod :: Get => write ! ( f, "get" ) ,
236+ ScopedMethod :: Take => write ! ( f, "take" ) ,
237+ }
238+ }
239+ }
240+
241+ fn is_scoped_get_or_take_method_call ( cx : & LateContext < ' _ > , expr : & Expr ) -> Option < ScopedMethod > {
224242 if let Some ( ( method, recv, _, _, _) ) = method_call ( expr)
225- && method == "get"
226243 && let typeck_results = cx. typeck_results ( )
227244 && let recv_ty = typeck_results. expr_ty ( recv)
228245 && is_scoped_ty ( cx, & recv_ty)
229246 {
230- true
247+ match method {
248+ "get" => Some ( ScopedMethod :: Get ) ,
249+ "take" => Some ( ScopedMethod :: Take ) ,
250+ _ => None ,
251+ }
231252 } else {
232- false
253+ None
233254 }
234255}
235256
0 commit comments