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.
This commit is contained in:
Joe Savona
2023-03-22 14:36:50 -07:00
parent 7d43730319
commit 9507493ee9
3 changed files with 68 additions and 0 deletions
+25
View File
@@ -524,6 +524,31 @@ function lowerStatement(
// ok
break;
}
case "MemberExpression": {
// A common pattern is switch statements where the case test values are properties of a global,
// eg `case ProductOptions.Option: { ... }`
// We therefore allow expressions where the innermost object is a global identifier, and reject
// all other member expressions (for now).
const test = testExpr as NodePath<t.MemberExpression>;
let innerObject: NodePath<t.Expression> = test;
while (innerObject.isMemberExpression()) {
innerObject = innerObject.get("object");
}
if (
innerObject.isIdentifier() &&
builder.resolveIdentifier(innerObject) === null // null means global
) {
// This is a property/computed load from a global, that's safe to evaluate as a test expression
break;
}
builder.errors.push({
reason:
"(BuildHIR::lowerStatement) Switch case test values must be identifiers or primitives, compound values are not yet supported",
severity: ErrorSeverity.Todo,
nodePath: testExpr,
});
break;
}
default: {
builder.errors.push({
reason:
@@ -0,0 +1,33 @@
## Input
```javascript
function Component(props) {
switch (props.value) {
case Global.Property: {
return true;
}
default: {
return false;
}
}
}
```
## Code
```javascript
function Component(props) {
switch (props.value) {
case Global.Property: {
return true;
}
default: {
return false;
}
}
}
```
@@ -0,0 +1,10 @@
function Component(props) {
switch (props.value) {
case Global.Property: {
return true;
}
default: {
return false;
}
}
}