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