Compare commits

..

2 Commits

Author SHA1 Message Date
Joe Savona
cca2c4beda [compiler] Simplify validation against writing refs during render
Uses a simple algorithm for detecting writes of refs during render, since the ref _read_ during render is now handled as part of ValidateNoImpureValuesInRender.
2026-01-14 17:21:24 -08:00
Joe Savona
13f6508c21 [compiler] Update snap to support --nodebug
Passing `--nodebug` will disable debug mode, even if only a single fixture is filtered. This can help when running tests one by one and checking if their output is correct - debug mode causes tests to fail because there is extra/different error output.
2026-01-14 13:40:15 -08:00
69 changed files with 1133 additions and 832 deletions

View File

@@ -8,7 +8,8 @@
"Bash(echo:*)",
"Bash(done)",
"Bash(cat:*)",
"Bash(sl revert:*)"
"Bash(sl revert:*)",
"Bash(yarn workspace snap run build:*)"
],
"deny": [],
"ask": []

View File

@@ -11,7 +11,7 @@ import {
HIRFunction,
IdentifierId,
Instruction,
Place,
SourceLocation,
isRefValueType,
isUseRefType,
} from '../HIR';
@@ -64,7 +64,7 @@ type GuardInfo = {
// Information about a mutation found in a function
type MutationInfo = {
place: Place;
loc: SourceLocation;
isCurrentProperty: boolean;
};
@@ -77,7 +77,10 @@ export function validateNoRefAccessInRender(
// Track which identifiers are functions that mutate refs
const refMutatingFunctions = new Map<IdentifierId, MutationInfo>();
validateFunction(fn, refs, refMutatingFunctions, true, errors);
// Track first initialization location for each ref (by refId)
const refInitLocations = new Map<number, SourceLocation>();
validateFunction(fn, refs, refMutatingFunctions, refInitLocations, true, errors);
if (errors.hasAnyErrors()) {
return Err(errors);
@@ -89,6 +92,7 @@ function validateFunction(
fn: HIRFunction,
refs: Map<IdentifierId, RefInfo>,
refMutatingFunctions: Map<IdentifierId, MutationInfo>,
refInitLocations: Map<number, SourceLocation>,
isTopLevel: boolean,
errors: CompilerError,
): MutationInfo | null {
@@ -159,6 +163,7 @@ function validateFunction(
nullables,
guards,
refMutatingFunctions,
refInitLocations,
safeRefIds,
isTopLevel,
errors,
@@ -217,6 +222,7 @@ function processInstruction(
nullables: Set<IdentifierId>,
guards: Map<IdentifierId, GuardInfo>,
refMutatingFunctions: Map<IdentifierId, MutationInfo>,
refInitLocations: Map<number, SourceLocation>,
safeRefIds: Set<number>,
isTopLevel: boolean,
errors: CompilerError,
@@ -339,17 +345,27 @@ function processInstruction(
const isNullGuardInit =
isCurrentProperty && refId != null && safeRefIds.has(refId);
if (!isNullGuardInit) {
if (isNullGuardInit && refId != null) {
// Check if this ref was already initialized
const firstInitLoc = refInitLocations.get(refId);
if (firstInitLoc != null) {
// Error: duplicate initialization
errors.pushDiagnostic(
makeDuplicateRefInitError(instr.loc, firstInitLoc),
);
return {loc: instr.loc, isCurrentProperty};
}
// Track this as the first initialization
refInitLocations.set(refId, instr.loc);
} else if (!isNullGuardInit) {
const mutation: MutationInfo = {
place: value.object,
loc: instr.loc,
isCurrentProperty,
};
if (isTopLevel) {
// Direct mutation at top level - error immediately
errors.pushDiagnostic(
makeRefMutationError(mutation.place),
);
errors.pushDiagnostic(makeRefMutationError(mutation.loc));
}
return mutation;
}
@@ -365,9 +381,7 @@ function processInstruction(
const mutationInfo = refMutatingFunctions.get(callee.identifier.id);
if (mutationInfo != null && isTopLevel) {
// Calling a ref-mutating function at top level - error
errors.pushDiagnostic(
makeRefMutationError(mutationInfo.place),
);
errors.pushDiagnostic(makeRefMutationError(mutationInfo.loc));
}
break;
}
@@ -380,6 +394,7 @@ function processInstruction(
value.loweredFunc.func,
refs,
refMutatingFunctions,
refInitLocations,
false,
errors,
);
@@ -397,18 +412,39 @@ function processInstruction(
return null;
}
function makeRefMutationError(place: Place): CompilerDiagnostic {
function makeRefMutationError(loc: SourceLocation): CompilerDiagnostic {
return CompilerDiagnostic.create({
category: ErrorCategory.Refs,
reason: 'Cannot access refs during render',
description: REF_ERROR_DESCRIPTION,
}).withDetails({
kind: 'error',
loc: place.loc,
loc,
message: 'Cannot update ref during render',
});
}
function makeDuplicateRefInitError(
loc: SourceLocation,
firstInitLoc: SourceLocation,
): CompilerDiagnostic {
return CompilerDiagnostic.create({
category: ErrorCategory.Refs,
reason: 'Cannot access refs during render',
description: REF_ERROR_DESCRIPTION,
})
.withDetails({
kind: 'error',
loc,
message: 'Ref is initialized more than once during render',
})
.withDetails({
kind: 'error',
loc: firstInitLoc,
message: 'Ref was first initialized here',
});
}
export const REF_ERROR_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. ' +

View File

@@ -0,0 +1,51 @@
## Input
```javascript
import {useRef} from 'react';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component() {
const ref = useRef(null);
const object = {};
object.foo = () => ref.current;
const refValue = object.foo();
return <div>{refValue}</div>;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component() {
const $ = _c(2);
const ref = useRef(null);
const object = {};
object.foo = () => ref.current;
const refValue = object.foo();
let t0;
if ($[0] !== refValue) {
t0 = <div>{refValue}</div>;
$[0] = refValue;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -1,5 +1,9 @@
import {useRef} from 'react';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component() {
const ref = useRef(null);
const object = {};

View File

@@ -0,0 +1,74 @@
## Input
```javascript
import {useRef} from 'react';
import {addOne} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useKeyCommand() {
const currentPosition = useRef(0);
const handleKey = direction => () => {
const position = currentPosition.current;
const nextPosition = direction === 'left' ? addOne(position) : position;
currentPosition.current = nextPosition;
};
const moveLeft = {
handler: handleKey('left')(),
};
const moveRight = {
handler: handleKey('right')(),
};
return [moveLeft, moveRight];
}
export const FIXTURE_ENTRYPOINT = {
fn: useKeyCommand,
params: [],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";
import { addOne } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useKeyCommand() {
const $ = _c(1);
const currentPosition = useRef(0);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const handleKey = (direction) => () => {
const position = currentPosition.current;
const nextPosition = direction === "left" ? addOne(position) : position;
currentPosition.current = nextPosition;
};
const moveLeft = { handler: handleKey("left")() };
const moveRight = { handler: handleKey("right")() };
t0 = [moveLeft, moveRight];
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: useKeyCommand,
params: [],
};
```
### Eval output
(kind: ok) [{},{}]

View File

@@ -1,6 +1,10 @@
import {useRef} from 'react';
import {addOne} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useKeyCommand() {
const currentPosition = useRef(0);
const handleKey = direction => () => {

View File

@@ -0,0 +1,60 @@
## Input
```javascript
import {useRef} from 'react';
import {Stringify} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component(props) {
const ref = useRef(props.value);
const object = {};
object.foo = () => ref.current;
return <Stringify object={object} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{value: 42}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { useRef } from "react";
import { Stringify } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component(props) {
const $ = _c(1);
const ref = useRef(props.value);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const object = {};
object.foo = () => ref.current;
t0 = <Stringify object={object} shouldInvokeFns={true} />;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{ value: 42 }],
};
```
### Eval output
(kind: ok) <div>{"object":{"foo":{"kind":"Function","result":42}},"shouldInvokeFns":true}</div>

View File

@@ -1,6 +1,10 @@
import {useRef} from 'react';
import {Stringify} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function Component(props) {
const ref = useRef(props.value);
const object = {};

View File

@@ -1,53 +0,0 @@
## Input
```javascript
import {useRef} from 'react';
import {addOne} from 'shared-runtime';
function useKeyCommand() {
const currentPosition = useRef(0);
const handleKey = direction => () => {
const position = currentPosition.current;
const nextPosition = direction === 'left' ? addOne(position) : position;
currentPosition.current = nextPosition;
};
const moveLeft = {
handler: handleKey('left')(),
};
const moveRight = {
handler: handleKey('right')(),
};
return [moveLeft, moveRight];
}
export const FIXTURE_ENTRYPOINT = {
fn: useKeyCommand,
params: [],
};
```
## Error
```
Found 1 error:
Error: Mutating refs during render is not allowed
React refs are mutable containers that should only be mutated outside of render, such as in event handlers or effects. Mutating a ref during render can cause bugs because the mutation may not be associated with a particular render. See https://react.dev/reference/react/useRef.
error.capture-ref-for-mutation.ts:9:4
7 | const position = currentPosition.current;
8 | const nextPosition = direction === 'left' ? addOne(position) : position;
> 9 | currentPosition.current = nextPosition;
| ^^^^^^^^^^^^^^^ Cannot mutate ref during render
10 | };
11 | const moveLeft = {
12 | handler: handleKey('left')(),
Refs may be mutated during render if initialized with `if (ref.current == null)`
```

View File

@@ -1,45 +0,0 @@
## Input
```javascript
import {useRef} from 'react';
function Component() {
const ref = useRef(null);
const object = {};
object.foo = () => ref.current;
const refValue = object.foo();
return <div>{refValue}</div>;
}
```
## Error
```
Found 1 error:
Error: Cannot access ref value during render
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).
error.invalid-access-ref-in-render-mutate-object-with-ref-function.ts:8:15
6 | object.foo = () => ref.current;
7 | const refValue = object.foo();
> 8 | return <div>{refValue}</div>;
| ^^^^^^^^ Ref value is used during render
9 | }
10 |
error.invalid-access-ref-in-render-mutate-object-with-ref-function.ts:6:21
4 | const ref = useRef(null);
5 | const object = {};
> 6 | object.foo = () => ref.current;
| ^^^^^^^^^^^ Ref is initially accessed
7 | const refValue = object.foo();
8 | return <div>{refValue}</div>;
9 | }
```

View File

@@ -27,7 +27,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
4 | component Example() {
5 | const fooRef = makeObject_Primitives();
> 6 | fooRef.current = true;
| ^^^^^^ Cannot update ref during render
| ^^^^^^^^^^^^^^ Cannot update ref during render
7 |
8 | return <Stringify foo={fooRef} />;
9 | }

View File

@@ -1,51 +0,0 @@
## Input
```javascript
import {useRef} from 'react';
import {Stringify} from 'shared-runtime';
function Component(props) {
const ref = useRef(props.value);
const object = {};
object.foo = () => ref.current;
return <Stringify object={object} shouldInvokeFns={true} />;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{value: 42}],
};
```
## Error
```
Found 1 error:
Error: Cannot access ref value during render
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).
error.invalid-capturing-ref-returning-function-in-rendered-object.ts:8:28
6 | const object = {};
7 | object.foo = () => ref.current;
> 8 | return <Stringify object={object} shouldInvokeFns={true} />;
| ^^^^^^ Ref value is used during render
9 | }
10 |
11 | export const FIXTURE_ENTRYPOINT = {
error.invalid-capturing-ref-returning-function-in-rendered-object.ts:7:21
5 | const ref = useRef(props.value);
6 | const object = {};
> 7 | object.foo = () => ref.current;
| ^^^^^^^^^^^ Ref is initially accessed
8 | return <Stringify object={object} shouldInvokeFns={true} />;
9 | }
10 |
```

View File

@@ -26,7 +26,7 @@ error.invalid-disallow-mutating-ref-in-render.ts:4:2
2 | function Component() {
3 | const ref = useRef(null);
> 4 | ref.current = false;
| ^^^ Cannot update ref during render
| ^^^^^^^^^^^ Cannot update ref during render
5 |
6 | return <button ref={ref} />;
7 | }

View File

@@ -31,7 +31,7 @@ error.invalid-disallow-mutating-refs-in-render-transitive.ts:6:4
4 |
5 | const setRef = () => {
> 6 | ref.current = false;
| ^^^ Cannot update ref during render
| ^^^^^^^^^^^ Cannot update ref during render
7 | };
8 | const changeRef = setRef;
9 | changeRef();

View File

@@ -1,47 +0,0 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {arrayPush, identity, makeArray} from 'shared-runtime';
function Component() {
const getDate = () => Date.now();
const now = getDate();
const array = [];
arrayPush(array, now);
return <Foo hasDate={array} />;
}
```
## Error
```
Found 1 error:
Error: Cannot access impure value during render
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-indirect-via-mutation.ts:10:23
8 | const array = [];
9 | arrayPush(array, now);
> 10 | return <Foo hasDate={array} />;
| ^^^^^ Cannot access impure value during render
11 | }
12 |
error.invalid-impure-functions-in-render-indirect-via-mutation.ts:6:24
4 |
5 | function Component() {
> 6 | const getDate = () => Date.now();
| ^^^^^^^^^^ `Date.now` is an impure function.
7 | const now = getDate();
8 | const array = [];
9 | arrayPush(array, now);
```

View File

@@ -1,46 +0,0 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {identity, makeArray} from 'shared-runtime';
function Component() {
const getDate = () => Date.now();
const array = makeArray(getDate());
const hasDate = identity(array);
return <Foo hasDate={hasDate} />;
}
```
## Error
```
Found 1 error:
Error: Cannot access impure value during render
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-indirect.ts:9:23
7 | const array = makeArray(getDate());
8 | const hasDate = identity(array);
> 9 | return <Foo hasDate={hasDate} />;
| ^^^^^^^ Cannot access impure value during render
10 | }
11 |
error.invalid-impure-functions-in-render-indirect.ts:6:24
4 |
5 | function Component() {
> 6 | const getDate = () => Date.now();
| ^^^^^^^^^^ `Date.now` is an impure function.
7 | const array = makeArray(getDate());
8 | const hasDate = identity(array);
9 | return <Foo hasDate={hasDate} />;
```

View File

@@ -1,52 +0,0 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {identity, makeArray} from 'shared-runtime';
function Component() {
const now = () => Date.now();
const f = () => {
// this should error but we currently lose track of the impurity bc
// the impure value comes from behind a call
const array = makeArray(now());
const hasDate = identity(array);
return hasDate;
};
const hasDate = f();
return <Foo hasDate={hasDate} />;
}
```
## Error
```
Found 1 error:
Error: Cannot access impure value during render
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-via-function-call-2.ts:15:23
13 | };
14 | const hasDate = f();
> 15 | return <Foo hasDate={hasDate} />;
| ^^^^^^^ Cannot access impure value during render
16 | }
17 |
error.invalid-impure-functions-in-render-via-function-call-2.ts:6:20
4 |
5 | function Component() {
> 6 | const now = () => Date.now();
| ^^^^^^^^^^ `Date.now` is an impure function.
7 | const f = () => {
8 | // this should error but we currently lose track of the impurity bc
9 | // the impure value comes from behind a call
```

View File

@@ -1,34 +0,0 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
return x.current;
}
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
error.invalid-pass-ref-to-function.ts:4:16
2 | function Component(props) {
3 | const ref = useRef(null);
> 4 | const x = foo(ref);
| ^^^ Passing a ref to a function may read its value during render
5 | return x.current;
6 | }
7 |
```

View File

@@ -1,6 +0,0 @@
// @validateRefAccessDuringRender
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
return x.current;
}

View File

@@ -32,7 +32,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
5 | const r = useRef(null);
6 | if (!r.current) {
> 7 | r.current = 1;
| ^ Cannot update ref during render
| ^^^^^^^^^ Cannot update ref during render
8 | }
9 | }
10 |

View File

@@ -1,53 +0,0 @@
## Input
```javascript
// @validateRefAccessDuringRender
function Foo({a}) {
const ref = useRef();
// type information is lost here as we don't track types of fields
const val = {ref};
// without type info, we don't know that val.ref.current is a ref value so we
// *would* end up depending on val.ref.current
// however, this is an instance of accessing a ref during render and is disallowed
// under React's rules, so we reject this input
const x = {a, val: val.ref.current};
return <VideoList videos={x} />;
}
```
## Error
```
Found 2 errors:
Error: Cannot access refs during render
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).
error.invalid-use-ref-added-to-dep-without-type-info.ts:10:21
8 | // however, this is an instance of accessing a ref during render and is disallowed
9 | // under React's rules, so we reject this input
> 10 | const x = {a, val: val.ref.current};
| ^^^^^^^^^^^^^^^ Cannot access ref value during render
11 |
12 | return <VideoList videos={x} />;
13 | }
Error: Cannot access refs during render
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).
error.invalid-use-ref-added-to-dep-without-type-info.ts:12:28
10 | const x = {a, val: val.ref.current};
11 |
> 12 | return <VideoList videos={x} />;
| ^ Cannot access ref value during render
13 | }
14 |
```

View File

@@ -1,13 +0,0 @@
// @validateRefAccessDuringRender
function Foo({a}) {
const ref = useRef();
// type information is lost here as we don't track types of fields
const val = {ref};
// without type info, we don't know that val.ref.current is a ref value so we
// *would* end up depending on val.ref.current
// however, this is an instance of accessing a ref during render and is disallowed
// under React's rules, so we reject this input
const x = {a, val: val.ref.current};
return <VideoList videos={x} />;
}

View File

@@ -27,7 +27,7 @@ error.invalid-write-but-dont-read-ref-in-render.ts:5:2
3 | const ref = useRef(null);
4 | // Writing to a ref in render is against the rules:
> 5 | ref.current = value;
| ^^^ Cannot update ref during render
| ^^^^^^^^^^^ Cannot update ref during render
6 | // returning a ref is allowed, so this alone doesn't trigger an error:
7 | return ref;
8 | }

View File

@@ -25,7 +25,7 @@ error.invalid-write-ref-prop-in-render.ts:4:2
2 | function Component(props) {
3 | const ref = props.ref;
> 4 | ref.current = true;
| ^^^ Cannot update ref during render
| ^^^^^^^^^^^ Cannot update ref during render
5 | return <div>{value}</div>;
6 | }
7 |

View File

@@ -34,7 +34,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
7 | const r = useRef(DEFAULT_VALUE);
8 | if (r.current == DEFAULT_VALUE) {
> 9 | r.current = 1;
| ^ Cannot update ref during render
| ^^^^^^^^^ Cannot update ref during render
10 | }
11 | }
12 |

View File

@@ -1,41 +0,0 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
component C() {
const r = useRef(null);
if (r.current == null) {
f(r);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
5 | const r = useRef(null);
6 | if (r.current == null) {
> 7 | f(r);
| ^ Passing a ref to a function may read its value during render
8 | }
9 | }
10 |
```

