@samitouri / QOS-React-2 / commits / 9507493ee9

Allow accessing properties of globals in switch test values

We limit the types of expressions allowed as switch case test values because we our HIR doesn't yet preserve order-of-evaluation for switch test values (we model them as being evaluated prior to entering the switch, as opposed to lazily, when the case is reached). One common pattern internally is test case values that are properties of a global, eg you have some bag of enum values and are comparing against that: ```javascript // at module scope, or imported from another module: const OPTIONS = {FOO: 'foo'}; // in a component switch (value) { case OPTIONS.FOO: { ... } } ``` This PR allows this specific case, ie member expressions where the innermost object is a global identifier.

Joe Savona committed Mar 22, 2023 at 14:36 UTC 9507493ee941ebd21641a845099160b9a9764e00
3 files changed +68
compiler/forget/src/HIR/BuildHIR.ts
+25
@@ -524,6 +524,31 @@ function lowerStatement(
524 // ok
525 break;
526 }
527 + case "MemberExpression": {
528 + // A common pattern is switch statements where the case test values are properties of a global,
529 + // eg `case ProductOptions.Option: { ... }`
530 + // We therefore allow expressions where the innermost object is a global identifier, and reject
531 + // all other member expressions (for now).
532 + const test = testExpr as NodePath<t.MemberExpression>;
533 + let innerObject: NodePath<t.Expression> = test;
534 + while (innerObject.isMemberExpression()) {
535 + innerObject = innerObject.get("object");
536 + }
537 + if (
538 + innerObject.isIdentifier() &&
539 + builder.resolveIdentifier(innerObject) === null // null means global
540 + ) {
541 + // This is a property/computed load from a global, that's safe to evaluate as a test expression
542 + break;
543 + }
544 + builder.errors.push({
545 + reason:
546 + "(BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported",
547 + severity: ErrorSeverity.Todo,
548 + nodePath: testExpr,
549 + });
550 + break;
551 + }
552 default: {
553 builder.errors.push({
554 reason:
compiler/forget/src/__tests__/fixtures/compiler/switch-global-propertyload-case-test.expect.md new
+33
@@ -0,0 +1,33 @@
1 +
2 +## Input
3 +
4 +```javascript
5 +function Component(props) {
6 + switch (props.value) {
7 + case Global.Property: {
8 + return true;
9 + }
10 + default: {
11 + return false;
12 + }
13 + }
14 +}
15 +
16 +```
17 +
18 +## Code
19 +
20 +```javascript
21 +function Component(props) {
22 + switch (props.value) {
23 + case Global.Property: {
24 + return true;
25 + }
26 + default: {
27 + return false;
28 + }
29 + }
30 +}
31 +
32 +```
33 +
\ No newline at end of file
compiler/forget/src/__tests__/fixtures/compiler/switch-global-propertyload-case-test.js new
+10
@@ -0,0 +1,10 @@
1 +function Component(props) {
2 + switch (props.value) {
3 + case Global.Property: {
4 + return true;
5 + }
6 + default: {
7 + return false;
8 + }
9 + }
10 +}