Eslint is expecting a map of [string] => RuleModule. Before we were
passing {rule: RuleModule, severity: ErrorSeverity} which was breaking
legacy Eslint configurations
Adds a `@enableNameAnonymousFunctions` feature to infer helpful names
for anonymous functions within components and hooks. The logic is
inspired by a custom Next.js transform, flagged to us by @eps1lon, that
does something similar. Implementing this transform within React
Compiler means that all React (Compiler) users can benefit from more
helpful names when debugging.
The idea builds on the fact that JS engines try to infer helpful names
for anonymous functions (in stack traces) when those functions are
accessed through an object property lookup:
```js
({'a[xyz]': () => {
throw new Error('hello!')
} }['a[xyz]'])()
// Stack trace:
Uncaught Error: hello!
at a[xyz] (<anonymous>:1:26) // <-- note the name here
at <anonymous>:1:60
```
The new NameAnonymousFunctions transform is gated by the above flag,
which is off by default. It attemps to infer names for functions as
follows:
First, determine a "local" name:
* Assigning a function to a named variable uses the variable name.
`const f = () => {}` gets the name "f".
* Passing the function as an argument to a function gets the name of the
function, ie `foo(() => ...)` get the name "foo()", `foo.bar(() => ...)`
gets the name "foo.bar()". Note the parenthesis to help understand that
it was part of a call.
* Passing the function to a known hook uses the name of the hook,
`useEffect(() => ...)` uses "useEffect()".
* Passing the function as a JSX prop uses the element and attr name, eg
`<div onClick={() => ...}` uses "<div>.onClick".
Second, the local name is combined with the name of the outer
component/hook, so the final names will be strings like `Component[f]`
or `useMyHook[useEffect()]`.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34410).
* #34434
* __->__ #34410
The compiler playground was crashing at any small syntax errors in the
`Input` panel due to updating the `CompilerErrorDetailOptions` type in
#34401. Updated the option to take in a `ErrorCategory` instead.
---------
Co-authored-by: lauren <poteto@users.noreply.github.com>
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
Added a "Show Internals" toggle switch to either show only the Config,
Input, Output, and Source Map tabs, or these tabs + all the additional
compiler options. The open/close state of these tabs will be preserved
(unless on page refresh, which is the same as the currently
functionality).
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
## How did you test this change?
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
https://github.com/user-attachments/assets/8eb0f69e-360c-4e9b-9155-7aa185a0c018
Adds missing locations to all the statement kinds that we produce in
codegenInstruction(), and adds generic handling of source locations for
the nodes produced by codegenInstructionValue(). There are definitely
some places where we are still missing a location, but this should
address some of the known issues we've seen such as missing location on
`throw`.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34406).
* #34394
* __->__ #34406
* #34346
Small fix to make all descriptions consistently printed with a single
period at the end.
Ran `grep -rn "description:" packages/babel-plugin-react-compiler/src
--include="*.ts" --exclude-dir="__tests__" | grep '\.\s*["\`]'` to find
all descriptions ending in a period and manually fixed them.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34404).
* #34409
* __->__ #34404
Now that we have a new CompilerDiagnostic type (which the CompilerError
aggregate can hold), the old CompilerErrorDetail type can be marked as
deprecated. Eventually we should migrate everything to the new
CompilerDiagnostic type.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34402).
* #34409
* #34404
* #34403
* __->__ #34402
* #34401
With #34176 we now have granular lint rules created for each compiler
ErrorCategory. However, we had remnants of our old error severities
still in use which makes reporting errors quite clunky. Previously you
would need to specify both a category and severity which often ended up
being the same.
This PR moves severity definition into our rules which are generated
from our categories. For now I decided to defer "upgrading" categories
from a simple string to a sum type since we are only using severities to
map errors to eslint severity.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34401).
* #34409
* #34404
* #34403
* #34402
* __->__ #34401
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
Part 3 of adding a "Config Override" panel to the React compiler
playground. Added a button to apply config changes to the Input panel,
as well as making the tab collapsible. Added validation for the the
PluginOptions type (although comes with a bit more boilerplate) to make
it very obvious what the possible config errors could be. Added some
toasts for trying to apply broken configs.
<!--
Explain the **motivation** for making this change. What existing problem
does the pull request solve?
-->
## How did you test this change?
https://github.com/user-attachments/assets/63ab8636-396f-45ba-aaa5-4136e62ccccc
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
I tried turning on `@enablePreserveExistingMemoizationGuarantees` by
default and cleaned up a couple small things:
* We emit freeze calls for StartMemoize deps but these had
ValueReason.Other so the message wasn't great. We now treat these like
other hook arguments.
* PruneNonEscapingScopes was being too aggressive in this mode and
memoizing even loads of globals. Switching to
MemoizationLevel.Conditional ensures we build a graph that connects
through to primitive-returning function calls, but doesn't unnecessarily
force memoization otherwise.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34346).
* #34347
* __->__ #34346
`@enablePreserveExistingMemoizationGuarantees` mode currently does not
guarantee memoization of primitive-returning functions. We're often able
to infer that a function returns a primitive based on how its result is
used, for example `foo() + 1` or `object[getIndex()]`, and by default we
do not currently memoize computation that produces a primitive. The
reasoning behind this is that the compiler is primarily focused on
stopping cascading updates — it's fine to recompute a primitive since we
can cheaply compare that primitive and avoid unnecessary downstream
recomputation. But we've gotten a lot of feedback that people find this
surprising, and that sometimes the computation can be expensive enough
that it should be memoized.
This PR changes `@enablePreserveExistingMemoizationGuarantees` mode to
ensure that primitive-returning functions get memoized. Other modes will
not memoize these functions. Separately from this we are considering
enabling this mode by default.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34343).
* #34347
* #34346
* __->__ #34343
* #34335
Fixes#34108. If a scope ends with with a conditional where some/all
branches exit via labeled break, we currently compile in a way that
works but bypasses memoization. We end up with a shape like
```js
let t0;
label: {
if (changed) {
...
if (cond) {
t0 = ...;
break label;
}
// we don't save the output if the break happens!
t0 = ...;
$[0] = t0;
} else {
t0 = $[0];
}
```
The fix here is to update AlignReactiveScopesToBlockScopes to take
account of breaks that don't go to the natural fallthrough. In this
case, we take any active scopes and extend them to start at least as
early as the label, and extend at least to the label fallthrough. Thus
we produce the correct:
```js
let t0;
if (changed) {
label: {
...
if (cond) {
t0 = ...;
break label;
}
t0 = ...;
}
// now the break jumps here, and we cache the value
$[0] = t0;
} else {
t0 = $[0];
}
```
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34335).
* #34347
* #34346
* #34343
* __->__ #34335
<!--
Thanks for submitting a pull request!
We appreciate you spending the time to work on these changes. Please
provide enough information so that others can review your pull request.
The three fields below are mandatory.
Before submitting a pull request, please make sure the following is
done:
1. Fork [the repository](https://github.com/facebook/react) and create
your branch from `main`.
2. Run `yarn` in the repository root.
3. If you've fixed a bug or added code that should be tested, add tests!
4. Ensure the test suite passes (`yarn test`). Tip: `yarn test --watch
TestName` is helpful in development.
5. Run `yarn test --prod` to test in the production environment. It
supports the same options as `yarn test`.
6. If you need a debugger, run `yarn test --debug --watch TestName`,
open `chrome://inspect`, and press "Inspect".
7. Format your code with
[prettier](https://github.com/prettier/prettier) (`yarn prettier`).
8. Make sure your code lints (`yarn lint`). Tip: `yarn linc` to only
check changed files.
9. Run the [Flow](https://flowtype.org/) type checks (`yarn flow`).
10. If you haven't already, complete the CLA.
Learn more about contributing:
https://reactjs.org/docs/how-to-contribute.html
-->
## Summary
Part 2 of adding a "Config Override" panel to the React compiler
playground. Added sync from the config editor (still only accessible
with the "showConfig" param) to the main source code editor. Adding a
valid config to the editor will add/replace the `@OVERRIDE` pragma above
the source code. Additionally refactored the old implementation to
remove `useEffect`s and unnecessary renders.
Realized upon testing that the user experience is quite jarring,
planning to add a `sync` button in the next PR to fix this.
## How did you test this change?
<!--
Demonstrate the code is solid. Example: The exact commands you ran and
their output, screenshots / videos if the pull request changes the user
interface.
How exactly did you verify that your PR solves the issue you wanted to
solve?
If you leave this empty, your PR will very likely be closed.
-->
https://github.com/user-attachments/assets/a71b1b5f-0539-4c00-8d5c-22426f0280f9
A few libraries are known to be incompatible with memoization, whether
manually via `useMemo()` or via React Compiler. This puts us in a tricky
situation. On the one hand, we understand that these libraries were
developed prior to our documenting the [Rules of
React](https://react.dev/reference/rules), and their designs were the
result of trying to deliver a great experience for their users and
balance multiple priorities around DX, performance, etc. At the same
time, using these libraries with memoization — and in particular with
automatic memoization via React Compiler — can break apps by causing the
components using these APIs not to update. Concretely, the APIs have in
common that they return a function which returns different values over
time, but where the function itself does not change. Memoizing the
result on the identity of the function will mean that the value never
changes. Developers reasonable interpret this as "React Compiler broke
my code".
Of course, the best solution is to work with developers of these
libraries to address the root cause, and we're doing that. We've
previously discussed this situation with both of the respective
libraries:
* React Hook Form:
https://github.com/react-hook-form/react-hook-form/issues/11910#issuecomment-2135608761
* TanStack Table:
https://github.com/facebook/react/issues/33057#issuecomment-2840600158
and https://github.com/TanStack/table/issues/5567
In the meantime we need to make sure that React Compiler can work out of
the box as much as possible. This means teaching it about popular
libraries that cannot be memoized. We also can't silently skip
compilation, as this confuses users, so we need these error messages to
be visible to users. To that end, this PR adds:
* A flag to mark functions/hooks as incompatible
* Validation against use of such functions
* A default type provider to provide declarations for two
known-incompatible libraries
Note that Mobx is also incompatible, but the `observable()` function is
called outside of the component itself, so the compiler cannot currently
detect it. We may add validation for such APIs in the future.
Again, we really empathize with the developers of these libraries. We've
tried to word the error message non-judgementally, because we get that
it's hard! We're open to feedback about the error message, please let us
know.
## Summary
Part 1 of adding a "Config Override" panel to the React compiler
playground. The panel is placed to the left of the current input
section, and supports converting the comment pragmas in the input
section to a JavaScript-based config. Backwards sync has not been
implemented yet.
NOTE: I have added support for a new `OVERRIDE` type pragma to add
support for Map and Function types. (For now, the old pragma format is
still intact)
## Testing
Example of the config overrides synced to the source code:
<img width="1542" height="527" alt="Screenshot 2025-08-28 at 3 38 13 PM"
src="https://github.com/user-attachments/assets/d46e7660-61b9-4145-93b5-a4005d30064a"
/>
In #34125 I added a hint where if you assign to the .current property of
a frozen object, we suggest naming the variable as `ref` or `-Ref`.
However, the tracking for mutations that assign to .current specifically
wasn't propagated past function expression boundaries, which meant that
the hint only showed up if you mutated the ref in the main body of the
component/hook. That's less likely to happen since most folks know not
to access refs in render. What's more likely is that you'll (correctly)
assign a ref in an effect or callback, but the compiler will throw an
error. By showing a hint in this case we can help people understand the
naming pattern.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34298).
* #34276
* __->__ #34298
We currently assume that any functions passes as props may be event
handlers or effect functions, and thus don't check for side effects such
as mutating globals. However, if a prop is a function that returns JSX
that is a sure sign that it's actually a render helper and not an event
handler or effect function. So we now emit a `Render` effect for any
prop that is a JSX-returning function, triggering all of our render
validation.
This required a small fix to InferTypes: we weren't correctly populating
the `return` type of function types during unification. I also improved
the printing of types so we can see the inferred return types.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/33647).
* #33643
* #33650
* #33642
* __->__ #33647
The docs site is in a separate repo, but this gives us a semi-automated
way to update the docs about our lint rules. The script generates
markdown files from the rule definitions which we can then manually
copy/paste into the docs site somewhere. In the future we can automate
this fully.
NOTE: this is a merged version of @mofeiZ's original PR along with my
edits per offline discussion. The description is updated to reflect the
latest approach.
The key problem we're trying to solve with this PR is to allow
developers more control over the compiler's various validations. The
idea is to have a number of rules targeting a specific category of
issues, such as enforcing immutability of props/state/etc or disallowing
access to refs during render. We don't want to have to run the compiler
again for every single rule, though, so @mofeiZ added an LRU cache that
caches the full compilation output of N most recent files. The first
rule to run on a given file will cause it to get cached, and then
subsequent rules can pull from the cache, with each rule filtering down
to its specific category of errors.
For the categories, I went through and assigned a category roughly 1:1
to existing validations, and then used my judgement on some places that
felt distinct enough to warrant a separate error. Every error in the
compiler now has to supply both a severity (for legacy reasons) and a
category (for ESLint). Each category corresponds 1:1 to a ESLint rule
definition, so that the set of rules is automatically populated based on
the defined categories.
Categories include a flag for whether they should be in the recommended
set or not.
Note that as with the original version of this PR, only
eslint-plugin-react-compiler is changed. We still have to update the
main lint rule.
## Test Plan
* Created a sample project using ESLint v9 and verified that the plugin
can be configured correctly and detects errors
* Edited `fixtures/eslint-v9` and introduced errors, verified that the w
latest config changes in that fixture it correctly detects the errors
* In the sample project, confirmed that the LRU caching is correctly
caching compiler output, ie compiling files just once.
Co-authored-by: Mofei Zhang <feifei0@meta.com>
While we still use this package internally, we now ask users to install
eslint-plugin-react-hooks instead, so this package can now be deprecated
on npm.
The new mutation/aliasing model significantly expands on the idea of
FunctionEffect. The type (and its usage in HIRFunction.effects) was only
necessary for the now-deleted old inference model so we can clean up
this code now.
Hints are meant as additional information to present to the developer
about an error. The first use-case here is for the suggestion to name
refs with "-Ref" if we encounter a mutation that looks like it might be
a ref. The original error printing used a second error detail which
printed the source code twice, a hint with just extra text is less
noisy.
If you have a ref that the compiler doesn't know is a ref (say, a value
returned from a custom hook) and try to assign its `.current = ...`, we
currently fail with a generic error that hook return values are not
mutable. However, an assignment to `.current` specifically is a very
strong hint that the value is likely to be a ref. So in this PR, we
track the reason for the mutation and if it ends up being an error, we
use it to show an additional hint to the user. See the fixture for an
example of the message.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34125).
* #34126
* __->__ #34125
* #34124
We try to merge consecutive reactive scopes that will always invalidate
together, but there's one common case that isn't handled.
```js
const y = [[x]];
```
Here we'll create two consecutive scopes for the inner and outer array
expressions. Because the input to the second scope is a temporary,
they'll merge into one scope.
But if we name the inner array, the merging stops:
```js
const array = [x];
const y = [array];
```
This is because the merging logic checks if all the dependencies of the
second scope are outputs of the first scope, but doesn't account for
renaming due to LoadLocal/StoreLocal. The fix is to track these
temporaries.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34049).
* __->__ #34049
* #34047
* #34044
Fixes remaining issue in #32261, where passing a previously useMemo()-d
value to `Object.entries()` makes the compiler think the value is
mutated and fail validatePreserveExistingMemo. While I was there I added
Object.keys() and Object.values() too.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34047).
* #34049
* __->__ #34047
* #34044
This currently throws an invariant which may be misleading. I checked
the ecma262 spec and used the same list of reserved words in our check.
To err on the side of being conservative, we also error when strict mode
reserved words are used.
Allows assigning a ref-accessing function to an object so long as that
object is not subsequently transitively mutated. We should likely
rewrite the ref validation to use the new mutation/aliasing effects,
which would provide a more consistent behavior across instruction types
and require fewer special cases like this.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34026).
* #34027
* __->__ #34026
Fixes#30782
When developers do an `if (ref.current == null)` guard for lazy ref
initialization, the "safe" blocks should extend up to the if's
fallthrough. Previously we only allowed writing to the ref in the if
consequent, but this meant that you couldn't use a ternary, logical, etc
in the if body.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34024).
* #34027
* #34026
* #34025
* __->__ #34024
We infer render helpers as functions whose result is immediately
interpolated into jsx. This is a very conservative approximation, to
help with common cases like `<Foo>{props.renderItem(ref)}</Foo>`. The
idea is similar to hooks that it's ultimately on the developer to catch
ref-in-render validations (and the runtime detects them too), so we can
be a bit more relaxed since there are valid reasons to use this pattern.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34006).
* #34027
* #34026
* #34025
* #34024
* #34005
* __->__ #34006
* #34004
Two related changes:
* ValidateNoRefAccessInRender now allows the mergeRefs pattern, ie a
function that aggregates multiple refs into a new ref. This is the main
case where we have seen false positive no-ref-in-render errors.
* Behind `@enableTreatRefLikeIdentifiersAsRefs`, we infer values passed
as the `ref` prop to some JSX as refs.
The second change is potentially helpful for situations such as
```js
function Component({ref: parentRef}) {
const childRef = useRef(null);
const mergedRef = mergeRefs(parentRef, childRef);
useEffect(() => {
// generally accesses childRef, not mergedRef
}, []);
return <Foo ref={mergedRef} />;
}
```
Ie where you create a merged ref but don't access its `.current`
property. Without inferring `ref` props as refs, we'd fail to allow this
merge refs case.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34004).
* #34027
* #34026
* #34025
* #34024
* #34005
* #34006
* __->__ #34004
We added the `@enableTreatRefLikeIdentifiersAsRefs` feature a while back
but never enabled it. Since then we've continued to see examples that
motivate this mode, so here we're fixing it up to prepare to enable by
default. It now works as follows:
* If we find a property load or property store where both a) the
object's name is ref-like (`ref` or `-Ref`) and b) the property is
`current`, we infer the object itself as a ref and the value of the
property as a ref value. Originally the feature only detected property
loads, not stores.
* Inferred refs are not considered stable (this is a change from the
original implementation). The only way to get a stable ref is by calling
`useRef()`. We've seen issues with assuming refs are stable.
With this change, cases like the following now correctly error:
```js
function Foo(props) {
const fooRef = props.fooRef;
fooRef.current = true;
^^^^^^^^^^^^^^ cannot modify ref in render
}
```
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34000).
* #34027
* #34026
* #34025
* #34024
* #34005
* #34006
* #34004
* #34003
* __->__ #34000
Noticed this from my previous PR that this pass was throwing on the
first error. This PR is a small refactor to aggregate every violation
and report them all at once.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34002).
* #34022
* __->__ #34002
Much of the logic in the new validation pass is already implemented in
DropManualMemoization, so let's combine them. I opted to keep the
environment flag so we can more precisely control the rollout.
---
[//]: # (BEGIN SAPLING FOOTER)
Stack created with [Sapling](https://sapling-scm.com). Best reviewed
with [ReviewStack](https://reviewstack.dev/facebook/react/pull/34001).
* #34022
* #34002
* __->__ #34001