Skip to content

feat(linter/react): implement react-compiler rule - #23202

Merged
camc314 merged 4 commits into
mainfrom
feat/linter-react-compiler-rule
Jun 15, 2026
Merged

camc314 merged 4 commits into
mainfrom
feat/linter-react-compiler-rule

Conversation

@Boshen

@Boshen Boshen commented Jun 10, 2026 •

Copy link
Copy Markdown
Member

Adds 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.

Rule

  • run_once rule calling oxc_react_compiler::lint() on the program; the compiler's own prefilter skips files with no React-like functions.
  • Lints with the same compiler options as eslint-plugin-react-compiler (COMPILER_OPTIONS in its RunReactCompiler.ts): lint output mode plus the validations the plugin force-enables (impure functions, capitalized calls, JSX in try, static components, setState in effects, etc.).
  • Reports only error-severity violations by default. Compiler bail-outs (skipped optimizations, e.g. unsupported syntax) are opt-in via reportAllBailouts.
  • Flow suppressions are handled like the eslint plugin: diagnostics whose primary location sits directly below a $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 experimental component declaration syntax that oxc does not parse. RustBackendComparison-test.ts replicates 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_compiler diagnostics 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 into help. This makes code frames and oxlint-disable directives work.

Example output:

⚠ react(react-compiler): RenderSetState: Cannot call setState during render
   ╭─[App.tsx:3:17]
 2 │                 const [state, setState] = useState(0);
 3 │                 setState(1);
   ·                 ────┬───
   ·                     ╰── Found setState() in render
 4 │                 return <div>{state}</div>;
   ╰────
  help: Calling setState during render may trigger an infinite loop.

🤖 Generated with Claude Code

Loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

A-cli Area - CLI A-linter Area - Linter

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants