Skip to content

Commit 0a93bf8

Browse files
committed
Add new ErrorCategory.EffectExhaustiveDependencies and update reason/description for effect dep errors
1 parent 5c7e374 commit 0a93bf8

12 files changed

+171
-42
lines changed

compiler/packages/babel-plugin-react-compiler/src/CompilerError.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,7 +601,8 @@ function printErrorSummary(category: ErrorCategory, message: string): string {
601601
case ErrorCategory.Syntax:
602602
case ErrorCategory.UseMemo:
603603
case ErrorCategory.VoidUseMemo:
604-
case ErrorCategory.MemoDependencies: {
604+
case ErrorCategory.MemoDependencies:
605+
case ErrorCategory.EffectExhaustiveDependencies: {
605606
heading = 'Error';
606607
break;
607608
}
@@ -683,6 +684,10 @@ export enum ErrorCategory {
683684
* Checks for memoized effect deps
684685
*/
685686
EffectDependencies = 'EffectDependencies',
687+
/**
688+
* Checks for exhaustive and extraneous effect dependencies
689+
*/
690+
EffectExhaustiveDependencies = 'EffectExhaustiveDependencies',
686691
/**
687692
* Checks for no setState in effect bodies
688693
*/
@@ -838,6 +843,16 @@ function getRuleForCategoryImpl(category: ErrorCategory): LintRule {
838843
preset: LintRulePreset.Off,
839844
};
840845
}
846+
case ErrorCategory.EffectExhaustiveDependencies: {
847+
return {
848+
category,
849+
severity: ErrorSeverity.Error,
850+
name: 'exhaustive-effect-dependencies',
851+
description:
852+
'Validates that effect dependencies are exhaustive and without extraneous values',
853+
preset: LintRulePreset.Off,
854+
};
855+
}
841856
case ErrorCategory.EffectDerivationsOfState: {
842857
return {
843858
category,

compiler/packages/babel-plugin-react-compiler/src/Validation/ValidateExhaustiveDependencies.ts

Lines changed: 67 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@ export function validateExhaustiveDependencies(
140140
startMemo.deps ?? [],
141141
reactive,
142142
startMemo.depsLoc,
143+
ErrorCategory.MemoDependencies,
143144
);
144145
if (diagnostic != null) {
145146
error.pushDiagnostic(diagnostic);
@@ -199,6 +200,7 @@ export function validateExhaustiveDependencies(
199200
manualDeps,
200201
reactive,
201202
null,
203+
ErrorCategory.EffectExhaustiveDependencies,
202204
);
203205
if (diagnostic != null) {
204206
error.pushDiagnostic(diagnostic);
@@ -215,6 +217,9 @@ function validateDependencies(
215217
manualDependencies: Array<ManualMemoDependency>,
216218
reactive: Set<IdentifierId>,
217219
manualMemoLoc: SourceLocation | null,
220+
category:
221+
| ErrorCategory.MemoDependencies
222+
| ErrorCategory.EffectExhaustiveDependencies,
218223
): CompilerDiagnostic | null {
219224
// Sort dependencies by name and path, with shorter/non-optional paths first
220225
inferred.sort((a, b) => {
@@ -373,23 +378,7 @@ function validateDependencies(
373378
.join(', ')}]`,
374379
};
375380
}
376-
const diagnostic = CompilerDiagnostic.create({
377-
category: ErrorCategory.MemoDependencies,
378-
reason: 'Found missing/extra memoization dependencies',
379-
description: [
380-
missing.length !== 0
381-
? 'Missing dependencies can cause a value to update less often than it should, ' +
382-
'resulting in stale UI'
383-
: null,
384-
extra.length !== 0
385-
? 'Extra dependencies can cause a value to update more often than it should, ' +
386-
'resulting in performance problems such as excessive renders or effects firing too often'
387-
: null,
388-
]
389-
.filter(Boolean)
390-
.join('. '),
391-
suggestions: suggestion != null ? [suggestion] : null,
392-
});
381+
const diagnostic = createDiagnostic(category, missing, extra, suggestion);
393382
for (const dep of missing) {
394383
let reactiveStableValueHint = '';
395384
if (isStableType(dep.identifier)) {
@@ -1001,3 +990,64 @@ function isOptionalDependency(
1001990
isPrimitiveType(inferredDependency.identifier))
1002991
);
1003992
}
993+
994+
function createDiagnostic(
995+
category:
996+
| ErrorCategory.MemoDependencies
997+
| ErrorCategory.EffectExhaustiveDependencies,
998+
missing: Array<InferredDependency>,
999+
extra: Array<ManualMemoDependency>,
1000+
suggestion: CompilerSuggestion | null,
1001+
): CompilerDiagnostic {
1002+
let reason: string;
1003+
let description: string;
1004+
1005+
function joinMissingExtraDetail(
1006+
missingString: string,
1007+
extraString: string,
1008+
joinStr: string,
1009+
): string {
1010+
return [
1011+
missing.length !== 0 ? missingString : null,
1012+
extra.length !== 0 ? extraString : null,
1013+
]
1014+
.filter(Boolean)
1015+
.join(joinStr);
1016+
}
1017+
1018+
switch (category) {
1019+
case ErrorCategory.MemoDependencies: {
1020+
reason = `Found ${joinMissingExtraDetail('missing', 'extra', '/')} memoization dependencies`;
1021+
description = joinMissingExtraDetail(
1022+
'Missing dependencies can cause a value to update less often than it should, resulting in stale UI',
1023+
'Extra dependencies can cause a value to update more often than it should, resulting in performance' +
1024+
' problems such as excessive renders or effects firing too often',
1025+
'. ',
1026+
);
1027+
break;
1028+
}
1029+
case ErrorCategory.EffectExhaustiveDependencies: {
1030+
reason = `Found ${joinMissingExtraDetail('missing', 'extra', '/')} effect dependencies`;
1031+
description = joinMissingExtraDetail(
1032+
'Missing dependencies can cause an effect to fire less often than it should',
1033+
'Extra dependencies can cause an effect to fire more often than it should, resulting' +
1034+
' in performance problems such as excessive renders and side effects',
1035+
'. ',
1036+
);
1037+
break;
1038+
}
1039+
default: {
1040+
CompilerError.simpleInvariant(false, {
1041+
reason: `Unexpected error category: ${category}`,
1042+
loc: GeneratedSource,
1043+
});
1044+
}
1045+
}
1046+
1047+
return CompilerDiagnostic.create({
1048+
category,
1049+
reason,
1050+
description,
1051+
suggestions: suggestion != null ? [suggestion] : null,
1052+
});
1053+
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-dep-on-ref-current-value.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function Component() {
2121
```
2222
Found 1 error:
2323
24-
Error: Found missing/extra memoization dependencies
24+
Error: Found extra memoization dependencies
2525
2626
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
2727

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-exhaustive-deps-disallow-unused-stable-types.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function Component() {
2525
```
2626
Found 1 error:
2727
28-
Error: Found missing/extra memoization dependencies
28+
Error: Found extra memoization dependencies
2929
3030
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
3131

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-exhaustive-deps.expect.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ error.invalid-exhaustive-deps.ts:17:6
101101

102102
Inferred dependencies: `[x?.y.z.a?.b]`
103103

104-
Error: Found missing/extra memoization dependencies
104+
Error: Found extra memoization dependencies
105105

106106
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
107107

@@ -143,7 +143,7 @@ error.invalid-exhaustive-deps.ts:31:23
143143

144144
Inferred dependencies: `[]`
145145

146-
Error: Found missing/extra memoization dependencies
146+
Error: Found missing memoization dependencies
147147

148148
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
149149

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-exhaustive-effect-deps.expect.md

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,23 @@ import {useEffect} from 'react';
88
function Component({x, y, z}) {
99
// error: missing dep - x
1010
useEffect(() => {
11-
console.log(x);
11+
log(x);
1212
}, []);
1313

1414
// error: extra dep - y
1515
useEffect(() => {
16-
console.log(x);
16+
log(x);
1717
}, [x, y]);
18+
19+
// error: missing dep - z; extra dep - y
20+
useEffect(() => {
21+
log(x, z);
22+
}, [x, y]);
23+
24+
// error: missing dep x
25+
useEffect(() => {
26+
log(x);
27+
}, [x.y]);
1828
}
1929

2030
```
@@ -23,32 +33,76 @@ function Component({x, y, z}) {
2333
## Error
2434

2535
```
26-
Found 2 errors:
36+
Found 4 errors:
2737
28-
Error: Found missing/extra memoization dependencies
38+
Error: Found missing effect dependencies
2939
30-
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
40+
Missing dependencies can cause an effect to fire less often than it should.
3141
32-
error.invalid-exhaustive-effect-deps.ts:7:16
42+
error.invalid-exhaustive-effect-deps.ts:7:8
3343
5 | // error: missing dep - x
3444
6 | useEffect(() => {
35-
> 7 | console.log(x);
36-
| ^ Missing dependency `x`
45+
> 7 | log(x);
46+
| ^ Missing dependency `x`
3747
8 | }, []);
3848
9 |
3949
10 | // error: extra dep - y
4050
41-
Error: Found missing/extra memoization dependencies
51+
Error: Found extra effect dependencies
4252
43-
Extra dependencies can cause a value to update more often than it should, resulting in performance problems such as excessive renders or effects firing too often.
53+
Extra dependencies can cause an effect to fire more often than it should, resulting in performance problems such as excessive renders and side effects.
4454
4555
error.invalid-exhaustive-effect-deps.ts:13:9
4656
11 | useEffect(() => {
47-
12 | console.log(x);
57+
12 | log(x);
4858
> 13 | }, [x, y]);
4959
| ^ Unnecessary dependency `y`
50-
14 | }
51-
15 |
60+
14 |
61+
15 | // error: missing dep - z; extra dep - y
62+
16 | useEffect(() => {
63+
64+
Error: Found missing/extra effect dependencies
65+
66+
Missing dependencies can cause an effect to fire less often than it should. Extra dependencies can cause an effect to fire more often than it should, resulting in performance problems such as excessive renders and side effects.
67+
68+
error.invalid-exhaustive-effect-deps.ts:17:11
69+
15 | // error: missing dep - z; extra dep - y
70+
16 | useEffect(() => {
71+
> 17 | log(x, z);
72+
| ^ Missing dependency `z`
73+
18 | }, [x, y]);
74+
19 |
75+
20 | // error: missing dep x
76+
77+
error.invalid-exhaustive-effect-deps.ts:18:9
78+
16 | useEffect(() => {
79+
17 | log(x, z);
80+
> 18 | }, [x, y]);
81+
| ^ Unnecessary dependency `y`
82+
19 |
83+
20 | // error: missing dep x
84+
21 | useEffect(() => {
85+
86+
Error: Found missing/extra effect dependencies
87+
88+
Missing dependencies can cause an effect to fire less often than it should. Extra dependencies can cause an effect to fire more often than it should, resulting in performance problems such as excessive renders and side effects.
89+
90+
error.invalid-exhaustive-effect-deps.ts:22:8
91+
20 | // error: missing dep x
92+
21 | useEffect(() => {
93+
> 22 | log(x);
94+
| ^ Missing dependency `x`
95+
23 | }, [x.y]);
96+
24 | }
97+
25 |
98+
99+
error.invalid-exhaustive-effect-deps.ts:23:6
100+
21 | useEffect(() => {
101+
22 | log(x);
102+
> 23 | }, [x.y]);
103+
| ^^^ Overly precise dependency `x.y`, use `x` instead
104+
24 | }
105+
25 |
52106
```
53107
54108

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-exhaustive-effect-deps.js

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,21 @@ import {useEffect} from 'react';
44
function Component({x, y, z}) {
55
// error: missing dep - x
66
useEffect(() => {
7-
console.log(x);
7+
log(x);
88
}, []);
99

1010
// error: extra dep - y
1111
useEffect(() => {
12-
console.log(x);
12+
log(x);
1313
}, [x, y]);
14+
15+
// error: missing dep - z; extra dep - y
16+
useEffect(() => {
17+
log(x, z);
18+
}, [x, y]);
19+
20+
// error: missing dep x
21+
useEffect(() => {
22+
log(x);
23+
}, [x.y]);
1424
}

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-missing-nonreactive-dep-inner-function.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ function useHook() {
2626
```
2727
Found 1 error:
2828
29-
Error: Found missing/extra memoization dependencies
29+
Error: Found missing memoization dependencies
3030
3131
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
3232

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-missing-nonreactive-dep-unmemoized.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ function useHook() {
2424
```
2525
Found 1 error:
2626
27-
Error: Found missing/extra memoization dependencies
27+
Error: Found missing memoization dependencies
2828
2929
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
3030

compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/exhaustive-deps/error.invalid-missing-nonreactive-dep.expect.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function useHook() {
2121
```
2222
Found 1 error:
2323
24-
Error: Found missing/extra memoization dependencies
24+
Error: Found missing memoization dependencies
2525
2626
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
2727

0 commit comments

Comments
 (0)