[validation] More detailed error diagnostics for validatePreserveExistingMemo

--- 

This should make it easier to grep through error diagnostics to understand state 
of the codebase: 

- no matching dependences -> likely that source is ignoring eslint failures 

- differences in ref.current access -> non-backwards compatible refs 

- subpath -> should be fixable in the compiler (unless source is ignore eslint 
failures)
This commit is contained in:
Mofei Zhang
2024-03-20 16:05:24 -04:00
parent 63bb781781
commit 4daa4eceb7
13 changed files with 70 additions and 20 deletions
@@ -102,10 +102,41 @@ function prettyPrintScopeDependency(val: ReactiveScopeDependency): string {
return `${rootStr}${val.path.length > 0 ? "." : ""}${val.path.join(".")}`;
}
enum CompareDependencyResult {
Ok = 0,
RootDifference = 1,
PathDifference = 2,
Subpath = 3,
RefAccessDifference = 4,
}
function merge(
a: CompareDependencyResult,
b: CompareDependencyResult
): CompareDependencyResult {
return Math.max(a, b);
}
function getCompareDependencyResultDescription(
result: CompareDependencyResult
): string {
switch (result) {
case CompareDependencyResult.Ok:
return "dependencies equal";
case CompareDependencyResult.RootDifference:
case CompareDependencyResult.PathDifference:
return "inferred different dependency than source";
case CompareDependencyResult.RefAccessDifference:
return "differences in ref.current access";
case CompareDependencyResult.Subpath:
return "inferred less specific property than source";
}
}
function compareDeps(
inferred: ManualMemoDependency,
source: ManualMemoDependency
): boolean {
): CompareDependencyResult {
const rootsEqual =
(inferred.root.kind === "Global" &&
source.root.kind === "Global" &&
@@ -114,7 +145,7 @@ function compareDeps(
source.root.kind === "NamedLocal" &&
inferred.root.value.identifier.id === source.root.value.identifier.id);
if (!rootsEqual) {
return false;
return CompareDependencyResult.RootDifference;
}
let isSubpath = true;
@@ -131,9 +162,20 @@ function compareDeps(
(inferred.path.length >= source.path.length &&
!inferred.path.includes("current")))
) {
return true;
return CompareDependencyResult.Ok;
} else {
return false;
if (isSubpath) {
if (
source.path.includes("current") ||
inferred.path.includes("current")
) {
return CompareDependencyResult.RefAccessDifference;
} else {
return CompareDependencyResult.Subpath;
}
} else {
return CompareDependencyResult.PathDifference;
}
}
}
@@ -197,9 +239,13 @@ function validateInferredDep(
return;
}
}
let errorDiagnostic: CompareDependencyResult | null = null;
for (const originalDep of validDepsInMemoBlock) {
if (compareDeps(normalizedDep, originalDep)) {
const compareResult = compareDeps(normalizedDep, originalDep);
if (compareResult === CompareDependencyResult.Ok) {
return;
} else {
errorDiagnostic = merge(errorDiagnostic ?? compareResult, compareResult);
}
}
errorState.push({
@@ -210,7 +256,11 @@ function validateInferredDep(
dep
)}\`, but the source dependencies were [${validDepsInMemoBlock
.map((dep) => printManualMemoDependency(dep, true))
.join(", ")}]`,
.join(", ")}]. Detail: ${
errorDiagnostic
? getCompareDependencyResultDescription(errorDiagnostic)
: "none"
}`,
loc: GeneratedSource,
suggestions: null,
});
@@ -17,7 +17,7 @@ function useHook(maybeRef) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `maybeRef.current`, but the source dependencies were [maybeRef]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `maybeRef.current`, but the source dependencies were [maybeRef]. Detail: differences in ref.current access
```
@@ -17,7 +17,7 @@ function useHook(maybeRef, shouldRead) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `maybeRef.current`, but the source dependencies were [shouldRead, maybeRef]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `maybeRef.current`, but the source dependencies were [shouldRead, maybeRef]. Detail: differences in ref.current access
```
@@ -19,7 +19,7 @@ function useHook(x) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `aliasedX`, but the source dependencies were [x, aliasedProp]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `aliasedX`, but the source dependencies were [x, aliasedProp]. Detail: inferred different dependency than source
```
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA, propB.x.y]. Detail: inferred less specific property than source
```
@@ -24,7 +24,7 @@ function Component({ propA, propB }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]. Detail: inferred less specific property than source
```
@@ -17,7 +17,7 @@ function Component({ propA }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]. Detail: inferred less specific property than source
```
@@ -19,7 +19,7 @@ function useHook(x) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `x`, but the source dependencies were [aliasedX, aliasedProp]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `x`, but the source dependencies were [aliasedX, aliasedProp]. Detail: inferred different dependency than source
```
@@ -24,9 +24,9 @@ function Component({ propA, propB }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]. Detail: inferred less specific property than source
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA.a, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA.a, propB.x.y]. Detail: inferred less specific property than source
```
@@ -24,9 +24,9 @@ function Component({ propA, propB }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.a, propB.x.y]. Detail: inferred less specific property than source
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA.a, propB.x.y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propB`, but the source dependencies were [propA.a, propB.x.y]. Detail: inferred less specific property than source
```
@@ -19,7 +19,7 @@ function Component({ propA }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]. Detail: inferred less specific property than source
```
@@ -17,7 +17,7 @@ function Component({ propA }) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `propA`, but the source dependencies were [propA.x]. Detail: inferred less specific property than source
```
@@ -30,7 +30,7 @@ function useFoo(input1) {
## Error
```
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `input1`, but the source dependencies were [y]
[ReactForget] Todo: Could not preserve manual memoization because an inferred dependency does not match the dependency list in source. The inferred dependency was `input1`, but the source dependencies were [y]. Detail: inferred different dependency than source
```