Improve remaining InvalidReact error messages
Lauren Tan committed
Jun 28, 2023 at 11:43 UTC
f850d3b26524db907f51592dcdf5d811e6aefc2e
29 files changed
+70
-50
compiler/forget/packages/babel-plugin-react-forget/src/HIR/BuildHIR.ts
+2
-2
@@ -2551,9 +2551,9 @@ function lowerIdentifierForAssignment(
2551
if (kind === InstructionKind.Reassign) {
2552
// Trying to reassign a global is not allowed
2553
builder.errors.push({
2554
- reason: `(BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported.`,
2554
+ reason: `This reassigns a variable which was not defined inside of the component. Components should be pure and side-effect free. If this variable is used in rendering, use useState instead. (https://react.dev/learn/keeping-components-pure)`,
2555
severity: ErrorSeverity.InvalidReact,
2556
- loc: path.node.loc ?? null,
2556
+ loc: path.parentPath.node.loc ?? null,
2557
suggestions: null,
2558
});
2559
} else {
compiler/forget/packages/babel-plugin-react-forget/src/Inference/InferReferenceEffects.ts
+10
-10
@@ -25,11 +25,9 @@ import {
25
} from "../HIR/HIR";
26
import { FunctionSignature } from "../HIR/ObjectShape";
27
import {
28
- printIdentifier,
28
printMixedHIR,
29
printPlace,
30
printSourceLocation,
32
- printType,
31
} from "../HIR/PrintHIR";
32
import {
33
eachInstructionOperand,
@@ -324,10 +322,11 @@ class InferenceState {
322
effect = Effect.Mutate;
323
} else {
324
CompilerError.invalidReact({
327
- reason: `InferReferenceEffects: inferred mutation of known immutable value`,
328
- description: `Found mutation of ${printIdentifier(
329
- place.identifier
330
- )}${printType(place.identifier.type)} (${valueKind})`,
325
+ reason: `This mutates a variable after it was passed to React, which means that React cannot observe changes to it`,
326
+ description:
327
+ place.identifier.name !== null
328
+ ? `Found mutation of ${place.identifier.name}`
329
+ : null,
330
loc: place.loc,
331
suggestions: null,
332
});
@@ -340,10 +339,11 @@ class InferenceState {
339
valueKind !== ValueKind.Context
340
) {
341
CompilerError.invalidReact({
343
- reason: `InferReferenceEffects: inferred mutation of known immutable value`,
344
- description: `Found mutation of ${printIdentifier(
345
- place.identifier
346
- )}${printType(place.identifier.type)} (${valueKind})`,
342
+ reason: `This mutates a variable after it was passed to React, which means that React cannot observe changes to it`,
343
+ description:
344
+ place.identifier.name !== null
345
+ ? `Found mutation of ${place.identifier.name}`
346
+ : null,
347
loc: place.loc,
348
suggestions: null,
349
});
compiler/forget/packages/babel-plugin-react-forget/src/Validation/ValidateFrozenLambdas.ts
+7
-2
@@ -132,11 +132,16 @@ function validateOperand(
132
state.temporaries.get(operand.identifier.id) ?? operand.identifier.id;
133
const lambda = state.lambdas.get(operandId);
134
if (lambda !== undefined) {
135
+ // TODO: these seem to always be null, we should try to preserve original names from source
136
+ const description =
137
+ lambda.name !== null && operand.identifier.name !== null
138
+ ? `\`${lambda.name}\` is a function that may mutate \`${operand.identifier.name}\`. If you must mutate \`${operand.identifier.name}\` try using a React API like useState and use its setter function instead`
139
+ : null;
140
return new CompilerErrorDetail({
136
- description: null,
141
+ description,
142
loc: typeof operand.loc !== "symbol" ? operand.loc : null,
143
reason:
139
- "Cannot use a mutable function where an immutable value is expected",
144
+ "This mutates a variable that is managed by React, where an immutable value or a function was expected",
145
severity: ErrorSeverity.InvalidReact,
146
suggestions: null,
147
});
compiler/forget/packages/babel-plugin-react-forget/src/Validation/ValidateNoRefAccesInRender.ts
+2
-2
@@ -107,7 +107,7 @@ function validateNonRefValue(error: CompilerError, operand: Place): void {
107
description: `Cannot access ref value at ${printPlace(operand)}`,
108
loc: typeof operand.loc !== "symbol" ? operand.loc : null,
109
reason:
110
- "Ref values (the `current` property) may not be accessed during render",
110
+ "Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef)",
111
severity: ErrorSeverity.InvalidReact,
112
suggestions: null,
113
})
@@ -123,7 +123,7 @@ function validateNonRefObject(error: CompilerError, operand: Place): void {
123
description: `Cannot access ref object at ${printPlace(operand)}`,
124
loc: typeof operand.loc !== "symbol" ? operand.loc : null,
125
reason:
126
- "Ref values may not be passed to functions because they could read the ref value (`current` property) during render",
126
+ "Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef)",
127
severity: ErrorSeverity.InvalidReact,
128
suggestions: null,
129
})
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-access-ref-during-render.expect.md
+1
-1
@@ -15,7 +15,7 @@ function Component(props) {
15
## Error
16
17
```
18
-[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at freeze $22:TObject<BuiltInRefValue> (5:5)
18
+[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $22:TObject<BuiltInRefValue> (5:5)
19
```
20
21
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-array-push-frozen.expect.md
+1
-1
@@ -15,7 +15,7 @@ function Component(props) {
15
## Error
16
17
```
18
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $19:TObject<BuiltInArray> (frozen) (4:4)
18
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (4:4)
19
```
20
21
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-capture-func-passed-to-jsx.expect.md
+1
-1
@@ -20,7 +20,7 @@ function component(a, b) {
20
## Error
21
22
```
23
-[ReactForget] InvalidReact: Cannot use a mutable function where an immutable value is expected (8:8)
23
+[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (8:8)
24
```
25
26
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-computed-store-to-frozen-value.expect.md
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
16
## Error
17
18
```
19
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $21 (frozen) (5:5)
19
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (5:5)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-delete-computed-property-of-frozen-value.expect.md
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
16
## Error
17
18
```
19
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $19 (frozen) (5:5)
19
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (5:5)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-delete-property-of-frozen-value.expect.md
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
16
## Error
17
18
```
19
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $18 (frozen) (5:5)
19
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (5:5)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-destructure-assignment-to-global.expect.md
+1
-1
@@ -13,7 +13,7 @@ function useFoo(props) {
13
## Error
14
15
```
16
-[ReactForget] InvalidReact: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. (2:2)
16
+[ReactForget] InvalidReact: This reassigns a variable which was not defined inside of the component. Components should be pure and side-effect free. If this variable is used in rendering, use useState instead. (https://react.dev/learn/keeping-components-pure) (2:2)
17
```
18
19
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-destructure-to-local-global-variables.expect.md
+1
-1
@@ -15,7 +15,7 @@ function Component(props) {
15
## Error
16
17
```
18
-[ReactForget] InvalidReact: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. (3:3)
18
+[ReactForget] InvalidReact: This reassigns a variable which was not defined inside of the component. Components should be pure and side-effect free. If this variable is used in rendering, use useState instead. (https://react.dev/learn/keeping-components-pure) (3:3)
19
```
20
21
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-freeze-conditionally-mutable-lambda.expect.md
+1
-1
@@ -25,7 +25,7 @@ function Component(props) {
25
## Error
26
27
```
28
-[ReactForget] InvalidReact: Cannot use a mutable function where an immutable value is expected (15:15)
28
+[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (15:15)
29
```
30
31
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-freeze-mutable-lambda-mutate-local.expect.md
+1
-1
@@ -18,7 +18,7 @@ function Component(props) {
18
## Error
19
20
```
21
-[ReactForget] InvalidReact: Cannot use a mutable function where an immutable value is expected (8:8)
21
+[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (8:8)
22
```
23
24
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-freeze-mutable-lambda-reassign-local.expect.md
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
16
## Error
17
18
```
19
-[ReactForget] InvalidReact: Cannot use a mutable function where an immutable value is expected (6:6)
19
+[ReactForget] InvalidReact: This mutates a variable that is managed by React, where an immutable value or a function was expected (6:6)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-function-expression-mutates-immutable-value.expect.md
+1
-1
@@ -18,7 +18,7 @@ function Component(props) {
18
## Error
19
20
```
21
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $39 (frozen) (5:5)
21
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (5:5)
22
```
23
24
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-mutate-after-aliased-freeze.expect.md
+1
-1
@@ -25,7 +25,7 @@ function Component(props) {
25
## Error
26
27
```
28
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $44:TObject<BuiltInArray> (frozen) (13:13)
28
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (13:13)
29
```
30
31
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-mutate-after-freeze.expect.md
+1
-1
@@ -19,7 +19,7 @@ function Component(props) {
19
## Error
20
21
```
22
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $26:TObject<BuiltInArray> (frozen) (7:7)
22
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (7:7)
23
```
24
25
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-pass-ref-to-function.expect.md
+1
-1
@@ -14,7 +14,7 @@ function Component(props) {
14
## Error
15
16
```
17
-[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at mutate? $21[6:8]:TObject<BuiltInUseRefId> (3:3)
17
+[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at mutate? $21[6:8]:TObject<BuiltInUseRefId> (3:3)
18
```
19
20
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-property-store-to-frozen-value.expect.md
+1
-1
@@ -16,7 +16,7 @@ function Component(props) {
16
## Error
17
18
```
19
-[ReactForget] InvalidReact: InferReferenceEffects: inferred mutation of known immutable value. Found mutation of $20 (frozen) (5:5)
19
+[ReactForget] InvalidReact: This mutates a variable after it was passed to React, which means that React cannot observe changes to it (5:5)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ref-in-callback-invoked-during-render.expect.md
+1
-1
@@ -18,7 +18,7 @@ function Component(props) {
18
## Error
19
20
```
21
-[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at capture $42[6:16]:TObject<BuiltInRefValue> (5:5)
21
+[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at capture $42[6:16]:TObject<BuiltInRefValue> (5:5)
22
```
23
24
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-ref-value-as-props.expect.md
+1
-1
@@ -13,7 +13,7 @@ function Component(props) {
13
## Error
14
15
```
16
-[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at freeze $19:TObject<BuiltInRefValue> (3:3)
16
+[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $19:TObject<BuiltInRefValue> (3:3)
17
```
18
19
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-set-and-read-ref-during-render.expect.md
+2
-2
@@ -14,9 +14,9 @@ function Component(props) {
14
## Error
15
16
```
17
-[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at store $21[7:9]:TObject<BuiltInUseRefId> (3:3)
17
+[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at store $21[7:9]:TObject<BuiltInUseRefId> (3:3)
18
19
-[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at freeze $24:TObject<BuiltInRefValue> (4:4)
19
+[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $24:TObject<BuiltInRefValue> (4:4)
20
```
21
22
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.invalid-use-ref-added-to-dep-without-type-info.expect.md
+1
-1
@@ -21,7 +21,7 @@ function Foo({ a }) {
21
## Error
22
23
```
24
-[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. Cannot access ref object at capture $29:TObject<BuiltInUseRefId> (4:4)
24
+[ReactForget] InvalidReact: Ref values may not be passed to functions because they could read the ref value (`current` property) during render. (https://react.dev/reference/react/useRef). Cannot access ref object at capture $29:TObject<BuiltInUseRefId> (4:4)
25
```
26
27
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.reassignment-to-global.expect.md
new
+22
@@ -0,0 +1,22 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component() {
6
+ // Cannot assign to globals
7
+ someUnknownGlobal = true;
8
+ moduleLocal = true;
9
+}
10
+
11
+```
12
+
13
+
14
+## Error
15
+
16
+```
17
+[ReactForget] InvalidReact: This reassigns a variable which was not defined inside of the component. Components should be pure and side-effect free. If this variable is used in rendering, use useState instead. (https://react.dev/learn/keeping-components-pure) (3:3)
18
+
19
+[ReactForget] InvalidReact: This reassigns a variable which was not defined inside of the component. Components should be pure and side-effect free. If this variable is used in rendering, use useState instead. (https://react.dev/learn/keeping-components-pure) (4:4)
20
+```
21
+
22
+
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.reassignment-to-global.js
new
+5
@@ -0,0 +1,5 @@
1
+function Component() {
2
+ // Cannot assign to globals
3
+ someUnknownGlobal = true;
4
+ moduleLocal = true;
5
+}
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.expect.md
-8
@@ -64,10 +64,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
64
}
65
}
66
67
- // Cannot assign to globals
68
- someUnknownGlobal = true;
69
- moduleLocal = true;
70
-
67
function component(a) {
68
// Add support for function declarations once we support `var` hoisting.
69
function t() {}
@@ -126,10 +122,6 @@ let moduleLocal = false;
122
[ReactForget] Todo: (BuildHIR::node.lowerReorderableExpression) Expression type 'CallExpression' cannot be safely reordered (55:55)
123
124
[ReactForget] Todo: (BuildHIR::node.lowerReorderableExpression) Expression type 'BinaryExpression' cannot be safely reordered (53:53)
129
-
130
-[ReactForget] InvalidReact: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. (64:64)
131
-
132
-[ReactForget] InvalidReact: (BuildHIR::lowerAssignment) Assigning to an identifier defined outside the function scope is not supported. (65:65)
125
```
126
127
\ No newline at end of file
compiler/forget/packages/babel-plugin-react-forget/src/__tests__/fixtures/compiler/error.todo-kitchensink.js
-4
@@ -60,10 +60,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
60
}
61
}
62
63
- // Cannot assign to globals
64
- someUnknownGlobal = true;
65
- moduleLocal = true;
66
-
63
function component(a) {
64
// Add support for function declarations once we support `var` hoisting.
65
function t() {}
compiler/forget/packages/eslint-plugin-react-forget/__tests__/ReactForgetDiagnostics-test.ts
+1
-1
@@ -72,7 +72,7 @@ const tests: ForgetTestCases = {
72
errors: [
73
{
74
message:
75
- "[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. Cannot access ref value at freeze $22:TObject<BuiltInRefValue> (6:6)",
75
+ "[ReactForget] InvalidReact: Ref values (the `current` property) may not be accessed during render. (https://react.dev/reference/react/useRef). Cannot access ref value at freeze $22:TObject<BuiltInRefValue> (6:6)",
76
line: 6,
77
column: 10,
78
endColumn: 15,