-
Notifications
You must be signed in to change notification settings - Fork 46.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[compiler] Examples of invalid code reassigning locals outside of render
ghstack-source-id: c48d0d0b86e47e2449982263c88cc2cfd44e326b Pull Request resolved: #30106
- Loading branch information
1 parent
8416ebe
commit b1d10c8
Showing
7 changed files
with
385 additions
and
0 deletions.
There are no files selected for viewing
98 changes: 98 additions & 0 deletions
98
...ts__/fixtures/compiler/todo.invalid-reassign-local-variable-in-effect.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import { useEffect } from "react"; | ||
|
||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const onMount = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
onMount(); | ||
}, [onMount]); | ||
|
||
return "ok"; | ||
} | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import { useEffect } from "react"; | ||
|
||
function Component() { | ||
const $ = _c(4); | ||
let local; | ||
let t0; | ||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
t0 = (newValue) => { | ||
local = newValue; | ||
}; | ||
$[0] = t0; | ||
} else { | ||
t0 = $[0]; | ||
} | ||
const reassignLocal = t0; | ||
let t1; | ||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) { | ||
t1 = (newValue_0) => { | ||
reassignLocal("hello"); | ||
if (local === newValue_0) { | ||
console.log("`local` was updated!"); | ||
} else { | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
$[1] = t1; | ||
} else { | ||
t1 = $[1]; | ||
} | ||
const onMount = t1; | ||
let t2; | ||
let t3; | ||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) { | ||
t2 = () => { | ||
onMount(); | ||
}; | ||
t3 = [onMount]; | ||
$[2] = t2; | ||
$[3] = t3; | ||
} else { | ||
t2 = $[2]; | ||
t3 = $[3]; | ||
} | ||
useEffect(t2, t3); | ||
return "ok"; | ||
} | ||
|
||
``` | ||
38 changes: 38 additions & 0 deletions
38
...ompiler/src/__tests__/fixtures/compiler/todo.invalid-reassign-local-variable-in-effect.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useEffect } from "react"; | ||
|
||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const onMount = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
onMount(); | ||
}, [onMount]); | ||
|
||
return "ok"; | ||
} |
96 changes: 96 additions & 0 deletions
96
...xtures/compiler/todo.invalid-reassign-local-variable-in-hook-argument.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,96 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
import { useEffect } from "react"; | ||
import { useIdentity } from "shared-runtime"; | ||
|
||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const callback = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
useIdentity(() => { | ||
callback(); | ||
}); | ||
|
||
return "ok"; | ||
} | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
import { useEffect } from "react"; | ||
import { useIdentity } from "shared-runtime"; | ||
|
||
function Component() { | ||
const $ = _c(3); | ||
let local; | ||
let t0; | ||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
t0 = (newValue) => { | ||
local = newValue; | ||
}; | ||
$[0] = t0; | ||
} else { | ||
t0 = $[0]; | ||
} | ||
const reassignLocal = t0; | ||
let t1; | ||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) { | ||
t1 = (newValue_0) => { | ||
reassignLocal("hello"); | ||
if (local === newValue_0) { | ||
console.log("`local` was updated!"); | ||
} else { | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
$[1] = t1; | ||
} else { | ||
t1 = $[1]; | ||
} | ||
const callback = t1; | ||
let t2; | ||
if ($[2] === Symbol.for("react.memo_cache_sentinel")) { | ||
t2 = () => { | ||
callback(); | ||
}; | ||
$[2] = t2; | ||
} else { | ||
t2 = $[2]; | ||
} | ||
useIdentity(t2); | ||
return "ok"; | ||
} | ||
|
||
``` | ||
39 changes: 39 additions & 0 deletions
39
.../src/__tests__/fixtures/compiler/todo.invalid-reassign-local-variable-in-hook-argument.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
import { useEffect } from "react"; | ||
import { useIdentity } from "shared-runtime"; | ||
|
||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const callback = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
useIdentity(() => { | ||
callback(); | ||
}); | ||
|
||
return "ok"; | ||
} |
77 changes: 77 additions & 0 deletions
77
...ixtures/compiler/todo.invalid-reassign-local-variable-in-jsx-callback.expect.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
|
||
## Input | ||
|
||
```javascript | ||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const onClick = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
return <button onClick={onClick}>Submit</button>; | ||
} | ||
|
||
``` | ||
|
||
## Code | ||
|
||
```javascript | ||
import { c as _c } from "react/compiler-runtime"; | ||
function Component() { | ||
const $ = _c(2); | ||
let local; | ||
let t0; | ||
if ($[0] === Symbol.for("react.memo_cache_sentinel")) { | ||
t0 = (newValue) => { | ||
local = newValue; | ||
}; | ||
$[0] = t0; | ||
} else { | ||
t0 = $[0]; | ||
} | ||
const reassignLocal = t0; | ||
let t1; | ||
if ($[1] === Symbol.for("react.memo_cache_sentinel")) { | ||
const onClick = (newValue_0) => { | ||
reassignLocal("hello"); | ||
if (local === newValue_0) { | ||
console.log("`local` was updated!"); | ||
} else { | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
t1 = <button onClick={onClick}>Submit</button>; | ||
$[1] = t1; | ||
} else { | ||
t1 = $[1]; | ||
} | ||
return t1; | ||
} | ||
|
||
``` | ||
32 changes: 32 additions & 0 deletions
32
...r/src/__tests__/fixtures/compiler/todo.invalid-reassign-local-variable-in-jsx-callback.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
function Component() { | ||
let local; | ||
|
||
const reassignLocal = (newValue) => { | ||
local = newValue; | ||
}; | ||
|
||
const onClick = (newValue) => { | ||
reassignLocal("hello"); | ||
|
||
if (local === newValue) { | ||
// Without React Compiler, `reassignLocal` is freshly created | ||
// on each render, capturing a binding to the latest `local`, | ||
// such that invoking reassignLocal will reassign the same | ||
// binding that we are observing in the if condition, and | ||
// we reach this branch | ||
console.log("`local` was updated!"); | ||
} else { | ||
// With React Compiler enabled, `reassignLocal` is only created | ||
// once, capturing a binding to `local` in that render pass. | ||
// Therefore, calling `reassignLocal` will reassign the wrong | ||
// version of `local`, and not update the binding we are checking | ||
// in the if condition. | ||
// | ||
// To protect against this, we disallow reassigning locals from | ||
// functions that escape | ||
throw new Error("`local` not updated!"); | ||
} | ||
}; | ||
|
||
return <button onClick={onClick}>Submit</button>; | ||
} |
Oops, something went wrong.