Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cca2c4beda | ||
|
|
13f6508c21 |
@@ -8,7 +8,8 @@
|
||||
"Bash(echo:*)",
|
||||
"Bash(done)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(sl revert:*)"
|
||||
"Bash(sl revert:*)",
|
||||
"Bash(yarn workspace snap run build:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
|
||||
@@ -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. ' +
|
||||
|
||||
@@ -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
|
||||
@@ -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 = {};
|
||||
@@ -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) [{},{}]
|
||||
@@ -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 => () => {
|
||||
@@ -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>
|
||||
@@ -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 = {};
|
||||
@@ -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)`
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
```
|
||||
|
||||
|
||||
@@ -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} />;
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// @validateRefAccessDuringRender
|
||||
function Component(props) {
|
||||
const ref = useRef(null);
|
||||
const x = foo(ref);
|
||||
return x.current;
|
||||
}
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -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} />;
|
||||
}
|
||||
@@ -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 | }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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)`
|
||||
```
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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 | </>
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
|
||||
@@ -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 |
|
||||
|
||||
@@ -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 | }
|
||||
|
||||
@@ -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 |
|
||||
```
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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();
|
||||
@@ -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
|
||||
@@ -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());
|
||||
@@ -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
|
||||
@@ -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;
|
||||
@@ -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}
|
||||
```
|
||||
|
||||
@@ -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}
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 = {
|
||||
```
|
||||
|
||||
|
||||
@@ -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 | }
|
||||
```
|
||||
|
||||
|
||||
@@ -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) [{}]
|
||||
@@ -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), []);
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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
|
||||
@@ -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) {
|
||||
@@ -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>
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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>
|
||||
@@ -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} />
|
||||
@@ -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
|
||||
@@ -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} />;
|
||||
}
|
||||
@@ -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]
|
||||
@@ -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>;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user