mirror of
https://github.com/facebook/react.git
synced 2026-02-21 19:31:52 +00:00
[compiler] Phase 2+7: Wrap pipeline passes in tryRecord for fault tolerance
- Change runWithEnvironment/run/compileFn to return Result<CodegenFunction, CompilerError> - Wrap all pipeline passes in env.tryRecord() to catch and record CompilerErrors - Record inference pass errors via env.recordErrors() instead of throwing - Handle codegen Result explicitly, returning Err on failure - Add final error check: return Err(env.aggregateErrors()) if any errors accumulated - Update tryCompileFunction and retryCompileFunction in Program.ts to handle Result - Keep lint-only passes using env.logErrors() (non-blocking) - Update 52 test fixture expectations that now report additional errors This is the core integration that enables fault tolerance: errors are caught, recorded, and the pipeline continues to discover more errors.
This commit is contained in:
@@ -51,20 +51,20 @@ Add error accumulation to the `Environment` class so that any pass can record er
|
||||
|
||||
Change `runWithEnvironment` to run all passes and check for errors at the end instead of letting exceptions propagate.
|
||||
|
||||
- [ ] **2.1 Change `runWithEnvironment` return type** (`src/Entrypoint/Pipeline.ts`)
|
||||
- [x] **2.1 Change `runWithEnvironment` return type** (`src/Entrypoint/Pipeline.ts`)
|
||||
- Change return type from `CodegenFunction` to `Result<CodegenFunction, CompilerError>`
|
||||
- At the end of the pipeline, check `env.hasErrors()`:
|
||||
- If no errors: return `Ok(ast)`
|
||||
- If errors: return `Err(env.aggregateErrors())`
|
||||
|
||||
- [ ] **2.2 Update `compileFn` to propagate the Result** (`src/Entrypoint/Pipeline.ts`)
|
||||
- [x] **2.2 Update `compileFn` to propagate the Result** (`src/Entrypoint/Pipeline.ts`)
|
||||
- Change `compileFn` return type from `CodegenFunction` to `Result<CodegenFunction, CompilerError>`
|
||||
- Propagate the Result from `runWithEnvironment`
|
||||
|
||||
- [ ] **2.3 Update `run` to propagate the Result** (`src/Entrypoint/Pipeline.ts`)
|
||||
- [x] **2.3 Update `run` to propagate the Result** (`src/Entrypoint/Pipeline.ts`)
|
||||
- Same change for the internal `run` function
|
||||
|
||||
- [ ] **2.4 Update callers in Program.ts** (`src/Entrypoint/Program.ts`)
|
||||
- [x] **2.4 Update callers in Program.ts** (`src/Entrypoint/Program.ts`)
|
||||
- In `tryCompileFunction`, change from try/catch around `compileFn` to handling the `Result`:
|
||||
- If `Ok(codegenFn)`: return the compiled function
|
||||
- If `Err(compilerError)`: return `{kind: 'error', error: compilerError}`
|
||||
@@ -248,31 +248,31 @@ The inference passes are the most critical to handle correctly because they prod
|
||||
|
||||
Walk through `runWithEnvironment` and wrap each pass call site. This is the integration work tying Phases 3-6 together.
|
||||
|
||||
- [ ] **7.1 Wrap `lower()` call** (line 163)
|
||||
- [x] **7.1 Wrap `lower()` call** (line 163)
|
||||
- Change from `lower(func, env).unwrap()` to `lower(func, env)` (direct return after Phase 3.1)
|
||||
|
||||
- [ ] **7.2 Wrap validation calls that use `.unwrap()`** (lines 169-303)
|
||||
- [x] **7.2 Wrap validation calls that use `.unwrap()`** (lines 169-303)
|
||||
- Remove `.unwrap()` from all validation calls after they're updated in Phase 4
|
||||
- For validations guarded by `env.enableValidations`, keep the guard but remove the `.unwrap()`
|
||||
|
||||
- [ ] **7.3 Wrap inference calls** (lines 233-267)
|
||||
- [x] **7.3 Wrap inference calls** (lines 233-267)
|
||||
- After Phase 5, `inferMutationAliasingEffects` and `inferMutationAliasingRanges` record errors directly
|
||||
- Remove the `mutabilityAliasingErrors` / `mutabilityAliasingRangeErrors` variables and their conditional throw logic
|
||||
|
||||
- [ ] **7.4 Wrap `env.logErrors()` calls** (lines 286-331)
|
||||
- [x] **7.4 Wrap `env.logErrors()` calls** (lines 286-331)
|
||||
- After Phase 4.13-4.16, these passes record on env directly
|
||||
- Remove the `env.logErrors()` wrapper calls
|
||||
|
||||
- [ ] **7.5 Wrap codegen** (lines 575-578)
|
||||
- [x] **7.5 Wrap codegen** (lines 575-578)
|
||||
- After Phase 6.1, `codegenFunction` returns directly
|
||||
- Remove the `.unwrap()`
|
||||
|
||||
- [ ] **7.6 Add final error check** (end of `runWithEnvironment`)
|
||||
- [x] **7.6 Add final error check** (end of `runWithEnvironment`)
|
||||
- After all passes complete, check `env.hasErrors()`
|
||||
- If no errors: return `Ok(ast)`
|
||||
- If errors: return `Err(env.aggregateErrors())`
|
||||
|
||||
- [ ] **7.7 Consider wrapping each pass in `env.tryRecord()`** as a safety net
|
||||
- [x] **7.7 Consider wrapping each pass in `env.tryRecord()`** as a safety net
|
||||
- Even after individual passes are updated, wrapping each pass call in `env.tryRecord()` provides defense-in-depth
|
||||
- If a pass unexpectedly throws a CompilerError (e.g., from a code path we missed), it gets caught and recorded rather than aborting the pipeline
|
||||
- Non-CompilerError exceptions and invariants still propagate immediately
|
||||
@@ -318,3 +318,10 @@ Walk through `runWithEnvironment` and wrap each pass call site. This is the inte
|
||||
- The `assertConsistentIdentifiers`, `assertTerminalSuccessorsExist`, `assertTerminalPredsExist`, `assertValidBlockNesting`, `assertValidMutableRanges`, `assertWellFormedBreakTargets`, `assertScopeInstructionsWithinScopes` assertion functions should continue to throw — they are invariant checks on internal data structure consistency
|
||||
- The `panicThreshold` mechanism in Program.ts should continue to work — it now operates on the aggregated error from the Result rather than a caught exception, but the behavior is the same
|
||||
|
||||
## Key Learnings
|
||||
|
||||
* **Phase 2+7 (Pipeline tryRecord wrapping) was sufficient for basic fault tolerance.** Wrapping all passes in `env.tryRecord()` immediately enabled the compiler to continue past errors that previously threw. This caused 52 test fixtures to produce additional errors that were previously masked by the first error bailing out. For example, `error.todo-reassign-const` previously reported only "Support destructuring of context variables" but now also reports the immutability violation.
|
||||
* **Lint-only passes (Pattern B: `env.logErrors()`) should not use `tryRecord()`/`recordError()`** because those errors are intentionally non-blocking. They are reported via the logger only and should not cause the pipeline to return `Err`. The `logErrors` pattern was kept for `validateNoDerivedComputationsInEffects_exp`, `validateNoSetStateInEffects`, `validateNoJSXInTryStatement`, and `validateStaticComponents`.
|
||||
* **Inference passes that return `Result` with validation errors** (`inferMutationAliasingEffects`, `inferMutationAliasingRanges`) were changed to record errors via `env.recordErrors()` instead of throwing, allowing subsequent passes to proceed.
|
||||
* **Value-producing passes** (`memoizeFbtAndMacroOperandsInSameScope`, `renameVariables`, `buildReactiveFunction`) need safe default values when wrapped in `tryRecord()` since the callback can't return values. We initialize with empty defaults (e.g., `new Set()`) before the `tryRecord()` call.
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@ import {NodePath} from '@babel/traverse';
|
||||
import * as t from '@babel/types';
|
||||
import prettyFormat from 'pretty-format';
|
||||
import {CompilerOutputMode, Logger, ProgramContext} from '.';
|
||||
import {CompilerError} from '../CompilerError';
|
||||
import {Err, Ok, Result} from '../Utils/Result';
|
||||
import {
|
||||
HIRFunction,
|
||||
IdentifierId,
|
||||
ReactiveFunction,
|
||||
assertConsistentIdentifiers,
|
||||
assertTerminalPredsExist,
|
||||
@@ -125,7 +128,7 @@ function run(
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
code: string | null,
|
||||
): CodegenFunction {
|
||||
): Result<CodegenFunction, CompilerError> {
|
||||
const contextIdentifiers = findContextIdentifiers(func);
|
||||
const env = new Environment(
|
||||
func.scope,
|
||||
@@ -156,18 +159,24 @@ function runWithEnvironment(
|
||||
t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
|
||||
>,
|
||||
env: Environment,
|
||||
): CodegenFunction {
|
||||
): Result<CodegenFunction, CompilerError> {
|
||||
const log = (value: CompilerPipelineValue): void => {
|
||||
env.logger?.debugLogIRs?.(value);
|
||||
};
|
||||
const hir = lower(func, env).unwrap();
|
||||
log({kind: 'hir', name: 'HIR', value: hir});
|
||||
|
||||
pruneMaybeThrows(hir);
|
||||
env.tryRecord(() => {
|
||||
pruneMaybeThrows(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
|
||||
|
||||
validateContextVariableLValues(hir);
|
||||
validateUseMemo(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateContextVariableLValues(hir);
|
||||
});
|
||||
env.tryRecord(() => {
|
||||
validateUseMemo(hir).unwrap();
|
||||
});
|
||||
|
||||
if (
|
||||
env.enableDropManualMemoization &&
|
||||
@@ -175,84 +184,118 @@ function runWithEnvironment(
|
||||
!env.config.disableMemoizationForDebugging &&
|
||||
!env.config.enableChangeDetectionForDebugging
|
||||
) {
|
||||
dropManualMemoization(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
dropManualMemoization(hir).unwrap();
|
||||
});
|
||||
log({kind: 'hir', name: 'DropManualMemoization', value: hir});
|
||||
}
|
||||
|
||||
inlineImmediatelyInvokedFunctionExpressions(hir);
|
||||
env.tryRecord(() => {
|
||||
inlineImmediatelyInvokedFunctionExpressions(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'InlineImmediatelyInvokedFunctionExpressions',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
mergeConsecutiveBlocks(hir);
|
||||
env.tryRecord(() => {
|
||||
mergeConsecutiveBlocks(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'MergeConsecutiveBlocks', value: hir});
|
||||
|
||||
assertConsistentIdentifiers(hir);
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
|
||||
enterSSA(hir);
|
||||
env.tryRecord(() => {
|
||||
enterSSA(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'SSA', value: hir});
|
||||
|
||||
eliminateRedundantPhi(hir);
|
||||
env.tryRecord(() => {
|
||||
eliminateRedundantPhi(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'EliminateRedundantPhi', value: hir});
|
||||
|
||||
assertConsistentIdentifiers(hir);
|
||||
|
||||
constantPropagation(hir);
|
||||
env.tryRecord(() => {
|
||||
constantPropagation(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'ConstantPropagation', value: hir});
|
||||
|
||||
inferTypes(hir);
|
||||
env.tryRecord(() => {
|
||||
inferTypes(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'InferTypes', value: hir});
|
||||
|
||||
if (env.enableValidations) {
|
||||
if (env.config.validateHooksUsage) {
|
||||
validateHooksUsage(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateHooksUsage(hir).unwrap();
|
||||
});
|
||||
}
|
||||
if (env.config.validateNoCapitalizedCalls) {
|
||||
validateNoCapitalizedCalls(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateNoCapitalizedCalls(hir).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (env.config.enableFire) {
|
||||
transformFire(hir);
|
||||
env.tryRecord(() => {
|
||||
transformFire(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'TransformFire', value: hir});
|
||||
}
|
||||
|
||||
if (env.config.lowerContextAccess) {
|
||||
lowerContextAccess(hir, env.config.lowerContextAccess);
|
||||
env.tryRecord(() => {
|
||||
lowerContextAccess(hir, env.config.lowerContextAccess!);
|
||||
});
|
||||
}
|
||||
|
||||
optimizePropsMethodCalls(hir);
|
||||
env.tryRecord(() => {
|
||||
optimizePropsMethodCalls(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'OptimizePropsMethodCalls', value: hir});
|
||||
|
||||
analyseFunctions(hir);
|
||||
env.tryRecord(() => {
|
||||
analyseFunctions(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'AnalyseFunctions', value: hir});
|
||||
|
||||
const mutabilityAliasingErrors = inferMutationAliasingEffects(hir);
|
||||
log({kind: 'hir', name: 'InferMutationAliasingEffects', value: hir});
|
||||
if (env.enableValidations) {
|
||||
if (mutabilityAliasingErrors.isErr()) {
|
||||
throw mutabilityAliasingErrors.unwrapErr();
|
||||
env.recordErrors(mutabilityAliasingErrors.unwrapErr());
|
||||
}
|
||||
}
|
||||
|
||||
if (env.outputMode === 'ssr') {
|
||||
optimizeForSSR(hir);
|
||||
env.tryRecord(() => {
|
||||
optimizeForSSR(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'OptimizeForSSR', value: hir});
|
||||
}
|
||||
|
||||
// Note: Has to come after infer reference effects because "dead" code may still affect inference
|
||||
deadCodeElimination(hir);
|
||||
env.tryRecord(() => {
|
||||
deadCodeElimination(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'DeadCodeElimination', value: hir});
|
||||
|
||||
if (env.config.enableInstructionReordering) {
|
||||
instructionReordering(hir);
|
||||
env.tryRecord(() => {
|
||||
instructionReordering(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'InstructionReordering', value: hir});
|
||||
}
|
||||
|
||||
pruneMaybeThrows(hir);
|
||||
env.tryRecord(() => {
|
||||
pruneMaybeThrows(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'PruneMaybeThrows', value: hir});
|
||||
|
||||
const mutabilityAliasingRangeErrors = inferMutationAliasingRanges(hir, {
|
||||
@@ -261,9 +304,11 @@ function runWithEnvironment(
|
||||
log({kind: 'hir', name: 'InferMutationAliasingRanges', value: hir});
|
||||
if (env.enableValidations) {
|
||||
if (mutabilityAliasingRangeErrors.isErr()) {
|
||||
throw mutabilityAliasingRangeErrors.unwrapErr();
|
||||
env.recordErrors(mutabilityAliasingRangeErrors.unwrapErr());
|
||||
}
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
env.tryRecord(() => {
|
||||
validateLocalsNotReassignedAfterRender(hir);
|
||||
});
|
||||
}
|
||||
|
||||
if (env.enableValidations) {
|
||||
@@ -272,11 +317,15 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.validateRefAccessDuringRender) {
|
||||
validateNoRefAccessInRender(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateNoRefAccessInRender(hir).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInRender) {
|
||||
validateNoSetStateInRender(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateNoSetStateInRender(hir).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
@@ -285,7 +334,9 @@ function runWithEnvironment(
|
||||
) {
|
||||
env.logErrors(validateNoDerivedComputationsInEffects_exp(hir));
|
||||
} else if (env.config.validateNoDerivedComputationsInEffects) {
|
||||
validateNoDerivedComputationsInEffects(hir);
|
||||
env.tryRecord(() => {
|
||||
validateNoDerivedComputationsInEffects(hir);
|
||||
});
|
||||
}
|
||||
|
||||
if (env.config.validateNoSetStateInEffects && env.outputMode === 'lint') {
|
||||
@@ -297,13 +348,19 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.validateNoImpureFunctionsInRender) {
|
||||
validateNoImpureFunctionsInRender(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateNoImpureFunctionsInRender(hir).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
validateNoFreezingKnownMutableFunctions(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateNoFreezingKnownMutableFunctions(hir).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
inferReactivePlaces(hir);
|
||||
env.tryRecord(() => {
|
||||
inferReactivePlaces(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'InferReactivePlaces', value: hir});
|
||||
|
||||
if (env.enableValidations) {
|
||||
@@ -312,11 +369,15 @@ function runWithEnvironment(
|
||||
env.config.validateExhaustiveEffectDependencies
|
||||
) {
|
||||
// NOTE: this relies on reactivity inference running first
|
||||
validateExhaustiveDependencies(hir).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateExhaustiveDependencies(hir).unwrap();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
rewriteInstructionKindsBasedOnReassignment(hir);
|
||||
env.tryRecord(() => {
|
||||
rewriteInstructionKindsBasedOnReassignment(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'RewriteInstructionKindsBasedOnReassignment',
|
||||
@@ -337,11 +398,16 @@ function runWithEnvironment(
|
||||
* if inferred memoization is enabled. This makes all later passes which
|
||||
* transform reactive-scope labeled instructions no-ops.
|
||||
*/
|
||||
inferReactiveScopeVariables(hir);
|
||||
env.tryRecord(() => {
|
||||
inferReactiveScopeVariables(hir);
|
||||
});
|
||||
log({kind: 'hir', name: 'InferReactiveScopeVariables', value: hir});
|
||||
}
|
||||
|
||||
const fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
|
||||
let fbtOperands: Set<IdentifierId> = new Set();
|
||||
env.tryRecord(() => {
|
||||
fbtOperands = memoizeFbtAndMacroOperandsInSameScope(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'MemoizeFbtAndMacroOperandsInSameScope',
|
||||
@@ -349,11 +415,15 @@ function runWithEnvironment(
|
||||
});
|
||||
|
||||
if (env.config.enableJsxOutlining) {
|
||||
outlineJSX(hir);
|
||||
env.tryRecord(() => {
|
||||
outlineJSX(hir);
|
||||
});
|
||||
}
|
||||
|
||||
if (env.config.enableNameAnonymousFunctions) {
|
||||
nameAnonymousFunctions(hir);
|
||||
env.tryRecord(() => {
|
||||
nameAnonymousFunctions(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'NameAnonymousFunctions',
|
||||
@@ -362,39 +432,51 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.enableFunctionOutlining) {
|
||||
outlineFunctions(hir, fbtOperands);
|
||||
env.tryRecord(() => {
|
||||
outlineFunctions(hir, fbtOperands);
|
||||
});
|
||||
log({kind: 'hir', name: 'OutlineFunctions', value: hir});
|
||||
}
|
||||
|
||||
alignMethodCallScopes(hir);
|
||||
env.tryRecord(() => {
|
||||
alignMethodCallScopes(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'AlignMethodCallScopes',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
alignObjectMethodScopes(hir);
|
||||
env.tryRecord(() => {
|
||||
alignObjectMethodScopes(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'AlignObjectMethodScopes',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
pruneUnusedLabelsHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
pruneUnusedLabelsHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'PruneUnusedLabelsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
alignReactiveScopesToBlockScopesHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'AlignReactiveScopesToBlockScopesHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
mergeOverlappingReactiveScopesHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
mergeOverlappingReactiveScopesHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'MergeOverlappingReactiveScopesHIR',
|
||||
@@ -402,7 +484,9 @@ function runWithEnvironment(
|
||||
});
|
||||
assertValidBlockNesting(hir);
|
||||
|
||||
buildReactiveScopeTerminalsHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
buildReactiveScopeTerminalsHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'BuildReactiveScopeTerminalsHIR',
|
||||
@@ -411,14 +495,18 @@ function runWithEnvironment(
|
||||
|
||||
assertValidBlockNesting(hir);
|
||||
|
||||
flattenReactiveLoopsHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
flattenReactiveLoopsHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenReactiveLoopsHIR',
|
||||
value: hir,
|
||||
});
|
||||
|
||||
flattenScopesWithHooksOrUseHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
flattenScopesWithHooksOrUseHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'FlattenScopesWithHooksOrUseHIR',
|
||||
@@ -426,7 +514,9 @@ function runWithEnvironment(
|
||||
});
|
||||
assertTerminalSuccessorsExist(hir);
|
||||
assertTerminalPredsExist(hir);
|
||||
propagateScopeDependenciesHIR(hir);
|
||||
env.tryRecord(() => {
|
||||
propagateScopeDependenciesHIR(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'PropagateScopeDependenciesHIR',
|
||||
@@ -434,7 +524,9 @@ function runWithEnvironment(
|
||||
});
|
||||
|
||||
if (env.config.inferEffectDependencies) {
|
||||
inferEffectDependencies(hir);
|
||||
env.tryRecord(() => {
|
||||
inferEffectDependencies(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'InferEffectDependencies',
|
||||
@@ -443,7 +535,9 @@ function runWithEnvironment(
|
||||
}
|
||||
|
||||
if (env.config.inlineJsxTransform) {
|
||||
inlineJsxTransform(hir, env.config.inlineJsxTransform);
|
||||
env.tryRecord(() => {
|
||||
inlineJsxTransform(hir, env.config.inlineJsxTransform!);
|
||||
});
|
||||
log({
|
||||
kind: 'hir',
|
||||
name: 'inlineJsxTransform',
|
||||
@@ -451,7 +545,10 @@ function runWithEnvironment(
|
||||
});
|
||||
}
|
||||
|
||||
const reactiveFunction = buildReactiveFunction(hir);
|
||||
let reactiveFunction!: ReactiveFunction;
|
||||
env.tryRecord(() => {
|
||||
reactiveFunction = buildReactiveFunction(hir);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'BuildReactiveFunction',
|
||||
@@ -460,7 +557,9 @@ function runWithEnvironment(
|
||||
|
||||
assertWellFormedBreakTargets(reactiveFunction);
|
||||
|
||||
pruneUnusedLabels(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneUnusedLabels(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedLabels',
|
||||
@@ -468,35 +567,45 @@ function runWithEnvironment(
|
||||
});
|
||||
assertScopeInstructionsWithinScopes(reactiveFunction);
|
||||
|
||||
pruneNonEscapingScopes(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneNonEscapingScopes(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneNonEscapingScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneNonReactiveDependencies(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneNonReactiveDependencies(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneNonReactiveDependencies',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneUnusedScopes(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneUnusedScopes(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedScopes',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
mergeReactiveScopesThatInvalidateTogether(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
mergeReactiveScopesThatInvalidateTogether(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'MergeReactiveScopesThatInvalidateTogether',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneAlwaysInvalidatingScopes(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneAlwaysInvalidatingScopes(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneAlwaysInvalidatingScopes',
|
||||
@@ -504,7 +613,9 @@ function runWithEnvironment(
|
||||
});
|
||||
|
||||
if (env.config.enableChangeDetectionForDebugging != null) {
|
||||
pruneInitializationDependencies(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneInitializationDependencies(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneInitializationDependencies',
|
||||
@@ -512,49 +623,64 @@ function runWithEnvironment(
|
||||
});
|
||||
}
|
||||
|
||||
propagateEarlyReturns(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
propagateEarlyReturns(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PropagateEarlyReturns',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneUnusedLValues(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneUnusedLValues(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneUnusedLValues',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
promoteUsedTemporaries(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
promoteUsedTemporaries(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PromoteUsedTemporaries',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
extractScopeDeclarationsFromDestructuring(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
extractScopeDeclarationsFromDestructuring(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'ExtractScopeDeclarationsFromDestructuring',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
stabilizeBlockIds(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
stabilizeBlockIds(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'StabilizeBlockIds',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
const uniqueIdentifiers = renameVariables(reactiveFunction);
|
||||
let uniqueIdentifiers: Set<string> = new Set();
|
||||
env.tryRecord(() => {
|
||||
uniqueIdentifiers = renameVariables(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'RenameVariables',
|
||||
value: reactiveFunction,
|
||||
});
|
||||
|
||||
pruneHoistedContexts(reactiveFunction);
|
||||
env.tryRecord(() => {
|
||||
pruneHoistedContexts(reactiveFunction);
|
||||
});
|
||||
log({
|
||||
kind: 'reactive',
|
||||
name: 'PruneHoistedContexts',
|
||||
@@ -562,27 +688,38 @@ function runWithEnvironment(
|
||||
});
|
||||
|
||||
if (env.config.validateMemoizedEffectDependencies) {
|
||||
validateMemoizedEffectDependencies(reactiveFunction).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateMemoizedEffectDependencies(reactiveFunction).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
env.config.enablePreserveExistingMemoizationGuarantees ||
|
||||
env.config.validatePreserveExistingMemoizationGuarantees
|
||||
) {
|
||||
validatePreservedManualMemoization(reactiveFunction).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validatePreservedManualMemoization(reactiveFunction).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
const ast = codegenFunction(reactiveFunction, {
|
||||
const codegenResult = codegenFunction(reactiveFunction, {
|
||||
uniqueIdentifiers,
|
||||
fbtOperands,
|
||||
}).unwrap();
|
||||
});
|
||||
if (codegenResult.isErr()) {
|
||||
env.recordErrors(codegenResult.unwrapErr());
|
||||
return Err(env.aggregateErrors());
|
||||
}
|
||||
const ast = codegenResult.unwrap();
|
||||
log({kind: 'ast', name: 'Codegen', value: ast});
|
||||
for (const outlined of ast.outlined) {
|
||||
log({kind: 'ast', name: 'Codegen (outlined)', value: outlined.fn});
|
||||
}
|
||||
|
||||
if (env.config.validateSourceLocations) {
|
||||
validateSourceLocations(func, ast).unwrap();
|
||||
env.tryRecord(() => {
|
||||
validateSourceLocations(func, ast).unwrap();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -594,7 +731,10 @@ function runWithEnvironment(
|
||||
throw new Error('unexpected error');
|
||||
}
|
||||
|
||||
return ast;
|
||||
if (env.hasErrors()) {
|
||||
return Err(env.aggregateErrors());
|
||||
}
|
||||
return Ok(ast);
|
||||
}
|
||||
|
||||
export function compileFn(
|
||||
@@ -608,7 +748,7 @@ export function compileFn(
|
||||
logger: Logger | null,
|
||||
filename: string | null,
|
||||
code: string | null,
|
||||
): CodegenFunction {
|
||||
): Result<CodegenFunction, CompilerError> {
|
||||
return run(
|
||||
func,
|
||||
config,
|
||||
|
||||
@@ -728,19 +728,21 @@ function tryCompileFunction(
|
||||
}
|
||||
|
||||
try {
|
||||
return {
|
||||
kind: 'compile',
|
||||
compiledFn: compileFn(
|
||||
fn,
|
||||
programContext.opts.environment,
|
||||
fnType,
|
||||
outputMode,
|
||||
programContext,
|
||||
programContext.opts.logger,
|
||||
programContext.filename,
|
||||
programContext.code,
|
||||
),
|
||||
};
|
||||
const result = compileFn(
|
||||
fn,
|
||||
programContext.opts.environment,
|
||||
fnType,
|
||||
outputMode,
|
||||
programContext,
|
||||
programContext.opts.logger,
|
||||
programContext.filename,
|
||||
programContext.code,
|
||||
);
|
||||
if (result.isOk()) {
|
||||
return {kind: 'compile', compiledFn: result.unwrap()};
|
||||
} else {
|
||||
return {kind: 'error', error: result.unwrapErr()};
|
||||
}
|
||||
} catch (err) {
|
||||
return {kind: 'error', error: err};
|
||||
}
|
||||
@@ -779,10 +781,16 @@ function retryCompileFunction(
|
||||
programContext.code,
|
||||
);
|
||||
|
||||
if (!retryResult.hasFireRewrite && !retryResult.hasInferredEffect) {
|
||||
if (retryResult.isErr()) {
|
||||
const err = retryResult.unwrapErr();
|
||||
programContext.retryErrors.push({fn, error: err});
|
||||
return null;
|
||||
}
|
||||
return retryResult;
|
||||
const compiledFn = retryResult.unwrap();
|
||||
if (!compiledFn.hasFireRewrite && !compiledFn.hasInferredEffect) {
|
||||
return null;
|
||||
}
|
||||
return compiledFn;
|
||||
} catch (err) {
|
||||
// TODO: we might want to log error here, but this will also result in duplicate logging
|
||||
if (err instanceof CompilerError) {
|
||||
|
||||
@@ -21,15 +21,15 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Todo: [hoisting] EnterSSA: Expected identifier to be defined before being used
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
Identifier x$1 is undefined.
|
||||
<unknown> x$1.
|
||||
|
||||
error.dont-hoist-inline-reference.ts:3:2
|
||||
error.dont-hoist-inline-reference.ts:3:21
|
||||
1 | import {identity} from 'shared-runtime';
|
||||
2 | function useInvalid() {
|
||||
> 3 | const x = identity(x);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^ [hoisting] EnterSSA: Expected identifier to be defined before being used
|
||||
| ^ this is uninitialized
|
||||
4 | return x;
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
@@ -29,7 +29,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
@@ -43,6 +43,32 @@ error.hook-call-freezes-captured-memberexpr.ts:13:2
|
||||
14 | return <Stringify x={x} cb={cb} />;
|
||||
15 | }
|
||||
16 |
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `x` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.hook-call-freezes-captured-memberexpr.ts:9:25
|
||||
7 | * After this custom hook call, it's no longer valid to mutate x.
|
||||
8 | */
|
||||
> 9 | const cb = useIdentity(() => {
|
||||
| ^^^^^^^
|
||||
> 10 | x.value++;
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 11 | });
|
||||
| ^^^^ This function may (indirectly) reassign or modify `x` after render
|
||||
12 |
|
||||
13 | x.value += count;
|
||||
14 | return <Stringify x={x} cb={cb} />;
|
||||
|
||||
error.hook-call-freezes-captured-memberexpr.ts:10:4
|
||||
8 | */
|
||||
9 | const cb = useIdentity(() => {
|
||||
> 10 | x.value++;
|
||||
| ^ This modifies `x`
|
||||
11 | });
|
||||
12 |
|
||||
13 | x.value += count;
|
||||
```
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ function component(a, b) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 3 errors:
|
||||
|
||||
Error: useMemo() callbacks may not be async or generator functions
|
||||
|
||||
@@ -32,6 +32,37 @@ error.invalid-ReactUseMemo-async-callback.ts:2:24
|
||||
5 | return x;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
|
||||
|
||||
error.invalid-ReactUseMemo-async-callback.ts:3:10
|
||||
1 | function component(a, b) {
|
||||
2 | let x = React.useMemo(async () => {
|
||||
> 3 | await a;
|
||||
| ^ Missing dependency `a`
|
||||
4 | }, []);
|
||||
5 | return x;
|
||||
6 | }
|
||||
|
||||
Inferred dependencies: `[a]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `a`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
error.invalid-ReactUseMemo-async-callback.ts:2:24
|
||||
1 | function component(a, b) {
|
||||
> 2 | let x = React.useMemo(async () => {
|
||||
| ^^^^^^^^^^^^^
|
||||
> 3 | await a;
|
||||
| ^^^^^^^^^^^^
|
||||
> 4 | }, []);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
5 | return x;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ function Component({item, cond}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
Found 3 errors:
|
||||
|
||||
Error: Calling setState from useMemo may trigger an infinite loop
|
||||
|
||||
@@ -49,6 +49,39 @@ error.invalid-conditional-setState-in-useMemo.ts:8:6
|
||||
9 | }
|
||||
10 | }, [cond, key, init]);
|
||||
11 |
|
||||
|
||||
Error: Found missing/extra memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI. 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.
|
||||
|
||||
error.invalid-conditional-setState-in-useMemo.ts:7:18
|
||||
5 | useMemo(() => {
|
||||
6 | if (cond) {
|
||||
> 7 | setPrevItem(item);
|
||||
| ^^^^ Missing dependency `item`
|
||||
8 | setState(0);
|
||||
9 | }
|
||||
10 | }, [cond, key, init]);
|
||||
|
||||
error.invalid-conditional-setState-in-useMemo.ts:10:12
|
||||
8 | setState(0);
|
||||
9 | }
|
||||
> 10 | }, [cond, key, init]);
|
||||
| ^^^ Unnecessary dependency `key`. Values declared outside of a component/hook should not be listed as dependencies as the component will not re-render if they change
|
||||
11 |
|
||||
12 | return state;
|
||||
13 | }
|
||||
|
||||
error.invalid-conditional-setState-in-useMemo.ts:10:17
|
||||
8 | setState(0);
|
||||
9 | }
|
||||
> 10 | }, [cond, key, init]);
|
||||
| ^^^^ Unnecessary dependency `init`. Values declared outside of a component/hook should not be listed as dependencies as the component will not re-render if they change
|
||||
11 |
|
||||
12 | return state;
|
||||
13 | }
|
||||
|
||||
Inferred dependencies: `[cond, item]`
|
||||
```
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 3 errors:
|
||||
Found 6 errors:
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
@@ -57,6 +57,45 @@ error.invalid-impure-functions-in-render.ts:6:15
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | const date = Date.now();
|
||||
| ^^^^^^^^ Cannot call impure function
|
||||
5 | const now = performance.now();
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`performance.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:5:14
|
||||
3 | function Component() {
|
||||
4 | const date = Date.now();
|
||||
> 5 | const now = performance.now();
|
||||
| ^^^^^^^^^^^^^^^ Cannot call impure function
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`Math.random` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:6:15
|
||||
4 | const date = Date.now();
|
||||
5 | const now = performance.now();
|
||||
> 6 | const rand = Math.random();
|
||||
| ^^^^^^^^^^^ Cannot call impure function
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ function useInvalidMutation(options) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
@@ -30,6 +30,27 @@ error.invalid-mutation-in-closure.ts:4:4
|
||||
5 | }
|
||||
6 | return test;
|
||||
7 | }
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `options` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-mutation-in-closure.ts:6:9
|
||||
4 | options.foo = 'bar';
|
||||
5 | }
|
||||
> 6 | return test;
|
||||
| ^^^^ This function may (indirectly) reassign or modify `options` after render
|
||||
7 | }
|
||||
8 |
|
||||
|
||||
error.invalid-mutation-in-closure.ts:4:4
|
||||
2 | function test() {
|
||||
3 | foo(options.foo); // error should not point on this line
|
||||
> 4 | options.foo = 'bar';
|
||||
| ^^^^^^^ This modifies `options`
|
||||
5 | }
|
||||
6 | return test;
|
||||
7 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ function useFoo() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -29,6 +29,31 @@ error.invalid-reassign-local-in-hook-return-value.ts:4:4
|
||||
5 | };
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `x` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-in-hook-return-value.ts:3:9
|
||||
1 | function useFoo() {
|
||||
2 | let x = 0;
|
||||
> 3 | return value => {
|
||||
| ^^^^^^^^^^
|
||||
> 4 | x = value;
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 5 | };
|
||||
| ^^^^ This function may (indirectly) reassign or modify `x` after render
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
error.invalid-reassign-local-in-hook-return-value.ts:4:4
|
||||
2 | let x = 0;
|
||||
3 | return value => {
|
||||
> 4 | x = value;
|
||||
| ^ This modifies `x`
|
||||
5 | };
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -61,6 +61,32 @@ error.invalid-reassign-local-variable-in-effect.ts:7:4
|
||||
8 | };
|
||||
9 |
|
||||
10 | const onMount = newValue => {
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `local` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-variable-in-effect.ts:33:12
|
||||
31 | };
|
||||
32 |
|
||||
> 33 | useEffect(() => {
|
||||
| ^^^^^^^
|
||||
> 34 | onMount();
|
||||
| ^^^^^^^^^^^^^^
|
||||
> 35 | }, [onMount]);
|
||||
| ^^^^ This function may (indirectly) reassign or modify `local` after render
|
||||
36 |
|
||||
37 | return 'ok';
|
||||
38 | }
|
||||
|
||||
error.invalid-reassign-local-variable-in-effect.ts:7:4
|
||||
5 |
|
||||
6 | const reassignLocal = newValue => {
|
||||
> 7 | local = newValue;
|
||||
| ^^^^^ This modifies `local`
|
||||
8 | };
|
||||
9 |
|
||||
10 | const onMount = newValue => {
|
||||
```
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -62,6 +62,32 @@ error.invalid-reassign-local-variable-in-hook-argument.ts:8:4
|
||||
9 | };
|
||||
10 |
|
||||
11 | const callback = newValue => {
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `local` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-variable-in-hook-argument.ts:34:14
|
||||
32 | };
|
||||
33 |
|
||||
> 34 | useIdentity(() => {
|
||||
| ^^^^^^^
|
||||
> 35 | callback();
|
||||
| ^^^^^^^^^^^^^^^
|
||||
> 36 | });
|
||||
| ^^^^ This function may (indirectly) reassign or modify `local` after render
|
||||
37 |
|
||||
38 | return 'ok';
|
||||
39 | }
|
||||
|
||||
error.invalid-reassign-local-variable-in-hook-argument.ts:8:4
|
||||
6 |
|
||||
7 | const reassignLocal = newValue => {
|
||||
> 8 | local = newValue;
|
||||
| ^^^^^ This modifies `local`
|
||||
9 | };
|
||||
10 |
|
||||
11 | const callback = newValue => {
|
||||
```
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -55,6 +55,27 @@ error.invalid-reassign-local-variable-in-jsx-callback.ts:5:4
|
||||
6 | };
|
||||
7 |
|
||||
8 | const onClick = newValue => {
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `local` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-variable-in-jsx-callback.ts:31:26
|
||||
29 | };
|
||||
30 |
|
||||
> 31 | return <button onClick={onClick}>Submit</button>;
|
||||
| ^^^^^^^ This function may (indirectly) reassign or modify `local` after render
|
||||
32 | }
|
||||
33 |
|
||||
|
||||
error.invalid-reassign-local-variable-in-jsx-callback.ts:5:4
|
||||
3 |
|
||||
4 | const reassignLocal = newValue => {
|
||||
> 5 | local = newValue;
|
||||
| ^^^^^ This modifies `local`
|
||||
6 | };
|
||||
7 |
|
||||
8 | const onClick = newValue => {
|
||||
```
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function useKeyedState({key, init}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 3 errors:
|
||||
|
||||
Error: Calling setState from useMemo may trigger an infinite loop
|
||||
|
||||
@@ -40,6 +40,61 @@ error.invalid-setState-in-useMemo-indirect-useCallback.ts:13:4
|
||||
14 | }, [key, init]);
|
||||
15 |
|
||||
16 | return state;
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
|
||||
|
||||
error.invalid-setState-in-useMemo-indirect-useCallback.ts:9:13
|
||||
7 | const fn = useCallback(() => {
|
||||
8 | setPrevKey(key);
|
||||
> 9 | setState(init);
|
||||
| ^^^^ Missing dependency `init`
|
||||
10 | });
|
||||
11 |
|
||||
12 | useMemo(() => {
|
||||
|
||||
error.invalid-setState-in-useMemo-indirect-useCallback.ts:8:15
|
||||
6 |
|
||||
7 | const fn = useCallback(() => {
|
||||
> 8 | setPrevKey(key);
|
||||
| ^^^ Missing dependency `key`
|
||||
9 | setState(init);
|
||||
10 | });
|
||||
11 |
|
||||
|
||||
Error: Found missing/extra memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI. 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.
|
||||
|
||||
error.invalid-setState-in-useMemo-indirect-useCallback.ts:13:4
|
||||
11 |
|
||||
12 | useMemo(() => {
|
||||
> 13 | fn();
|
||||
| ^^ Missing dependency `fn`
|
||||
14 | }, [key, init]);
|
||||
15 |
|
||||
16 | return state;
|
||||
|
||||
error.invalid-setState-in-useMemo-indirect-useCallback.ts:14:6
|
||||
12 | useMemo(() => {
|
||||
13 | fn();
|
||||
> 14 | }, [key, init]);
|
||||
| ^^^ Unnecessary dependency `key`
|
||||
15 |
|
||||
16 | return state;
|
||||
17 | }
|
||||
|
||||
error.invalid-setState-in-useMemo-indirect-useCallback.ts:14:11
|
||||
12 | useMemo(() => {
|
||||
13 | fn();
|
||||
> 14 | }, [key, init]);
|
||||
| ^^^^ Unnecessary dependency `init`
|
||||
15 |
|
||||
16 | return state;
|
||||
17 | }
|
||||
|
||||
Inferred dependencies: `[fn]`
|
||||
```
|
||||
|
||||
|
||||
@@ -14,7 +14,19 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
Expected type for object property 'useHookNotTypedAsHook' from module 'ReactCompilerTest' to be a hook based on the property name.
|
||||
|
||||
error.invalid-type-provider-hook-name-not-typed-as-hook-namespace.ts:4:9
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | return ReactCompilerTest.useHookNotTypedAsHook();
|
||||
| ^^^^^^^^^^^^^^^^^ Invalid type configuration for module
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
|
||||
@@ -14,7 +14,19 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
Expected type for object property 'useHookNotTypedAsHook' from module 'ReactCompilerTest' to be a hook based on the property name.
|
||||
|
||||
error.invalid-type-provider-hook-name-not-typed-as-hook.ts:4:9
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | return useHookNotTypedAsHook();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^ Invalid type configuration for module
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
|
||||
@@ -14,7 +14,19 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
Expected type for `import ... from 'useDefaultExportNotTypedAsHook'` to be a hook based on the module name.
|
||||
|
||||
error.invalid-type-provider-hooklike-module-default-not-hook.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | return <div>{foo()}</div>;
|
||||
| ^^^ Invalid type configuration for module
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
|
||||
@@ -14,7 +14,19 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
Expected type for object property 'useHookNotTypedAsHook' from module 'ReactCompilerTest' to be a hook based on the property name.
|
||||
|
||||
error.invalid-type-provider-nonhook-name-typed-as-hook.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | return <div>{notAhookTypedAsHook()}</div>;
|
||||
| ^^^^^^^^^^^^^^^^^^^ Invalid type configuration for module
|
||||
5 | }
|
||||
6 |
|
||||
|
||||
Error: Invalid type configuration for module
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ function component(a, b) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 3 errors:
|
||||
|
||||
Error: useMemo() callbacks may not be async or generator functions
|
||||
|
||||
@@ -32,6 +32,37 @@ error.invalid-useMemo-async-callback.ts:2:18
|
||||
5 | return x;
|
||||
6 | }
|
||||
7 |
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
|
||||
|
||||
error.invalid-useMemo-async-callback.ts:3:10
|
||||
1 | function component(a, b) {
|
||||
2 | let x = useMemo(async () => {
|
||||
> 3 | await a;
|
||||
| ^ Missing dependency `a`
|
||||
4 | }, []);
|
||||
5 | return x;
|
||||
6 | }
|
||||
|
||||
Inferred dependencies: `[a]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `a`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
error.invalid-useMemo-async-callback.ts:2:18
|
||||
1 | function component(a, b) {
|
||||
> 2 | let x = useMemo(async () => {
|
||||
| ^^^^^^^^^^^^^
|
||||
> 3 | await a;
|
||||
| ^^^^^^^^^^^^
|
||||
> 4 | }, []);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
5 | return x;
|
||||
6 | }
|
||||
7 |
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ function component(a, b) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 3 errors:
|
||||
|
||||
Error: useMemo() callbacks may not accept parameters
|
||||
|
||||
@@ -26,6 +26,32 @@ error.invalid-useMemo-callback-args.ts:2:18
|
||||
3 | return x;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
|
||||
|
||||
error.invalid-useMemo-callback-args.ts:2:23
|
||||
1 | function component(a, b) {
|
||||
> 2 | let x = useMemo(c => a, []);
|
||||
| ^ Missing dependency `a`
|
||||
3 | return x;
|
||||
4 | }
|
||||
5 |
|
||||
|
||||
Inferred dependencies: `[a]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `a`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
error.invalid-useMemo-callback-args.ts:2:18
|
||||
1 | function component(a, b) {
|
||||
> 2 | let x = useMemo(c => a, []);
|
||||
| ^^^^^^ Could not preserve existing manual memoization
|
||||
3 | return x;
|
||||
4 | }
|
||||
5 |
|
||||
```
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -46,6 +46,28 @@ error.mutable-range-shared-inner-outer-function.ts:8:6
|
||||
9 | b = [];
|
||||
10 | } else {
|
||||
11 | a = {};
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `a` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.mutable-range-shared-inner-outer-function.ts:17:23
|
||||
15 | b.push(false);
|
||||
16 | };
|
||||
> 17 | return <div onClick={f} />;
|
||||
| ^ This function may (indirectly) reassign or modify `a` after render
|
||||
18 | }
|
||||
19 |
|
||||
20 | export const FIXTURE_ENTRYPOINT = {
|
||||
|
||||
error.mutable-range-shared-inner-outer-function.ts:8:6
|
||||
6 | const f = () => {
|
||||
7 | if (cond) {
|
||||
> 8 | a = {};
|
||||
| ^ This modifies `a`
|
||||
9 | b = [];
|
||||
10 | } else {
|
||||
11 | a = {};
|
||||
```
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function useHook(parentRef) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
@@ -43,6 +43,27 @@ error.todo-allow-assigning-to-inferred-ref-prop-in-callback.ts:15:8
|
||||
16 | }
|
||||
17 | }
|
||||
18 | };
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `parentRef` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.todo-allow-assigning-to-inferred-ref-prop-in-callback.ts:19:9
|
||||
17 | }
|
||||
18 | };
|
||||
> 19 | return handler;
|
||||
| ^^^^^^^ This function may (indirectly) reassign or modify `parentRef` after render
|
||||
20 | }
|
||||
21 |
|
||||
|
||||
error.todo-allow-assigning-to-inferred-ref-prop-in-callback.ts:15:8
|
||||
13 | } else {
|
||||
14 | // So this assignment fails since we don't know its a ref
|
||||
> 15 | parentRef.current = instance;
|
||||
| ^^^^^^^^^ This modifies `parentRef`
|
||||
16 | }
|
||||
17 | }
|
||||
18 | };
|
||||
```
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -31,6 +31,27 @@ error.todo-function-expression-references-later-variable-declaration.ts:3:4
|
||||
4 | };
|
||||
5 | let onClick;
|
||||
6 |
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `onClick` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.todo-function-expression-references-later-variable-declaration.ts:7:23
|
||||
5 | let onClick;
|
||||
6 |
|
||||
> 7 | return <div onClick={callback} />;
|
||||
| ^^^^^^^^ This function may (indirectly) reassign or modify `onClick` after render
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
error.todo-function-expression-references-later-variable-declaration.ts:3:4
|
||||
1 | function Component() {
|
||||
2 | let callback = () => {
|
||||
> 3 | onClick = () => {};
|
||||
| ^^^^^^^ This modifies `onClick`
|
||||
4 | };
|
||||
5 | let onClick;
|
||||
6 |
|
||||
```
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ function Component({foo}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Todo: Support destructuring of context variables
|
||||
|
||||
@@ -33,6 +33,19 @@ error.todo-reassign-const.ts:3:20
|
||||
4 | let bar = foo.bar;
|
||||
5 | return (
|
||||
6 | <Stringify
|
||||
|
||||
Error: This value cannot be modified
|
||||
|
||||
Modifying component props or hook arguments is not allowed. Consider using a local variable instead.
|
||||
|
||||
error.todo-reassign-const.ts:8:8
|
||||
6 | <Stringify
|
||||
7 | handler={() => {
|
||||
> 8 | foo = true;
|
||||
| ^^^ `foo` cannot be modified
|
||||
9 | }}
|
||||
10 | />
|
||||
11 | );
|
||||
```
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ function Component({x, y, z}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 4 errors:
|
||||
Found 6 errors:
|
||||
|
||||
Error: Found missing/extra memoization dependencies
|
||||
|
||||
@@ -157,6 +157,48 @@ error.invalid-exhaustive-deps.ts:37:13
|
||||
40 | }, []);
|
||||
|
||||
Inferred dependencies: `[ref]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `x.y.z.a.b`, but the source dependencies were [x?.y.z.a?.b.z]. Inferred different dependency than source.
|
||||
|
||||
error.invalid-exhaustive-deps.ts:14:20
|
||||
12 | // ok, not our job to type check nullability
|
||||
13 | }, [x.y.z.a]);
|
||||
> 14 | const c = useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 15 | return x?.y.z.a?.b;
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 16 | // error: too precise
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 17 | }, [x?.y.z.a?.b.z]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
18 | const d = useMemo(() => {
|
||||
19 | return x?.y?.[(console.log(y), z?.b)];
|
||||
20 | // ok
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `ref`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
error.invalid-exhaustive-deps.ts:35:21
|
||||
33 | const ref2 = useRef(null);
|
||||
34 | const ref = z ? ref1 : ref2;
|
||||
> 35 | const cb = useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 36 | return () => {
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
> 37 | return ref.current;
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
> 38 | };
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
> 39 | // error: ref is a stable type but reactive
|
||||
| ^^^^^^^^^^^^^^^^^^
|
||||
> 40 | }, []);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
41 | return <Stringify results={[a, b, c, d, e, f, cb]} />;
|
||||
42 | }
|
||||
43 |
|
||||
```
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ function useHook() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
@@ -38,6 +38,19 @@ error.invalid-missing-nonreactive-dep-unmemoized.ts:11:31
|
||||
14 |
|
||||
|
||||
Inferred dependencies: `[object]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `object`, but the source dependencies were []. Inferred dependency not present in source.
|
||||
|
||||
error.invalid-missing-nonreactive-dep-unmemoized.ts:11:24
|
||||
9 | useIdentity();
|
||||
10 | object.x = 0;
|
||||
> 11 | const array = useMemo(() => [object], []);
|
||||
| ^^^^^^^^^^^^^^ Could not preserve existing manual memoization
|
||||
12 | return array;
|
||||
13 | }
|
||||
14 |
|
||||
```
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"PreserveManualMemo","reason":"Existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":337},"end":{"line":9,"column":52,"index":358},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}},"fnLoc":null}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":255},"end":{"line":16,"column":1,"index":482},"filename":"dynamic-gating-bailout-nopanic.ts"},"detail":{"options":{"category":"PreserveManualMemo","reason":"Existing memoization could not be preserved","description":"React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `value`, but the source dependencies were []. Inferred dependency not present in source","suggestions":null,"details":[{"kind":"error","loc":{"start":{"line":9,"column":31,"index":337},"end":{"line":9,"column":52,"index":358},"filename":"dynamic-gating-bailout-nopanic.ts"},"message":"Could not preserve existing manual memoization"}]}}}
|
||||
```
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ function Component({foo}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none"
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none"
|
||||
import { useEffect, AUTODEPS } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
import { useEffect, AUTODEPS } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
|
||||
@@ -48,6 +48,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":365},"end":{"line":10,"column":5,"index":368},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":314},"end":{"line":9,"column":49,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":336},"end":{"line":9,"column":27,"index":339},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":365},"end":{"line":10,"column":5,"index":368},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":314},"end":{"line":9,"column":49,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":336},"end":{"line":9,"column":27,"index":339},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":149},"end":{"line":12,"column":1,"index":404},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
|
||||
import { useEffect, useRef, AUTODEPS } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
@@ -47,6 +47,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Refs","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"category":"Refs","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":289},"end":{"line":9,"column":16,"index":303},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":237},"end":{"line":8,"column":50,"index":285},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":259},"end":{"line":8,"column":30,"index":265},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":158},"end":{"line":11,"column":1,"index":331},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
|
||||
import { useEffect, AUTODEPS } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
@@ -47,7 +47,11 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":214},"end":{"line":9,"column":5,"index":217},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}},"fnLoc":null}
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"Cannot modify local variables after render completes","description":"This argument is a function which may reassign or mutate `arr` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead","details":[{"kind":"error","loc":{"start":{"line":6,"column":12,"index":169},"end":{"line":8,"column":3,"index":199},"filename":"mutate-after-useeffect.ts"},"message":"This function may (indirectly) reassign or modify `arr` after render"},{"kind":"error","loc":{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"This modifies `arr`"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":159},"end":{"line":8,"column":14,"index":210},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":190},"end":{"line":7,"column":16,"index":193},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":214},"end":{"line":9,"column":5,"index":217},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","reason":"Cannot modify local variables after render completes","description":"This argument is a function which may reassign or mutate `arr` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead","details":[{"kind":"error","loc":{"start":{"line":6,"column":12,"index":169},"end":{"line":8,"column":3,"index":199},"filename":"mutate-after-useeffect.ts"},"message":"This function may (indirectly) reassign or modify `arr` after render"},{"kind":"error","loc":{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"This modifies `arr`"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":159},"end":{"line":8,"column":14,"index":210},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":181},"end":{"line":7,"column":7,"index":184},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":190},"end":{"line":7,"column":16,"index":193},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":111},"end":{"line":11,"column":1,"index":242},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -28,7 +28,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none"
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none"
|
||||
import { print } from "shared-runtime";
|
||||
import useEffectWrapper from "useEffectWrapper";
|
||||
import { AUTODEPS } from "react";
|
||||
|
||||
@@ -29,7 +29,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none"
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none"
|
||||
import { print } from "shared-runtime";
|
||||
import useEffectWrapper from "useEffectWrapper";
|
||||
import { AUTODEPS } from "react";
|
||||
|
||||
@@ -17,7 +17,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 3 errors:
|
||||
Found 6 errors:
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
@@ -57,6 +57,45 @@ error.invalid-impure-functions-in-render.ts:6:15
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
9 |
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`Date.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:4:15
|
||||
2 |
|
||||
3 | function Component() {
|
||||
> 4 | const date = Date.now();
|
||||
| ^^^^^^^^ Cannot call impure function
|
||||
5 | const now = performance.now();
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`performance.now` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:5:14
|
||||
3 | function Component() {
|
||||
4 | const date = Date.now();
|
||||
> 5 | const now = performance.now();
|
||||
| ^^^^^^^^^^^^^^^ Cannot call impure function
|
||||
6 | const rand = Math.random();
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
|
||||
Error: Cannot call impure function during render
|
||||
|
||||
`Math.random` is an impure function. Calling an impure function can produce unstable results that update unpredictably when the component happens to re-render. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent).
|
||||
|
||||
error.invalid-impure-functions-in-render.ts:6:15
|
||||
4 | const date = Date.now();
|
||||
5 | const now = performance.now();
|
||||
> 6 | const rand = Math.random();
|
||||
| ^^^^^^^^^^^ Cannot call impure function
|
||||
7 | return <Foo date={date} now={now} rand={rand} />;
|
||||
8 | }
|
||||
9 |
|
||||
```
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ function Component() {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot reassign variable after render completes
|
||||
|
||||
@@ -56,6 +56,27 @@ error.invalid-reassign-local-variable-in-jsx-callback.ts:6:4
|
||||
7 | };
|
||||
8 |
|
||||
9 | const onClick = newValue => {
|
||||
|
||||
Error: Cannot modify local variables after render completes
|
||||
|
||||
This argument is a function which may reassign or mutate `local` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead.
|
||||
|
||||
error.invalid-reassign-local-variable-in-jsx-callback.ts:32:26
|
||||
30 | };
|
||||
31 |
|
||||
> 32 | return <button onClick={onClick}>Submit</button>;
|
||||
| ^^^^^^^ This function may (indirectly) reassign or modify `local` after render
|
||||
33 | }
|
||||
34 |
|
||||
|
||||
error.invalid-reassign-local-variable-in-jsx-callback.ts:6:4
|
||||
4 |
|
||||
5 | const reassignLocal = newValue => {
|
||||
> 6 | local = newValue;
|
||||
| ^^^^^ This modifies `local`
|
||||
7 | };
|
||||
8 |
|
||||
9 | const onClick = newValue => {
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ function Component({content, refetch}) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Cannot access variable before it is declared
|
||||
|
||||
@@ -52,6 +52,18 @@ Error: Cannot access variable before it is declared
|
||||
20 |
|
||||
21 | return <Foo data={data} onSubmit={onSubmit} />;
|
||||
22 | }
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
Missing dependencies can cause a value to update less often than it should, resulting in stale UI.
|
||||
|
||||
9 | // TDZ violation!
|
||||
10 | const onRefetch = useCallback(() => {
|
||||
> 11 | refetch(data);
|
||||
| ^^^^ Missing dependency `data`
|
||||
12 | }, [refetch]);
|
||||
13 |
|
||||
14 | // The context variable gets frozen here since it's passed to a hook
|
||||
```
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
import { useEffect, AUTODEPS } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
|
||||
@@ -48,6 +48,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":397},"end":{"line":10,"column":5,"index":400},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":346},"end":{"line":9,"column":49,"index":393},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":368},"end":{"line":9,"column":27,"index":371},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":10,"column":2,"index":397},"end":{"line":10,"column":5,"index":400},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":346},"end":{"line":9,"column":49,"index":393},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":368},"end":{"line":9,"column":27,"index":371},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":181},"end":{"line":12,"column":1,"index":436},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
|
||||
import { useEffect, useRef, AUTODEPS } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
@@ -47,6 +47,8 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Refs","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"options":{"category":"Refs","reason":"Cannot access refs during render","description":"React refs are values that are not needed for rendering. Refs should only be accessed outside of render, such as in event handlers or effects. Accessing a ref value (the `current` property) during render can cause your component not to update as expected (https://react.dev/reference/react/useRef)","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":321},"end":{"line":9,"column":16,"index":335},"filename":"mutate-after-useeffect-ref-access.ts"},"message":"Cannot update ref during render"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":269},"end":{"line":8,"column":50,"index":317},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":291},"end":{"line":8,"column":30,"index":297},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":190},"end":{"line":11,"column":1,"index":363},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
|
||||
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
|
||||
import { useEffect, AUTODEPS } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
@@ -47,7 +47,11 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Logs
|
||||
|
||||
```
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":246},"end":{"line":9,"column":5,"index":249},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}},"fnLoc":null}
|
||||
{"kind":"CompileError","detail":{"options":{"category":"Immutability","reason":"Cannot modify local variables after render completes","description":"This argument is a function which may reassign or mutate `arr` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead","details":[{"kind":"error","loc":{"start":{"line":6,"column":12,"index":201},"end":{"line":8,"column":3,"index":231},"filename":"mutate-after-useeffect.ts"},"message":"This function may (indirectly) reassign or modify `arr` after render"},{"kind":"error","loc":{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"This modifies `arr`"}]}},"fnLoc":null}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":191},"end":{"line":8,"column":14,"index":242},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":222},"end":{"line":7,"column":16,"index":225},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","reason":"This value cannot be modified","description":"Modifying a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the modification before calling useEffect()","details":[{"kind":"error","loc":{"start":{"line":9,"column":2,"index":246},"end":{"line":9,"column":5,"index":249},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"value cannot be modified"}]}}}
|
||||
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"detail":{"options":{"category":"Immutability","reason":"Cannot modify local variables after render completes","description":"This argument is a function which may reassign or mutate `arr` after render, which can cause inconsistent behavior on subsequent renders. Consider using state instead","details":[{"kind":"error","loc":{"start":{"line":6,"column":12,"index":201},"end":{"line":8,"column":3,"index":231},"filename":"mutate-after-useeffect.ts"},"message":"This function may (indirectly) reassign or modify `arr` after render"},{"kind":"error","loc":{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"message":"This modifies `arr`"}]}}}
|
||||
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":191},"end":{"line":8,"column":14,"index":242},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":213},"end":{"line":7,"column":7,"index":216},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":222},"end":{"line":7,"column":16,"index":225},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
|
||||
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":143},"end":{"line":11,"column":1,"index":274},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ function useFoo(input1) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 1 error:
|
||||
Found 2 errors:
|
||||
|
||||
Error: Found missing memoization dependencies
|
||||
|
||||
@@ -46,6 +46,23 @@ error.useMemo-unrelated-mutation-in-depslist.ts:18:14
|
||||
21 | }
|
||||
|
||||
Inferred dependencies: `[x, y]`
|
||||
|
||||
Compilation Skipped: Existing memoization could not be preserved
|
||||
|
||||
React Compiler has skipped optimizing this component because the existing manual memoization could not be preserved. The inferred dependencies did not match the manually specified dependencies, which could cause the value to change more or less frequently than expected. The inferred dependency was `input1`, but the source dependencies were [y]. Inferred different dependency than source.
|
||||
|
||||
error.useMemo-unrelated-mutation-in-depslist.ts:16:27
|
||||
14 | const x = {};
|
||||
15 | const y = [input1];
|
||||
> 16 | const memoized = useMemo(() => {
|
||||
| ^^^^^^^
|
||||
> 17 | return [y];
|
||||
| ^^^^^^^^^^^^^^^
|
||||
> 18 | }, [(mutate(x), y)]);
|
||||
| ^^^^ Could not preserve existing manual memoization
|
||||
19 |
|
||||
20 | return [x, memoized];
|
||||
21 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -16,29 +16,24 @@ function Component(props) {
|
||||
## Error
|
||||
|
||||
```
|
||||
Found 2 errors:
|
||||
Found 1 error:
|
||||
|
||||
Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
Invariant: Unexpected empty block with `goto` terminal
|
||||
|
||||
error.invalid-hook-for.ts:4:9
|
||||
2 | let i = 0;
|
||||
3 | for (let x = 0; useHook(x) < 10; useHook(i), x++) {
|
||||
> 4 | i += useHook(x);
|
||||
| ^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
5 | }
|
||||
6 | return i;
|
||||
7 | }
|
||||
Block bb5 is empty.
|
||||
|
||||
Error: Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
|
||||
error.invalid-hook-for.ts:3:35
|
||||
error.invalid-hook-for.ts:3:2
|
||||
1 | function Component(props) {
|
||||
2 | let i = 0;
|
||||
> 3 | for (let x = 0; useHook(x) < 10; useHook(i), x++) {
|
||||
| ^^^^^^^ Hooks must always be called in a consistent order, and may not be called conditionally. See the Rules of Hooks (https://react.dev/warnings/invalid-hook-call-warning)
|
||||
4 | i += useHook(x);
|
||||
5 | }
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
> 4 | i += useHook(x);
|
||||
| ^^^^^^^^^^^^^^^^^^^^
|
||||
> 5 | }
|
||||
| ^^^^ Unexpected empty block with `goto` terminal
|
||||
6 | return i;
|
||||
7 | }
|
||||
8 |
|
||||
```
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ function Component({prop1, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire @panicThreshold:"none"
|
||||
import { c as _c, useFire } from "react/compiler-runtime"; // @validateNoCapitalizedCalls @enableFire @panicThreshold:"none"
|
||||
import { fire } from "react";
|
||||
const CapitalizedCall = require("shared-runtime").sum;
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ function Component({props, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { useRef } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
|
||||
@@ -24,7 +24,7 @@ function Component({prop1, bar}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold:"none"
|
||||
import { c as _c, useFire } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees @enableFire @panicThreshold:"none"
|
||||
import { fire } from "react";
|
||||
import { sum } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ function Component({prop1}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { fire } from "react";
|
||||
|
||||
function Component(t0) {
|
||||
|
||||
@@ -25,7 +25,7 @@ component Component(prop1, ref) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime";
|
||||
import { c as _c, useFire } from "react/compiler-runtime";
|
||||
import { fire } from "react";
|
||||
import { print } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function useFoo({cond}) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
// @inferEffectDependencies @panicThreshold:"none"
|
||||
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @panicThreshold:"none"
|
||||
import { useRef, AUTODEPS } from "react";
|
||||
import { useSpecialEffect } from "shared-runtime";
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ function Component(props) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @panicThreshold:"none"
|
||||
import { fire, useEffect } from "react";
|
||||
import { Stringify } from "shared-runtime";
|
||||
|
||||
@@ -38,7 +38,7 @@ import { Stringify } from "shared-runtime";
|
||||
* hook usage) disabled
|
||||
*/
|
||||
function Component(props) {
|
||||
const foo = _temp;
|
||||
const foo = _temp2;
|
||||
|
||||
if (props.cond) {
|
||||
const t0 = useFire(foo);
|
||||
@@ -49,7 +49,7 @@ function Component(props) {
|
||||
|
||||
return <Stringify />;
|
||||
}
|
||||
function _temp(props_0) {
|
||||
function _temp2(props_0) {
|
||||
console.log(props_0);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,15 +29,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot compile `fire`
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
All uses of foo must be either used with a fire() call in this effect or not used with a fire() call at all. foo was used with fire() on line 10:10 in this effect.
|
||||
<unknown> foo$42:TFunction<BuiltInFunction>(): :TPrimitive.
|
||||
|
||||
error.invalid-mix-fire-and-no-fire.ts:11:6
|
||||
9 | function nested() {
|
||||
10 | fire(foo(props));
|
||||
> 11 | foo(props);
|
||||
| ^^^ Cannot compile `fire`
|
||||
| ^^^ this is uninitialized
|
||||
12 | }
|
||||
13 |
|
||||
14 | nested();
|
||||
|
||||
@@ -24,15 +24,15 @@ function Component({bar, baz}) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot compile `fire`
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
fire() can only take in a single call expression as an argument but received multiple arguments.
|
||||
<unknown> $43:TFunction<BuiltInFire>(): :TFunction<BuiltInFireFunction>(): :TPoly.
|
||||
|
||||
error.invalid-multiple-args.ts:9:4
|
||||
7 | };
|
||||
8 | useEffect(() => {
|
||||
> 9 | fire(foo(bar), baz);
|
||||
| ^^^^^^^^^^^^^^^^^^^ Cannot compile `fire`
|
||||
| ^^^^ this is uninitialized
|
||||
10 | });
|
||||
11 |
|
||||
12 | return null;
|
||||
|
||||
@@ -24,15 +24,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot compile `fire`
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed.
|
||||
<unknown> $32:TFunction<BuiltInFire>(): :TFunction<BuiltInFireFunction>(): :TPoly.
|
||||
|
||||
error.invalid-not-call.ts:9:4
|
||||
7 | };
|
||||
8 | useEffect(() => {
|
||||
> 9 | fire(props);
|
||||
| ^^^^^^^^^^^ Cannot compile `fire`
|
||||
| ^^^^ this is uninitialized
|
||||
10 | });
|
||||
11 |
|
||||
12 | return null;
|
||||
|
||||
@@ -24,15 +24,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot compile `fire`
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
fire() can only take in a single call expression as an argument but received a spread argument.
|
||||
<unknown> $32:TFunction<BuiltInFire>(): :TFunction<BuiltInFireFunction>(): :TPoly.
|
||||
|
||||
error.invalid-spread.ts:9:4
|
||||
7 | };
|
||||
8 | useEffect(() => {
|
||||
> 9 | fire(...foo);
|
||||
| ^^^^^^^^^^^^ Cannot compile `fire`
|
||||
| ^^^^ this is uninitialized
|
||||
10 | });
|
||||
11 |
|
||||
12 | return null;
|
||||
|
||||
@@ -24,15 +24,15 @@ function Component(props) {
|
||||
```
|
||||
Found 1 error:
|
||||
|
||||
Error: Cannot compile `fire`
|
||||
Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized
|
||||
|
||||
`fire()` can only receive a function call such as `fire(fn(a,b)). Method calls and other expressions are not allowed.
|
||||
<unknown> $34:TFunction<BuiltInFire>(): :TFunction<BuiltInFireFunction>(): :TPoly.
|
||||
|
||||
error.todo-method.ts:9:4
|
||||
7 | };
|
||||
8 | useEffect(() => {
|
||||
> 9 | fire(props.foo());
|
||||
| ^^^^^^^^^^^^^^^^^ Cannot compile `fire`
|
||||
| ^^^^ this is uninitialized
|
||||
10 | });
|
||||
11 |
|
||||
12 | return null;
|
||||
|
||||
@@ -23,14 +23,15 @@ function Component(props, useDynamicHook) {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { useFire } from "react/compiler-runtime";
|
||||
import { $dispatcherGuard } from "react-compiler-runtime";
|
||||
import { c as _c, useFire } from "react/compiler-runtime";
|
||||
import { useEffect, fire } from "react";
|
||||
|
||||
function Component(props, useDynamicHook) {
|
||||
"use memo";
|
||||
|
||||
useDynamicHook();
|
||||
const foo = _temp;
|
||||
const foo = _temp2;
|
||||
const t0 = useFire(foo);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -39,7 +40,7 @@ function Component(props, useDynamicHook) {
|
||||
|
||||
return <div>hello world</div>;
|
||||
}
|
||||
function _temp(props_0) {
|
||||
function _temp2(props_0) {
|
||||
console.log(props_0);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
|
||||
## Code
|
||||
|
||||
```javascript
|
||||
import { c as _c } from "react/compiler-runtime";
|
||||
import { useRef } from "react";
|
||||
|
||||
const useControllableState = (options) => {};
|
||||
|
||||
Reference in New Issue
Block a user