Compare commits

...

5 Commits

Author SHA1 Message Date
Joe Savona
f4809b3524 [compiler] FunctionExpression context locations point to first reference
This has always been awkward: `FunctionExpression.context` places have locations set to the declaration of the identifier, whereas other references have locations pointing to the reference itself. Here, we update context operands to have their location point to the first reference of that variable within the function.
2025-06-18 13:00:42 -07:00
Joe Savona
77f30d6f61 [commit] Better error message for invalid hoisting
We're already tracking which variables are hoisted context variables, so if we see a mutation of a frozen value we can emit a custom error message to help users identify the problem.
2025-06-18 13:00:42 -07:00
Joe Savona
8f54b8aea5 [compiler] Fix AnalyzeFunctions to fully reset context identifiers
AnalyzeFunctions had logic to reset the mutable ranges of context variables after visiting inner function expressions. However, there was a bug in that logic: InferReactiveScopeVariables makes all the identifiers in a scope point to the same mutable range instance. That meant that it was possible for a later function expression to indirectly cause an earlier function expressions' context variables to get a non-zero mutable range.

The fix is to not just reset start/end of context var ranges, but assign a new range instance. Thanks for the help on debugging, @mofeiz!
2025-06-18 13:00:42 -07:00
Joe Savona
f3789e4caf [compiler] Enable new inference by default 2025-06-18 13:00:42 -07:00
Joe Savona
cebe9dc9e2 [compiler] Update fixtures for new inference 2025-06-18 13:00:42 -07:00
81 changed files with 751 additions and 391 deletions

View File

