diff --git a/apps/oxlint/src/snapshots/_--ignore-path fixtures__linter__.customignore --no-ignore fixtures__linter__nan.js@oxlint.snap b/apps/oxlint/src/snapshots/_--ignore-path fixtures__linter__.customignore --no-ignore fixtures__linter__nan.js@oxlint.snap index 6d568ea60d02b..5096157a14dda 100644 --- a/apps/oxlint/src/snapshots/_--ignore-path fixtures__linter__.customignore --no-ignore fixtures__linter__nan.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_--ignore-path fixtures__linter__.customignore --no-ignore fixtures__linter__nan.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: help: Use the isNaN function to compare with NaN. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_--ignore-pattern _____.js --ignore-pattern _____.vue fixtures__linter@oxlint.snap b/apps/oxlint/src/snapshots/_--ignore-pattern _____.js --ignore-pattern _____.vue fixtures__linter@oxlint.snap index f1f1c5b398c6a..588f08796ff29 100644 --- a/apps/oxlint/src/snapshots/_--ignore-pattern _____.js --ignore-pattern _____.vue fixtures__linter@oxlint.snap +++ b/apps/oxlint/src/snapshots/_--ignore-pattern _____.js --ignore-pattern _____.vue fixtures__linter@oxlint.snap @@ -6,7 +6,7 @@ arguments: --ignore-pattern **/*.js --ignore-pattern **/*.vue fixtures/linter working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 0 files with 97 rules using 1 threads. +Finished in ms on 0 files with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_--import-plugin -A all -D no-cycle fixtures__flow__@oxlint.snap b/apps/oxlint/src/snapshots/_--import-plugin -A all -D no-cycle fixtures__flow__@oxlint.snap index 8f959c8e87d10..9d57950fc16b6 100644 --- a/apps/oxlint/src/snapshots/_--import-plugin -A all -D no-cycle fixtures__flow__@oxlint.snap +++ b/apps/oxlint/src/snapshots/_--import-plugin -A all -D no-cycle fixtures__flow__@oxlint.snap @@ -6,7 +6,7 @@ arguments: --import-plugin -A all -D no-cycle fixtures/flow/ working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 2 files with 99 rules using 1 threads. +Finished in ms on 2 files with 100 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_--import-plugin fixtures__flow__index.mjs@oxlint.snap b/apps/oxlint/src/snapshots/_--import-plugin fixtures__flow__index.mjs@oxlint.snap index ff9d8b8f7c01e..8a95dbdfa58c8 100644 --- a/apps/oxlint/src/snapshots/_--import-plugin fixtures__flow__index.mjs@oxlint.snap +++ b/apps/oxlint/src/snapshots/_--import-plugin fixtures__flow__index.mjs@oxlint.snap @@ -6,7 +6,7 @@ arguments: --import-plugin fixtures/flow/index.mjs working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 99 rules using 1 threads. +Finished in ms on 1 file with 100 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_--vitest-plugin -c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap b/apps/oxlint/src/snapshots/_--vitest-plugin -c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap index ef87edbb28814..cde36ed63560f 100644 --- a/apps/oxlint/src/snapshots/_--vitest-plugin -c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_--vitest-plugin -c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap @@ -23,7 +23,7 @@ working directory: help: Remove the appending `.skip` Found 1 warning and 1 error. -Finished in ms on 1 file with 109 rules using 1 threads. +Finished in ms on 1 file with 110 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-D correctness fixtures__linter__debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/_-D correctness fixtures__linter__debugger.js@oxlint.snap index ce5a38d352932..173c9a74e3b71 100644 --- a/apps/oxlint/src/snapshots/_-D correctness fixtures__linter__debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-D correctness fixtures__linter__debugger.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: help: Delete this code. Found 0 warnings and 1 error. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-W correctness -A no-debugger fixtures__linter__debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/_-W correctness -A no-debugger fixtures__linter__debugger.js@oxlint.snap index a514ea6152214..9ac0ebd24d226 100644 --- a/apps/oxlint/src/snapshots/_-W correctness -A no-debugger fixtures__linter__debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-W correctness -A no-debugger fixtures__linter__debugger.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -W correctness -A no-debugger fixtures/linter/debugger.js working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 96 rules using 1 threads. +Finished in ms on 1 file with 97 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__eslintrc_env__eslintrc_no_env.json fixtures__eslintrc_env__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__eslintrc_env__eslintrc_no_env.json fixtures__eslintrc_env__test.js@oxlint.snap index 5030c7705381a..a41380d006010 100644 --- a/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__eslintrc_env__eslintrc_no_env.json fixtures__eslintrc_env__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__eslintrc_env__eslintrc_no_env.json fixtures__eslintrc_env__test.js@oxlint.snap @@ -13,7 +13,7 @@ working directory: `---- Found 1 warning and 0 errors. -Finished in ms on 1 file with 98 rules using 1 threads. +Finished in ms on 1 file with 99 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__no_undef__eslintrc.json fixtures__no_undef__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__no_undef__eslintrc.json fixtures__no_undef__test.js@oxlint.snap index fedd20c9c43e1..c8b15c987b55a 100644 --- a/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__no_undef__eslintrc.json fixtures__no_undef__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-W no-undef -c fixtures__no_undef__eslintrc.json fixtures__no_undef__test.js@oxlint.snap @@ -13,7 +13,7 @@ working directory: `---- Found 1 warning and 0 errors. -Finished in ms on 1 file with 98 rules using 1 threads. +Finished in ms on 1 file with 99 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__config_ignore_patterns__ignore_extension__eslintrc.json fixtures__config_ignore_patterns__ignore_extension@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__config_ignore_patterns__ignore_extension__eslintrc.json fixtures__config_ignore_patterns__ignore_extension@oxlint.snap index c5a053665f027..95bc7ca2113d4 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__config_ignore_patterns__ignore_extension__eslintrc.json fixtures__config_ignore_patterns__ignore_extension@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__config_ignore_patterns__ignore_extension__eslintrc.json fixtures__config_ignore_patterns__ignore_extension@oxlint.snap @@ -12,7 +12,7 @@ working directory: help: Delete this file or add some code to it. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_env__eslintrc_env_browser.json fixtures__eslintrc_env__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_env__eslintrc_env_browser.json fixtures__eslintrc_env__test.js@oxlint.snap index 0916c946e3fdc..e79323f8a1a5b 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_env__eslintrc_env_browser.json fixtures__eslintrc_env__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_env__eslintrc_env_browser.json fixtures__eslintrc_env__test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c fixtures/eslintrc_env/eslintrc_env_browser.json fixtures/eslintrc_ working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 98 rules using 1 threads. +Finished in ms on 1 file with 99 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_off__eslintrc.json fixtures__eslintrc_off__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_off__eslintrc.json fixtures__eslintrc_off__test.js@oxlint.snap index 4d3dbcc5afc13..8f748789bc352 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_off__eslintrc.json fixtures__eslintrc_off__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_off__eslintrc.json fixtures__eslintrc_off__test.js@oxlint.snap @@ -12,7 +12,7 @@ working directory: help: Delete this file or add some code to it. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap index b8610c8fe604b..4ff236ce5fc1d 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__eslintrc_vitest_replace__eslintrc.json fixtures__eslintrc_vitest_replace__foo.test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c fixtures/eslintrc_vitest_replace/eslintrc.json fixtures/eslintrc_v working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__linter__eslintrc.json fixtures__linter__debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__linter__eslintrc.json fixtures__linter__debugger.js@oxlint.snap index 325b661a5d95f..f69b246d08185 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__linter__eslintrc.json fixtures__linter__debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__linter__eslintrc.json fixtures__linter__debugger.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: help: Delete this code. Found 0 warnings and 1 error. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__no_console_off__eslintrc.json fixtures__no_console_off__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__no_console_off__eslintrc.json fixtures__no_console_off__test.js@oxlint.snap index 0779e0924e275..eb0cecc961bc3 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__no_console_off__eslintrc.json fixtures__no_console_off__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__no_console_off__eslintrc.json fixtures__no_console_off__test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c fixtures/no_console_off/eslintrc.json fixtures/no_console_off/test working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__no_empty_allow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_allow_empty_catch__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__no_empty_allow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_allow_empty_catch__test.js@oxlint.snap index 6de0b5b0f2854..916c27aa074ff 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__no_empty_allow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_allow_empty_catch__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__no_empty_allow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_allow_empty_catch__test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c fixtures/no_empty_allow_empty_catch/eslintrc.json -W no-empty fixt working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 98 rules using 1 threads. +Finished in ms on 1 file with 99 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__no_empty_disallow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_disallow_empty_catch__test.js@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__no_empty_disallow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_disallow_empty_catch__test.js@oxlint.snap index 434b55b6251b7..248a3785745eb 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__no_empty_disallow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_disallow_empty_catch__test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__no_empty_disallow_empty_catch__eslintrc.json -W no-empty fixtures__no_empty_disallow_empty_catch__test.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: help: Remove this block or add a comment inside it Found 1 warning and 0 errors. -Finished in ms on 1 file with 98 rules using 1 threads. +Finished in ms on 1 file with 99 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.js -c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.ts -c fixtures__overrides__.oxlintrc.json fixtures__overrides__other.jsx@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.js -c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.ts -c fixtures__overrides__.oxlintrc.json fixtures__overrides__other.jsx@oxlint.snap index 50ab81d4bda08..dadc20f0ffb1e 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.js -c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.ts -c fixtures__overrides__.oxlintrc.json fixtures__overrides__other.jsx@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.js -c fixtures__overrides__.oxlintrc.json fixtures__overrides__test.ts -c fixtures__overrides__.oxlintrc.json fixtures__overrides__other.jsx@oxlint.snap @@ -15,7 +15,7 @@ working directory: help: Replace var with let or const Found 0 warnings and 1 error. -Finished in ms on 1 file with 99 rules using 1 threads. +Finished in ms on 1 file with 100 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- @@ -42,7 +42,7 @@ working directory: help: Delete this console statement. Found 1 warning and 1 error. -Finished in ms on 1 file with 99 rules using 1 threads. +Finished in ms on 1 file with 100 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- @@ -61,7 +61,7 @@ working directory: help: Replace var with let or const Found 0 warnings and 1 error. -Finished in ms on 1 file with 99 rules using 1 threads. +Finished in ms on 1 file with 100 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__overrides__directories-config.json fixtures__overrides@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__overrides__directories-config.json fixtures__overrides@oxlint.snap index 3f814d0191016..5396815ed8312 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__overrides__directories-config.json fixtures__overrides@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__overrides__directories-config.json fixtures__overrides@oxlint.snap @@ -35,7 +35,7 @@ working directory: help: Delete this code. Found 2 warnings and 2 errors. -Finished in ms on 7 files with 96 rules using 1 threads. +Finished in ms on 7 files with 97 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json --disable-typescript-plugin fixtures__typescript_eslint__test.ts@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json --disable-typescript-plugin fixtures__typescript_eslint__test.ts@oxlint.snap index a434dc5da0759..5e701cdecaf3d 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json --disable-typescript-plugin fixtures__typescript_eslint__test.ts@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json --disable-typescript-plugin fixtures__typescript_eslint__test.ts@oxlint.snap @@ -23,7 +23,7 @@ working directory: `---- Found 1 warning and 1 error. -Finished in ms on 1 file with 48 rules using 1 threads. +Finished in ms on 1 file with 49 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json fixtures__typescript_eslint__test.ts@oxlint.snap b/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json fixtures__typescript_eslint__test.ts@oxlint.snap index 6ab12f4b12c34..4548ba332d5e3 100644 --- a/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json fixtures__typescript_eslint__test.ts@oxlint.snap +++ b/apps/oxlint/src/snapshots/_-c fixtures__typescript_eslint__eslintrc.json fixtures__typescript_eslint__test.ts@oxlint.snap @@ -31,7 +31,7 @@ working directory: `---- Found 2 warnings and 1 error. -Finished in ms on 1 file with 59 rules using 1 threads. +Finished in ms on 1 file with 60 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__astro__debugger.astro@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__astro__debugger.astro@oxlint.snap index c1f23e4d8bc60..9efe13bc1a76d 100644 --- a/apps/oxlint/src/snapshots/_fixtures__astro__debugger.astro@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__astro__debugger.astro@oxlint.snap @@ -39,7 +39,7 @@ working directory: help: Delete this code. Found 4 warnings and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap index 7077e6e7a5b04..ba76d052df6cc 100644 --- a/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__linter@oxlint.snap @@ -28,7 +28,7 @@ working directory: help: Use the isNaN function to compare with NaN. Found 3 warnings and 0 errors. -Finished in ms on 3 files with 97 rules using 1 threads. +Finished in ms on 3 files with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js fixtures__linter__nan.js@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js fixtures__linter__nan.js@oxlint.snap index 7c5d651c6eb68..1dfded89c41e6 100644 --- a/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js fixtures__linter__nan.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js fixtures__linter__nan.js@oxlint.snap @@ -21,7 +21,7 @@ working directory: help: Use the isNaN function to compare with NaN. Found 2 warnings and 0 errors. -Finished in ms on 2 files with 97 rules using 1 threads. +Finished in ms on 2 files with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js@oxlint.snap index fc5b3831bc9cd..e6dfdeca45810 100644 --- a/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__linter__debugger.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: help: Delete this code. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__svelte__debugger.svelte@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__svelte__debugger.svelte@oxlint.snap index 7dc46c606ba16..3538ff1bb2556 100644 --- a/apps/oxlint/src/snapshots/_fixtures__svelte__debugger.svelte@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__svelte__debugger.svelte@oxlint.snap @@ -16,7 +16,7 @@ working directory: help: Delete this code. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__vue__debugger.vue@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__vue__debugger.vue@oxlint.snap index 385426831299c..c335bb0f538e4 100644 --- a/apps/oxlint/src/snapshots/_fixtures__vue__debugger.vue@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__vue__debugger.vue@oxlint.snap @@ -23,7 +23,7 @@ working directory: help: Delete this code. Found 2 warnings and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_fixtures__vue__empty.vue@oxlint.snap b/apps/oxlint/src/snapshots/_fixtures__vue__empty.vue@oxlint.snap index 5eed85f8f33eb..829728692f753 100644 --- a/apps/oxlint/src/snapshots/_fixtures__vue__empty.vue@oxlint.snap +++ b/apps/oxlint/src/snapshots/_fixtures__vue__empty.vue@oxlint.snap @@ -6,7 +6,7 @@ arguments: fixtures/vue/empty.vue working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/_foo.asdf@oxlint.snap b/apps/oxlint/src/snapshots/_foo.asdf@oxlint.snap index 8095bc407c5a5..3e4213c24af75 100644 --- a/apps/oxlint/src/snapshots/_foo.asdf@oxlint.snap +++ b/apps/oxlint/src/snapshots/_foo.asdf@oxlint.snap @@ -6,7 +6,7 @@ arguments: foo.asdf working directory: ---------- Found 0 warnings and 0 errors. -Finished in ms on 0 files with 97 rules using 1 threads. +Finished in ms on 0 files with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__auto_config_detection_debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__auto_config_detection_debugger.js@oxlint.snap index 3a1e5de6691b6..351f78d5b244f 100644 --- a/apps/oxlint/src/snapshots/fixtures__auto_config_detection_debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__auto_config_detection_debugger.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: fixtures/auto_config_detection help: Delete this code. Found 0 warnings and 1 error. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__ignore_directory_-c eslintrc.json@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__ignore_directory_-c eslintrc.json@oxlint.snap index 304dd16504312..ed17b47f7ba13 100644 --- a/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__ignore_directory_-c eslintrc.json@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__ignore_directory_-c eslintrc.json@oxlint.snap @@ -14,7 +14,7 @@ working directory: fixtures/config_ignore_patterns/ignore_directory help: Delete this file or add some code to it. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__with_oxlintrc_-c .__test__eslintrc.json --ignore-pattern _.ts .@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__with_oxlintrc_-c .__test__eslintrc.json --ignore-pattern _.ts .@oxlint.snap index 617bc4e00d6a7..82c650d9a6965 100644 --- a/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__with_oxlintrc_-c .__test__eslintrc.json --ignore-pattern _.ts .@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__config_ignore_patterns__with_oxlintrc_-c .__test__eslintrc.json --ignore-pattern _.ts .@oxlint.snap @@ -12,7 +12,7 @@ working directory: fixtures/config_ignore_patterns/with_oxlintrc help: Delete this file or add some code to it. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__disable_eslint_and_unicorn_alias_rules_-c .oxlintrc-eslint.json test.js -c .oxlintrc-unicorn.json test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__disable_eslint_and_unicorn_alias_rules_-c .oxlintrc-eslint.json test.js -c .oxlintrc-unicorn.json test.js@oxlint.snap index 764934e922143..f344b0ecb8b36 100644 --- a/apps/oxlint/src/snapshots/fixtures__disable_eslint_and_unicorn_alias_rules_-c .oxlintrc-eslint.json test.js -c .oxlintrc-unicorn.json test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__disable_eslint_and_unicorn_alias_rules_-c .oxlintrc-eslint.json test.js -c .oxlintrc-unicorn.json test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c .oxlintrc-eslint.json test.js working directory: fixtures/disable_eslint_and_unicorn_alias_rules ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 48 rules using 1 threads. +Finished in ms on 1 file with 49 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- @@ -16,7 +16,7 @@ arguments: -c .oxlintrc-unicorn.json test.js working directory: fixtures/disable_eslint_and_unicorn_alias_rules ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 61 rules using 1 threads. +Finished in ms on 1 file with 62 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__import_-c .oxlintrc.json test.js -c .oxlintrc-import-x.json test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__import_-c .oxlintrc.json test.js -c .oxlintrc-import-x.json test.js@oxlint.snap index 6ce21ddfaed88..6a2cf10d55a2f 100644 --- a/apps/oxlint/src/snapshots/fixtures__import_-c .oxlintrc.json test.js -c .oxlintrc-import-x.json test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__import_-c .oxlintrc.json test.js -c .oxlintrc-import-x.json test.js@oxlint.snap @@ -15,7 +15,7 @@ working directory: fixtures/import `---- Found 0 warnings and 1 error. -Finished in ms on 1 file with 50 rules using 1 threads. +Finished in ms on 1 file with 51 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- @@ -34,7 +34,7 @@ working directory: fixtures/import `---- Found 0 warnings and 1 error. -Finished in ms on 1 file with 50 rules using 1 threads. +Finished in ms on 1 file with 51 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__linter_debugger.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__linter_debugger.js@oxlint.snap index b51710cd14722..bf227f2dfe90e 100644 --- a/apps/oxlint/src/snapshots/fixtures__linter_debugger.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__linter_debugger.js@oxlint.snap @@ -14,7 +14,7 @@ working directory: fixtures/linter help: Delete this code. Found 1 warning and 0 errors. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__output_formatter_diagnostic_--format=default test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__output_formatter_diagnostic_--format=default test.js@oxlint.snap index 53be3460fb0c9..b5f77a93f0617 100644 --- a/apps/oxlint/src/snapshots/fixtures__output_formatter_diagnostic_--format=default test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__output_formatter_diagnostic_--format=default test.js@oxlint.snap @@ -33,7 +33,7 @@ working directory: fixtures/output_formatter_diagnostic help: Delete this code. Found 2 warnings and 1 error. -Finished in ms on 1 file with 97 rules using 1 threads. +Finished in ms on 1 file with 98 rules using 1 threads. ---------- CLI result: LintFoundErrors ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__overrides_env_globals_-c .oxlintrc.json .@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__overrides_env_globals_-c .oxlintrc.json .@oxlint.snap index 34a647377f1fa..fa2245acef4c6 100644 --- a/apps/oxlint/src/snapshots/fixtures__overrides_env_globals_-c .oxlintrc.json .@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__overrides_env_globals_-c .oxlintrc.json .@oxlint.snap @@ -51,7 +51,7 @@ working directory: fixtures/overrides_env_globals `---- Found 5 warnings and 0 errors. -Finished in ms on 3 files with 97 rules using 1 threads. +Finished in ms on 3 files with 98 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/apps/oxlint/src/snapshots/fixtures__two_rules_with_same_rule_name_-c .oxlintrc.json test.js@oxlint.snap b/apps/oxlint/src/snapshots/fixtures__two_rules_with_same_rule_name_-c .oxlintrc.json test.js@oxlint.snap index a388090bc5e29..344d577bfc5f4 100644 --- a/apps/oxlint/src/snapshots/fixtures__two_rules_with_same_rule_name_-c .oxlintrc.json test.js@oxlint.snap +++ b/apps/oxlint/src/snapshots/fixtures__two_rules_with_same_rule_name_-c .oxlintrc.json test.js@oxlint.snap @@ -6,7 +6,7 @@ arguments: -c .oxlintrc.json test.js working directory: fixtures/two_rules_with_same_rule_name ---------- Found 0 warnings and 0 errors. -Finished in ms on 1 file with 61 rules using 1 threads. +Finished in ms on 1 file with 62 rules using 1 threads. ---------- CLI result: LintSucceeded ---------- diff --git a/crates/oxc_linter/fixtures/import/foo.cjs b/crates/oxc_linter/fixtures/import/foo.cjs new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index b86202ad68b4f..9e535c72b654e 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -7,7 +7,7 @@ use oxc_semantic::{AstNode, IsGlobalReference, NodeId, ReferenceId, Semantic, Sy use oxc_span::{GetSpan, Span}; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator}; -use crate::LintContext; +use crate::{utils::get_function_nearest_jsdoc_node, LintContext}; /// Test if an AST node is a boolean value that never changes. Specifically we /// test for: @@ -592,3 +592,181 @@ pub fn could_be_error(ctx: &LintContext, expr: &Expression) -> bool { _ => false, } } + +pub fn is_callee<'a>(node: &AstNode<'a>, semantic: &Semantic<'a>) -> bool { + let parent = outermost_paren_parent(node, semantic); + parent.is_some_and(|node | matches!(node.kind(), AstKind::CallExpression(call_expr) if call_expr.callee.span().contains_inclusive(node.kind().span()))) +} + +fn has_jsdoc_this_tag<'a>(semantic: &Semantic<'a>, node: &AstNode<'a>) -> bool { + let Some(jsdocs) = get_function_nearest_jsdoc_node(node, semantic) + .and_then(|node| semantic.jsdoc().get_all_by_node(node)) + else { + return false; + }; + + for jsdoc in jsdocs { + for tag in jsdoc.tags() { + if tag.kind.parsed() == "this" { + return true; + } + } + } + + false +} + +const METHOD_WHICH_HAS_THIS_ARG: [&str; 10] = [ + "every", + "filter", + "find", + "findIndex", + "findLast", + "findLastIndex", + "flatMap", + "forEach", + "map", + "some", +]; + +pub fn is_default_this_binding<'a>( + semantic: &Semantic<'a>, + node: &AstNode<'a>, + cap_is_constructor: bool, +) -> bool { + let is_anonymous = match node.kind() { + AstKind::Function(func) if cap_is_constructor => { + let is_constructor = func.id.as_ref().is_some_and(|id| { + id.name.chars().next().is_some_and(|char| char.is_ascii_uppercase()) + }); + + if is_constructor || has_jsdoc_this_tag(semantic, node) { + return false; + } + + func.id.is_some() + } + AstKind::StaticBlock(_) => { + return false; + } + _ => true, + }; + + let is_anonymous_and_cap_is_constructor = is_anonymous && cap_is_constructor; + + let mut current_node = node; + loop { + let parent = semantic.nodes().parent_node(current_node.id()).unwrap(); + match parent.kind() { + AstKind::ChainExpression(_) + | AstKind::ConditionalExpression(_) + | AstKind::LogicalExpression(_) + | AstKind::ParenthesizedExpression(_) => { + current_node = parent; + } + AstKind::ReturnStatement(_) => { + let upper_func = semantic.nodes().ancestors(parent.id()).skip(1).find(|node| { + matches!( + node.kind(), + AstKind::Function(_) | AstKind::ArrowFunctionExpression(_) + ) + }); + if upper_func.map_or(true, |node| !is_callee(node, semantic)) { + return true; + } + current_node = parent; + } + AstKind::ArrowFunctionExpression(expr) => { + if current_node.span() != expr.body.span || !is_callee(parent, semantic) { + return true; + } + current_node = parent; + } + AstKind::ObjectProperty(obj) => { + return obj.value.span() != current_node.span(); + } + AstKind::MethodDefinition(method) => { + return method.value.span != current_node.span(); + } + AstKind::PropertyDefinition(def) => { + if let Some(expr) = &def.value { + return expr.span() != current_node.span(); + } + return true; + } + AstKind::AssignmentExpression(expr) => { + if expr.left.is_member_expression() { + return false; + } + + let is_constructor = is_anonymous_and_cap_is_constructor + && expr.left.get_identifier_name().is_some_and(|name| { + name.chars().next().is_some_and(|char| char.is_ascii_uppercase()) + }); + + return !is_constructor; + } + AstKind::AssignmentPattern(pattern) => { + let is_constructor = is_anonymous_and_cap_is_constructor + && pattern.left.get_identifier_name().is_some_and(|name| { + name.chars().next().is_some_and(|char| char.is_ascii_uppercase()) + }); + + return !is_constructor; + } + AstKind::VariableDeclarator(var) => { + let is_constructor = is_anonymous_and_cap_is_constructor + && var.init.as_ref().is_some_and(|init| init.span() == current_node.span()) + && var.id.get_identifier_name().is_some_and(|name| { + name.chars().next().is_some_and(|char| char.is_ascii_uppercase()) + }); + + return !is_constructor; + } + AstKind::MemberExpression(mem_expr) => { + if mem_expr.object().span() == current_node.span() + && matches!(mem_expr.static_property_name(), Some("apply" | "bind" | "call")) + { + let node = outermost_paren_parent(parent, semantic).unwrap(); + if let AstKind::CallExpression(call_expr) = node.kind() { + if let Some(arg) = + call_expr.arguments.first().and_then(|arg| arg.as_expression()) + { + return arg.is_null_or_undefined(); + } + } + } + return true; + } + AstKind::CallExpression(call_expr) => { + if call_expr.callee.is_specific_member_access("Reflect", "apply") { + return call_expr.arguments.len() != 3 + || call_expr.arguments[0].span() != current_node.span() + || call_expr.arguments[1] + .as_expression() + .is_some_and(Expression::is_null_or_undefined); + } + if call_expr.callee.is_specific_member_access("Array", "from") { + return call_expr.arguments.len() != 3 + || call_expr.arguments[1].span() != current_node.span() + || call_expr.arguments[2] + .as_expression() + .is_some_and(Expression::is_null_or_undefined); + } + if call_expr.callee.get_member_expr().is_some_and(|mem_expr| { + mem_expr + .static_property_name() + .is_some_and(|name| METHOD_WHICH_HAS_THIS_ARG.binary_search(&name).is_ok()) + }) { + return call_expr.arguments.len() != 2 + || call_expr.arguments[0].span() != current_node.span() + || call_expr.arguments[1] + .as_expression() + .is_some_and(Expression::is_null_or_undefined); + } + return true; + } + _ => return true, + } + } +} diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index dcfcd606b5b7e..dc32bcdd5f2ef 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -1,11 +1,15 @@ -// Ported from https://github.com/eslint/eslint/tree/v9.9.1/lib/rules/no-eval.js -use oxc_ast::{ast::Expression, AstKind}; +use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_semantic::AstNode; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; -use crate::{context::LintContext, rule::Rule}; +use crate::{ + ast_util::{self}, + config::GlobalValue, + context::LintContext, + rule::Rule, + AstNode, +}; fn no_eval_diagnostic(span: Span) -> OxcDiagnostic { OxcDiagnostic::warn("eval can be harmful.").with_label(span) @@ -13,39 +17,84 @@ fn no_eval_diagnostic(span: Span) -> OxcDiagnostic { #[derive(Debug, Default, Clone)] pub struct NoEval { - /// Whether to allow references to the `eval` function as long as they are - /// not called. For example, the following code is valid if this property is - /// true: - /// - /// ```javascript - /// const foo = eval; - /// foo(); - /// - /// (function(exec) { - /// exec(); - /// })(eval); - /// ``` - /// - /// The default value is `false`. - pub allow_indirect: bool, + allow_indirect: bool, } declare_oxc_lint!( /// ### What it does - /// Disallows referencing the 'eval' function. + /// + /// Disallows referencing the `eval` function. This rule is aimed at preventing + /// potentially dangerous, unnecessary, and slow code by disallowing the use of + /// the `eval()` function. /// /// ### Why is this bad? - /// Calling 'eval' is not supported in some secure contexts and can lead to - /// vulnerabilities. /// - /// ### Example - /// ```javascript - /// const someString = "console.log('pwned')" - /// eval(someString); + /// JavaScript’s `eval()` function is potentially dangerous and is often misused. + /// Using `eval()` on untrusted code can open a program up to several different + /// injection attacks. The use of `eval()` in most contexts can be substituted for + /// a better, safer alternative approach to solving the problem, such as using + /// `JSON.parse()` or `Function` constructors in safer ways. + /// + /// ### Examples + /// + /// Examples of **incorrect** code for this rule: + /// ```js + /// const obj = { x: "foo" }, + /// key = "x", + /// value = eval("obj." + key); + /// + /// (0, eval)("const a = 0"); + /// + /// const foo = eval; + /// foo("const a = 0"); + /// + /// this.eval("const a = 0"); + /// ``` + /// + /// Examples of **correct** code for this rule: + /// ```js + /// const obj = { x: "foo" }, + /// key = "x", + /// value = obj[key]; + /// + /// class A { + /// foo() { + /// this.eval("const a = 0"); + /// } + /// + /// eval() { } + /// + /// static { + /// this.eval("const a = 0"); + /// } + /// + /// static eval() { } + /// } + /// ``` + /// + /// ### Options + /// + /// #### allowIndirect + /// + /// `{ type: boolean, default: false }` + /// + /// This `allowIndirect` option allows indirect `eval()` calls. + /// + /// Indirect calls to `eval`(e.g., `window['eval']`) are less dangerous + /// than direct calls because they cannot dynamically change the scope. + /// Indirect `eval()` calls also typically have less impact on performance + /// compared to direct calls, as they do not invoke JavaScript's scope chain. + /// + /// Example: + /// ```json + /// "eslint/no-eval": [ + /// "error", + /// { "allowIndirect": true } + /// ] /// ``` NoEval, eslint, - restriction + correctness ); impl Rule for NoEval { @@ -58,184 +107,284 @@ impl Rule for NoEval { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let kind = node.kind(); + match node.kind() { + AstKind::Program(_) if !self.allow_indirect => { + let globals = + ["eval", "global", "window", "globalThis"].into_iter().filter(|name| { + ctx.get_global_variable_value(name) + .is_some_and(|var| var != GlobalValue::Off) + }); - if let AstKind::IdentifierReference(ident) = kind { - if ident.name == "eval" { - ctx.diagnostic(no_eval_diagnostic(ident.span)); + for name in globals { + let Some(references) = ctx.scopes().root_unresolved_references().get(name) + else { + continue; + }; + + for reference_id in references { + let reference = ctx.symbols().get_reference(*reference_id); + let node = ctx.nodes().get_node(reference.node_id()); + let mut parent = Self::outermost_mem_expr(node, ctx).unwrap(); + + if name == "eval" { + if !matches!(parent.kind(), AstKind::CallExpression(_)) { + ctx.diagnostic(no_eval_diagnostic(node.span())); + } + } else { + while let Some(mem_expr) = parent.kind().as_member_expression() { + if mem_expr.static_property_name().is_some_and(|p| p == name) { + parent = Self::outermost_mem_expr(parent, ctx).unwrap(); + } else { + break; + } + } + + let Some(mem_expr) = parent.kind().as_member_expression() else { + continue; + }; + let Some((span, name)) = mem_expr.static_property_info() else { + continue; + }; + if name == "eval" { + ctx.diagnostic(no_eval_diagnostic(span)); + } + } + } + } } - return; - } + AstKind::ThisExpression(_) if !self.allow_indirect => { + let parent = ctx.nodes().parent_node(node.id()).unwrap(); + let Some(mem_expr) = parent.kind().as_member_expression() else { return }; + let Some((span, name)) = mem_expr.static_property_info() else { return }; + + if name == "eval" { + let scope_id = ctx.scopes().ancestors(parent.scope_id()).find(|scope_id| { + let scope_flags = ctx.scopes().get_flags(*scope_id); + scope_flags.is_var() && !scope_flags.is_arrow() + }); + + let scope_id = scope_id.unwrap(); + let scope_flags = ctx.scopes().get_flags(scope_id); - let AstKind::MemberExpression(data) = kind else { - return; - }; + // The `TsModuleBlock` shouldn't be considered + if scope_flags.is_ts_module_block() { + return; + } - let Some((eval_span, "eval")) = data.static_property_info() else { - return; - }; + let root = ctx.nodes().get_node(ctx.nodes().root().unwrap()); + let program = root.kind().as_program().unwrap(); - let mut object = Some(data.object().get_inner_expression()); + let is_valid = if scope_flags.is_top() { + program.source_type.is_script() + } else { + let node = ctx.nodes().get_node(ctx.scopes().get_node_id(scope_id)); + ast_util::is_default_this_binding(ctx, node, true) + && (!scope_flags.is_strict_mode() || scope_flags.is_arrow()) + }; - loop { - let (new_object, name) = match object { - Some(object) if object.is_member_expression() => { - let member_expr = object.to_member_expression(); - ( - Some(member_expr.object().get_inner_expression()), - member_expr.static_property_name(), - ) + if is_valid { + ctx.diagnostic(no_eval_diagnostic(span)); + } } - Some(Expression::Identifier(ident)) => (None, Some(ident.name.as_str())), - Some(Expression::ThisExpression(_)) => (None, Some("this")), - None => break, - _ => return, - }; - object = new_object; - - match name { - Some("this") => { - // let scope = ctx.scope(node); - - // if scope.is_get_accessor() - // || scope.is_set_accessor() - // || scope.is_static_block() - // || scope.is_constructor() - // || (scope.is_function() && scope.strict_mode()) - // || (scope.is_top() && ctx.source_type().is_module()) - // { - return; - // } + } + AstKind::CallExpression(call_expr) => { + let is_valid = !self.allow_indirect || !call_expr.optional; + if is_valid && call_expr.callee.is_specific_id("eval") { + ctx.diagnostic(no_eval_diagnostic(call_expr.callee.span())); } - Some("window" | "global" | "globalThis") => {} - _ => return, - }; + } + _ => {} } + } +} - ctx.diagnostic(no_eval_diagnostic(eval_span)); +impl NoEval { + fn outermost_mem_expr<'a, 'b>( + node: &'a AstNode<'b>, + semantic: &'a LintContext<'b>, + ) -> Option<&'a AstNode<'b>> { + semantic.nodes().ancestors(node.id()).skip(1).find(|parent| { + !matches!( + parent.kind(), + AstKind::ParenthesizedExpression(_) | AstKind::ChainExpression(_) + ) + }) } } #[test] fn test() { + use std::path::PathBuf; + use crate::tester::Tester; + #[expect(clippy::unnecessary_wraps)] + fn allow_indirect_with_true() -> Option { + Some(serde_json::json!([{ "allowIndirect": true }])) + } + + #[expect(clippy::unnecessary_wraps)] + fn env_with_node() -> Option { + Some(serde_json::json!({ + "env": { + "node": true + } + })) + } + + #[expect(clippy::unnecessary_wraps)] + fn env_with_browser() -> Option { + Some(serde_json::json!({ + "env": { + "browser": true + } + })) + } + let pass = vec![ - ("Eval(foo)", None), - ("setTimeout('foo')", None), - ("setInterval('foo')", None), - ("window.setTimeout('foo')", None), - ("window.setInterval('foo')", None), - // ("window.eval('foo')", None), - // ("window.eval('foo')", None), - ("window.noeval('foo')", None), - // ("function foo() { var eval = 'foo'; window[eval]('foo') }", None), - // ("global.eval('foo')", None), - // ("global.eval('foo')", None), - ("global.noeval('foo')", None), - // ("function foo() { var eval = 'foo'; global[eval]('foo') }", None), - // ("globalThis.eval('foo')", None), - // ("globalThis.eval('foo')", None), - // ("globalThis.eval('foo')", None), - ("globalThis.noneval('foo')", None), - // ("function foo() { var eval = 'foo'; globalThis[eval]('foo') }", None), - ("this.noeval('foo');", None), - ("function foo() { 'use strict'; this.eval('foo'); }", None), - ("'use strict'; this.eval('foo');", None), - ("this.eval('foo');", None), - ("function foo() { this.eval('foo'); }", None), - ("function foo() { this.eval('foo'); }", None), - ("var obj = {foo: function() { this.eval('foo'); }}", None), - ("var obj = {}; obj.foo = function() { this.eval('foo'); }", None), - ("() => { this.eval('foo') }", None), - ("function f() { 'use strict'; () => { this.eval('foo') } }", None), - ("(function f() { 'use strict'; () => { this.eval('foo') } })", None), - ("class A { foo() { this.eval(); } }", None), - ("class A { static foo() { this.eval(); } }", None), - ("class A { field = this.eval(); }", None), - ("class A { field = () => this.eval(); }", None), - ("class A { static { this.eval(); } }", None), - // ("(0, eval)('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("(0, window.eval)('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("(0, window['eval'])('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("var EVAL = eval; EVAL('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("var EVAL = this.eval; EVAL('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ( - // "(function(exe){ exe('foo') })(eval);", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - // ("window.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("window.window.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("window.window['eval']('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("global.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("global.global.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("this.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ( - // "function foo() { this.eval('foo') }", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - // ("(0, globalThis.eval)('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("(0, globalThis['eval'])('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ( - // "var EVAL = globalThis.eval; EVAL('foo')", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - // ( - // "function foo() { globalThis.eval('foo') }", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - // ( - // "globalThis.globalThis.eval('foo');", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - // ("eval?.('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("window?.eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("(window?.eval)('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), + ("Eval(foo)", None, None, None), + ("setTimeout('foo')", None, None, None), + ("setInterval('foo')", None, None, None), + ("window.setTimeout('foo')", None, env_with_browser(), None), + ("window.setInterval('foo')", None, env_with_browser(), None), + ("window.eval('foo')", None, None, None), + ("window.eval('foo')", None, None, Some(PathBuf::from("foo.cjs"))), + ("window.noeval('foo')", None, env_with_browser(), None), + ("function foo() { var eval = 'foo'; window[eval]('foo') }", None, env_with_browser(), None), + ("global.eval('foo')", None, None, None), + ("global.noeval('foo')", None, None, Some(PathBuf::from("foo.cjs"))), + ("function foo() { var eval = 'foo'; global[eval]('foo') }", None, env_with_node(), Some(PathBuf::from("foo.cjs"))), + // ("globalThis.eval('foo')", None, None, None), // { "ecmaVersion": 2017 }, globalThis is not supported + ("globalThis.noneval('foo')", None, None, None), + ("function foo() { var eval = 'foo'; globalThis[eval]('foo') }", None, env_with_browser(), None), + ("this.noeval('foo');", None, None, None), + ("function foo() { 'use strict'; this.eval('foo'); }", None, None, None), + ("'use strict'; this.eval('foo');", None, None, None), // { "parserOptions": { "ecmaFeatures": { "globalReturn": true } } }, + ("this.eval('foo');", None, None, None), // { "ecmaVersion": 6, "sourceType": "module" }, + ("function foo() { this.eval('foo'); }", None, None, None), + ("var obj = {foo: function() { this.eval('foo'); }}", None, None, None), + ("var obj = {}; obj.foo = function() { this.eval('foo'); }", None, None, None), + ("() => { this.eval('foo') }", None, None, None), // { "ecmaVersion": 6, "sourceType": "module" }, + ("function f() { 'use strict'; () => { this.eval('foo') } }", None, None, None), // { "ecmaVersion": 6 }, + ("(function f() { 'use strict'; () => { this.eval('foo') } })", None, None, None), // { "ecmaVersion": 6 }, + ("class C extends function () { this.eval('foo'); } {}", None, None, None), // { "ecmaVersion": 6 }, + ("class A { foo() { this.eval(); } }", None, None, None), // { "ecmaVersion": 6 }, + ("class A { static foo() { this.eval(); } }", None, None, None), // { "ecmaVersion": 6 }, + ("class A { field = this.eval(); }", None, None, None), // { "ecmaVersion": 2022 }, + ("class A { field = () => this.eval(); }", None, None, None), // { "ecmaVersion": 2022 }, + ("class A { static { this.eval(); } }", None, None, None), // { "ecmaVersion": 2022 }, + ("array.findLast(function (x) { return this.eval.includes(x); }, { eval: ['foo', 'bar'] });", None, None, None), + ("callbacks.findLastIndex(function (cb) { return cb(this.eval); }, this);", None, None, None), + ("['1+1'].flatMap(function (str) { return this.eval(str); }, new Evaluator);", None, None, None), + ("(0, eval)('foo')", allow_indirect_with_true(), None, None), + ("(0, window.eval)('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("(0, window['eval'])('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("var EVAL = eval; EVAL('foo')", allow_indirect_with_true(), None, None), + ("var EVAL = this.eval; EVAL('foo')", allow_indirect_with_true(), None, None), + ("(function(exe){ exe('foo') })(eval);", allow_indirect_with_true(), None, None), + ("window.eval('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("window.window.eval('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("window.window['eval']('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("global.eval('foo')", allow_indirect_with_true(), env_with_node(), None), + ("global.global.eval('foo')", allow_indirect_with_true(), env_with_node(), None), + ("this.eval('foo')", allow_indirect_with_true(), None, None), + ("function foo() { this.eval('foo') }", allow_indirect_with_true(), None, None), + ("(0, globalThis.eval)('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("(0, globalThis['eval'])('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("var EVAL = globalThis.eval; EVAL('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("function foo() { globalThis.eval('foo') }", allow_indirect_with_true(), env_with_browser(), None), + ("globalThis.globalThis.eval('foo');", allow_indirect_with_true(), env_with_browser(), None), + ("eval?.('foo')", allow_indirect_with_true(), None, None), + ("window?.eval('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("(window?.eval)('foo')", allow_indirect_with_true(), env_with_browser(), None), + ("sinon.test(/** @this sinon.Sandbox */function() { this.eval(); });", None, None, Some(PathBuf::from("foo.cjs"))) ]; let fail = vec![ - ("eval(foo)", None), - ("eval('foo')", None), - ("function foo(eval) { eval('foo') }", None), - // ("eval(foo)", Some(serde_json::json!([{ "allowIndirect": true }]))), - // ("eval('foo')", Some(serde_json::json!([{ "allowIndirect": true }]))), + ("eval(foo)", None, None, None), + ("eval('foo')", None, None, None), + ("function foo(eval) { eval('foo') }", None, None, None), + ("eval(foo)", allow_indirect_with_true(), None, None), + ("eval('foo')", allow_indirect_with_true(), None, None), + ("function foo(eval) { eval('foo') }", allow_indirect_with_true(), None, None), + ("(0, eval)('foo')", None, None, None), + ("(0, window.eval)('foo')", None, env_with_browser(), None), + ("(0, window['eval'])('foo')", None, env_with_browser(), None), + ("var EVAL = eval; EVAL('foo')", None, None, None), + ("var EVAL = this.eval; EVAL('foo')", None, None, Some(PathBuf::from("foo.cjs"))), + ( + "'use strict'; var EVAL = this.eval; EVAL('foo')", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), + ( + "function foo() { ('use strict'); this.eval; }", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), + ("() => { this.eval('foo'); }", None, None, Some(PathBuf::from("foo.cjs"))), // { "ecmaVersion": 6 }, + ("() => { 'use strict'; this.eval('foo'); }", None, None, Some(PathBuf::from("foo.cjs"))), // { "ecmaVersion": 6 }, + ("'use strict'; () => { this.eval('foo'); }", None, None, Some(PathBuf::from("foo.cjs"))), // { "ecmaVersion": 6 }, + ( + "() => { 'use strict'; () => { this.eval('foo'); } }", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), // { "ecmaVersion": 6 }, + ("(function(exe){ exe('foo') })(eval);", None, None, None), + ("window.eval('foo')", None, env_with_browser(), None), + ("window.window.eval('foo')", None, env_with_browser(), None), + ("window.window['eval']('foo')", None, env_with_browser(), None), + ("global.eval('foo')", None, env_with_node(), None), + ("global.global.eval('foo')", None, env_with_node(), None), + ("global.global[`eval`]('foo')", None, env_with_node(), None), // { "ecmaVersion": 6, "sourceType": "commonjs" }, + ("this.eval('foo')", None, None, Some(PathBuf::from("foo.cjs"))), + ("'use strict'; this.eval('foo')", None, None, Some(PathBuf::from("foo.cjs"))), + ("function foo() { this.eval('foo') }", None, None, Some(PathBuf::from("foo.cjs"))), + ("var EVAL = globalThis.eval; EVAL('foo')", None, env_with_browser(), None), + ("globalThis.eval('foo')", None, env_with_browser(), None), + ("globalThis.globalThis.eval('foo')", None, env_with_browser(), None), + ("globalThis.globalThis['eval']('foo')", None, env_with_browser(), None), + ("(0, globalThis.eval)('foo')", None, env_with_browser(), None), + ("(0, globalThis['eval'])('foo')", None, env_with_browser(), None), + ("window?.eval('foo')", None, env_with_browser(), None), + ("(window?.eval)('foo')", None, env_with_browser(), None), + ("(window?.window).eval('foo')", None, env_with_browser(), None), + ("class C { [this.eval('foo')] }", None, None, Some(PathBuf::from("foo.cjs"))), // { "ecmaVersion": 2022 }, + ( + "'use strict'; class C { [this.eval('foo')] }", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), // { "ecmaVersion": 2022 }, + ("class A { static {} [this.eval()]; }", None, None, Some(PathBuf::from("foo.cjs"))), // { "ecmaVersion": 2022 }, // ( - // "function foo(eval) { eval('foo') }", - // Some(serde_json::json!([{ "allowIndirect": true }])), - // ), - ("(0, eval)('foo')", None), - ("(0, window.eval)('foo')", None), - ("(0, window['eval'])('foo')", None), - // ("var EVAL = eval; EVAL('foo')", None), - // ("var EVAL = this.eval; EVAL('foo')", None), - // ("'use strict'; var EVAL = this.eval; EVAL('foo')", None), - // ("() => { this.eval('foo'); }", None), - // ("() => { 'use strict'; this.eval('foo'); }", None), - // ("'use strict'; () => { this.eval('foo'); }", None), - // ("() => { 'use strict'; () => { this.eval('foo'); } }", None), - // ("(function(exe){ exe('foo') })(eval);", None), - ("window.eval('foo')", None), - ("window.window.eval('foo')", None), - ("window.window['eval']('foo')", None), - ("global.eval('foo')", None), - ("global.global.eval('foo')", None), - ("global.global[`eval`]('foo')", None), - // ("this.eval('foo')", None), - // ("'use strict'; this.eval('foo')", None), - // ("function foo() { this.eval('foo') }", None), - ("var EVAL = globalThis.eval; EVAL('foo')", None), - ("globalThis.eval('foo')", None), - ("globalThis.globalThis.eval('foo')", None), - ("globalThis.globalThis['eval']('foo')", None), - ("(0, globalThis.eval)('foo')", None), - ("(0, globalThis['eval'])('foo')", None), - ("window?.eval('foo')", None), - ("(window?.eval)('foo')", None), - // ("(window?.window).eval('foo')", None), - // ("class C { [this.eval('foo')] }", None), - // ("'use strict'; class C { [this.eval('foo')] }", None), - // ("class A { static {} [this.eval()]; }", None), - // ("function foo() { 'use strict'; this.eval(); }", None), + // "function foo() { 'use strict'; this.eval(); }", + // None, + // None, + // Some(PathBuf::from("foo.cjs")), + // ), // { "ecmaVersion": 3 }, in es3, "use strict" directives do not apply + ( + "array.findLast(x => this.eval.includes(x), { eval: 'abc' });", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), // { "ecmaVersion": 2023 }, + ("callbacks.findLastIndex(function (cb) { return cb(eval); }, this);", None, None, None), + ( + "['1+1'].flatMap(function (str) { return this.eval(str); });", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), + ( + "['1'].reduce(function (a, b) { return this.eval(a) ? a : b; }, '0');", + None, + None, + Some(PathBuf::from("foo.cjs")), + ), ]; Tester::new(NoEval::NAME, NoEval::PLUGIN, pass, fail).test_and_snapshot(); diff --git a/crates/oxc_linter/src/snapshots/eslint_no_eval.snap b/crates/oxc_linter/src/snapshots/eslint_no_eval.snap index a1f34596e79b1..8624dc9a39796 100644 --- a/crates/oxc_linter/src/snapshots/eslint_no_eval.snap +++ b/crates/oxc_linter/src/snapshots/eslint_no_eval.snap @@ -19,6 +19,24 @@ source: crates/oxc_linter/src/tester.rs · ──── ╰──── + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:1] + 1 │ eval(foo) + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:1] + 1 │ eval('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:22] + 1 │ function foo(eval) { eval('foo') } + · ──── + ╰──── + ⚠ eslint(no-eval): eval can be harmful. ╭─[no_eval.tsx:1:5] 1 │ (0, eval)('foo') @@ -37,6 +55,60 @@ source: crates/oxc_linter/src/tester.rs · ────── ╰──── + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:12] + 1 │ var EVAL = eval; EVAL('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:17] + 1 │ var EVAL = this.eval; EVAL('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:31] + 1 │ 'use strict'; var EVAL = this.eval; EVAL('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:39] + 1 │ function foo() { ('use strict'); this.eval; } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:14] + 1 │ () => { this.eval('foo'); } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:28] + 1 │ () => { 'use strict'; this.eval('foo'); } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:28] + 1 │ 'use strict'; () => { this.eval('foo'); } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:36] + 1 │ () => { 'use strict'; () => { this.eval('foo'); } } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:31] + 1 │ (function(exe){ exe('foo') })(eval); + · ──── + ╰──── + ⚠ eslint(no-eval): eval can be harmful. ╭─[no_eval.tsx:1:8] 1 │ window.eval('foo') @@ -73,6 +145,24 @@ source: crates/oxc_linter/src/tester.rs · ────── ╰──── + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:6] + 1 │ this.eval('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:20] + 1 │ 'use strict'; this.eval('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:23] + 1 │ function foo() { this.eval('foo') } + · ──── + ╰──── + ⚠ eslint(no-eval): eval can be harmful. ╭─[no_eval.tsx:1:23] 1 │ var EVAL = globalThis.eval; EVAL('foo') @@ -120,3 +210,51 @@ source: crates/oxc_linter/src/tester.rs 1 │ (window?.eval)('foo') · ──── ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:18] + 1 │ (window?.window).eval('foo') + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:17] + 1 │ class C { [this.eval('foo')] } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:31] + 1 │ 'use strict'; class C { [this.eval('foo')] } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:27] + 1 │ class A { static {} [this.eval()]; } + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:26] + 1 │ array.findLast(x => this.eval.includes(x), { eval: 'abc' }); + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:51] + 1 │ callbacks.findLastIndex(function (cb) { return cb(eval); }, this); + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:46] + 1 │ ['1+1'].flatMap(function (str) { return this.eval(str); }); + · ──── + ╰──── + + ⚠ eslint(no-eval): eval can be harmful. + ╭─[no_eval.tsx:1:44] + 1 │ ['1'].reduce(function (a, b) { return this.eval(a) ? a : b; }, '0'); + · ──── + ╰──── diff --git a/crates/oxc_linter/src/utils/jsdoc.rs b/crates/oxc_linter/src/utils/jsdoc.rs index 80f2851c1ac56..7e86c5f480244 100644 --- a/crates/oxc_linter/src/utils/jsdoc.rs +++ b/crates/oxc_linter/src/utils/jsdoc.rs @@ -2,11 +2,11 @@ use oxc_ast::{ ast::{BindingPattern, BindingPatternKind, Expression, FormalParameters}, AstKind, }; -use oxc_semantic::JSDoc; +use oxc_semantic::{JSDoc, Semantic}; use oxc_span::Span; use rustc_hash::FxHashSet; -use crate::{config::JSDocPluginSettings, context::LintContext, AstNode}; +use crate::{config::JSDocPluginSettings, AstNode}; /// JSDoc is often attached on the parent node of a function. /// @@ -27,11 +27,11 @@ use crate::{config::JSDocPluginSettings, context::LintContext, AstNode}; /// ``` pub fn get_function_nearest_jsdoc_node<'a, 'b>( node: &'b AstNode<'a>, - ctx: &'b LintContext<'a>, + semantic: &'b Semantic<'a>, ) -> Option<&'b AstNode<'a>> { let mut current_node = node; // Whether the node has attached JSDoc or not is determined by `JSDocBuilder` - while ctx.jsdoc().get_all_by_node(current_node).is_none() { + while semantic.jsdoc().get_all_by_node(current_node).is_none() { // Tie-breaker, otherwise every loop will end at `Program` node! // Maybe more checks should be added match current_node.kind() { @@ -50,13 +50,13 @@ pub fn get_function_nearest_jsdoc_node<'a, 'b>( => { // /** This JSDoc should NOT found for `VariableDeclaration` */ // export const foo = () => {} - let parent_node = ctx.nodes().parent_node(current_node.id())?; + let parent_node = semantic.nodes().parent_node(current_node.id())?; match parent_node.kind() { AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => return Some(parent_node), _ => return None } }, - _ => current_node = ctx.nodes().parent_node(current_node.id())?, + _ => current_node = semantic.nodes().parent_node(current_node.id())?, } }