Skip to content

Commit

Permalink
[compiler] Prune all unused array destructure items during DCE
Browse files Browse the repository at this point in the history
We didn't originally support holes within array patterns, so DCE was only able to prune unused items from the end of an array pattern. Now that we support holes we can replace any unused item with a hole, and then just prune the items to the last identifier/spread entry.

Note: this was motivated by finding useState where either the state or setState go unused — both are strong indications that you're violating the rules in some way. By DCE-ing the unused portions of the useState destructuring we can easily check if you're ignoring either value.

ghstack-source-id: 0721ece5e602e3550c0891d8b9e1291eebed8e4b
Pull Request resolved: #31603
  • Loading branch information
josephsavona committed Nov 21, 2024
1 parent c11c951 commit 8edcb8c
Show file tree
Hide file tree
Showing 12 changed files with 27 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -184,29 +184,28 @@ function rewriteInstruction(instr: Instruction, state: State): void {
switch (instr.value.lvalue.pattern.kind) {
case 'ArrayPattern': {
/*
* For arrays, we can only eliminate unused items from the end of the array,
* so we iterate from the end and break once we find a used item. Note that
* we already know at least one item is used, from the pruneableValue check.
* For arrays, we can prune items prior to the end by replacing
* them with a hole. Items at the end can simply be dropped.
*/
let nextItems: ArrayPattern['items'] | null = null;
const originalItems = instr.value.lvalue.pattern.items;
for (let i = originalItems.length - 1; i >= 0; i--) {
const item = originalItems[i];
let lastEntryIndex = 0;
const items = instr.value.lvalue.pattern.items;
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.kind === 'Identifier') {
if (state.isIdOrNameUsed(item.identifier)) {
nextItems = originalItems.slice(0, i + 1);
break;
if (!state.isIdOrNameUsed(item.identifier)) {
items[i] = {kind: 'Hole'};
} else {
lastEntryIndex = i;
}
} else if (item.kind === 'Spread') {
if (state.isIdOrNameUsed(item.place.identifier)) {
nextItems = originalItems.slice(0, i + 1);
break;
if (!state.isIdOrNameUsed(item.place.identifier)) {
items[i] = {kind: 'Hole'};
} else {
lastEntryIndex = i;
}
}
}
if (nextItems !== null) {
instr.value.lvalue.pattern.items = nextItems;
}
items.length = lastEntryIndex + 1;
break;
}
case 'ObjectPattern': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ function component() {
import { c as _c } from "react/compiler-runtime";
function component() {
const $ = _c(1);
const [x, setX] = useState(0);
const [, setX] = useState(0);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const handler = (v) => setX(v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,7 @@ function Component(props) {
}
let d;
if ($[2] !== props.c) {
const [c, ...t0] = props.c;
d = t0;
[, ...d] = props.c;
$[2] = props.c;
$[3] = d;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Component(props) {
import { c as _c } from "react/compiler-runtime";
function Component(props) {
const $ = _c(2);
const [value, setValue] = useState(null);
const [, setValue] = useState(null);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = (e) => setValue((value_0) => value_0 + e.target.value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import { useState } from "react";

function Component(props) {
const $ = _c(1);
const [_state, setState] = useState();
const [, setState] = useState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const a = () => b();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import { useCallback, useTransition } from "react";

function useFoo() {
const $ = _c(1);
const [t, start] = useTransition();
const [, start] = useTransition();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ function Component(props) {
const $ = _c(5);
React.useContext(FooContext);
const ref = React.useRef();
const [x, setX] = React.useState(false);
const [, setX] = React.useState(false);
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
t0 = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ import { useState } from "react";

function Component(props) {
const $ = _c(5);
const [x, setX] = useState(false);
const [y, setY] = useState(false);
const [, setX] = useState(false);
const [, setY] = useState(false);
let setState;
if (props.cond) {
setState = setX;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,7 @@ function Component(props) {
const { buttons } = props;
let nonPrimaryButtons;
if ($[0] !== buttons) {
const [primaryButton, ...t0] = buttons;
nonPrimaryButtons = t0;
[, ...nonPrimaryButtons] = buttons;
$[0] = buttons;
$[1] = nonPrimaryButtons;
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export const FIXTURE_ENTRYPOINT = {

```javascript
function foo(props) {
const [x, unused, y] = props.a;
const [x, , y] = props.a;
return x + y;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import { useActionState } from "react";

function Component() {
const $ = _c(1);
const [actionState, dispatchAction] = useActionState();
const [, dispatchAction] = useActionState();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onSubmitAction = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ import { useReducer } from "react";

function f() {
const $ = _c(1);
const [state, dispatch] = useReducer();
const [, dispatch] = useReducer();
let t0;
if ($[0] === Symbol.for("react.memo_cache_sentinel")) {
const onClick = () => {
Expand Down

0 comments on commit 8edcb8c

Please sign in to comment.