Repository: rokucommunity/brighterscript (ts)

Issue:
Enum does not support negative integers
This should be valid, but currently isn't.

![image](https://user-images.githubusercontent.com/2544493/193129912-9831507a-6be9-43a3-a303-eebfe7200ec4.png)


Pull request:
Fix enum error for negative values
Fixes #702 

Patch:
diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts
index 1be5816d..68b0389c 100644
--- a/src/bscPlugin/validation/BrsFileValidator.ts
+++ b/src/bscPlugin/validation/BrsFileValidator.ts
@@ -1,10 +1,10 @@
-import { isClassStatement, isCommentStatement, isConstStatement, isDottedGetExpression, isEnumStatement, isForEachStatement, isForStatement, isFunctionStatement, isImportStatement, isInterfaceStatement, isLibraryStatement, isLiteralExpression, isNamespacedVariableNameExpression, isNamespaceStatement, isWhileStatement } from '../../astUtils/reflection';
+import { isClassStatement, isCommentStatement, isConstStatement, isDottedGetExpression, isEnumStatement, isForEachStatement, isForStatement, isFunctionStatement, isImportStatement, isInterfaceStatement, isLibraryStatement, isLiteralExpression, isNamespacedVariableNameExpression, isNamespaceStatement, isUnaryExpression, isWhileStatement } from '../../astUtils/reflection';
 import { createVisitor, WalkMode } from '../../astUtils/visitors';
 import { DiagnosticMessages } from '../../DiagnosticMessages';
 import type { BrsFile } from '../../files/BrsFile';
 import type { OnFileValidateEvent } from '../../interfaces';
 import { TokenKind } from '../../lexer/TokenKind';
-import type { AstNode } from '../../parser/AstNode';
+import type { AstNode, Expression } from '../../parser/AstNode';
 import type { LiteralExpression } from '../../parser/Expression';
 import { ParseMode } from '../../parser/Parser';
 import type { ContinueStatement, EnumMemberStatement, EnumStatement, ForEachStatement, ForStatement, ImportStatement, LibraryStatement, WhileStatement } from '../../parser/Statement';
@@ -187,19 +187,27 @@ export class BrsFileValidator {
     }
 
     private validateEnumValueTypes(member: EnumMemberStatement, enumValueKind: TokenKind) {
-        const memberValueKind = (member.value as LiteralExpression)?.token?.kind;
-
+        let memberValueKind: TokenKind;
+        let memberValue: Expression;
+        if (isUnaryExpression(member.value)) {
+            memberValueKind = (member.value?.right as LiteralExpression)?.token?.kind;
+            memberValue = member.value?.right;
+        } else {
+            memberValueKind = (member.value as LiteralExpression)?.token?.kind;
+            memberValue = member.value;
+        }
+        const range = (memberValue ?? member)?.range;
         if (
             //is integer enum, has value, that value type is not integer
             (enumValueKind === TokenKind.IntegerLiteral && memberValueKind && memberValueKind !== enumValueKind) ||
             //has value, that value is not a literal
-            (member.value && !isLiteralExpression(member.value))
+            (memberValue && !isLiteralExpression(memberValue))
         ) {
             this.event.file.addDiagnostic({
                 ...DiagnosticMessages.enumValueMustBeType(
                     enumValueKind.replace(/literal$/i, '').toLowerCase()
                 ),
-                range: (member.value ?? member)?.range
+                range: range
             });
         }
 
@@ -213,7 +221,7 @@ export class BrsFileValidator {
                         ...DiagnosticMessages.enumValueMustBeType(
                             enumValueKind.replace(/literal$/i, '').toLowerCase()
                         ),
-                        range: (member.value ?? member)?.range
+                        range: range
                     });
                 }
 
@@ -224,7 +232,7 @@ export class BrsFileValidator {
                     ...DiagnosticMessages.enumValueIsRequired(
                         enumValueKind.replace(/literal$/i, '').toLowerCase()
                     ),
-                    range: (member.value ?? member)?.range
+                    range: range
                 });
             }
         }
diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts
index 7ad82b9a..f7c73447 100644
--- a/src/parser/Statement.ts
+++ b/src/parser/Statement.ts
@@ -10,7 +10,7 @@ import type { BrsTranspileState } from './BrsTranspileState';
 import { ParseMode } from './Parser';
 import type { WalkVisitor, WalkOptions } from '../astUtils/visitors';
 import { InternalWalkMode, walk, createVisitor, WalkMode, walkArray } from '../astUtils/visitors';
-import { isCallExpression, isCommentStatement, isEnumMemberStatement, isExpression, isExpressionStatement, isFieldStatement, isFunctionStatement, isIfStatement, isInterfaceFieldStatement, isInterfaceMethodStatement, isInvalidType, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypedefProvider, isVoidType } from '../astUtils/reflection';
+import { isCallExpression, isCommentStatement, isEnumMemberStatement, isExpression, isExpressionStatement, isFieldStatement, isFunctionStatement, isIfStatement, isInterfaceFieldStatement, isInterfaceMethodStatement, isInvalidType, isLiteralExpression, isMethodStatement, isNamespaceStatement, isTypedefProvider, isUnaryExpression, isVoidType } from '../astUtils/reflection';
 import type { TranspileResult, TypedefProvider } from '../interfaces';
 import { createInvalidLiteral, createMethodStatement, createToken, interpolatedRange } from '../astUtils/creators';
 import { DynamicType } from '../types/DynamicType';
@@ -2312,6 +2312,10 @@ export class EnumStatement extends Statement implements TypedefProvider {
                 }
                 result.set(member.name?.toLowerCase(), member.value.token.text);
 
+                //simple unary expressions (like `-1`)
+            } else if (isUnaryExpression(member.value) && isLiteralExpression(member.value.right)) {
+                result.set(member.name?.toLowerCase(), member.value.operator.text + member.value.right.token.text);
+
                 //all other values
             } else {
                 result.set(member.name?.toLowerCase(), (member.value as LiteralExpression)?.token?.text ?? 'invalid');


Test patch:
diff --git a/src/parser/tests/statement/Enum.spec.ts b/src/parser/tests/statement/Enum.spec.ts
index c94a0f2e..f02a88c3 100644
--- a/src/parser/tests/statement/Enum.spec.ts
+++ b/src/parser/tests/statement/Enum.spec.ts
@@ -362,6 +362,17 @@ describe('EnumStatement', () => {
             }]);
         });
 
+        it('considers -1 to be an integer', () => {
+            program.setFile('source/main.bs', `
+                enum AppConfig
+                    alpha = 1
+                    beta = -1
+                end enum
+            `);
+            program.validate();
+            expectZeroDiagnostics(program);
+        });
+
         it('flags missing value for string enum where string is not first item', () => {
             program.setFile('source/main.bs', `
                 enum Direction
@@ -499,6 +510,21 @@ describe('EnumStatement', () => {
     });
 
     describe('transpile', () => {
+        it('transpiles negative number', () => {
+            testTranspile(`
+                sub main()
+                    print Direction.up
+                end sub
+                enum Direction
+                    up = -1
+                end enum
+            `, `
+                sub main()
+                    print -1
+                end sub
+            `, undefined, undefined, false);
+        });
+
         it('includes original value when no value could be computed', () => {
             testTranspile(`
                 sub main()