feat(linter/react): implement react-compiler rule#23202
Conversation
Merging this PR will degrade performance by 73.33%
|
| Mode | Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|---|
| ❌ | Simulation | linter[RadixUIAdoptionSection.jsx] |
885.6 µs | 10,306.7 µs | -91.41% |
| ❌ | Simulation | linter[App.tsx] |
174.1 ms | 210.4 ms | -17.25% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing feat/linter-react-compiler-rule (e0cd81e) with main (d6d1189)
Footnotes
-
9 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
Add a nursery `react/react-compiler` rule that runs the React Compiler (`oxc_react_compiler`) in lint-only mode and reports Rules of React violations: conditional hook calls, setState during render, ref access during render, mutation errors, etc. - Attach span labels to `oxc_react_compiler` diagnostics (byte offsets flow through from oxc spans), and move long descriptions to `help`, so code frames and disable directives work. - Report only error-severity violations by default; compiler bail-outs are opt-in via `reportAllBailouts`, matching eslint-plugin-react-compiler's default behavior. - Alias the `react-compiler` plugin into the `react` plugin so `react-compiler/react-compiler` from ESLint configs resolves to `react/react-compiler`, same as the existing react-hooks alias.
Port all test cases from eslint-plugin-react-compiler's __tests__ directory (MIT licensed). The only case not ported is 'Basic example with component syntax', which uses Flow's experimental `component` declaration syntax that oxc does not parse. To match the upstream plugin's behavior: - Lint with the same compiler options as the eslint plugin (RunReactCompiler.ts COMPILER_OPTIONS): `lint` output mode plus the validations it force-enables (impure functions, capitalized calls, JSX in try, static components, setState in effects, etc.). - Handle Flow suppressions like the eslint plugin: disable the compiler's own suppression scan and instead drop diagnostics whose primary location sits directly below a `$FlowFixMe[react-rule-hook]` or `$FlowFixMe[react-rule-unsafe-ref]` comment, so other diagnostics in the same function still surface. - Re-export `EnvironmentConfig` from oxc_react_compiler so the rule can configure validations. Also drop the react-compiler plugin aliases — the rule is part of the react plugin.
4430e2f to
1cc29f2
Compare
camc314
left a comment
There was a problem hiding this comment.
nice!
I've marked it explicitly as experimental so that I can make some more changes to it when time allows over the next few weeks.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e0cd81ef8d
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| validate_static_components: true, | ||
| validate_no_freezing_known_mutable_functions: true, | ||
| validate_no_void_use_memo: true, | ||
| validate_no_capitalized_calls: Some(vec![]), |
There was a problem hiding this comment.
Allow React Compiler options to flow through
For projects that intentionally allowlist an uppercase helper call via React Compiler's validateNoCapitalizedCalls config, this hard-coded empty allowlist still reports CapitalizedCalls, and the rule's config type only accepts reportAllBailouts with deny_unknown_fields. That makes the diagnostic's suggested “allowlist it via the compiler config” path impossible in oxlint, so these legitimate cases become unfixable false positives unless users disable the whole rule.
Useful? React with 👍 / 👎.
|
Waited for this! |
### 💥 BREAKING CHANGES - 7a24911 codegen: [**BREAKING**] Borrow sourcemaps from codegen (#23422) (Boshen) - bb0ed44 transformer: [**BREAKING**] Disable styled-components transpileTemplateLiterals by default (#23171) (Boshen) ### 🚀 Features - 1490a0a linter/react: Implement react-compiler rule (#23202) (Boshen) - 6c0bdf0 transformer/react-refresh: Support `module.property.useHook()` (#23190) (Dunqing) - 47991bd semantic: Report TS1228 for invalid type predicates (#23174) (camc314) - 1d3af58 parser: Add TS2398 parameter property diagnostic (#23216) (camc314) - 44313da semantic: Add `scope_is_descendant_of` api (#22313) (camc314) - e5050c0 parser: Improve diagnostic for rest initializer (#23205) (camc314) - ec266bb transformer: Run React Compiler as a feature-gated transform pass (#23201) (Boshen) - e7374fe parser: Report error for `const` modifier on interface type parameter (#23173) (camc314) - a7c1c9b parser: Report ambient definite variable assertions (#23165) (camc314) - d169fcd parser: Report invalid class definite assertions (#23164) (camc314) - 00244d8 parser: Report definite property initializer errors (#23160) (camc314) ### 🐛 Bug Fixes - 52d0c31 transformer: Replace ambient dot defines (#23231) (camc314) - 2c28748 transformer/class: Parent generated constructors to class scope (#23222) (camc314) - 8edd234 parser: Report accessor definite assertion on token (#23203) (camc314) - de38a3f react_compiler: Keep imports referenced only by a local re-export (#23176) (Boshen) - f5721c2 codegen: Preserve parentheses around `intrinsic` type reference (#23156) (Boshen) - e89f81d parser: Don't emit TS1477 for parenthesized instantiation expression (#23147) (Boshen) - 8a04149 parser: Reject module-referencing imports/exports in a namespace body (#22829) (Boshen) ### ⚡ Performance - 2783295 parser: Table-driven operator precedence lookup (#23346) (Boshen) - 231d5de parser: Single-match member expression dispatch (#23347) (Boshen) - e89729b codegen: Accept one-shot wrap closures (#23265) (camc314) - a6c11fa parser: Force-inline read_non_decimal to fold per-digit number dispatch (#23157) (Boshen) - d74964c parser: Store class definite assertion offset (#23170) (camc314) - f0fda4d parser: Shrink-wrap cold diagnostic tails out of hot frames (#23159) (Boshen) - a082180 parser: Store definite assertion offset (#23167) (camc314) - 534f9c6 oxc: Conditionally rebuild semantic in compiler pipeline (#23153) (Boshen) - b435c6a parser: Skip checkpoint for `infer T extends U` constraint in disallow context (#23128) (Boshen) - 7464dce parser: Peek instead of checkpoint/rewind for `export default` modifier (#23124) (Boshen) - 80a9a32 parser: Fast-path single-keyword TS declarations (#23083) (Boshen) - da1a6c6 diagnostics: Migrate to allocation-optimized oxc-miette (#23094) (Boshen) - b7b08ce parser: Peek once for the static modifier disambiguation (#23079) (Boshen) - e7e07a3 parser: Fold unary dispatch into a single match (#23076) (Boshen) ### 📚 Documentation - d241add semantic: Add `AGENTS.md` test guidance for agents (#23441) (camc314) - 026f1ae parser: Add `AGENTS.md` test guidance for agents (#23440) (camc314) - 09755ac transformer: Add `AGENTS.md` test guidance for agents (#23439) (camc314) - e6bdfd4 lexer: Correct reference link for `byte_handlers!` (#23313) (Dunqing) - 65b6d7a allocator: Fix memory leaks in `Arena` examples (#23257) (overlookmotel) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
# Oxlint ### 🚀 Features - 5e1627d linter/unicorn: Flag all Error() calls without new in `throw-new-error` (#23363) (Cason Kervis) - 2e8bda4 linter/vue: Implement no-dupe-keys rule (#23350) (bab) - 1490a0a linter/react: Implement react-compiler rule (#23202) (Boshen) - dd560ae linter/unicorn: Implement `no-array-fill-with-reference-type` rule (#23397) (Mikhail Baev) - af36c2f linter: Add schema for `react/jsx-curly-brace-presence` (#23400) (WaterWhisperer) - 47d34a3 linter: Add schema for `react/jsx-handler-names` (#23393) (WaterWhisperer) - f4250d0 linter: Add schema for `unicorn/import-style` (#23386) (WaterWhisperer) - 30c74ce linter: Add schema for `jsx_a11y/no-noninteractive-element-to-interactive-role` (#23384) (Sysix) - cfbe8dc linter: Add schema for `jsx_a11y/no-interactive-element-to-noninteractive-role` (#23382) (WaterWhisperer) - d15b7ff linter: Add schema for `typescript/no-restricted-types` (#23381) (WaterWhisperer) - 028a811 linter: Add schema for `jsx-a11y/media-has-caption` (#23377) (Sysix) - b3b1038 linter: Add schema for `jsx-a11y/label-has-associated-control` (#23376) (Sysix) - 7ada6b2 linter: Add schema for `jsx_a11y/no-distracting-elements` (#23379) (WaterWhisperer) - ee3dd49 linter: Add schema for `jsx-a11y/img-redundant-alt` (#23374) (Sysix) - df5f8dd linter: Add short descriptions to most lint rules. (#23365) (Connor Shea) - e3fd735 linter: Add schema for `jsx_a11y/alt-text` (#23369) (Sysix) - 0f2fff4 linter: Add schema for `react/exhaustive-deps` (#23372) (Mikhail Baev) - e3e4e10 linter: Add schema for `react_perf/jsx-no-new-object-as-prop` (#23368) (Mikhail Baev) - 9366d44 linter: Add schema for `unicorn/prefer-at` (#23366) (WaterWhisperer) - f57b55d linter: Add schema for `typescript/array-type` (#23355) (Sysix) - 0dcf912 linter: Add schema for `typescript/ban-ts-comment` (#23354) (Sysix) - 51fa83e linter: Add schema for `react/no-did-update-set-state` (#23357) (Mikhail Baev) - 59db0bd linter: Add schema for `consistent-generic-constructors` (#23353) (Sysix) - c4775c0 linter: Add schema for `typescript/consistent-type-assertions` (#23349) (Sysix) - 6e516f7 linter: Add schema for `typescript/consistent-type-imports` (#23348) (Sysix) - 012134d linter: Add schema for `react/jsx-no-target-blank` (#23345) (WaterWhisperer) - 0806aae linter: Add schema for `jsx_a11y/no-noninteractive-tabindex` (#23337) (Mikhail Baev) - 0708b5a linter: Add schema for `react/jsx-filename-extension` (#23315) (Mikhail Baev) - 150bce1 linter: Add schema for `typescript/no-empty-object-type` (#23309) (Sysix) - f9e36f1 linter: Add schema for `typescript/no-duplicate-type-constituents` (#23308) (Sysix) - 937accf linter: Add schema for `typescript/no-invalid-void-type` (#23307) (Sysix) - 3e042b9 linter: Add schema for `typescript/no-misused-promises` (#23306) (Sysix) - da212d1 linter: Add schema for `typescript/no-unnecessary-condition` (#23305) (Sysix) - f8f0d38 linter: Add schema for `typescript/parameter-properties` (#23304) (Sysix) - 2275fc7 linter: Add schema for `typescript/prefer-nullish-coalescing` (#23302) (Sysix) - d353858 linter: Add schema for `typescript/prefer-string-starts-ends-with` (#23301) (Sysix) - 03060f5 linter: Add schema for `typescript/triple-slash-reference` (#23300) (Sysix) - 6619cee linter: Add schema for `promise/param-names` (#23298) (Sysix) - 8bf108e linter: Add schema for `promise/catch-or-return` (#23297) (Sysix) - 48158d0 linter: Add schema for `vitest/consistent-each-for` (#23294) (Sysix) - 7e74c98 linter: Add schema for `vitest/consistent-test-filename` (#23293) (Sysix) - ff94d4a linter: Add schema for `vitest/consistent-vitest-vi` (#23292) (Sysix) - 2409a10 linter: Add schema for `vitest/prefer-import-in-mock` (#23291) (Sysix) - 3d782b7 linter: Add schema for `react/no-unstable-nested-components` (#23287) (Mikhail Baev) - 0a0bc2f linter/jsx-a11y: Add `allowedRedundantRoles` option to `no-redundant-roles` (#22820) (bab) - 80758a5 linter/vue: Implement no-side-effects-in-computed-properties rule (#23282) (bab) - e3869ac linter: Add schema for `react/no-object-type-as-default-prop` (#23279) (Mikhail Baev) - 4480609 linter: Add schema for `react/jsx-props-no-spreading` (#23276) (Mikhail Baev) - 08d68a5 linter/react: Implement `jsx-no-literals` rule (#23145) (kapobajza) - 9a2788b linter/unicorn: Implement `prefer-export-from` rule (#22935) (AliceLanniste) - bdb723c linter/unicorn: Implement prefer-single-call rule (#23235) (Yuzhe Shi) - 31543ed linter: Add schema for `vue/define-props-destructuring` (#23252) (Sysix) - 21b6c3d linter: Add schema for `oxc/no-async-endpoint-handlers` (#23251) (Sysix) - e77ff81 linter: Add schema for `unicorn/prefer-object-from-entries` (#23249) (Mikhail Baev) - bcac2d6 linter: Add schema for `jest/vitest/no-restricted-matchers` (#23247) (Sysix) - 539f036 linter: Add schema for `jest/vitest/no-restricted-*-methods` (#23246) (Sysix) - dd1b927 linter/vue: Implement require-default-prop rule (#22951) (bab) - 3f018e7 linter: Add schema for `unicorn/no-instanceof-builtins` (#23225) (Mikhail Baev) - 5b9a793 linter/react-perf: Support `nativeAllowList` config (#22996) (Cole Ellison) - b4e19d4 linter: Improve promise/catch-or-return termination diagnostics (#23218) (camc314) - 9b72a41 linter/promise/catch-or-return: Add support for `allowThenStrict` option (#23206) (camc314) - e0d0f78 linter: Verify promise/no-callback-in-promise schema (#23141) (beanscg) - 123d4f4 linter: Add schema for `jest/vitest/valid-expect` (#23185) (Sysix) - 46c8a21 linter: Add schema for `jest/vitest/require-top-level-describe` (#23184) (Sysix) - 41465cf linter: Add schema for `jest/vitest/prefer-snapshot-hint` (#23183) (Sysix) - d068b9b linter: Add schema for `jest/vitest/prefer-expect-assertions` (#23181) (Sysix) - 064a1ee linter: Add schema for `jest/prefer-ending-with-an-expect` (#23180) (Sysix) - d046797 linter: Add schema for `jest/vitest/no-standalone-expect` (#23179) (Sysix) - 137b9a6 linter: Add schema for `jest/vitest/no-large-snapshots` (#23178) (Sysix) - 0f3e4a5 linter: Add schema for `jest/vitest/no-hooks` (#23177) (Sysix) - 00244d8 parser: Report definite property initializer errors (#23160) (camc314) - cd0b384 linter: Add schema for `unicorn/explicit-length-check` (#23155) (Mikhail Baev) - 01b74c4 linter: Add schema for `jest/no-deprecated-functions` (#23136) (Sysix) - 9d6a387 linter: Add schema for `unicorn/catch-error-name` (#23137) (Mikhail Baev) - 0da8efa linter: Add schema for `jest/vitest/max-nested-describe` (#23131) (Sysix) - d71c9fd linter: Add schema for `eslint/no-use-before-define` (#23129) (Sysix) ### 🐛 Bug Fixes - 019be2e linter/unicorn: Align throw-new-error parity (#23437) (camc314) - b9e5e51 linter/jest: Validate no-restricted-jest-methods config (#23436) (camc314) - f302f41 linter: Normalize oxlint jsPlugin prefixes (#23335) (konh) - 2bdf8ce linter/jest: Validate no-restricted-matchers config (#23434) (camc314) - 5c2fb45 linter/eslint: Validate max-depth config (#23430) (camc314) - eafc280 linter/eslint: Validate no-unused-vars config (#23433) (camc314) - 25aebb0 linter/typescript: Validate no-empty-object-type config (#23432) (camc314) - 7cafe3b linter/unicorn: Validate catch-error-name config (#23431) (camc314) - af1b897 linter/unicorn: Validate prefer-object-from-entries config (#23429) (camc314) - 68d48fe linter/eslint: Validate max-classes-per-file config (#23428) (camc314) - 02932b6 linter/eslint: Validate `complexity` config (#23427) (camc314) - 375e574 linter/eslint: Use shared id-length regex config (#23426) (camc314) - ba3e2e0 linter/eslint: Preserve used imports in no-unused-vars fixer (#23425) (camc314) - 26ddac6 linter: Avoid config schema generation for `jsx_a11y/no-noninteractive-element-interactions` (#23385) (Sysix) - 40556ad linter: Parse `jsx-a11y/control-has-associated-label` config with `DefaultRuleConfig` (#23373) (Sysix) - dacbda8 linter/unicorn/prefer-string-starts-ends-with: Mark rule as deprecated (#23342) (camc314) - 956309a lsp: Use protocol line breaks for positions (#23329) (konh) - 71e9648 linter: Expose no-noninteractive-element-interactions schema (#23283) (camc314) - 4c4c19c linter: `no-noninteractive-element-interactions` fix default values (#23288) (Sysix) - e63a139 linter/jsx-a11y: Restore control label defaults (#22737) (konh) - 0e4e59f linter/jsx-a11y: No-redundant-roles attribute-aware implicit roles for `<input>`, skip ancestor-conditional elements (#23015) (Gaurav Dubey) - 06e215e linter/vitest: Validate `consistent-each-for` config (#23278) (camc314) - cd278e1 linter/promise: Validate param-names config (#23277) (camc314) - 7f46013 linter/react/display-name: Handle default export function correctly (#23271) (camc314) - 22ff6a6 linter/unicorn: Deserialize filename-case config with serde (#23266) (camc314) - b644da0 linter/eslint: Add fixer for `sort_vars` (#23262) (Cason Kervis) - 778af4d linter/unicorn: Support array of patterns for `unicorn/filename-case` `ignore` option (#23260) (kapobajza) - 127fb2c linter: Deserialize rule config with serde for `unicorn/prefer-object-from-entries` (#23253) (Sysix) - f89b909 linter/react/jsx-no-target-blank: Handle compound conditions (#23245) (camc314) - c30419a linter/typescript: Deserialize rule config with serde for `consistent-generic-constructors` (#23238) (camc314) - 7d17d37 linter/react: Report invalid exhaustive-deps config (#23239) (camc314) - 1060186 linter/eslint: Add `deny_unknown_fields` to `prefer-destructuring` (#23237) (camc314) - fd2e4eb linter/react: Add `deny_unknown_fields` to `exhaustive-deps` (#23236) (camc314) - 6c3c904 linter/react/jsx-curly-brace-presence: Avoid false positive with comment within braces (#23234) (camc314) - 6c86d1c linter/react-perf: Correct nativeAllowList all schema (#23229) (camc314) - c393760 linter/react/rules-of-hooks: Align try/catch semantics (#23221) (camc314) - 4dd52de linter/react-perf: Re-generate stale snapshots (#23228) (camc314) - de2d23b linter/unicorn/prefer-dom-node-dataset: Preserve optional chaining in fixer (#23148) (Bruno Carvalho) - d7038de linter/react/exhaustive-deps: Ignore non `React` namespace hooks (#23175) (camc314) - 7111903 linter/react/exhaustive-deps: FP with wrapped literal expressions (#23172) (camc314) - 93326e7 linter/react: Align hook errors with anonomous async callbacks (#23158) (camc314) - 8f3db61 linter: Allow options for `eslint/capitalized-comments` (#23139) (Sysix) - f533565 linter: Support nofunc string option in no-use-before-define (#23115) (bab) ### ⚡ Performance - f09707e linter: `jest/no-deprecated-functions` store config version as `usize` (#23138) (Sysix) - da1a6c6 diagnostics: Migrate to allocation-optimized oxc-miette (#23094) (Boshen) ### 📚 Documentation - f682e25 linter: Remove manually written options doc for `eslint/prefer-arrow-callback` (#23438) (Mikhail Baev) - 64c942c linter: Remove manually written options doc for `eslint/no-sequences` (#23420) (Mikhail Baev) - d6d1189 linter: Add `short_descriptions` to more rules (#23417) (Connor Shea) - b979722 linter: Clarify valid-define-emits description (#23408) (camc314) - ccd0a69 linter: Clarify only-used-in-recursion description (#23407) (camc314) - b0fd30b linter: Clarify no-sync-scripts description (#23406) (camc314) - c23290d linter: Clarify no-cycle description (#23405) (camc314) - 3612e0b linter: Clarify no-nodejs-modules description (#23403) (camc314) - c67c5b1 linter: Clarify no-misused-new description (#23402) (camc314) - 88bf0bc linter: Clarify require-render-return description (#23401) (camc314) - 9fff263 linter: Add short descriptions to more rules that lacked them (#23389) (Connor Shea) - ac69042 linter: Add short_description fields to more lint rules (#23391) (Connor Shea) - 99a3c78 linter: Add info for more skipped rule schemas (#23380) (Sysix) - c966384 linter: Fix the example of `unicorn/prefer-string-replace-all` (#23367) (Liang) - 2120ecf linter: Describe problems on not verified rules schema (#23351) (Sysix) - 14abf32 linter/react-perf: Use autogenerated docs (#23227) (camc314) # Oxfmt ### 🚀 Features - 9a2788b linter/unicorn: Implement `prefer-export-from` rule (#22935) (AliceLanniste) - 0c6fb23 oxfmt: Format `parser:json-stringify` files by `oxc_formatter_json` (#23194) (leaysgur) - 8cc82c4 formatter_json: Implement json-stringify variant (#23192) (leaysgur) ### 🐛 Bug Fixes - 956309a lsp: Use protocol line breaks for positions (#23329) (konh) - 769de65 oxfmt: Handle compound json extensions (#23198) (leaysgur) - b0b5d39 formatter_json: Support JSON5/JSON6 value in json/jsonc/json5 (#23193) (leaysgur) ### 📚 Documentation - 4986613 formatter_json: Update AGENTS.md (#23199) (leaysgur) Co-authored-by: Boshen <1430279+Boshen@users.noreply.github.com>
Adds a nursery
react/react-compilerrule that runs the React Compiler (oxc_react_compiler) in lint-only mode and reports Rules of React violations: conditional hook calls, setState during render, ref access during render, mutation errors, etc.Rule
run_oncerule callingoxc_react_compiler::lint()on the program; the compiler's own prefilter skips files with no React-like functions.eslint-plugin-react-compiler(COMPILER_OPTIONSin itsRunReactCompiler.ts):lintoutput mode plus the validations the plugin force-enables (impure functions, capitalized calls, JSX in try, static components, setState in effects, etc.).reportAllBailouts.$FlowFixMe[react-rule-hook]/$FlowFixMe[react-rule-unsafe-ref]comment are dropped, while other diagnostics in the same function still surface.Tests
All test cases from
eslint-plugin-react-compiler/__tests__(MIT) are ported, covering hooks order, capitalized calls, impure calls in render, JSX in try/catch, ref access in render, useMemo diagnostics, useState mutation (TS),'use memo'/'use no forget'directives, and Flow suppressions. The only case not ported is "Basic example with component syntax", which uses Flow's experimentalcomponentdeclaration syntax that oxc does not parse.RustBackendComparison-test.tsreplicates the same cases for backend comparison, so it adds no new ones. Every diagnostic message and location in the snapshot matches the upstream assertions.Diagnostics
oxc_react_compilerdiagnostics previously had no spans. They now carry labeled spans built from the compiler's source locations (byte offsets flow through from oxc spans):detail.loc→ sub-detail locations with their messages (e.g. "Found setState() in render") → enclosing function as fallback. Long descriptions moved from the message intohelp. This makes code frames andoxlint-disabledirectives work.Example output:
🤖 Generated with Claude Code