@samitouri / QOS-React-2 / commits / 46339720d7

compiler: error on reassigning to const

We currently don't report an error if the code attempts to reassign a const. Our thinking has been that we're not trying to catch all possible mistakes you could make in JavaScript — that's what ESLint, TypeScript, and Flow are for — and that we want to focus on React errors. However, accidentally reassigning a const is easy to catch and doesn't get in the way of other analysis so let's implement it. Note that React Compiler's ESLint plugin won't report these errors by default, but they will show up in playground. Fixes #29598 ghstack-source-id: a0af8b9a486d74a8991413322efddc3e3028c755 Pull Request resolved: https://github.com/facebook/react/pull/29619

Joe Savona committed May 28, 2024 at 12:12 UTC 46339720d75337ae1d1e113fd56ac99e7fd1a0b3
5 files changed +49 -2
compiler/packages/babel-plugin-react-compiler/src/HIR/BuildHIR.ts
+14
@@ -3336,6 +3336,20 @@ function lowerIdentifierForAssignment(
3336 });
3337 return null;
3338 }
3339 + } else if (
3340 + binding.bindingKind === "const" &&
3341 + kind === InstructionKind.Reassign
3342 + ) {
3343 + builder.errors.push({
3344 + reason: `Cannot reassign a \`const\` variable`,
3345 + severity: ErrorSeverity.InvalidJS,
3346 + loc: path.node.loc ?? null,
3347 + description:
3348 + binding.identifier.name != null
3349 + ? `\`${binding.identifier.name.value}\` is declared as const`
3350 + : null,
3351 + });
3352 + return null;
3353 }
3354
3355 const place: Place = {
compiler/packages/babel-plugin-react-compiler/src/HIR/HIR.ts
+2 -1
@@ -5,6 +5,7 @@
5 * LICENSE file in the root directory of this source tree.
6 */
7
8 +import { BindingKind } from "@babel/traverse";
9 import * as t from "@babel/types";
10 import { CompilerError, CompilerErrorDetailOptions } from "../CompilerError";
11 import { assertExhaustive } from "../Utils/utils";
@@ -1105,7 +1106,7 @@ export type MutableRange = {
1106
1107 export type VariableBinding =
1108 // let, const, etc declared within the current component/hook
1108 - | { kind: "Identifier"; identifier: Identifier }
1109 + | { kind: "Identifier"; identifier: Identifier; bindingKind: BindingKind }
1110 // bindings declard outside the current component/hook
1111 | NonLocalBinding;
1112
compiler/packages/babel-plugin-react-compiler/src/HIR/HIRBuilder.ts
+5 -1
@@ -280,7 +280,11 @@ export default class HIRBuilder {
280 if (resolvedBinding.name && resolvedBinding.name.value !== originalName) {
281 babelBinding.scope.rename(originalName, resolvedBinding.name.value);
282 }
283 - return { kind: "Identifier", identifier: resolvedBinding };
283 + return {
284 + kind: "Identifier",
285 + identifier: resolvedBinding,
286 + bindingKind: babelBinding.kind,
287 + };
288 }
289
290 isContextIdentifier(path: NodePath<t.Identifier | t.JSXIdentifier>): boolean {
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-const.expect.md new
+24
@@ -0,0 +1,24 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component() {
6 + const x = 0;
7 + x = 1;
8 +}
9 +
10 +```
11 +
12 +
13 +## Error
14 +
15 +```
16 + 1 | function Component() {
17 + 2 | const x = 0;
18 +> 3 | x = 1;
19 + | ^ InvalidJS: Cannot reassign a `const` variable. `x` is declared as const (3:3)
20 + 4 | }
21 + 5 |
22 +```
23 +
24 +
\ No newline at end of file
compiler/packages/babel-plugin-react-compiler/src/__tests__/fixtures/compiler/error.invalid-reassign-const.js new
+4
@@ -0,0 +1,4 @@
1 +function Component() {
2 + const x = 0;
3 + x = 1;
4 +}