View File

@@ -1,41 +0,0 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
component C() {
const r = useRef(null);
if (r.current == null) {
f(r.current);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
5 | const r = useRef(null);
6 | if (r.current == null) {
> 7 | f(r.current);
| ^^^^^^^^^ Passing a ref to a function may read its value during render
8 | }
9 | }
10 |
```

View File

@@ -24,35 +24,27 @@ export const FIXTURE_ENTRYPOINT = {
## Error
```
Found 2 errors:
Found 1 error:
Error: Mutating refs during render is not allowed
Error: Cannot access refs during render
React refs are mutable containers that should only be mutated outside of render, such as in event handlers or effects. Mutating a ref during render can cause bugs because the mutation may not be associated with a particular render. See https://react.dev/reference/react/useRef.
5 | const r = useRef(null);
6 | if (r.current == null) {
> 7 | r.current = 42;
| ^ Cannot mutate ref during render
8 | r.current = 42;
9 | }
10 | }
Refs may be mutated during render if initialized with `if (ref.current == null)`
Error: Mutating refs during render is not allowed
React refs are mutable containers that should only be mutated outside of render, such as in event handlers or effects. Mutating a ref during render can cause bugs because the mutation may not be associated with a particular render. See https://react.dev/reference/react/useRef.
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).
6 | if (r.current == null) {
7 | r.current = 42;
> 8 | r.current = 42;
| ^ Cannot mutate ref during render
| ^^^^^^^^^ Ref is initialized more than once during render
9 | }
10 | }
11 |
Refs may be mutated during render if initialized with `if (ref.current == null)`
5 | const r = useRef(null);
6 | if (r.current == null) {
> 7 | r.current = 42;
| ^^^^^^^^^ Ref was first initialized here
8 | r.current = 42;
9 | }
10 | }
```

View File

@@ -33,7 +33,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
6 | const guard = r.current == null;
7 | if (guard) {
> 8 | r.current = 1;
| ^ Cannot update ref during render
| ^^^^^^^^^ Cannot update ref during render
9 | }
10 | }
11 |

View File

@@ -33,7 +33,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
6 | const r2 = useRef(null);
7 | if (r.current == null) {
> 8 | r2.current = 1;
| ^^ Cannot update ref during render
| ^^^^^^^^^^ Cannot update ref during render
9 | }
10 | }
11 |

View File

@@ -1,44 +0,0 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
component C() {
const r = useRef(null);
if (r.current == null) {
r.current = 1;
}
f(r.current);
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Error
```
Found 1 error:
Error: Mutating refs during render is not allowed
React refs are mutable containers that should only be mutated outside of render, such as in event handlers or effects. Mutating a ref during render can cause bugs because the mutation may not be associated with a particular render. See https://react.dev/reference/react/useRef.
5 | const r = useRef(null);
6 | if (r.current == null) {
> 7 | r.current = 1;
| ^ Cannot mutate ref during render
8 | }
9 | f(r.current);
10 | }
Refs may be mutated during render if initialized with `if (ref.current == null)`
```

View File

@@ -33,7 +33,7 @@ React refs are values that are not needed for rendering. Refs should only be acc
7 | r.current = 1;
8 | }
> 9 | r.current = 1;
| ^ Cannot update ref during render
| ^^^^^^^^^ Cannot update ref during render
10 | }
11 |
12 | export const FIXTURE_ENTRYPOINT = {

View File

@@ -1,69 +0,0 @@
## Input
```javascript
// @enableInferEventHandlers
import {useRef} from 'react';
// Simulates a custom component wrapper
function CustomForm({onSubmit, children}: any) {
return <form onSubmit={onSubmit}>{children}</form>;
}
// Simulates react-hook-form's handleSubmit
function handleSubmit<T>(callback: (data: T) => void) {
return (event: any) => {
event.preventDefault();
callback({} as T);
};
}
function Component() {
const ref = useRef<HTMLInputElement>(null);
const onSubmit = (data: any) => {
// This should error: passing function with ref access to custom component
// event handler, even though it would be safe on a native <form>
if (ref.current !== null) {
console.log(ref.current.value);
}
};
return (
<>
<input ref={ref} />
<CustomForm onSubmit={handleSubmit(onSubmit)}>
<button type="submit">Submit</button>
</CustomForm>
</>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
error.ref-value-in-custom-component-event-handler-wrapper.ts:31:41
29 | <>
30 | <input ref={ref} />
> 31 | <CustomForm onSubmit={handleSubmit(onSubmit)}>
| ^^^^^^^^ Passing a ref to a function may read its value during render
32 | <button type="submit">Submit</button>
33 | </CustomForm>
34 | </>
```

View File

@@ -1,64 +0,0 @@
## Input
```javascript
// @enableInferEventHandlers
import {useRef} from 'react';
// Simulates a handler wrapper
function handleClick(value: any) {
return () => {
console.log(value);
};
}
function Component() {
const ref = useRef(null);
// This should still error: passing ref.current directly to a wrapper
// The ref value is accessed during render, not in the event handler
return (
<>
<input ref={ref} />
<button onClick={handleClick(ref.current)}>Click</button>
</>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
## Error
```
Found 1 error:
Error: Cannot access ref value during render
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).
error.ref-value-in-event-handler-wrapper.ts:19:6
17 | <>
18 | <input ref={ref} />
> 19 | <button onClick={handleClick(ref.current)}>Click</button>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Ref value is used during render
20 | </>
21 | );
22 | }
error.ref-value-in-event-handler-wrapper.ts:19:35
17 | <>
18 | <input ref={ref} />
> 19 | <button onClick={handleClick(ref.current)}>Click</button>
| ^^^^^^^^^^^ Ref is initially accessed
20 | </>
21 | );
22 | }
```

View File

@@ -41,7 +41,7 @@ error.todo-useCallback-set-ref-nested-property-ref-modified-later-preserve-memoi
12 |
13 | // The ref is modified later, extending its range and preventing memoization of onChange
> 14 | ref.current.inner = null;
| ^^^^^^^^^^^ Cannot update ref during render
| ^^^^^^^^^^^^^^^^^ Cannot update ref during render
15 |
16 | return <input onChange={onChange} />;
17 | }

View File

@@ -44,7 +44,7 @@ error.useCallback-accesses-ref-mutated-later-via-function-preserve-memoization.t
13 | // The ref is modified later, extending its range and preventing memoization of onChange
14 | const reset = () => {
> 15 | ref.current.inner = null;
| ^^^^^^^^^^^ Cannot update ref during render
| ^^^^^^^^^^^^^^^^^ Cannot update ref during render
16 | };
17 | reset();
18 |

View File

@@ -40,7 +40,7 @@ error.useCallback-set-ref-nested-property-dont-preserve-memoization.ts:13:2
11 | });
12 |
> 13 | ref.current.inner = null;
| ^^^^^^^^^^^ Cannot update ref during render
| ^^^^^^^^^^^^^^^^^ Cannot update ref during render
14 |
15 | return <input onChange={onChange} />;
16 | }

