Skip to content

Commit

Permalink
fix(#1167): function calls prefixed with void together with the opt…
Browse files Browse the repository at this point in the history
…ional chain (?.) are skipped (#1178)

Summary:
Fixes: #1167

`constant-folding-plugin` replaces this call `void foo?.();` with `undefined;`.

This is because the `evaluate()` function marks the optional call expressions as safe, making the `UnaryExpression` visitor think it is safe to replace it with the `value` returned by the `evaluate()`, in this case `undefined`.

This PR makes the `evaluate()` function mark the optional call expressions as unsafe.

```
* **[Fix]**: `constant-folding-plugin`: Don't fold optional function calls (`foo.?()`).
```

Pull Request resolved: #1178

Test Plan:
I have added 2 tests to cover these changes:
- does not transform optional chained call into `undefined`
- does not transform `void` prefixed optional chained call into `undefined`

Reviewed By: GijsWeterings

Differential Revision: D52780448

Pulled By: robhogan

fbshipit-source-id: c84288adc1fec6c2e875fd766c5a6b0997a36c62
  • Loading branch information
Gamote authored and robhogan committed Jan 29, 2024
1 parent 7c4de9a commit 265fef1
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -361,4 +361,20 @@ describe('constant expressions', () => {

compare([constantFoldingPlugin], code, expected);
});

it('does not transform optional chained call into `undefined`', () => {
const code = `foo?.();`;

const expected = `foo?.();`;

compare([constantFoldingPlugin], code, expected);
});

it('does not transform `void` prefixed optional chained call into `undefined`', () => {
const code = `void foo?.();`;

const expected = `void foo?.();`;

compare([constantFoldingPlugin], code, expected);
});
});
12 changes: 10 additions & 2 deletions packages/metro-transform-plugins/src/constant-folding-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,16 +38,24 @@ function constantFoldingPlugin(context: {
const unsafe = (
path:
| NodePath<BabelNodeAssignmentExpression>
| NodePath<BabelNodeCallExpression>,
| NodePath<BabelNodeCallExpression>
| NodePath<BabelNodeOptionalCallExpression>,
state: {safe: boolean},
) => {
state.safe = false;
};

path.traverse(
{
CallExpression: unsafe,
AssignmentExpression: unsafe,
CallExpression: unsafe,
/**
* This will mark `foo?.()` as unsafe, so it is not replaced with `undefined` down the line.
*
* We saw this case in the wild, where the unary expression `void foo?.()` was replaced with `undefined`
* resulting in the expression call being skipped.
*/
OptionalCallExpression: unsafe,
},
state,
);
Expand Down

0 comments on commit 265fef1

Please sign in to comment.