@@ -72,7 +72,7 @@ export function lower(
env: Environment,
// Bindings captured from the outer function, in case lower() is called recursively (for lambdas)
bindings: Bindings | null = null,
capturedRefs: Array<t.Identifier> = [],
capturedRefs: Map<t.Identifier, SourceLocation> = new Map(),
): Result<HIRFunction, CompilerError> {
const builder = new HIRBuilder(env, {
bindings,
@@ -80,13 +80,13 @@ export function lower(
});
const context: HIRFunction['context'] = [];
for (const ref of capturedRefs ?? []) {
for (const [ref, loc] of capturedRefs ?? []) {
context.push({
kind: 'Identifier',
identifier: builder.resolveBinding(ref),
effect: Effect.Unknown,
reactive: false,
loc: ref.loc ?? GeneratedSource,
loc,
});
}
@@ -3439,10 +3439,12 @@ function lowerFunction(
* This isn't a problem in practice because use Babel's scope analysis to
* identify the correct references.
*/
const lowering = lower(expr, builder.environment, builder.bindings, [
...builder.context,
...capturedContext,
]);
const lowering = lower(
expr,
builder.environment,
builder.bindings,
new Map([...builder.context, ...capturedContext]),
);
let loweredFunc: HIRFunction;
if (lowering.isErr()) {
lowering
@@ -4160,6 +4162,11 @@ function captureScopes({from, to}: {from: Scope; to: Scope}): Set<Scope> {
return scopes;
}
/**
* Returns a mapping of "context" identifiers — references to free variables that
* will become part of the function expression's `context` array — along with the
* source location of their first reference within the function.
*/
function gatherCapturedContext(
fn: NodePath<
| t.FunctionExpression
@@ -4168,8 +4175,8 @@ function gatherCapturedContext(
| t.ObjectMethod
>,
componentScope: Scope,
): Array<t.Identifier> {
const capturedIds = new Set<t.Identifier>();
): Map<t.Identifier, SourceLocation> {
const capturedIds = new Map<t.Identifier, SourceLocation>();
/*
* Capture all the scopes from the parent of this function up to and including
@@ -4212,8 +4219,15 @@ function gatherCapturedContext(
// Add the base identifier binding as a dependency.
const binding = baseIdentifier.scope.getBinding(baseIdentifier.node.name);
if (binding !== undefined && pureScopes.has(binding.scope)) {
capturedIds.add(binding.identifier);
if (
binding !== undefined &&
pureScopes.has(binding.scope) &&
!capturedIds.has(binding.identifier)
) {
capturedIds.set(
binding.identifier,
path.node.loc ?? binding.identifier.loc ?? GeneratedSource,
);
}
}
@@ -4250,7 +4264,7 @@ function gatherCapturedContext(
},
});
return [...capturedIds.keys()];
return capturedIds;
}
function notNull<T>(value: T | null): value is T {

View File

@@ -246,7 +246,7 @@ export const EnvironmentConfigSchema = z.object({
/**
* Enable a new model for mutability and aliasing inference
*/
enableNewMutationAliasingModel: z.boolean().default(false),
enableNewMutationAliasingModel: z.boolean().default(true),
/**
* Enables inference of optional dependency chains. Without this flag

View File

@@ -106,7 +106,7 @@ export default class HIRBuilder {
#current: WipBlock;
#entry: BlockId;
#scopes: Array<Scope> = [];
#context: Array<t.Identifier>;
#context: Map<t.Identifier, SourceLocation>;
#bindings: Bindings;
#env: Environment;
#exceptionHandlerStack: Array<BlockId> = [];
@@ -121,7 +121,7 @@ export default class HIRBuilder {
return this.#env.nextIdentifierId;
}
get context(): Array<t.Identifier> {
get context(): Map<t.Identifier, SourceLocation> {
return this.#context;
}
@@ -137,13 +137,13 @@ export default class HIRBuilder {
env: Environment,
options?: {
bindings?: Bindings | null;
context?: Array<t.Identifier>;
context?: Map<t.Identifier, SourceLocation>;
entryBlockKind?: BlockKind;
},
) {
this.#env = env;
this.#bindings = options?.bindings ?? new Map();
this.#context = options?.context ?? [];
this.#context = options?.context ?? new Map();
this.#entry = makeBlockId(env.nextBlockId);
this.#current = newBlock(this.#entry, options?.entryBlockKind ?? 'block');
}

View File

@@ -42,8 +42,16 @@ export default function analyseFunctions(func: HIRFunction): void {
* Reset mutable range for outer inferReferenceEffects
*/
for (const operand of instr.value.loweredFunc.func.context) {
operand.identifier.mutableRange.start = makeInstructionId(0);
operand.identifier.mutableRange.end = makeInstructionId(0);
/**
* NOTE: inferReactiveScopeVariables makes identifiers in the scope
* point to the *same* mutableRange instance. Resetting start/end
* here is insufficient, because a later mutation of the range
* for any one identifier could affect the range for other identifiers.
*/
operand.identifier.mutableRange = {
start: makeInstructionId(0),
end: makeInstructionId(0),
};
operand.identifier.scope = null;
}
break;

View File

@@ -901,11 +901,36 @@ function applyEffect(
console.log(prettyFormat(state.debugAbstractValue(value)));
}
const reason = getWriteErrorReason({
kind: value.kind,
reason: value.reason,
context: new Set(),
});
let reason: string;
let description: string | null = null;
if (
mutationKind === 'mutate-frozen' &&
context.hoistedContextDeclarations.has(
effect.value.identifier.declarationId,
)
) {
reason = `This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time`;
if (
effect.value.identifier.name !== null &&
effect.value.identifier.name.kind === 'named'
) {
description = `Move the declaration of \`${effect.value.identifier.name.value}\` to before it is first referenced`;
}
} else {
reason = getWriteErrorReason({
kind: value.kind,
reason: value.reason,
context: new Set(),
});
if (
effect.value.identifier.name !== null &&
effect.value.identifier.name.kind === 'named'
) {
description = `Found mutation of \`${effect.value.identifier.name.value}\``;
}
}
effects.push({
kind:
value.kind === ValueKind.Frozen ? 'MutateFrozen' : 'MutateGlobal',
@@ -913,11 +938,7 @@ function applyEffect(
error: {
severity: ErrorSeverity.InvalidReact,
reason,
description:
effect.value.identifier.name !== null &&
effect.value.identifier.name.kind === 'named'
? `Found mutation of \`${effect.value.identifier.name.value}\``
: null,
description,
loc: effect.value.loc,
suggestions: null,
},

View File

@@ -175,21 +175,14 @@ import {
* and mutability.
*/
function Component(t0) {
const $ = _c(4);
const $ = _c(2);
const { prop } = t0;
let t1;
if ($[0] !== prop) {
const obj = shallowCopy(prop);
const aliasedObj = identity(obj);
let t2;
if ($[2] !== obj) {
t2 = [obj.id];
$[2] = obj;
$[3] = t2;
} else {
t2 = $[3];
}
const id = t2;
const id = [obj.id];
mutate(aliasedObj);
setPropertyByKey(aliasedObj, "id", prop.id + 1);

View File

@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0][1]) {
y = {};
y = x[0][1];
$[0] = a;
$[1] = y;
$[2] = x[0][1];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -29,20 +29,29 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a, b) {
const $ = _c(3);
let y;
const $ = _c(6);
let t0;
if ($[0] !== a || $[1] !== b) {
const x = [a, b];
t0 = [a, b];
$[0] = a;
$[1] = b;
$[2] = t0;
} else {
t0 = $[2];
}
const x = t0;
let y;
if ($[3] !== x[0][1] || $[4] !== x[1][0]) {
y = {};
let t = {};
y = x[0][1];
t = x[1][0];
$[0] = a;
$[1] = b;
$[2] = y;
$[3] = x[0][1];
$[4] = x[1][0];
$[5] = y;
} else {
y = $[2];
y = $[5];
}
return y;
}

View File

@@ -25,17 +25,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0].a[1]) {
y = {};
y = x[0].a[1];
$[0] = a;
$[1] = y;
$[2] = x[0].a[1];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -24,17 +24,25 @@ export const FIXTURE_ENTRYPOINT = {
```javascript
import { c as _c } from "react/compiler-runtime";
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0]) {
y = {};
y = x[0];
$[0] = a;
$[1] = y;
$[2] = x[0];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -41,7 +41,7 @@ export const FIXTURE_ENTRYPOINT = {
19 | useEffect(() => setState(2), []);
20 |
> 21 | const [state, setState] = useState(0);
| ^^^^^^^^ InvalidReact: Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect(). Found mutation of `setState` (21:21)
| ^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Move the declaration of `setState` to before it is first referenced (21:21)
22 | return <Stringify state={state} />;
23 | }
24 |

View File

@@ -20,7 +20,7 @@ function Component() {
2 |
3 | function Component() {
> 4 | const date = Date.now();
| ^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
| ^^^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `performance.now` is an impure function whose results may change on every call (5:5)

View File

@@ -27,7 +27,7 @@ function SomeComponent() {
9 | return (
10 | <Button
> 11 | onPress={() => (sharedVal.value = Math.random())}
| ^^^^^^^^^ InvalidReact: Mutating a value returned from a function whose return value should not be mutated. Found mutation of `sharedVal` (11:11)
| ^^^^^^^^^ InvalidReact: This mutates a variable that React considers immutable. Found mutation of `sharedVal` (11:11)
12 | title="Randomize"
13 | />
14 | );

View File

@@ -16,6 +16,8 @@ function useHook(a, b) {
1 | function useHook(a, b) {
> 2 | b.test = 1;
| ^ InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (2:2)
InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (3:3)
3 | a.test = 2;
4 | }
5 |

View File

@@ -21,6 +21,8 @@ function Component(props) {
4 | foo(() => {
> 5 | x.a = 10;
| ^ InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (5:5)
InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (6:6)
6 | x.a = 20;
7 | });
8 | }

View File

@@ -21,6 +21,8 @@ function Component() {
3 | // Cannot assign to globals
> 4 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (5:5)
5 | moduleLocal = true;
6 | };
7 | foo();

View File

@@ -18,6 +18,8 @@ function Component() {
2 | // Cannot assign to globals
> 3 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
4 | moduleLocal = true;
5 | }
6 |

View File

@@ -22,7 +22,7 @@ function Component(props) {
7 | return hasErrors;
8 | }
> 9 | return hasErrors();
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$15 (9:9)
| ^^^^^^^^^ Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized. <unknown> hasErrors_0$15:TFunction (9:9)
10 | }
11 |
```

View File

@@ -34,13 +34,13 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
13 | return bar();
11 |
12 | function foo() {
> 13 | return bar();
| ^^^ Todo: [PruneHoistedContexts] Rewrite hoisted function references (13:13)
14 | }
> 15 | function bar() {
| ^^^ Todo: [PruneHoistedContexts] Rewrite hoisted function references (15:15)
15 | function bar() {
16 | return 42;
17 | }
18 |
```

View File

@@ -48,7 +48,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":10,"column":2,"index":345},"end":{"line":10,"column":5,"index":348},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":345},"end":{"line":10,"column":5,"index":348},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":304},"end":{"line":9,"column":39,"index":341},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":326},"end":{"line":9,"column":27,"index":329},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"loc":{"start":{"line":9,"column":2,"index":269},"end":{"line":9,"column":16,"index":283},"filename":"mutate-after-useeffect-ref-access.ts"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":269},"end":{"line":9,"column":16,"index":283},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":227},"end":{"line":8,"column":40,"index":265},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":249},"end":{"line":8,"column":30,"index":255},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":9,"column":2,"index":194},"end":{"line":9,"column":5,"index":197},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":194},"end":{"line":9,"column":5,"index":197},"filename":"mutate-after-useeffect.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":149},"end":{"line":8,"column":4,"index":190},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":180},"end":{"line":7,"column":16,"index":183},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```

View File

@@ -52,7 +52,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":11,"column":2,"index":320},"end":{"line":11,"column":6,"index":324},"filename":"retry-no-emit.ts","identifierName":"arr2"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":11,"column":2,"index":320},"end":{"line":11,"column":6,"index":324},"filename":"retry-no-emit.ts","identifierName":"arr2"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":7,"column":2,"index":216},"end":{"line":7,"column":36,"index":250},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":7,"column":31,"index":245},"end":{"line":7,"column":34,"index":248},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":10,"column":2,"index":274},"end":{"line":10,"column":44,"index":316},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":35,"index":307},"end":{"line":10,"column":42,"index":314},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}

View File

@@ -34,22 +34,28 @@ import { print } from "shared-runtime";
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
*/
function ReactiveRefInEffect(props) {
const $ = _c(2);
const $ = _c(4);
const [, setState1] = useRef("initial value");
const [, setState2] = useRef("initial value");
let setState;
if (props.foo) {
setState = setState1;
if ($[0] !== props.foo) {
if (props.foo) {
setState = setState1;
} else {
setState = setState2;
}
$[0] = props.foo;
$[1] = setState;
} else {
setState = setState2;
setState = $[1];
}
let t0;
if ($[0] !== setState) {
if ($[2] !== setState) {
t0 = () => print(setState);
$[0] = setState;
$[1] = t0;
$[2] = setState;
$[3] = t0;
} else {
t0 = $[1];
t0 = $[3];
}
useEffect(t0, [setState]);
}

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {
Stringify,
mutate,
@@ -101,7 +102,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import {
Stringify,
mutate,
@@ -175,21 +176,14 @@ import {
* and mutability.
*/
function Component(t0) {
const $ = _c(4);
const $ = _c(2);
const { prop } = t0;
let t1;
if ($[0] !== prop) {
const obj = shallowCopy(prop);
const aliasedObj = identity(obj);
let t2;
if ($[2] !== obj) {
t2 = [obj.id];
$[2] = obj;
$[3] = t2;
} else {
t2 = $[3];
}
const id = t2;
const id = [obj.id];
mutate(aliasedObj);
setPropertyByKey(aliasedObj, "id", prop.id + 1);

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {
Stringify,
mutate,

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {Stringify} from 'shared-runtime';
/**
@@ -43,7 +44,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { Stringify } from "shared-runtime";
/**

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {Stringify} from 'shared-runtime';
/**

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};
@@ -23,19 +24,27 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0][1]) {
y = {};
y = x[0][1];
$[0] = a;
$[1] = y;
$[2] = x[0][1];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function bar(a, b) {
let x = [a, b];
let y = {};
@@ -27,22 +28,31 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
function bar(a, b) {
const $ = _c(3);
let y;
const $ = _c(6);
let t0;
if ($[0] !== a || $[1] !== b) {
const x = [a, b];
t0 = [a, b];
$[0] = a;
$[1] = b;
$[2] = t0;
} else {
t0 = $[2];
}
const x = t0;
let y;
if ($[3] !== x[0][1] || $[4] !== x[1][0]) {
y = {};
let t = {};
y = x[0][1];
t = x[1][0];
$[0] = a;
$[1] = b;
$[2] = y;
$[3] = x[0][1];
$[4] = x[1][0];
$[5] = y;
} else {
y = $[2];
y = $[5];
}
return y;
}

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function bar(a, b) {
let x = [a, b];
let y = {};

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};
@@ -23,19 +24,27 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0].a[1]) {
y = {};
y = x[0].a[1];
$[0] = a;
$[1] = y;
$[2] = x[0].a[1];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};
@@ -22,19 +23,27 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
function bar(a) {
const $ = _c(2);
let y;
const $ = _c(4);
let t0;
if ($[0] !== a) {
const x = [a];
t0 = [a];
$[0] = a;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let y;
if ($[2] !== x[0]) {
y = {};
y = x[0];
$[0] = a;
$[1] = y;
$[2] = x[0];
$[3] = y;
} else {
y = $[1];
y = $[3];
}
return y;
}

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function bar(a) {
let x = [a];
let y = {};

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
// @validateNoImpureFunctionsInRender @enableNewMutationAliasingModel
function Component() {
const date = Date.now();
@@ -20,7 +20,7 @@ function Component() {
2 |
3 | function Component() {
> 4 | const date = Date.now();
| ^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
| ^^^^^^^^^^ InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `Date.now` is an impure function whose results may change on every call (4:4)
InvalidReact: Calling an impure function can produce unstable results. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#components-and-hooks-must-be-idempotent). `performance.now` is an impure function whose results may change on every call (5:5)

View File

@@ -1,4 +1,4 @@
// @validateNoImpureFunctionsInRender
// @validateNoImpureFunctionsInRender @enableNewMutationAliasingModel
function Component() {
const date = Date.now();

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function Component() {
let local;
@@ -41,13 +42,13 @@ function Component() {
## Error
```
3 |
4 | const reassignLocal = newValue => {
> 5 | local = newValue;
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (5:5)
6 | };
7 |
8 | const onClick = newValue => {
4 |
5 | const reassignLocal = newValue => {
> 6 | local = newValue;
| ^^^^^ InvalidReact: Reassigning a variable after render has completed can cause inconsistent behavior on subsequent renders. Consider using state instead. Variable `local` cannot be reassigned after render (6:6)
7 | };
8 |
9 | const onClick = newValue => {
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function Component() {
let local;

View File

@@ -0,0 +1,43 @@
## Input
```javascript
//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {useIdentity} from 'shared-runtime';
function Component({content, refetch}) {
// This callback function accesses a hoisted const as a dependency,
// but it cannot reference it as a dependency since that would be a
// TDZ violation!
const onRefetch = useCallback(() => {
refetch(data);
}, [refetch]);
// The context variable gets frozen here since it's passed to a hook
const onSubmit = useIdentity(onRefetch);
// This has to error: onRefetch needs to memoize with `content` as a
// dependency, but the dependency comes later
const {data = null} = content;
return <Foo data={data} onSubmit={onSubmit} />;
}
```
## Error
```
17 | // This has to error: onRefetch needs to memoize with `content` as a
18 | // dependency, but the dependency comes later
> 19 | const {data = null} = content;
| ^^^^^^^^^^^ InvalidReact: This variable is accessed before it is declared, which prevents the earlier access from updating when this value changes over time. Move the declaration of `data` to before it is first referenced (19:19)
20 |
21 | return <Foo data={data} onSubmit={onSubmit} />;
22 | }
```

View File

@@ -0,0 +1,22 @@
//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {useIdentity} from 'shared-runtime';
function Component({content, refetch}) {
// This callback function accesses a hoisted const as a dependency,
// but it cannot reference it as a dependency since that would be a
// TDZ violation!
const onRefetch = useCallback(() => {
refetch(data);
}, [refetch]);
// The context variable gets frozen here since it's passed to a hook
const onSubmit = useIdentity(onRefetch);
// This has to error: onRefetch needs to memoize with `content` as a
// dependency, but the dependency comes later
const {data = null} = content;
return <Foo data={data} onSubmit={onSubmit} />;
}

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function useHook(a, b) {
b.test = 1;
a.test = 2;
@@ -13,12 +14,15 @@ function useHook(a, b) {
## Error
```
1 | function useHook(a, b) {
> 2 | b.test = 1;
| ^ InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (2:2)
3 | a.test = 2;
4 | }
5 |
1 | // @enableNewMutationAliasingModel
2 | function useHook(a, b) {
> 3 | b.test = 1;
| ^ InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (3:3)
InvalidReact: Mutating component props or hook arguments is not allowed. Consider using a local variable instead (4:4)
4 | a.test = 2;
5 | }
6 |
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function useHook(a, b) {
b.test = 1;
a.test = 2;

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
let x = {a: 42};
function Component(props) {
@@ -17,13 +18,15 @@ function Component(props) {
## Error
```
3 | function Component(props) {
4 | foo(() => {
> 5 | x.a = 10;
| ^ InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (5:5)
6 | x.a = 20;
7 | });
8 | }
4 | function Component(props) {
5 | foo(() => {
> 6 | x.a = 10;
| ^ InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (6:6)
InvalidReact: Writing to a variable defined outside a component or hook is not allowed. Consider using an effect (7:7)
7 | x.a = 20;
8 | });
9 | }
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
let x = {a: 42};
function Component(props) {

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function Component() {
const foo = () => {
// Cannot assign to globals
@@ -17,13 +18,15 @@ function Component() {
## Error
```
2 | const foo = () => {
3 | // Cannot assign to globals
> 4 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
5 | moduleLocal = true;
6 | };
7 | foo();
3 | const foo = () => {
4 | // Cannot assign to globals
> 5 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (5:5)
InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (6:6)
6 | moduleLocal = true;
7 | };
8 | foo();
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function Component() {
const foo = () => {
// Cannot assign to globals

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function Component() {
// Cannot assign to globals
someUnknownGlobal = true;
@@ -14,13 +15,15 @@ function Component() {
## Error
```
1 | function Component() {
2 | // Cannot assign to globals
> 3 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (3:3)
4 | moduleLocal = true;
5 | }
6 |
2 | function Component() {
3 | // Cannot assign to globals
> 4 | someUnknownGlobal = true;
| ^^^^^^^^^^^^^^^^^ InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (4:4)
InvalidReact: Unexpected reassignment of a variable which was defined outside of the component. Components and hooks should be pure and side-effect free, but variable reassignment is a form of side-effect. If this variable is used in rendering, use useState instead. (https://react.dev/reference/rules/components-and-hooks-must-be-pure#side-effects-must-run-outside-of-render) (5:5)
5 | moduleLocal = true;
6 | }
7 |
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function Component() {
// Cannot assign to globals
someUnknownGlobal = true;

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
function Component(props) {
function hasErrors() {
let hasErrors = false;
@@ -19,12 +20,12 @@ function Component(props) {
## Error
```
7 | return hasErrors;
8 | }
> 9 | return hasErrors();
| ^^^^^^^^^ Invariant: [hoisting] Expected value for identifier to be initialized. hasErrors_0$15 (9:9)
10 | }
11 |
8 | return hasErrors;
9 | }
> 10 | return hasErrors();
| ^^^^^^^^^ Invariant: [InferMutationAliasingEffects] Expected value kind to be initialized. <unknown> hasErrors_0$15:TFunction (10:10)
11 | }
12 |
```

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
function Component(props) {
function hasErrors() {
let hasErrors = false;

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
import {print} from 'shared-runtime';
@@ -25,7 +25,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { useEffect } from "react";
import { print } from "shared-runtime";
@@ -48,9 +48,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":10,"column":2,"index":345},"end":{"line":10,"column":5,"index":348},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":304},"end":{"line":9,"column":39,"index":341},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":326},"end":{"line":9,"column":27,"index":329},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":139},"end":{"line":12,"column":1,"index":384},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":171},"end":{"line":12,"column":1,"index":416},"filename":"mutate-after-useeffect-optional-chain.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":10,"column":2,"index":377},"end":{"line":10,"column":5,"index":380},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":9,"column":2,"index":336},"end":{"line":9,"column":39,"index":373},"filename":"mutate-after-useeffect-optional-chain.ts"},"decorations":[{"start":{"line":9,"column":24,"index":358},"end":{"line":9,"column":27,"index":361},"filename":"mutate-after-useeffect-optional-chain.ts","identifierName":"arr"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":171},"end":{"line":12,"column":1,"index":416},"filename":"mutate-after-useeffect-optional-chain.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
import {print} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect, useRef} from 'react';
import {print} from 'shared-runtime';
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { useEffect, useRef } from "react";
import { print } from "shared-runtime";
@@ -47,9 +47,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"loc":{"start":{"line":9,"column":2,"index":269},"end":{"line":9,"column":16,"index":283},"filename":"mutate-after-useeffect-ref-access.ts"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":227},"end":{"line":8,"column":40,"index":265},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":249},"end":{"line":8,"column":30,"index":255},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":148},"end":{"line":11,"column":1,"index":311},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":6,"column":0,"index":180},"end":{"line":11,"column":1,"index":343},"filename":"mutate-after-useeffect-ref-access.ts"},"detail":{"reason":"Mutating component props or hook arguments is not allowed. Consider using a local variable instead","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":301},"end":{"line":9,"column":16,"index":315},"filename":"mutate-after-useeffect-ref-access.ts"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":8,"column":2,"index":259},"end":{"line":8,"column":40,"index":297},"filename":"mutate-after-useeffect-ref-access.ts"},"decorations":[{"start":{"line":8,"column":24,"index":281},"end":{"line":8,"column":30,"index":287},"filename":"mutate-after-useeffect-ref-access.ts","identifierName":"arrRef"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":6,"column":0,"index":180},"end":{"line":11,"column":1,"index":343},"filename":"mutate-after-useeffect-ref-access.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect, useRef} from 'react';
import {print} from 'shared-runtime';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
function Component({foo}) {
@@ -24,7 +24,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { useEffect } from "react";
function Component(t0) {
@@ -47,9 +47,9 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":9,"column":2,"index":194},"end":{"line":9,"column":5,"index":197},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":149},"end":{"line":8,"column":4,"index":190},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":171},"end":{"line":7,"column":7,"index":174},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":180},"end":{"line":7,"column":16,"index":183},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":101},"end":{"line":11,"column":1,"index":222},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":4,"column":0,"index":133},"end":{"line":11,"column":1,"index":254},"filename":"mutate-after-useeffect.ts"},"detail":{"reason":"Updating a value used previously in an effect function or as an effect dependency is not allowed. Consider moving the mutation before calling useEffect()","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":9,"column":2,"index":226},"end":{"line":9,"column":5,"index":229},"filename":"mutate-after-useeffect.ts","identifierName":"arr"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":6,"column":2,"index":181},"end":{"line":8,"column":4,"index":222},"filename":"mutate-after-useeffect.ts"},"decorations":[{"start":{"line":7,"column":4,"index":203},"end":{"line":7,"column":7,"index":206},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":4,"index":203},"end":{"line":7,"column":7,"index":206},"filename":"mutate-after-useeffect.ts","identifierName":"arr"},{"start":{"line":7,"column":13,"index":212},"end":{"line":7,"column":16,"index":215},"filename":"mutate-after-useeffect.ts","identifierName":"foo"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":4,"column":0,"index":133},"end":{"line":11,"column":1,"index":254},"filename":"mutate-after-useeffect.ts"},"fnName":"Component","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {useEffect} from 'react';
function Component({foo}) {

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {identity, mutate} from 'shared-runtime';
function Component(props) {
@@ -23,38 +24,22 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { identity, mutate } from "shared-runtime";
function Component(props) {
const $ = _c(5);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = {};
$[0] = t0;
} else {
t0 = $[0];
}
const key = t0;
let t1;
if ($[1] !== props.value) {
t1 = identity([props.value]);
$[1] = props.value;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[3] !== t1) {
t2 = { [key]: t1 };
$[3] = t1;
$[4] = t2;
} else {
t2 = $[4];
}
const context = t2;
const $ = _c(2);
let context;
if ($[0] !== props.value) {
const key = {};
context = { [key]: identity([props.value]) };
mutate(key);
mutate(key);
$[0] = props.value;
$[1] = context;
} else {
context = $[1];
}
return context;
}

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {identity, mutate} from 'shared-runtime';
function Component(props) {

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {identity, mutate, mutateAndReturn} from 'shared-runtime';
function Component(props) {
@@ -23,15 +24,26 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { identity, mutate, mutateAndReturn } from "shared-runtime";
function Component(props) {
const $ = _c(2);
const $ = _c(4);
let context;
if ($[0] !== props.value) {
const key = { a: "key" };
context = { [key.a]: identity([props.value]) };
const t0 = key.a;
const t1 = identity([props.value]);
let t2;
if ($[2] !== t1) {
t2 = { [t0]: t1 };
$[2] = t1;
$[3] = t2;
} else {
t2 = $[3];
}
context = t2;
mutate(key);
$[0] = props.value;

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {identity, mutate, mutateAndReturn} from 'shared-runtime';
function Component(props) {

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @inferEffectDependencies
// @inferEffectDependencies @enableNewMutationAliasingModel
import {useEffect, useState} from 'react';
import {print} from 'shared-runtime';
@@ -26,7 +26,7 @@ function ReactiveRefInEffect(props) {
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies
import { c as _c } from "react/compiler-runtime"; // @inferEffectDependencies @enableNewMutationAliasingModel
import { useEffect, useState } from "react";
import { print } from "shared-runtime";
@@ -34,22 +34,28 @@ import { print } from "shared-runtime";
* setState types are not enough to determine to omit from deps. Must also take reactivity into account.
*/
function ReactiveRefInEffect(props) {
const $ = _c(2);
const $ = _c(4);
const [, setState1] = useRef("initial value");
const [, setState2] = useRef("initial value");
let setState;
if (props.foo) {
setState = setState1;
if ($[0] !== props.foo) {
if (props.foo) {
setState = setState1;
} else {
setState = setState2;
}
$[0] = props.foo;
$[1] = setState;
} else {
setState = setState2;
setState = $[1];
}
let t0;
if ($[0] !== setState) {
if ($[2] !== setState) {
t0 = () => print(setState);
$[0] = setState;
$[1] = t0;
$[2] = setState;
$[3] = t0;
} else {
t0 = $[1];
t0 = $[3];
}
useEffect(t0, [setState]);
}

View File

@@ -1,4 +1,4 @@
// @inferEffectDependencies
// @inferEffectDependencies @enableNewMutationAliasingModel
import {useEffect, useState} from 'react';
import {print} from 'shared-runtime';

View File

@@ -0,0 +1,80 @@
## Input
```javascript
//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
component Component(
onAsyncSubmit?: (() => void) => void,
onClose: (isConfirmed: boolean) => void
) {
// When running inferReactiveScopeVariables,
// onAsyncSubmit and onClose update to share
// a mutableRange instance.
const onSubmit = useCallback(() => {
if (onAsyncSubmit) {
onAsyncSubmit(() => {
onClose(true);
});
return;
}
}, [onAsyncSubmit, onClose]);
// When running inferReactiveScopeVariables here,
// first the existing range gets updated (affecting
// onAsyncSubmit) and then onClose gets assigned a
// different mutable range instance, which is the
// one reset after AnalyzeFunctions.
// The fix is to fully reset mutable ranges *instances*
// after AnalyzeFunctions visit a function expression
return <Dialog onSubmit={onSubmit} onClose={() => onClose(false)} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
function Component(t0) {
const $ = _c(8);
const { onAsyncSubmit, onClose } = t0;
let t1;
if ($[0] !== onAsyncSubmit || $[1] !== onClose) {
t1 = () => {
if (onAsyncSubmit) {
onAsyncSubmit(() => {
onClose(true);
});
return;
}
};
$[0] = onAsyncSubmit;
$[1] = onClose;
$[2] = t1;
} else {
t1 = $[2];
}
const onSubmit = t1;
let t2;
if ($[3] !== onClose) {
t2 = () => onClose(false);
$[3] = onClose;
$[4] = t2;
} else {
t2 = $[4];
}
let t3;
if ($[5] !== onSubmit || $[6] !== t2) {
t3 = <Dialog onSubmit={onSubmit} onClose={t2} />;
$[5] = onSubmit;
$[6] = t2;
$[7] = t3;
} else {
t3 = $[7];
}
return t3;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,25 @@
//@flow @validatePreserveExistingMemoizationGuarantees @enableNewMutationAliasingModel
component Component(
onAsyncSubmit?: (() => void) => void,
onClose: (isConfirmed: boolean) => void
) {
// When running inferReactiveScopeVariables,
// onAsyncSubmit and onClose update to share
// a mutableRange instance.
const onSubmit = useCallback(() => {
if (onAsyncSubmit) {
onAsyncSubmit(() => {
onClose(true);
});
return;
}
}, [onAsyncSubmit, onClose]);
// When running inferReactiveScopeVariables here,
// first the existing range gets updated (affecting
// onAsyncSubmit) and then onClose gets assigned a
// different mutable range instance, which is the
// one reset after AnalyzeFunctions.
// The fix is to fully reset mutable ranges *instances*
// after AnalyzeFunctions visit a function expression
return <Dialog onSubmit={onSubmit} onClose={() => onClose(false)} />;
}

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';
@@ -27,7 +27,7 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import { print } from "shared-runtime";
import useEffectWrapper from "useEffectWrapper";
@@ -52,10 +52,10 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"loc":{"start":{"line":11,"column":2,"index":320},"end":{"line":11,"column":6,"index":324},"filename":"retry-no-emit.ts","identifierName":"arr2"},"suggestions":null,"severity":"InvalidReact"}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":7,"column":2,"index":216},"end":{"line":7,"column":36,"index":250},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":7,"column":31,"index":245},"end":{"line":7,"column":34,"index":248},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":10,"column":2,"index":274},"end":{"line":10,"column":44,"index":316},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":25,"index":297},"end":{"line":10,"column":29,"index":301},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":35,"index":307},"end":{"line":10,"column":42,"index":314},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":163},"end":{"line":13,"column":1,"index":357},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
{"kind":"CompileError","fnLoc":{"start":{"line":5,"column":0,"index":195},"end":{"line":13,"column":1,"index":389},"filename":"retry-no-emit.ts"},"detail":{"reason":"This mutates a variable that React considers immutable","description":null,"severity":"InvalidReact","suggestions":null,"loc":{"start":{"line":11,"column":2,"index":352},"end":{"line":11,"column":6,"index":356},"filename":"retry-no-emit.ts","identifierName":"arr2"}}}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":7,"column":2,"index":248},"end":{"line":7,"column":36,"index":282},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":7,"column":31,"index":277},"end":{"line":7,"column":34,"index":280},"filename":"retry-no-emit.ts","identifierName":"arr"}]}
{"kind":"AutoDepsDecorations","fnLoc":{"start":{"line":10,"column":2,"index":306},"end":{"line":10,"column":44,"index":348},"filename":"retry-no-emit.ts"},"decorations":[{"start":{"line":10,"column":25,"index":329},"end":{"line":10,"column":29,"index":333},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":25,"index":329},"end":{"line":10,"column":29,"index":333},"filename":"retry-no-emit.ts","identifierName":"arr2"},{"start":{"line":10,"column":35,"index":339},"end":{"line":10,"column":42,"index":346},"filename":"retry-no-emit.ts","identifierName":"propVal"}]}
{"kind":"CompileSuccess","fnLoc":{"start":{"line":5,"column":0,"index":195},"end":{"line":13,"column":1,"index":389},"filename":"retry-no-emit.ts"},"fnName":"Foo","memoSlots":0,"memoBlocks":0,"memoValues":0,"prunedMemoBlocks":0,"prunedMemoValues":0}
```
### Eval output

View File

@@ -1,4 +1,4 @@
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly
// @inferEffectDependencies @noEmit @panicThreshold:"none" @loggerTestOnly @enableNewMutationAliasingModel
import {print} from 'shared-runtime';
import useEffectWrapper from 'useEffectWrapper';

View File

@@ -2,7 +2,7 @@
## Input
```javascript
// @enableFire
// @enableFire @enableNewMutationAliasingModel
import {fire} from 'react';
function Component({bar, baz}) {
@@ -26,7 +26,7 @@ function Component({bar, baz}) {
## Code
```javascript
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire
import { c as _c, useFire } from "react/compiler-runtime"; // @enableFire @enableNewMutationAliasingModel
import { fire } from "react";
function Component(t0) {

View File

@@ -1,4 +1,4 @@
// @enableFire
// @enableFire @enableNewMutationAliasingModel
import {fire} from 'react';
function Component({bar, baz}) {

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
@@ -35,44 +36,51 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { useCallback } from "react";
import { Stringify } from "shared-runtime";
function Foo(t0) {
const $ = _c(8);
const $ = _c(10);
const { arr1, arr2, foo } = t0;
let getVal1;
let t1;
if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) {
const x = [arr1];
if ($[0] !== arr1) {
t1 = [arr1];
$[0] = arr1;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
let getVal1;
let t2;
if ($[2] !== arr2 || $[3] !== foo || $[4] !== x) {
let y = [];
getVal1 = _temp;
t1 = () => [y];
t2 = () => [y];
foo ? (y = x.concat(arr2)) : y;
$[0] = arr1;
$[1] = arr2;
$[2] = foo;
$[3] = getVal1;
$[4] = t1;
} else {
getVal1 = $[3];
t1 = $[4];
}
const getVal2 = t1;
let t2;
if ($[5] !== getVal1 || $[6] !== getVal2) {
t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
$[2] = arr2;
$[3] = foo;
$[4] = x;
$[5] = getVal1;
$[6] = getVal2;
$[7] = t2;
$[6] = t2;
} else {
t2 = $[7];
getVal1 = $[5];
t2 = $[6];
}
return t2;
const getVal2 = t2;
let t3;
if ($[7] !== getVal1 || $[8] !== getVal2) {
t3 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
$[7] = getVal1;
$[8] = getVal2;
$[9] = t3;
} else {
t3 = $[9];
}
return t3;
}
function _temp() {
return { x: 2 };

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';
@@ -30,37 +31,44 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { useCallback } from "react";
import { Stringify } from "shared-runtime";
// We currently produce invalid output (incorrect scoping for `y` declaration)
function useFoo(arr1, arr2) {
const $ = _c(5);
const $ = _c(7);
let t0;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
if ($[0] !== arr1) {
t0 = [arr1];
$[0] = arr1;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let t1;
if ($[2] !== arr2 || $[3] !== x) {
let y;
t0 = () => ({ y });
t1 = () => ({ y });
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = t0;
} else {
t0 = $[2];
}
const getVal = t0;
let t1;
if ($[3] !== getVal) {
t1 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
$[3] = getVal;
$[2] = arr2;
$[3] = x;
$[4] = t1;
} else {
t1 = $[4];
}
return t1;
const getVal = t1;
let t2;
if ($[5] !== getVal) {
t2 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
$[5] = getVal;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {useCallback} from 'react';
import {Stringify} from 'shared-runtime';

View File

@@ -2,6 +2,7 @@
## Input
```javascript
// @enableNewMutationAliasingModel
import {useMemo} from 'react';
function useFoo(arr1, arr2) {
@@ -26,33 +27,40 @@ export const FIXTURE_ENTRYPOINT = {
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { c as _c } from "react/compiler-runtime"; // @enableNewMutationAliasingModel
import { useMemo } from "react";
function useFoo(arr1, arr2) {
const $ = _c(5);
let y;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = y;
} else {
y = $[2];
}
const $ = _c(7);
let t0;
let t1;
if ($[3] !== y) {
t1 = { y };
$[3] = y;
$[4] = t1;
if ($[0] !== arr1) {
t0 = [arr1];
$[0] = arr1;
$[1] = t0;
} else {
t1 = $[4];
t0 = $[1];
}
t0 = t1;
return t0;
const x = t0;
let y;
if ($[2] !== arr2 || $[3] !== x) {
(y = x.concat(arr2)), y;
$[2] = arr2;
$[3] = x;
$[4] = y;
} else {
y = $[4];
}
let t1;
let t2;
if ($[5] !== y) {
t2 = { y };
$[5] = y;
$[6] = t2;
} else {
t2 = $[6];
}
t1 = t2;
return t1;
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -1,3 +1,4 @@
// @enableNewMutationAliasingModel
import {useMemo} from 'react';
function useFoo(arr1, arr2) {

View File

@@ -27,34 +27,18 @@ import { c as _c } from "react/compiler-runtime";
import { identity, mutate } from "shared-runtime";
function Component(props) {
const $ = _c(5);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = {};
$[0] = t0;
} else {
t0 = $[0];
}
const key = t0;
let t1;
if ($[1] !== props.value) {
t1 = identity([props.value]);
$[1] = props.value;
$[2] = t1;
} else {
t1 = $[2];
}
let t2;
if ($[3] !== t1) {
t2 = { [key]: t1 };
$[3] = t1;
$[4] = t2;
} else {
t2 = $[4];
}
const context = t2;
const $ = _c(2);
let context;
if ($[0] !== props.value) {
const key = {};
context = { [key]: identity([props.value]) };
mutate(key);
mutate(key);
$[0] = props.value;
$[1] = context;
} else {
context = $[1];
}
return context;
}

View File

@@ -27,11 +27,22 @@ import { c as _c } from "react/compiler-runtime";
import { identity, mutate, mutateAndReturn } from "shared-runtime";
function Component(props) {
const $ = _c(2);
const $ = _c(4);
let context;
if ($[0] !== props.value) {
const key = { a: "key" };
context = { [key.a]: identity([props.value]) };
const t0 = key.a;
const t1 = identity([props.value]);
let t2;
if ($[2] !== t1) {
t2 = { [t0]: t1 };
$[2] = t1;
$[3] = t2;
} else {
t2 = $[3];
}
context = t2;
mutate(key);
$[0] = props.value;

View File

@@ -40,39 +40,46 @@ import { useCallback } from "react";
import { Stringify } from "shared-runtime";
function Foo(t0) {
const $ = _c(8);
const $ = _c(10);
const { arr1, arr2, foo } = t0;
let getVal1;
let t1;
if ($[0] !== arr1 || $[1] !== arr2 || $[2] !== foo) {
const x = [arr1];
if ($[0] !== arr1) {
t1 = [arr1];
$[0] = arr1;
$[1] = t1;
} else {
t1 = $[1];
}
const x = t1;
let getVal1;
let t2;
if ($[2] !== arr2 || $[3] !== foo || $[4] !== x) {
let y = [];
getVal1 = _temp;
t1 = () => [y];
t2 = () => [y];
foo ? (y = x.concat(arr2)) : y;
$[0] = arr1;
$[1] = arr2;
$[2] = foo;
$[3] = getVal1;
$[4] = t1;
} else {
getVal1 = $[3];
t1 = $[4];
}
const getVal2 = t1;
let t2;
if ($[5] !== getVal1 || $[6] !== getVal2) {
t2 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
$[2] = arr2;
$[3] = foo;
$[4] = x;
$[5] = getVal1;
$[6] = getVal2;
$[7] = t2;
$[6] = t2;
} else {
t2 = $[7];
getVal1 = $[5];
t2 = $[6];
}
return t2;
const getVal2 = t2;
let t3;
if ($[7] !== getVal1 || $[8] !== getVal2) {
t3 = <Stringify val1={getVal1} val2={getVal2} shouldInvokeFns={true} />;
$[7] = getVal1;
$[8] = getVal2;
$[9] = t3;
} else {
t3 = $[9];
}
return t3;
}
function _temp() {
return { x: 2 };

View File

@@ -36,31 +36,38 @@ import { Stringify } from "shared-runtime";
// We currently produce invalid output (incorrect scoping for `y` declaration)
function useFoo(arr1, arr2) {
const $ = _c(5);
const $ = _c(7);
let t0;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
if ($[0] !== arr1) {
t0 = [arr1];
$[0] = arr1;
$[1] = t0;
} else {
t0 = $[1];
}
const x = t0;
let t1;
if ($[2] !== arr2 || $[3] !== x) {
let y;
t0 = () => ({ y });
t1 = () => ({ y });
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = t0;
} else {
t0 = $[2];
}
const getVal = t0;
let t1;
if ($[3] !== getVal) {
t1 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
$[3] = getVal;
$[2] = arr2;
$[3] = x;
$[4] = t1;
} else {
t1 = $[4];
}
return t1;
const getVal = t1;
let t2;
if ($[5] !== getVal) {
t2 = <Stringify getVal={getVal} shouldInvokeFns={true} />;
$[5] = getVal;
$[6] = t2;
} else {
t2 = $[6];
}
return t2;
}
export const FIXTURE_ENTRYPOINT = {

View File

@@ -30,29 +30,36 @@ import { c as _c } from "react/compiler-runtime";
import { useMemo } from "react";
function useFoo(arr1, arr2) {
const $ = _c(5);
let y;
if ($[0] !== arr1 || $[1] !== arr2) {
const x = [arr1];
(y = x.concat(arr2)), y;
$[0] = arr1;
$[1] = arr2;
$[2] = y;
} else {
y = $[2];
}
const $ = _c(7);
let t0;
let t1;
if ($[3] !== y) {
t1 = { y };
$[3] = y;
$[4] = t1;
if ($[0] !== arr1) {
t0 = [arr1];
$[0] = arr1;
$[1] = t0;
} else {
t1 = $[4];
t0 = $[1];
}
t0 = t1;
return t0;
const x = t0;
let y;
if ($[2] !== arr2 || $[3] !== x) {
(y = x.concat(arr2)), y;
$[2] = arr2;
$[3] = x;
$[4] = y;
} else {
y = $[4];
}
let t1;
let t2;
if ($[5] !== y) {
t2 = { y };
$[5] = y;
$[6] = t2;
} else {
t2 = $[6];
}
t1 = t2;
return t1;
}
export const FIXTURE_ENTRYPOINT = {