Fix update assignment on computed memberexpression
Joe Savona committed
Feb 10, 2023 at 08:59 UTC
6877ac6500d7d000ee63e8158cf4ad8e5ef24d3d
5 files changed
+226
-203
compiler/forget/src/HIR/BuildHIR.ts
+21
-31
@@ -1147,25 +1147,11 @@ function lowerExpression(
1147
case "MemberExpression": {
1148
// a.b.c += <right>
1149
const leftExpr = left as NodePath<t.MemberExpression>;
1150
- // Lower everything up to the final property to a temporary, eg `a.b`
1151
- const object = lowerExpressionToPlace(
1150
+ const { object, property, value } = lowerMemberExpression(
1151
builder,
1153
- leftExpr.get("object")
1152
+ leftExpr
1153
);
1155
- // Extract the final property to be read from and re-assigned, eg 'c'
1156
- const property = leftExpr.get("property");
1157
- if (!property.isIdentifier()) {
1158
- builder.errors.push({
1159
- reason: `(BuildHIR::lowerExpression) Handle ${property.type} properties in MemberExpression`,
1160
- severity: ErrorSeverity.Todo,
1161
- nodePath: property,
1162
- });
1163
- return {
1164
- kind: "UnsupportedNode",
1165
- node: leftExpr.node,
1166
- loc: leftExpr.node.loc ?? GeneratedSource,
1167
- };
1168
- }
1154
+
1155
// Store the previous value to a temporary
1156
const previousValuePlace: Place = buildTemporaryPlace(
1157
builder,
@@ -1177,13 +1163,7 @@ function lowerExpression(
1163
place: { ...previousValuePlace },
1164
kind: InstructionKind.Const,
1165
},
1180
- value: {
1181
- kind: "PropertyLoad",
1182
- object: { ...object },
1183
- property: property.node.name,
1184
- loc: leftExpr.node.loc ?? GeneratedSource,
1185
- optional: false, // LVal cannot be optional
1186
- },
1166
+ value,
1167
loc: leftExpr.node.loc ?? GeneratedSource,
1168
});
1169
// Store the new value to a temporary
@@ -1205,13 +1185,23 @@ function lowerExpression(
1185
});
1186
1187
// Save the result back to the property
1208
- return {
1209
- kind: "PropertyStore",
1210
- object: { ...object },
1211
- property: property.node.name,
1212
- value: { ...newValuePlace },
1213
- loc: leftExpr.node.loc ?? GeneratedSource,
1214
- };
1188
+ if (typeof property === "string") {
1189
+ return {
1190
+ kind: "PropertyStore",
1191
+ object: { ...object },
1192
+ property,
1193
+ value: { ...newValuePlace },
1194
+ loc: leftExpr.node.loc ?? GeneratedSource,
1195
+ };
1196
+ } else {
1197
+ return {
1198
+ kind: "ComputedStore",
1199
+ object: { ...object },
1200
+ property: { ...property },
1201
+ value: { ...newValuePlace },
1202
+ loc: leftExpr.node.loc ?? GeneratedSource,
1203
+ };
1204
+ }
1205
}
1206
default: {
1207
builder.errors.push({
compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.expect.md
new
+37
@@ -0,0 +1,37 @@
1
+
2
+## Input
3
+
4
+```javascript
5
+function Component(props) {
6
+ const x = [props.x];
7
+ const index = 0;
8
+ x[index] *= 2;
9
+ x["0"] += 3;
10
+ return x;
11
+}
12
+
13
+```
14
+
15
+## Code
16
+
17
+```javascript
18
+function Component(props) {
19
+ const $ = React.unstable_useMemoCache();
20
+ const c_0 = $[0] !== props.x;
21
+ let x;
22
+ if (c_0) {
23
+ x = [props.x];
24
+ const index = 0;
25
+ x[index] = x[index] * 2;
26
+ const t0 = "0";
27
+ x[t0] = x[t0] + 3;
28
+ $[0] = props.x;
29
+ $[1] = x;
30
+ } else {
31
+ x = $[1];
32
+ }
33
+ return x;
34
+}
35
+
36
+```
37
+
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/assignment-expression-computed.js
new
+7
@@ -0,0 +1,7 @@
1
+function Component(props) {
2
+ const x = [props.x];
3
+ const index = 0;
4
+ x[index] *= 2;
5
+ x["0"] += 3;
6
+ return x;
7
+}
compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.expect.md
+161
-171
@@ -17,7 +17,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
17
const h = [...b];
18
new c(...args);
19
c(...args);
20
- g["e"] += 1;
20
const [y, ...yy] = useState(0);
21
const { z, aa = "aa", ...zz } = useCustom();
22
@@ -148,7 +147,7 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
147
| ^^^^
148
14 | new c(...args);
149
15 | c(...args);
151
- 16 | g["e"] += 1;
150
+ 16 | const [y, ...yy] = useState(0);
151
152
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in NewExpression
153
12 | const g = { ...a, b() {}, c: () => {} };
@@ -156,260 +155,251 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
155
> 14 | new c(...args);
156
| ^^^^^^^
157
15 | c(...args);
159
- 16 | g["e"] += 1;
160
- 17 | const [y, ...yy] = useState(0);
158
+ 16 | const [y, ...yy] = useState(0);
159
+ 17 | const { z, aa = "aa", ...zz } = useCustom();
160
161
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle SpreadElement arguments in CallExpression
162
13 | const h = [...b];
163
14 | new c(...args);
164
> 15 | c(...args);
165
| ^^^^^^^
167
- 16 | g["e"] += 1;
168
- 17 | const [y, ...yy] = useState(0);
169
- 18 | const { z, aa = "aa", ...zz } = useCustom();
170
-
171
-[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle StringLiteral properties in MemberExpression
172
- 14 | new c(...args);
173
- 15 | c(...args);
174
-> 16 | g["e"] += 1;
175
- | ^^^
176
- 17 | const [y, ...yy] = useState(0);
177
- 18 | const { z, aa = "aa", ...zz } = useCustom();
178
- 19 |
166
+ 16 | const [y, ...yy] = useState(0);
167
+ 17 | const { z, aa = "aa", ...zz } = useCustom();
168
+ 18 |
169
170
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement in ArrayPattern
171
+ 14 | new c(...args);
172
15 | c(...args);
182
- 16 | g["e"] += 1;
183
-> 17 | const [y, ...yy] = useState(0);
173
+> 16 | const [y, ...yy] = useState(0);
174
| ^^^^^
185
- 18 | const { z, aa = "aa", ...zz } = useCustom();
186
- 19 |
187
- 20 | <Button {...args}></Button>;
175
+ 17 | const { z, aa = "aa", ...zz } = useCustom();
176
+ 18 |
177
+ 19 | <Button {...args}></Button>;
178
179
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle AssignmentPattern assignments
190
- 16 | g["e"] += 1;
191
- 17 | const [y, ...yy] = useState(0);
192
-> 18 | const { z, aa = "aa", ...zz } = useCustom();
180
+ 15 | c(...args);
181
+ 16 | const [y, ...yy] = useState(0);
182
+> 17 | const { z, aa = "aa", ...zz } = useCustom();
183
| ^^^^^^^^^
194
- 19 |
195
- 20 | <Button {...args}></Button>;
196
- 21 | <Button xlink:href="localhost:3000"></Button>;
184
+ 18 |
185
+ 19 | <Button {...args}></Button>;
186
+ 20 | <Button xlink:href="localhost:3000"></Button>;
187
188
[ReactForget] TodoError: (BuildHIR::lowerAssignment) Handle RestElement properties in ObjectPattern
199
- 16 | g["e"] += 1;
200
- 17 | const [y, ...yy] = useState(0);
201
-> 18 | const { z, aa = "aa", ...zz } = useCustom();
189
+ 15 | c(...args);
190
+ 16 | const [y, ...yy] = useState(0);
191
+> 17 | const { z, aa = "aa", ...zz } = useCustom();
192
| ^^^^^
203
- 19 |
204
- 20 | <Button {...args}></Button>;
205
- 21 | <Button xlink:href="localhost:3000"></Button>;
193
+ 18 |
194
+ 19 | <Button {...args}></Button>;
195
+ 20 | <Button xlink:href="localhost:3000"></Button>;
196
197
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle JSXNamespacedName attribute names in JSXElement
208
- 19 |
209
- 20 | <Button {...args}></Button>;
210
-> 21 | <Button xlink:href="localhost:3000"></Button>;
198
+ 18 |
199
+ 19 | <Button {...args}></Button>;
200
+> 20 | <Button xlink:href="localhost:3000"></Button>;
201
| ^^^^^^^^^^
212
- 22 | <Button haha={1}></Button>;
213
- 23 | <Button>{/** empty */}</Button>;
214
- 24 | <DesignSystem.Button />;
202
+ 21 | <Button haha={1}></Button>;
203
+ 22 | <Button>{/** empty */}</Button>;
204
+ 23 | <DesignSystem.Button />;
205
206
[ReactForget] TodoError: (BuildHIR::lowerJsxElement) Handle JSXEmptyExpression expressions
217
- 21 | <Button xlink:href="localhost:3000"></Button>;
218
- 22 | <Button haha={1}></Button>;
219
-> 23 | <Button>{/** empty */}</Button>;
207
+ 20 | <Button xlink:href="localhost:3000"></Button>;
208
+ 21 | <Button haha={1}></Button>;
209
+> 22 | <Button>{/** empty */}</Button>;
210
| ^^^^^^^^^^^^
221
- 24 | <DesignSystem.Button />;
222
- 25 |
223
- 26 | const j = function bar([quz, qux], ...args) {};
211
+ 23 | <DesignSystem.Button />;
212
+ 24 |
213
+ 25 | const j = function bar([quz, qux], ...args) {};
214
215
[ReactForget] TodoError: (BuildHIR::lowerJsxElementName) Handle JSXMemberExpression tags
226
- 22 | <Button haha={1}></Button>;
227
- 23 | <Button>{/** empty */}</Button>;
228
-> 24 | <DesignSystem.Button />;
216
+ 21 | <Button haha={1}></Button>;
217
+ 22 | <Button>{/** empty */}</Button>;
218
+> 23 | <DesignSystem.Button />;
219
| ^^^^^^^^^^^^^^^^^^^
230
- 25 |
231
- 26 | const j = function bar([quz, qux], ...args) {};
232
- 27 |
220
+ 24 |
221
+ 25 | const j = function bar([quz, qux], ...args) {};
222
+ 26 |
223
224
[ReactForget] TodoError: (BuildHIR::lower) Handle ArrayPattern params
235
- 24 | <DesignSystem.Button />;
236
- 25 |
237
-> 26 | const j = function bar([quz, qux], ...args) {};
225
+ 23 | <DesignSystem.Button />;
226
+ 24 |
227
+> 25 | const j = function bar([quz, qux], ...args) {};
228
| ^^^^^^^^^^
239
- 27 |
240
- 28 | for (; i < 3; i += 1) {
241
- 29 | x.push(i);
229
+ 26 |
230
+ 27 | for (; i < 3; i += 1) {
231
+ 28 | x.push(i);
232
233
[ReactForget] TodoError: (BuildHIR::lower) Handle RestElement params
244
- 24 | <DesignSystem.Button />;
245
- 25 |
246
-> 26 | const j = function bar([quz, qux], ...args) {};
234
+ 23 | <DesignSystem.Button />;
235
+ 24 |
236
+> 25 | const j = function bar([quz, qux], ...args) {};
237
| ^^^^^^^
248
- 27 |
249
- 28 | for (; i < 3; i += 1) {
250
- 29 | x.push(i);
238
+ 26 |
239
+ 27 | for (; i < 3; i += 1) {
240
+ 28 | x.push(i);
241
242
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
253
- 26 | const j = function bar([quz, qux], ...args) {};
254
- 27 |
255
-> 28 | for (; i < 3; i += 1) {
243
+ 25 | const j = function bar([quz, qux], ...args) {};
244
+ 26 |
245
+> 27 | for (; i < 3; i += 1) {
246
| ^
257
- 29 | x.push(i);
258
- 30 | }
259
- 31 | for (; i < 3; ) {
247
+ 28 | x.push(i);
248
+ 29 | }
249
+ 30 | for (; i < 3; ) {
250
251
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
262
- 29 | x.push(i);
263
- 30 | }
264
-> 31 | for (; i < 3; ) {
252
+ 28 | x.push(i);
253
+ 29 | }
254
+> 30 | for (; i < 3; ) {
255
| ^
266
- 32 | break;
267
- 33 | }
268
- 34 | for (;;) {
256
+ 31 | break;
257
+ 32 | }
258
+ 33 | for (;;) {
259
260
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
271
- 29 | x.push(i);
272
- 30 | }
273
-> 31 | for (; i < 3; ) {
261
+ 28 | x.push(i);
262
+ 29 | }
263
+> 30 | for (; i < 3; ) {
264
| ^
275
- 32 | break;
276
- 33 | }
277
- 34 | for (;;) {
265
+ 31 | break;
266
+ 32 | }
267
+ 33 | for (;;) {
268
269
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle non-variable initialization in ForStatement
280
- 32 | break;
281
- 33 | }
282
-> 34 | for (;;) {
270
+ 31 | break;
271
+ 32 | }
272
+> 33 | for (;;) {
273
| ^
284
- 35 | break;
285
- 36 | }
286
- 37 |
274
+ 34 | break;
275
+ 35 | }
276
+ 36 |
277
278
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty update in ForStatement
289
- 32 | break;
290
- 33 | }
291
-> 34 | for (;;) {
279
+ 31 | break;
280
+ 32 | }
281
+> 33 | for (;;) {
282
| ^
293
- 35 | break;
294
- 36 | }
295
- 37 |
283
+ 34 | break;
284
+ 35 | }
285
+ 36 |
286
287
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle empty test in ForStatement
298
- 32 | break;
299
- 33 | }
300
-> 34 | for (;;) {
288
+ 31 | break;
289
+ 32 | }
290
+> 33 | for (;;) {
291
| ^
302
- 35 | break;
303
- 36 | }
304
- 37 |
292
+ 34 | break;
293
+ 35 | }
294
+ 36 |
295
296
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template with interpolations
307
- 36 | }
308
- 37 |
309
-> 38 | graphql`
297
+ 35 | }
298
+ 36 |
299
+> 37 | graphql`
300
| ^
311
- 39 | ${g}
312
- 40 | `;
313
- 41 |
301
+ 38 | ${g}
302
+ 39 | `;
303
+ 40 |
304
305
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle tagged template where cooked value is different from raw value
316
- 40 | `;
317
- 41 |
318
-> 42 | graphql`\\t\n`;
306
+ 39 | `;
307
+ 40 |
308
+> 41 | graphql`\\t\n`;
309
| ^^^^^^^^^^^^^^
320
- 43 |
321
- 44 | for (const c of [1, 2]) {
322
- 45 | }
310
+ 42 |
311
+ 43 | for (const c of [1, 2]) {
312
+ 44 | }
313
314
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForOfStatement statements
325
- 42 | graphql`\\t\n`;
326
- 43 |
327
-> 44 | for (const c of [1, 2]) {
315
+ 41 | graphql`\\t\n`;
316
+ 42 |
317
+> 43 | for (const c of [1, 2]) {
318
| ^
329
- 45 | }
330
- 46 |
331
- 47 | for (let x in { a: 1 }) {
319
+ 44 | }
320
+ 45 |
321
+ 46 | for (let x in { a: 1 }) {
322
323
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle ForInStatement statements
334
- 45 | }
335
- 46 |
336
-> 47 | for (let x in { a: 1 }) {
324
+ 44 | }
325
+ 45 |
326
+> 46 | for (let x in { a: 1 }) {
327
| ^
338
- 48 | }
339
- 49 |
340
- 50 | do {} while (i < 3);
328
+ 47 | }
329
+ 48 |
330
+ 49 | do {} while (i < 3);
331
332
[ReactForget] TodoError: (BuildHIR::lowerStatement) Handle DoWhileStatement statements
343
- 48 | }
344
- 49 |
345
-> 50 | do {} while (i < 3);
333
+ 47 | }
334
+ 48 |
335
+> 49 | do {} while (i < 3);
336
| ^^^^^^^^^^^^^^^^^^^^
347
- 51 |
348
- 52 | let updateIdentifier = 0;
349
- 53 | --updateIdentifier;
337
+ 50 |
338
+ 51 | let updateIdentifier = 0;
339
+ 52 | --updateIdentifier;
340
341
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
352
- 51 |
353
- 52 | let updateIdentifier = 0;
354
-> 53 | --updateIdentifier;
342
+ 50 |
343
+ 51 | let updateIdentifier = 0;
344
+> 52 | --updateIdentifier;
345
| ^^^^^^^^^^^^^^^^^^
356
- 54 | ++updateIdentifier;
357
- 55 | updateIdentifier.y++;
358
- 56 | updateIdentifier.y--;
346
+ 53 | ++updateIdentifier;
347
+ 54 | updateIdentifier.y++;
348
+ 55 | updateIdentifier.y--;
349
350
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle prefix UpdateExpression
361
- 52 | let updateIdentifier = 0;
362
- 53 | --updateIdentifier;
363
-> 54 | ++updateIdentifier;
351
+ 51 | let updateIdentifier = 0;
352
+ 52 | --updateIdentifier;
353
+> 53 | ++updateIdentifier;
354
| ^^^^^^^^^^^^^^^^^^
365
- 55 | updateIdentifier.y++;
366
- 56 | updateIdentifier.y--;
367
- 57 |
355
+ 54 | updateIdentifier.y++;
356
+ 55 | updateIdentifier.y--;
357
+ 56 |
358
359
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
370
- 53 | --updateIdentifier;
371
- 54 | ++updateIdentifier;
372
-> 55 | updateIdentifier.y++;
360
+ 52 | --updateIdentifier;
361
+ 53 | ++updateIdentifier;
362
+> 54 | updateIdentifier.y++;
363
| ^^^^^^^^^^^^^^^^^^^^
374
- 56 | updateIdentifier.y--;
375
- 57 |
376
- 58 | switch (i) {
364
+ 55 | updateIdentifier.y--;
365
+ 56 |
366
+ 57 | switch (i) {
367
368
[ReactForget] TodoError: (BuildHIR::lowerExpression) Handle UpdateExpression with MemberExpression argument
379
- 54 | ++updateIdentifier;
380
- 55 | updateIdentifier.y++;
381
-> 56 | updateIdentifier.y--;
369
+ 53 | ++updateIdentifier;
370
+ 54 | updateIdentifier.y++;
371
+> 55 | updateIdentifier.y--;
372
| ^^^^^^^^^^^^^^^^^^^^
383
- 57 |
384
- 58 | switch (i) {
385
- 59 | case 1 + 1: {
373
+ 56 |
374
+ 57 | switch (i) {
375
+ 58 | case 1 + 1: {
376
377
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
388
- 61 | case foo(): {
389
- 62 | }
390
-> 63 | case x.y: {
378
+ 60 | case foo(): {
379
+ 61 | }
380
+> 62 | case x.y: {
381
| ^^^
392
- 64 | }
393
- 65 | default: {
394
- 66 | }
382
+ 63 | }
383
+ 64 | default: {
384
+ 65 | }
385
386
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
397
- 59 | case 1 + 1: {
398
- 60 | }
399
-> 61 | case foo(): {
387
+ 58 | case 1 + 1: {
388
+ 59 | }
389
+> 60 | case foo(): {
390
| ^^^^^
401
- 62 | }
402
- 63 | case x.y: {
403
- 64 | }
391
+ 61 | }
392
+ 62 | case x.y: {
393
+ 63 | }
394
395
[ReactForget] TodoError: (BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported
406
- 57 |
407
- 58 | switch (i) {
408
-> 59 | case 1 + 1: {
396
+ 56 |
397
+ 57 | switch (i) {
398
+> 58 | case 1 + 1: {
399
| ^^^^^
410
- 60 | }
411
- 61 | case foo(): {
412
- 62 | }
400
+ 59 | }
401
+ 60 | case foo(): {
402
+ 61 | }
403
```
404
405
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/hir/error.todo-kitchensink.js
-1
@@ -13,7 +13,6 @@ function foo([a, b], { c, d, e = "e" }, f = "f", ...args) {
13
const h = [...b];
14
new c(...args);
15
c(...args);
16
- g["e"] += 1;
16
const [y, ...yy] = useState(0);
17
const { z, aa = "aa", ...zz } = useCustom();
18