[compiler][be] Less ambiguous error messages for validateMemo bailout
ghstack-source-id: 312093ec74d733bccd2d2d8400eaba267c9e33a7 Pull Request resolved: https://github.com/facebook/react/pull/30601
Mofei Zhang committed
Aug 7, 2024 at 16:11 UTC
e662b0a24b1d8a1c8ec86558aef4b7e5c4427116
7 files changed
+52
-46
compiler/packages/babel-plugin-react-compiler/src/Validation/ValidatePreservedManualMemoization.ts
+43
-33
@@ -433,15 +433,16 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
433
* recursively visits ReactiveValues and instructions
434
*/
435
this.recordTemporaries(instruction, state);
436
- if (instruction.value.kind === 'StartMemoize') {
436
+ const value = instruction.value;
437
+ if (value.kind === 'StartMemoize') {
438
let depsFromSource: Array<ManualMemoDependency> | null = null;
438
- if (instruction.value.deps != null) {
439
- depsFromSource = instruction.value.deps;
439
+ if (value.deps != null) {
440
+ depsFromSource = value.deps;
441
}
442
CompilerError.invariant(state.manualMemoState == null, {
443
reason: 'Unexpected nested StartMemoize instructions',
443
- description: `Bad manual memoization ids: ${state.manualMemoState?.manualMemoId}, ${instruction.value.manualMemoId}`,
444
- loc: instruction.value.loc,
444
+ description: `Bad manual memoization ids: ${state.manualMemoState?.manualMemoId}, ${value.manualMemoId}`,
445
+ loc: value.loc,
446
suggestions: null,
447
});
448
@@ -449,48 +450,57 @@ class Visitor extends ReactiveFunctionVisitor<VisitorState> {
450
loc: instruction.loc,
451
decls: new Set(),
452
depsFromSource,
452
- manualMemoId: instruction.value.manualMemoId,
453
+ manualMemoId: value.manualMemoId,
454
};
454
- }
455
- if (instruction.value.kind === 'FinishMemoize') {
456
- CompilerError.invariant(
457
- state.manualMemoState != null &&
458
- state.manualMemoState.manualMemoId === instruction.value.manualMemoId,
459
- {
460
- reason: 'Unexpected mismatch between StartMemoize and FinishMemoize',
461
- description: `Encountered StartMemoize id=${state.manualMemoState?.manualMemoId} followed by FinishMemoize id=${instruction.value.manualMemoId}`,
462
- loc: instruction.value.loc,
463
- suggestions: null,
464
- },
465
- );
466
- state.manualMemoState = null;
467
- }
455
469
- const isDep = instruction.value.kind === 'StartMemoize';
470
- const isDecl =
471
- instruction.value.kind === 'FinishMemoize' && !instruction.value.pruned;
472
- if (isDep || isDecl) {
473
- for (const value of eachInstructionValueOperand(
474
- instruction.value as InstructionValue,
456
+ for (const {identifier, loc} of eachInstructionValueOperand(
457
+ value as InstructionValue,
458
)) {
459
if (
477
- (isDep &&
478
- value.identifier.scope != null &&
479
- !this.scopes.has(value.identifier.scope.id) &&
480
- !this.prunedScopes.has(value.identifier.scope.id)) ||
481
- (isDecl && isUnmemoized(value.identifier, this.scopes))
460
+ identifier.scope != null &&
461
+ !this.scopes.has(identifier.scope.id) &&
462
+ !this.prunedScopes.has(identifier.scope.id)
463
) {
464
state.errors.push({
465
reason:
485
- 'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly',
466
+ 'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly',
467
description: null,
468
severity: ErrorSeverity.CannotPreserveMemoization,
488
- loc: typeof instruction.loc !== 'symbol' ? instruction.loc : null,
469
+ loc,
470
suggestions: null,
471
});
472
}
473
}
474
}
475
+ if (value.kind === 'FinishMemoize') {
476
+ CompilerError.invariant(
477
+ state.manualMemoState != null &&
478
+ state.manualMemoState.manualMemoId === value.manualMemoId,
479
+ {
480
+ reason: 'Unexpected mismatch between StartMemoize and FinishMemoize',
481
+ description: `Encountered StartMemoize id=${state.manualMemoState?.manualMemoId} followed by FinishMemoize id=${value.manualMemoId}`,
482
+ loc: value.loc,
483
+ suggestions: null,
484
+ },
485
+ );
486
+ state.manualMemoState = null;
487
+ if (!value.pruned) {
488
+ for (const {identifier, loc} of eachInstructionValueOperand(
489
+ value as InstructionValue,
490
+ )) {
491
+ if (isUnmemoized(identifier, this.scopes)) {
492
+ state.errors.push({
493
+ reason:
494
+ 'React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output.',
495
+ description: null,
496
+ severity: ErrorSeverity.CannotPreserveMemoization,
497
+ loc,
498
+ suggestions: null,
499
+ });
500
+ }
501
+ }
502
+ }
503
+ }
504
}
505
}
506
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-repro-unmemoized-callback-captured-in-context-variable.expect.md
+1
-1
@@ -53,7 +53,7 @@ export const FIXTURE_ENTRYPOINT = {
53
9 | const a = useHook();
54
10 | // Because b is also part of that same mutable range, it can't be memoized either
55
> 11 | const b = useMemo(() => ({}), []);
56
- | ^^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (11:11)
56
+ | ^^^^^^^^^^^^^^^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (11:11)
57
12 |
58
13 | // Conditional assignment without a subsequent mutation normally doesn't create a mutable
59
14 | // range, but in this case we're reassigning a context variable
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-useCallback-accesses-ref-mutated-later-via-function-preserve-memoization.expect.md
+1
-1
@@ -45,7 +45,7 @@ export const FIXTURE_ENTRYPOINT = {
45
> 10 | ref.current.inner = event.target.value;
46
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
47
> 11 | });
48
- | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (7:11)
48
+ | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (7:11)
49
12 |
50
13 | // The ref is modified later, extending its range and preventing memoization of onChange
51
14 | const reset = () => {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.todo-useCallback-set-ref-nested-property-ref-modified-later-preserve-memoization.expect.md
+1
-1
@@ -42,7 +42,7 @@ export const FIXTURE_ENTRYPOINT = {
42
> 10 | ref.current.inner = event.target.value;
43
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
> 11 | });
45
- | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (7:11)
45
+ | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (7:11)
46
12 |
47
13 | // The ref is modified later, extending its range and preventing memoization of onChange
48
14 | ref.current.inner = null;
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.false-positive-useMemo-infer-mutate-deps.expect.md
+3
-7
@@ -29,14 +29,10 @@ export const FIXTURE_ENTRYPOINT = {
29
## Error
30
31
```
32
- 10 | const val = [1, 2, 3];
33
- 11 |
34
-> 12 | return useMemo(() => {
35
- | ^^^^^^^
36
-> 13 | return identity(val);
37
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
32
+ 12 | return useMemo(() => {
33
+ 13 | return identity(val);
34
> 14 | }, [val]);
39
- | ^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (12:14)
35
+ | ^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly (14:14)
36
15 | }
37
16 |
38
17 | export const FIXTURE_ENTRYPOINT = {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.invalid-useCallback-captures-reassigned-context.expect.md
+2
-2
@@ -33,9 +33,9 @@ export const FIXTURE_ENTRYPOINT = {
33
10 |
34
11 | // makeArray() is captured, but depsList contains [props]
35
> 12 | const cb = useCallback(() => [x], [x]);
36
- | ^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (12:12)
36
+ | ^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This dependency may be mutated later, which could cause the value to change unexpectedly (12:12)
37
38
-CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (12:12)
38
+CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (12:12)
39
13 |
40
14 | x = makeArray();
41
15 |
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/preserve-memo-validation/error.todo-useCallback-captures-invalidating-value.expect.md
+1
-1
@@ -31,7 +31,7 @@ export const FIXTURE_ENTRYPOINT = {
31
11 | x.push(props);
32
12 |
33
> 13 | return useCallback(() => [x], [x]);
34
- | ^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value may be mutated later, which could cause the value to change unexpectedly (13:13)
34
+ | ^^^^^^^^^ CannotPreserveMemoization: React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. This value was memoized in source but not in compilation output. (13:13)
35
14 | }
36
15 |
37
16 | export const FIXTURE_ENTRYPOINT = {