fix(linter): fix panic in jest/expect-expect (#3324)

closes #3323
This commit is contained in:
Boshen 2024-05-17 07:56:01 +00:00
parent 27030b9eb4
commit 95e9b6986c
2 changed files with 61 additions and 11 deletions

View file

@ -127,7 +127,8 @@ fn run<'a>(
}
}
let has_assert_function = check_arguments(call_expr, &rule.assert_function_names, ctx);
let has_assert_function =
check_arguments(call_expr, &rule.assert_function_names, None, ctx);
if !has_assert_function {
ctx.diagnostic(expect_expect_diagnostic(call_expr.callee.span()));
@ -139,31 +140,41 @@ fn run<'a>(
fn check_arguments<'a>(
call_expr: &'a CallExpression<'a>,
assert_function_names: &[String],
fn_expr_name: Option<&'a str>,
ctx: &LintContext<'a>,
) -> bool {
call_expr.arguments.iter().any(|argument| {
for argument in &call_expr.arguments {
if let Some(expr) = argument.as_expression() {
return check_assert_function_used(expr, assert_function_names, ctx);
if check_assert_function_used(expr, assert_function_names, fn_expr_name, ctx) {
return true;
}
}
false
})
}
false
}
fn check_assert_function_used<'a>(
expr: &'a Expression<'a>,
assert_function_names: &[String],
fn_expr_name: Option<&'a str>,
ctx: &LintContext<'a>,
) -> bool {
match expr {
Expression::FunctionExpression(fn_expr) => {
let body = &fn_expr.body;
if let Some(body) = body {
return check_statements(&body.statements, assert_function_names, ctx);
let fn_expr_name = fn_expr.id.as_ref().map(|id| id.name.as_str());
return check_statements(
&body.statements,
assert_function_names,
fn_expr_name,
ctx,
);
}
}
Expression::ArrowFunctionExpression(arrow_expr) => {
let body = &arrow_expr.body;
return check_statements(&body.statements, assert_function_names, ctx);
return check_statements(&body.statements, assert_function_names, fn_expr_name, ctx);
}
Expression::CallExpression(call_expr) => {
let name = get_node_name(&call_expr.callee);
@ -171,7 +182,8 @@ fn check_assert_function_used<'a>(
return true;
}
let has_assert_function = check_arguments(call_expr, assert_function_names, ctx);
let has_assert_function =
check_arguments(call_expr, assert_function_names, fn_expr_name, ctx);
return has_assert_function;
}
@ -182,13 +194,24 @@ fn check_assert_function_used<'a>(
let AstKind::Function(function) = node.kind() else {
return false;
};
// Stop recursing into self
if let Some(name) = fn_expr_name {
if function.id.as_ref().is_some_and(|id| id.name.as_str() == name) {
return false;
}
}
let Some(body) = &function.body else {
return false;
};
return check_statements(&body.statements, assert_function_names, ctx);
return check_statements(&body.statements, assert_function_names, fn_expr_name, ctx);
}
Expression::AwaitExpression(expr) => {
return check_assert_function_used(&expr.argument, assert_function_names, ctx);
return check_assert_function_used(
&expr.argument,
assert_function_names,
fn_expr_name,
ctx,
);
}
_ => {}
};
@ -199,11 +222,17 @@ fn check_assert_function_used<'a>(
fn check_statements<'a>(
statements: &'a oxc_allocator::Vec<Statement<'a>>,
assert_function_names: &[String],
fn_expr_name: Option<&'a str>,
ctx: &LintContext<'a>,
) -> bool {
statements.iter().any(|statement| {
if let Statement::ExpressionStatement(expr_stmt) = statement {
return check_assert_function_used(&expr_stmt.expression, assert_function_names, ctx);
return check_assert_function_used(
&expr_stmt.expression,
assert_function_names,
fn_expr_name,
ctx,
);
}
false
})
@ -434,6 +463,18 @@ fn test() {
"#,
None,
),
(
r#"
test("event emitters bound to CLS context", function(t) {
t.test("emitter with newListener that removes handler", function(t) {
ee.on("newListener", function handler(event: any) {
this.removeListener("newListener", handler);
});
});
});
"#,
None,
),
];
Tester::new(ExpectExpect::NAME, pass, fail).with_jest_plugin(true).test_and_snapshot();

View file

@ -119,3 +119,12 @@ expression: expect_expect
3 │ const asyncFunction = async () => {
╰────
help: Add assertion(s) in this Test
⚠ eslint-plugin-jest(expect-expect): Test has no assertions
╭─[expect_expect.tsx:2:13]
1 │
2 │ test("event emitters bound to CLS context", function(t) {
· ────
3 │ t.test("emitter with newListener that removes handler", function(t) {
╰────
help: Add assertion(s) in this Test