View File

@@ -1,39 +0,0 @@
## Input
```javascript
// @validateRefAccessDuringRender:true
function Foo(props, ref) {
console.log(ref.current);
return <div>{props.bar}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{bar: 'foo'}, {ref: {cuurrent: 1}}],
isComponent: true,
};
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
error.validate-mutate-ref-arg-in-render.ts:3:14
1 | // @validateRefAccessDuringRender:true
2 | function Foo(props, ref) {
> 3 | console.log(ref.current);
| ^^^^^^^^^^^ Passing a ref to a function may read its value during render
4 | return <div>{props.bar}</div>;
5 | }
6 |
```

View File

@@ -0,0 +1,58 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {arrayPush, identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const getDate = () => Date.now();
const now = getDate();
const array = [];
arrayPush(array, now);
return <Foo hasDate={array} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateNoImpureFunctionsInRender
import { arrayPush, identity, makeArray } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const $ = _c(1);
const getDate = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const now = getDate();
const array = [];
arrayPush(array, now);
t0 = <Foo hasDate={array} />;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
function _temp() {
return Date.now();
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -2,6 +2,11 @@
import {arrayPush, identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const getDate = () => Date.now();
const now = getDate();

View File

@@ -0,0 +1,56 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const getDate = () => Date.now();
const array = makeArray(getDate());
const hasDate = identity(array);
return <Foo hasDate={hasDate} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateNoImpureFunctionsInRender
import { identity, makeArray } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const $ = _c(1);
const getDate = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const array = makeArray(getDate());
const hasDate = identity(array);
t0 = <Foo hasDate={hasDate} />;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
function _temp() {
return Date.now();
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -2,6 +2,11 @@
import {identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const getDate = () => Date.now();
const array = makeArray(getDate());

View File

@@ -0,0 +1,64 @@
## Input
```javascript
// @validateNoImpureFunctionsInRender
import {identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const now = () => Date.now();
const f = () => {
const array = makeArray(now());
const hasDate = identity(array);
return hasDate;
};
const hasDate = f();
return <Foo hasDate={hasDate} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateNoImpureFunctionsInRender
import { identity, makeArray } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const $ = _c(1);
const now = _temp;
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const f = () => {
const array = makeArray(now());
const hasDate = identity(array);
return hasDate;
};
const hasDate_0 = f();
t0 = <Foo hasDate={hasDate_0} />;
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
function _temp() {
return Date.now();
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -2,11 +2,14 @@
import {identity, makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render. The impurity is lost
* when passed through external function calls.
*/
function Component() {
const now = () => Date.now();
const f = () => {
// this should error but we currently lose track of the impurity bc
// the impure value comes from behind a call
const array = makeArray(now());
const hasDate = identity(array);
return hasDate;

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"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":"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":20,"index":307},"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}
```

View File

@@ -47,7 +47,7 @@ export const FIXTURE_ENTRYPOINT = {
## Logs
```
{"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":"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":20,"index":339},"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}
```

View File

@@ -0,0 +1,45 @@
## Input
```javascript
// @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. The return type of foo() is unknown.
*/
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
return x.current;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. The return type of foo() is unknown.
*/
function Component(props) {
const $ = _c(1);
const ref = useRef(null);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = foo(ref);
$[0] = t0;
} else {
t0 = $[0];
}
const x = t0;
return x.current;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,11 @@
// @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. The return type of foo() is unknown.
*/
function Component(props) {
const ref = useRef(null);
const x = foo(ref);
return x.current;
}

View File

@@ -1,42 +0,0 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees:true
import {useRef, useMemo} from 'react';
import {makeArray} from 'shared-runtime';
function useFoo() {
const r = useRef();
return useMemo(() => makeArray(r), []);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [],
};
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
error.maybe-mutable-ref-not-preserved.ts:8:33
6 | function useFoo() {
7 | const r = useRef();
> 8 | return useMemo(() => makeArray(r), []);
| ^ Passing a ref to a function may read its value during render
9 | }
10 |
11 | export const FIXTURE_ENTRYPOINT = {
```

View File

@@ -1,37 +0,0 @@
## Input
```javascript
// @flow @validatePreserveExistingMemoizationGuarantees
import {identity} from 'shared-runtime';
component Component(disableLocalRef, ref) {
const localRef = useFooRef();
const mergedRef = useMemo(() => {
return disableLocalRef ? ref : identity(ref, localRef);
}, [disableLocalRef, ref, localRef]);
return <div ref={mergedRef} />;
}
```
## Error
```
Found 1 error:
Error: Cannot access refs during render
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).
5 | const localRef = useFooRef();
6 | const mergedRef = useMemo(() => {
> 7 | return disableLocalRef ? ref : identity(ref, localRef);
| ^^^ Passing a ref to a function may read its value during render
8 | }, [disableLocalRef, ref, localRef]);
9 | return <div ref={mergedRef} />;
10 | }
```

View File

@@ -0,0 +1,59 @@
## Input
```javascript
// @validatePreserveExistingMemoizationGuarantees:true
import {useRef, useMemo} from 'react';
import {makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useFoo() {
const r = useRef();
return useMemo(() => makeArray(r), []);
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validatePreserveExistingMemoizationGuarantees:true
import { useRef, useMemo } from "react";
import { makeArray } from "shared-runtime";
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useFoo() {
const $ = _c(1);
const r = useRef();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = makeArray(r);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: useFoo,
params: [],
};
```
### Eval output
(kind: ok) [{}]

View File

@@ -3,6 +3,10 @@
import {useRef, useMemo} from 'react';
import {makeArray} from 'shared-runtime';
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses an impure value during render.
*/
function useFoo() {
const r = useRef();
return useMemo(() => makeArray(r), []);

View File

@@ -0,0 +1,52 @@
## Input
```javascript
// @flow @validatePreserveExistingMemoizationGuarantees
import {identity} from 'shared-runtime';
component Component(disableLocalRef, ref) {
const localRef = useFooRef();
const mergedRef = useMemo(() => {
return disableLocalRef ? ref : identity(ref, localRef);
}, [disableLocalRef, ref, localRef]);
return <div ref={mergedRef} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime";
import { identity } from "shared-runtime";
const Component = React.forwardRef(Component_withRef);
function Component_withRef(t0, ref) {
const $ = _c(6);
const { disableLocalRef } = t0;
const localRef = useFooRef();
let t1;
if ($[0] !== disableLocalRef || $[1] !== localRef || $[2] !== ref) {
t1 = disableLocalRef ? ref : identity(ref, localRef);
$[0] = disableLocalRef;
$[1] = localRef;
$[2] = ref;
$[3] = t1;
} else {
t1 = $[3];
}
const mergedRef = t1;
let t2;
if ($[4] !== mergedRef) {
t2 = <div ref={mergedRef} />;
$[4] = mergedRef;
$[5] = t2;
} else {
t2 = $[5];
}
return t2;
}
```

View File

@@ -0,0 +1,46 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {
f(r);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Code
```javascript
import { useRef } from "react";
function C() {
const r = useRef(null);
if (r.current == null) {
f(r);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
### Eval output
(kind: exception) f is not defined

View File

@@ -1,6 +1,10 @@
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {

View File

@@ -0,0 +1,46 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {
f(r.current);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Code
```javascript
import { useRef } from "react";
function C() {
const r = useRef(null);
if (r.current == null) {
f(r.current);
}
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
### Eval output
(kind: exception) f is not defined

View File

@@ -1,6 +1,10 @@
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {

View File

@@ -0,0 +1,49 @@
## Input
```javascript
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {
r.current = 1;
}
f(r.current);
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
## Code
```javascript
import { useRef } from "react";
function C() {
const r = useRef(null);
if (r.current == null) {
r.current = 1;
}
f(r.current);
}
export const FIXTURE_ENTRYPOINT = {
fn: C,
params: [{}],
};
```
### Eval output
(kind: exception) f is not defined

View File

@@ -1,6 +1,10 @@
//@flow
import {useRef} from 'react';
/**
* Allowed: we aren't sure that the ref.current value flows into the render
* output, so we optimistically assume it's safe
*/
component C() {
const r = useRef(null);
if (r.current == null) {

View File

@@ -0,0 +1,121 @@
## Input
```javascript
// @enableInferEventHandlers
import {useRef} from 'react';
// Simulates a custom component wrapper
function CustomForm({onSubmit, children}: any) {
return <form onSubmit={onSubmit}>{children}</form>;
}
// Simulates react-hook-form's handleSubmit
function handleSubmit<T>(callback: (data: T) => void) {
return (event: any) => {
event.preventDefault();
callback({} as T);
};
}
function Component() {
const ref = useRef<HTMLInputElement>(null);
const onSubmit = (data: any) => {
// Allowed: we aren't sure that the ref.current value flows into the render
// output, so we optimistically assume it's safe
if (ref.current !== null) {
console.log(ref.current.value);
}
};
return (
<>
<input ref={ref} />
<CustomForm onSubmit={handleSubmit(onSubmit)}>
<button type="submit">Submit</button>
</CustomForm>
</>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableInferEventHandlers
import { useRef } from "react";
// Simulates a custom component wrapper
function CustomForm(t0) {
const $ = _c(3);
const { onSubmit, children } = t0;
let t1;
if ($[0] !== children || $[1] !== onSubmit) {
t1 = <form onSubmit={onSubmit}>{children}</form>;
$[0] = children;
$[1] = onSubmit;
$[2] = t1;
} else {
t1 = $[2];
}
return t1;
}
// Simulates react-hook-form's handleSubmit
function handleSubmit(callback) {
const $ = _c(2);
let t0;
if ($[0] !== callback) {
t0 = (event) => {
event.preventDefault();
callback({} as T);
};
$[0] = callback;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
function Component() {
const $ = _c(1);
const ref = useRef(null);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onSubmit = (data) => {
if (ref.current !== null) {
console.log(ref.current.value);
}
};
t0 = (
<>
<input ref={ref} />
<CustomForm onSubmit={handleSubmit(onSubmit)}>
<button type="submit">Submit</button>
</CustomForm>
</>
);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
### Eval output
(kind: ok) <input><form><button type="submit">Submit</button></form>

View File

@@ -18,8 +18,8 @@ function Component() {
const ref = useRef<HTMLInputElement>(null);
const onSubmit = (data: any) => {
// This should error: passing function with ref access to custom component
// event handler, even though it would be safe on a native <form>
// Allowed: we aren't sure that the ref.current value flows into the render
// output, so we optimistically assume it's safe
if (ref.current !== null) {
console.log(ref.current.value);
}

View File

@@ -0,0 +1,83 @@
## Input
```javascript
// @enableInferEventHandlers
import {useRef} from 'react';
// Simulates a handler wrapper
function handleClick(value: any) {
return () => {
console.log(value);
};
}
function Component() {
const ref = useRef(null);
// Allowed: we aren't sure that the ref.current value flows into the render
// output, so we optimistically assume it's safe
return (
<>
<input ref={ref} />
<button onClick={handleClick(ref.current)}>Click</button>
</>
);
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @enableInferEventHandlers
import { useRef } from "react";
// Simulates a handler wrapper
function handleClick(value) {
const $ = _c(2);
let t0;
if ($[0] !== value) {
t0 = () => {
console.log(value);
};
$[0] = value;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
function Component() {
const $ = _c(1);
const ref = useRef(null);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (
<>
<input ref={ref} />
<button onClick={handleClick(ref.current)}>Click</button>
</>
);
$[0] = t0;
} else {
t0 = $[0];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Component,
params: [{}],
};
```
### Eval output
(kind: ok) <input><button>Click</button>

View File

@@ -11,8 +11,8 @@ function handleClick(value: any) {
function Component() {
const ref = useRef(null);
// This should still error: passing ref.current directly to a wrapper
// The ref value is accessed during render, not in the event handler
// Allowed: we aren't sure that the ref.current value flows into the render
// output, so we optimistically assume it's safe
return (
<>
<input ref={ref} />

View File

@@ -0,0 +1,59 @@
## Input
```javascript
// @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. Type info is lost when ref is
* stored in an object field.
*/
function Foo({a}) {
const ref = useRef();
const val = {ref};
const x = {a, val: val.ref.current};
return <VideoList videos={x} />;
}
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. Type info is lost when ref is
* stored in an object field.
*/
function Foo(t0) {
const $ = _c(3);
const { a } = t0;
const ref = useRef();
let t1;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t1 = { ref };
$[0] = t1;
} else {
t1 = $[0];
}
const val = t1;
let t2;
if ($[1] !== a) {
const x = { a, val: val.ref.current };
t2 = <VideoList videos={x} />;
$[1] = a;
$[2] = t2;
} else {
t2 = $[2];
}
return t2;
}
```
### Eval output
(kind: exception) Fixture not implemented

View File

@@ -0,0 +1,14 @@
// @validateRefAccessDuringRender
/**
* Allowed: we don't have sufficient type information to be sure that
* this accesses a ref during render. Type info is lost when ref is
* stored in an object field.
*/
function Foo({a}) {
const ref = useRef();
const val = {ref};
const x = {a, val: val.ref.current};
return <VideoList videos={x} />;
}

View File

@@ -0,0 +1,51 @@
## Input
```javascript
// @validateRefAccessDuringRender:true
function Foo(props, ref) {
// Allowed: the value is not guaranteed to flow into something that's rendered
console.log(ref.current);
return <div>{props.bar}</div>;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{bar: 'foo'}, {ref: {cuurrent: 1}}],
isComponent: true,
};
```
## Code
```javascript
import { c as _c } from "react/compiler-runtime"; // @validateRefAccessDuringRender:true
function Foo(props, ref) {
const $ = _c(2);
console.log(ref.current);
let t0;
if ($[0] !== props.bar) {
t0 = <div>{props.bar}</div>;
$[0] = props.bar;
$[1] = t0;
} else {
t0 = $[1];
}
return t0;
}
export const FIXTURE_ENTRYPOINT = {
fn: Foo,
params: [{ bar: "foo" }, { ref: { cuurrent: 1 } }],
isComponent: true,
};
```
### Eval output
(kind: ok) <div>foo</div>
logs: [undefined]

View File

@@ -1,5 +1,7 @@
// @validateRefAccessDuringRender:true
function Foo(props, ref) {
// Allowed: the value is not guaranteed to flow into something that's rendered
console.log(ref.current);
return <div>{props.bar}</div>;
}

View File

@@ -36,6 +36,7 @@ type RunnerOptions = {
filter: boolean;
update: boolean;
pattern?: string;
nodebug: boolean;
};
const opts: RunnerOptions = yargs
@@ -71,6 +72,12 @@ const opts: RunnerOptions = yargs
'pattern',
'Optional glob pattern to filter fixtures (e.g., "error.*", "use-memo")',
)
.boolean('nodebug')
.describe(
'nodebug',
'Do not enable debug logging, even if only one test is matched',
)
.default('nodebug', false)
.help('help')
.strict()
.parseSync(hideBin(process.argv)) as RunnerOptions;
@@ -236,7 +243,7 @@ export async function main(opts: RunnerOptions): Promise<void> {
let testFilter: TestFilter | null = null;
if (opts.pattern) {
testFilter = {
debug: true,
debug: !opts.nodebug,
paths: [opts.pattern],
};
} else if (opts.filter) {