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
+}