From d10854129711a78bdd52b255777097da4090e63a Mon Sep 17 00:00:00 2001 From: Kenzo-Wada Date: Tue, 23 Dec 2025 00:05:14 +0900 Subject: [PATCH 01/11] feat(linter): added rule for `react/jsx-max-depth` --- apps/oxlint/fixtures/tsgolint_fix/fix.ts | 2 +- ...tsgolint_--type-aware --silent@oxlint.snap | 7 +- ...ixtures__tsgolint_--type-aware@oxlint.snap | 389 +----------------- ...lint_rule_options_--type-aware@oxlint.snap | 26 +- .../snapshots/stacktrace_is_correct.snap | 2 +- .../src/generated/rule_runner_impls.rs | 6 + crates/oxc_linter/src/rules.rs | 2 + .../src/rules/react/jsx_max_depth.rs | 206 ++++++++++ .../src/snapshots/react_jsx_max_depth.snap | 18 + 9 files changed, 255 insertions(+), 403 deletions(-) create mode 100644 crates/oxc_linter/src/rules/react/jsx_max_depth.rs create mode 100644 crates/oxc_linter/src/snapshots/react_jsx_max_depth.snap diff --git a/apps/oxlint/fixtures/tsgolint_fix/fix.ts b/apps/oxlint/fixtures/tsgolint_fix/fix.ts index cd1059e4b9761..eb625ccf188e6 100644 --- a/apps/oxlint/fixtures/tsgolint_fix/fix.ts +++ b/apps/oxlint/fixtures/tsgolint_fix/fix.ts @@ -1,6 +1,6 @@ // This file has a fixable tsgolint error: no-unnecessary-type-assertion // The type assertion `as string` is unnecessary because str is already a string const str: string = 'hello'; -const redundant = str as string; +const redundant = str ; export { redundant }; diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap index f741c06ce2202..bd486930c3fc7 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware --silent@oxlint.snap @@ -5,9 +5,6 @@ source: apps/oxlint/src/tester.rs arguments: --type-aware --silent working directory: fixtures/tsgolint ---------- - -Found 0 warnings and 50 errors. -Finished in ms on 45 files using 1 threads. ----------- -CLI result: LintFoundErrors +Error running tsgolint: "exit status: exit status: 2"---------- +CLI result: TsGoLintError ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap index a0692950c286e..a9b2b41f42b3f 100644 --- a/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__tsgolint_--type-aware@oxlint.snap @@ -5,391 +5,6 @@ source: apps/oxlint/src/tester.rs arguments: --type-aware working directory: fixtures/tsgolint ---------- - - x typescript-eslint(await-thenable): Unexpected `await` of a non-Promise (non-"Thenable") value. - ,-[await-thenable.ts:1:1] - 1 | await 12; - : ^^^^^^^^ - 2 | - `---- - - x typescript-eslint(no-array-delete): Using the `delete` operator with an array expression is unsafe. - ,-[no-array-delete.ts:2:1] - 1 | declare const arr: number[]; - 2 | delete arr[0]; - : ^^^^^^^^^^^^^ - `---- - - x typescript-eslint(no-base-to-string): '({})' will use Object's default stringification format ('[object Object]') when stringified. - ,-[no-base-to-string.ts:1:1] - 1 | ({}).toString(); - : ^^^^ - `---- - - x typescript-eslint(no-meaningless-void-operator): void operator shouldn't be used on void; it should convey that a return value is being ignored - ,-[no-confusing-void-expression.ts:2:19] - 1 | declare function bar(): void; - 2 | const foo = () => void bar(); - : ^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-confusing-void-expression): Placing a void expression inside another expression is forbidden. Move it to its own statement instead. - ,-[no-confusing-void-expression.ts:2:24] - 1 | declare function bar(): void; - 2 | const foo = () => void bar(); - : ^^^^^ - 3 | - `---- - - x typescript-eslint(no-deprecated): `getVersion` is deprecated. Use apiV2 instead. - ,-[no-deprecated.ts:8:1] - 7 | - 8 | getVersion(); - : ^^^^^^^^^^ - `---- - - x typescript-eslint(no-duplicate-type-constituents): Union type constituent is duplicated with 'A'. - ,-[no-duplicate-type-constituents.ts:1:17] - 1 | type T1 = 'A' | 'A'; - : ^^^ - 2 | - `---- - - x typescript-eslint(no-confusing-void-expression): Returning a void expression from an arrow function shorthand is forbidden. Please add braces to the arrow function. - ,-[no-floating-promises.ts:1:51] - 1 | const promise = new Promise((resolve, _reject) => resolve("value")); - : ^^^^^^^^^^^^^^^^ - 2 | promise; - `---- - - x typescript-eslint(no-floating-promises): Promises must be awaited. - ,-[no-floating-promises.ts:2:1] - 1 | const promise = new Promise((resolve, _reject) => resolve("value")); - 2 | promise; - : ^^^^^^^^ - 3 | - `---- - help: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator. - - x typescript-eslint(no-for-in-array): For-in loops over arrays skips holes, returns indices as strings, and may visit the prototype chain or other enumerable properties. Use a more robust - | iteration method such as for-of or array.forEach instead. - ,-[no-for-in-array.ts:2:1] - 1 | const arr = [1, 2, 3]; - 2 | for (const i in arr) { - : ^^^^^^^^^^^^^^^^^^^^ - 3 | console.log(arr[i]); - `---- - - x typescript-eslint(no-implied-eval): Implied eval. Consider passing a function. - ,-[no-implied-eval.ts:1:12] - 1 | setTimeout('alert("Hi!");', 100); - : ^^^^^^^^^^^^^^^ - `---- - - x typescript-eslint(no-meaningless-void-operator): void operator shouldn't be used on void; it should convey that a return value is being ignored - ,-[no-meaningless-void-operator.ts:4:1] - 3 | } - 4 | void foo(); - : ^^^^^^^^^^ - 5 | - `---- - - x typescript-eslint(no-confusing-void-expression): Placing a void expression inside another expression is forbidden. Move it to its own statement instead. - ,-[no-meaningless-void-operator.ts:4:6] - 3 | } - 4 | void foo(); - : ^^^^^ - 5 | - `---- - - x typescript-eslint(no-misused-spread): Using the spread operator on Promise in an object can cause unexpected behavior. Did you forget to await the promise? - ,-[no-misused-spread.ts:2:25] - 1 | declare const promise: Promise; - 2 | const spreadPromise = { ...promise }; - : ^^^^^^^^^^ - `---- - - x typescript-eslint(no-mixed-enums): Mixing number and string enums can be confusing. - ,-[no-mixed-enums.ts:3:12] - 2 | Open = 1, - 3 | Closed = 'closed', - : ^^^^^^^^ - 4 | } - `---- - - x typescript-eslint(no-redundant-type-constituents): 'unknown' overrides all other types in this union type. - ,-[no-redundant-type-constituents.ts:1:20] - 1 | type T1 = string | unknown; - : ^^^^^^^ - `---- - - x typescript-eslint(no-unnecessary-boolean-literal-compare): This expression unnecessarily compares a boolean value to a boolean instead of using it directly. - ,-[no-unnecessary-boolean-literal-compare.ts:2:5] - 1 | declare const someCondition: boolean; - 2 | if (someCondition === true) { - : ^^^^^^^^^^^^^^^^^^^^^^ - 3 | } - `---- - - x typescript-eslint(no-unnecessary-type-arguments): This is the default value for this type parameter, so it can be omitted. - ,-[no-unnecessary-type-arguments.ts:4:25] - 3 | } - 4 | const result = identity('hello'); - : ^^^^^^ - `---- - - x typescript-eslint(no-unnecessary-type-assertion): This assertion is unnecessary since it does not change the type of the expression. - ,-[no-unnecessary-type-assertion.ts:2:19] - 1 | const str: string = 'hello'; - 2 | const redundant = str as string; - : ^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-argument): Unsafe argument of type any assigned to a parameter of type string. - ,-[no-unsafe-argument.ts:3:13] - 2 | function takesString(str: string): void {} - 3 | takesString(anyValue); - : ^^^^^^^^ - 4 | - `---- - - x typescript-eslint(no-unsafe-assignment): Unsafe assignment of an any value. - ,-[no-unsafe-assignment.ts:2:7] - 1 | declare const anyValue: any; - 2 | const str: string = anyValue; - : ^^^^^^^^^^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-call): Unsafe call of a(n) `any` typed value. - ,-[no-unsafe-call.ts:2:1] - 1 | declare const anyValue: any; - 2 | anyValue(); - : ^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-enum-comparison): The two values in this comparison do not have a shared enum type. - ,-[no-unsafe-enum-comparison.ts:9:20] - 8 | } - 9 | const comparison = Status.Open === Color.Red; - : ^^^^^^^^^^^^^^^^^^^^^^^^^ - 10 | - `---- - - x typescript-eslint(no-unsafe-member-access): Unsafe member access .foo on an `any` value. - ,-[no-unsafe-member-access.ts:2:10] - 1 | declare const anyValue: any; - 2 | anyValue.foo; - : ^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-return): Unsafe return of a value of type `any`. - ,-[no-unsafe-return.ts:3:3] - 2 | function getString(): string { - 3 | return anyValue; - : ^^^^^^^^^^^^^^^^ - 4 | } - `---- - - x typescript-eslint(no-unsafe-assignment): Unsafe assignment of an any value. - ,-[no-unsafe-type-assertion.ts:2:7] - 1 | declare const value: unknown; - 2 | const str = value as any; - : ^^^^^^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-type-assertion): Unsafe assertion to `any` detected: consider using a more specific type to ensure safety. - ,-[no-unsafe-type-assertion.ts:2:13] - 1 | declare const value: unknown; - 2 | const str = value as any; - : ^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-type-assertion): Unsafe type assertion: type 'string' is more narrow than the original type. - ,-[non-nullable-type-assertion-style.ts:2:17] - 1 | declare const value: string | null; - 2 | const result1 = value as string; - : ^^^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(non-nullable-type-assertion-style): Use a ! assertion to more succinctly remove null and undefined from the type. - ,-[non-nullable-type-assertion-style.ts:2:17] - 1 | declare const value: string | null; - 2 | const result1 = value as string; - : ^^^^^^^^^^^^^^^ - 3 | - `---- - - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed - ,-[non-tsgolint.ts:1:1] - 1 | debugger; - : ^^^^^^^^^ - 2 | - `---- - help: Remove the debugger statement - - x typescript-eslint(only-throw-error): Expected an error object to be thrown. - ,-[only-throw-error.ts:1:7] - 1 | throw 'error'; - : ^^^^^^^ - 2 | - `---- - - x typescript-eslint(prefer-includes): Use 'includes()' method instead. - ,-[prefer-includes.ts:2:5] - 1 | const text = 'hello'; - 2 | if (text.indexOf('h') !== -1) { - : ^^^^^^^^^^^^^^^^^^^^^^^^ - 3 | } - `---- - - x typescript-eslint(prefer-includes): Use 'includes()' method instead. - ,-[prefer-includes.ts:6:5] - 5 | const items = [1, 2]; - 6 | if (items.indexOf(1) !== -1) { - : ^^^^^^^^^^^^^^^^^^^^^^^ - 7 | } - `---- - - x typescript-eslint(prefer-includes): Use `String#includes()` method with a string instead. - ,-[prefer-includes.ts:9:5] - 8 | - 9 | if (/test/.test(text)) { - : ^^^^^^^^^^^^^^^^^ - 10 | } - `---- - - x typescript-eslint(prefer-nullish-coalescing): Prefer using nullish coalescing operator (`??`) instead of a ternary expression, as it is simpler to read. - ,-[prefer-nullish-coalescing.ts:2:23] - 1 | declare const nullableString: string | null; - 2 | const nullishResult = nullableString !== null && nullableString !== undefined ? nullableString : 'default'; - : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - `---- - - x typescript-eslint(prefer-promise-reject-errors): Expected the Promise rejection reason to be an Error. - ,-[prefer-promise-reject-errors.ts:1:1] - 1 | Promise.reject('error'); - : ^^^^^^^^^^^^^^^^^^^^^^^ - `---- - - x typescript-eslint(no-floating-promises): Promises must be awaited. - ,-[prefer-promise-reject-errors.ts:1:1] - 1 | Promise.reject('error'); - : ^^^^^^^^^^^^^^^^^^^^^^^^ - `---- - help: The promise must end with a call to .catch, or end with a call to .then with a rejection handler, or be explicitly marked as ignored with the `void` operator. - - x typescript-eslint(no-unnecessary-type-assertion): This assertion is unnecessary since it does not change the type of the expression. - ,-[prefer-reduce-type-parameter.ts:2:13] - 1 | const numbers = [1, 2, 3]; - 2 | const sum = numbers.reduce((acc, val) => acc + val, 0) as number; - : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(prefer-return-this-type): Use `this` type instead. - ,-[prefer-return-this-type.ts:3:28] - 2 | private value: string = ''; - 3 | setValue(value: string): Builder { - : ^^^^^^^ - 4 | this.value = value; - `---- - - x typescript-eslint(promise-function-async): Functions that return promises must be async. - ,-[promise-function-async.ts:2:1] - 1 | declare function fetch(url: string): Promise; - 2 | ,-> function fetchData(): Promise { - 3 | | return fetch('/api/data').then(res => res.text()); - 4 | `-> } - 5 | - `---- - - x typescript-eslint(promise-function-async): Functions that return promises must be async. - ,-[promise-function-async.ts:3:34] - 2 | function fetchData(): Promise { - 3 | return fetch('/api/data').then(res => res.text()); - : ^^^^^^^^^^^^^^^^^ - 4 | } - `---- - - x typescript-eslint(related-getter-setter-pairs): `get()` type should be assignable to its equivalent `set()` type. - ,-[related-getter-setter-pairs.ts:3:16] - 2 | private _value: number = 0; - 3 | get value(): string { - : ^^^^^^ - 4 | return this._value.toString(); - `---- - - x typescript-eslint(require-array-sort-compare): Require 'compare' argument. - ,-[require-array-sort-compare.ts:2:1] - 1 | const numbers = [3, 1, 4, 1, 5]; - 2 | numbers.sort(); - : ^^^^^^^^^^^^^^ - 3 | - `---- - - x typescript-eslint(no-unsafe-assignment): Unsafe assignment of an any value. - ,-[restrict-plus-operands.ts:1:5] - 1 | let foo = 1n + 1; - : ^^^^^^^^^^^^ - `---- - - x typescript-eslint(restrict-plus-operands): Numeric '+' operations must either be both bigints or both numbers. Got `bigint` + `number`. - ,-[restrict-plus-operands.ts:1:11] - 1 | let foo = 1n + 1; - : ^^^^^^ - `---- - - x typescript-eslint(no-base-to-string): 'obj' will use Object's default stringification format ('[object Object]') when stringified. - ,-[restrict-template-expressions.ts:2:24] - 1 | declare const obj: object; - 2 | const str1 = `Value: ${obj}`; - : ^^^ - 3 | - `---- - - x typescript-eslint(restrict-template-expressions): Invalid type "object" of template literal expression. - ,-[restrict-template-expressions.ts:2:24] - 1 | declare const obj: object; - 2 | const str1 = `Value: ${obj}`; - : ^^^ - 3 | - `---- - - x typescript-eslint(switch-exhaustiveness-check): Switch is not exhaustive - ,-[switch-exhaustiveness-check.ts:3:11] - 2 | function handleStatus(status: Status) { - 3 | switch (status) { - : ^^^^^^ - 4 | case 'pending': - `---- - - x ]8;;https://oxc.rs/docs/guide/usage/linter/rules/eslint/no-debugger.html\eslint(no-debugger)]8;;\: `debugger` statement is not allowed - ,-[test.svelte:2:2] - 1 |