Skip to content
Permalink

Comparing changes

This is a direct comparison between two commits made in this repository or its related repositories. View the default comparison for this range or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: oxc-project/oxc
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: 5242ec4811882d1132a17c7ff33be138d827b4b6
Choose a base ref
..
head repository: oxc-project/oxc
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: c83ce5c6cf193e68d9b786cc969bcdccc1305091
Choose a head ref
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules.rs
Original file line number Diff line number Diff line change
@@ -238,6 +238,7 @@ mod jest {
pub mod no_untyped_mock_factory;
pub mod prefer_called_with;
pub mod prefer_comparison_matcher;
pub mod prefer_each;
pub mod prefer_equality_matcher;
pub mod prefer_expect_resolves;
pub mod prefer_hooks_in_order;
@@ -517,7 +518,6 @@ mod promise {
mod vitest {
pub mod no_conditional_tests;
pub mod no_import_node_test;
pub mod prefer_each;
pub mod prefer_to_be_falsy;
pub mod prefer_to_be_object;
pub mod prefer_to_be_truthy;
@@ -708,6 +708,7 @@ oxc_macros::declare_all_lint_rules! {
jest::no_test_prefixes,
jest::no_test_return_statement,
jest::no_untyped_mock_factory,
jest::prefer_each,
jest::prefer_called_with,
jest::prefer_comparison_matcher,
jest::prefer_equality_matcher,
@@ -1004,7 +1005,6 @@ oxc_macros::declare_all_lint_rules! {
unicorn::throw_new_error,
vitest::no_conditional_tests,
vitest::no_import_node_test,
vitest::prefer_each,
vitest::prefer_to_be_falsy,
vitest::prefer_to_be_object,
vitest::prefer_to_be_truthy,
62 changes: 52 additions & 10 deletions crates/oxc_linter/src/rules/eslint/prefer_promise_reject_errors.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use oxc_allocator::Box;
use oxc_ast::ast::MemberExpression;
use oxc_ast::{
ast::{Argument, CallExpression, Expression, FormalParameters},
AstKind,
@@ -136,31 +137,65 @@ fn check_reject_call(call_expr: &CallExpression, ctx: &LintContext, allow_empty_
}
}

#[allow(clippy::float_cmp, clippy::cast_precision_loss)]
fn check_reject_in_function(
params: &Box<'_, FormalParameters<'_>>,
ctx: &LintContext,
allow_empty_reject: bool,
) {
if params.parameters_count() <= 1 {
if params.items.len() >= 2 {
let Some(reject_arg) = params.items[1].pattern.get_binding_identifier() else {
return;
};

ctx.symbol_references(reject_arg.symbol_id()).for_each(|reference| {
let Some(node) = ctx.nodes().parent_node(reference.node_id()) else {
return;
};
if let AstKind::CallExpression(call_expr) = node.kind() {
check_reject_call(call_expr, ctx, allow_empty_reject);
}
});
return;
}

let Some(reject_arg) = params.items[1].pattern.get_binding_identifier() else {
return;
};
let Some(rest_param) = &params.rest else { return };
let Some(rest_arg) = rest_param.argument.get_binding_identifier() else { return };
let rest_index = (1 - params.items.len()) as f64;
for reference in ctx.symbol_references(rest_arg.symbol_id()) {
let node = ctx.nodes().get_node(reference.node_id());

ctx.symbol_references(reject_arg.symbol_id()).for_each(|reference| {
let Some(node) = ctx.nodes().parent_node(reference.node_id()) else {
return;
if !matches!(node.kind(), AstKind::IdentifierReference(_)) {
continue;
}

let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else { continue };
let AstKind::MemberExpression(MemberExpression::ComputedMemberExpression(member_expr)) =
parent.kind()
else {
continue;
};

let Expression::NumericLiteral(literal) = &member_expr.expression else {
continue;
};

if literal.value != rest_index {
continue;
}

let Some(node) = ctx.nodes().parent_node(parent.id()) else {
continue;
};

if let AstKind::CallExpression(call_expr) = node.kind() {
check_reject_call(call_expr, ctx, allow_empty_reject);
}
});
}
}

fn is_undefined(arg: &Argument) -> bool {
match arg.as_expression().map(oxc_ast::ast::Expression::get_inner_expression) {
match arg.as_expression().map(Expression::get_inner_expression) {
Some(Expression::Identifier(ident)) => ident.name == "undefined",
_ => false,
}
@@ -196,7 +231,12 @@ fn test() {
("Promise.reject(foo.bar ??= 5)", None),
("Promise.reject(foo[bar] ??= 5)", None),
("class C { #reject; foo() { Promise.#reject(5); } }", None),
("class C { #error; foo() { Promise.reject(this.#error); } }", None)
("class C { #error; foo() { Promise.reject(this.#error); } }", None),
("new Promise(function (resolve, ...rest) { rest[0](new Error('')); });", None),
("new Promise(function (...rest) { rest[0](new Error('')); });", None),
("new Promise(function (...rest) { rest[1](new Error('')); });", None),
// This is fundamentally false, but we can not recognize the value of `i`.
("new Promise(function (resolve, ...rest) { rest[i](5); });", None),
];

let fail = vec![
@@ -255,6 +295,8 @@ fn test() {
// evaluates either to a falsy value of `foo` (which, then, cannot be an Error object), or to `5`
("Promise.reject(foo && 5)", None),
("Promise.reject(foo &&= 5)", None),
("new Promise(function (resolve, ...rest) { rest[0](5); });", None),
("new Promise(function (...rest) { rest[1](5); });", None),
];

Tester::new(PreferPromiseRejectErrors::NAME, PreferPromiseRejectErrors::PLUGIN, pass, fail)
Original file line number Diff line number Diff line change
@@ -64,7 +64,7 @@ declare_oxc_lint!(
/// })
/// ```
PreferEach,
vitest,
jest,
style,
);

@@ -147,7 +147,140 @@ impl PreferEach {
fn test() {
use crate::tester::Tester;

let pass = vec![
let pass_jest = vec![
r#"it("is true", () => { expect(true).toBe(false) });"#,
r#"it.each(getNumbers())("only returns numbers that are greater than seven", number => {
expect(number).toBeGreaterThan(7);
});"#,
r#"it("returns numbers that are greater than five", function () {
for (const number of getNumbers()) {
expect(number).toBeGreaterThan(5);
}
});"#,
r#"it("returns things that are less than ten", function () {
for (const thing in things) {
expect(thing).toBeLessThan(10);
}
});"#,
r#"it("only returns numbers that are greater than seven", function () {
const numbers = getNumbers();
for (let i = 0; i < numbers.length; i++) {
expect(numbers[i]).toBeGreaterThan(7);
}
});"#,
];

let fail_jest = vec![
"for (const [input, expected] of data) {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
describe(\\`when the input is $\\{input}\\`, () => {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
});
}",
"for (const [input, expected] of data) {
describe(\\`when the input is $\\{input}\\`, () => {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
});
}
for (const [input, expected] of data) {
it.skip(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
it.skip(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"it('is true', () => {
expect(true).toBe(false);
});
for (const [input, expected] of data) {
it.skip(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
it.skip(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}
it('is true', () => {
expect(true).toBe(false);
});",
"it('is true', () => {
expect(true).toBe(false);
});
for (const [input, expected] of data) {
it.skip(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}
it('is true', () => {
expect(true).toBe(false);
});",
"for (const [input, expected] of data) {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}
for (const [input, expected] of data) {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
it(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}
for (const [input, expected] of data) {
test(\\`results in $\\{expected}\\`, () => {
expect(fn(input)).toBe(expected)
});
}",
"for (const [input, expected] of data) {
beforeEach(() => setupSomething(input));
test(\\`results in $\\{expected}\\`, () => {
expect(doSomething()).toBe(expected)
});
}",
r#"for (const [input, expected] of data) {
it("only returns numbers that are greater than seven", function () {
const numbers = getNumbers(input);
for (let i = 0; i < numbers.length; i++) {
expect(numbers[i]).toBeGreaterThan(7);
}
});
}"#,
r#"for (const [input, expected] of data) {
beforeEach(() => setupSomething(input));
it("only returns numbers that are greater than seven", function () {
const numbers = getNumbers();
for (let i = 0; i < numbers.length; i++) {
expect(numbers[i]).toBeGreaterThan(7);
}
});
}"#,
];

let pass_vitest = vec![
r#"it("is true", () => { expect(true).toBe(false) });"#,
r#"it.each(getNumbers())("only returns numbers that are greater than seven", number => {
expect(number).toBeGreaterThan(7);
@@ -171,7 +304,7 @@ fn test() {
});"#,
];

let fail = vec![
let fail_vitest = vec![
" for (const [input, expected] of data) {
it(`results in ${expected}`, () => {
expect(fn(input)).toBe(expected)
@@ -286,5 +419,12 @@ fn test() {
"#,
];

let mut pass = vec![];
pass.extend(pass_jest);
pass.extend(pass_vitest);
let mut fail = vec![];
fail.extend(fail_jest);
fail.extend(fail_vitest);

Tester::new(PreferEach::NAME, PreferEach::PLUGIN, pass, fail).test_and_snapshot();
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 356
snapshot_kind: text
---
eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:1]
@@ -240,3 +238,15 @@ snapshot_kind: text
1Promise.reject(foo &&= 5)
· ─────────────────────────
╰────

eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:43]
1new Promise(function (resolve, ...rest) { rest[0](5); });
· ──────────
╰────

eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error
╭─[prefer_promise_reject_errors.tsx:1:34]
1new Promise(function (...rest) { rest[1](5); });
· ──────────
╰────
396 changes: 396 additions & 0 deletions crates/oxc_linter/src/snapshots/jest_prefer_each.snap

Large diffs are not rendered by default.

121 changes: 0 additions & 121 deletions crates/oxc_linter/src/snapshots/vitest_prefer_each.snap

This file was deleted.

1 change: 1 addition & 0 deletions crates/oxc_linter/src/utils/mod.rs
Original file line number Diff line number Diff line change
@@ -29,6 +29,7 @@ const VITEST_COMPATIBLE_JEST_RULES: phf::Set<&'static str> = phf::phf_set! {
"no-identical-title",
"no-restricted-jest-methods",
"no-test-prefixes",
"prefer-each",
"prefer-hooks-in-order",
"valid-describe-callback",
"valid-expect",