@samitouri / QOS-React-2 / commits / 6877ac6500

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