Skip to content

Commit 7ea0a0e

Browse files
committed
fix: Aapos suggestions
1 parent eafee71 commit 7ea0a0e

File tree

2 files changed

+47
-26
lines changed

2 files changed

+47
-26
lines changed

nova_lint/src/immediately_bind_scoped.rs

Lines changed: 46 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
use std::ops::ControlFlow;
1+
use std::{fmt::Display, ops::ControlFlow};
22

33
use crate::{is_scoped_ty, method_call};
44
use 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;
1617
dylint_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

5458
impl<'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.
146154
fn 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

nova_lint/ui/immediately_bind_scoped.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
use nova_vm::{
44
ecmascript::{execution::Agent, types::Value},
55
engine::{
6-
context::{Bindable, GcScope, NoGcScope},
76
Scoped,
7+
context::{Bindable, NoGcScope},
88
},
99
};
1010

0 commit comments

Comments
 (0)