From 091221b3263fbc8439bcf220dacff346fd0bf469 Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Tue, 18 Jul 2023 07:28:45 -0400 Subject: [PATCH] Support passing enums as their literal values (#849) --- .../validation/ScopeValidator.spec.ts | 62 +++++++++++++++++++ src/parser/Statement.ts | 15 ++++- src/types/EnumType.ts | 13 +++- src/types/StringType.ts | 8 ++- 4 files changed, 91 insertions(+), 7 deletions(-) diff --git a/src/bscPlugin/validation/ScopeValidator.spec.ts b/src/bscPlugin/validation/ScopeValidator.spec.ts index 777693978..67ad1e97a 100644 --- a/src/bscPlugin/validation/ScopeValidator.spec.ts +++ b/src/bscPlugin/validation/ScopeValidator.spec.ts @@ -128,6 +128,68 @@ describe('ScopeValidator', () => { }); describe('argumentTypeMismatch', () => { + + it('treats string enums as strings when assigned to string vars', () => { + program.setFile('source/file.bs', ` + sub main() + printDirection(Direction.up) + end sub + + sub printDirection(theDirection as string) + print theDirection + end sub + + enum Direction + up = "up" + down = "down" + end enum + `); + program.validate(); + expectZeroDiagnostics(program); + }); + + it('does not treat strings as a string enum', () => { + program.setFile('source/file.bs', ` + sub main() + printDirection("up") + end sub + + sub printDirection(theDirection as Direction) + print theDirection + end sub + + enum Direction + up = "up" + down = "down" + end enum + + `); + program.validate(); + expectDiagnostics(program, [ + DiagnosticMessages.argumentTypeMismatch('string', 'Direction') + ]); + }); + + it('supports passing enum type as enum type', () => { + program.setFile('source/file.bs', ` + sub test(theDirection as Direction) + printDirection(theDirection) + end sub + + sub printDirection(theDirection as Direction) + print theDirection + end sub + + enum Direction + up = "up" + down = "down" + end enum + `); + program.validate(); + expectDiagnostics(program, [ + ]); + }); + it('Catches argument type mismatches on function calls', () => { program.setFile('source/file.brs', ` sub a(age as integer) diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index 48fc2d28b..bb89a3060 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -2640,9 +2640,14 @@ export class EnumStatement extends Statement implements TypedefProvider { } getType(options: GetTypeOptions) { - const resultType = new EnumType(this.fullName); + const members = this.getMembers(); + + const resultType = new EnumType( + this.fullName, + members[0]?.getType(options) + ); resultType.pushMemberProvider(() => this.getSymbolTable()); - for (const statement of this.getMembers()) { + for (const statement of members) { resultType.addMember(statement?.tokens?.name?.text, statement?.range, statement.getType(options), SymbolTypeFlag.runtime); } @@ -2704,7 +2709,11 @@ export class EnumMemberStatement extends Statement implements TypedefProvider { } getType(options: GetTypeOptions) { - return new EnumMemberType((this.parent as EnumStatement)?.fullName, this.tokens?.name?.text); + return new EnumMemberType( + (this.parent as EnumStatement)?.fullName, + this.tokens?.name?.text, + this.value?.getType(options) + ); } } diff --git a/src/types/EnumType.ts b/src/types/EnumType.ts index 20e4bcad6..2f8657180 100644 --- a/src/types/EnumType.ts +++ b/src/types/EnumType.ts @@ -1,10 +1,15 @@ import { isDynamicType, isEnumMemberType, isEnumType } from '../astUtils/reflection'; import { BscType } from './BscType'; import { BscTypeKind } from './BscTypeKind'; +import { DynamicType } from './DynamicType'; export class EnumType extends BscType { constructor( - public name: string + public name: string, + /** + * The runtime type for this enum (i.e. what type the value will be transpiled into) + */ + public underlyingType: BscType = DynamicType.instance ) { super(name); } @@ -36,7 +41,11 @@ export class EnumType extends BscType { export class EnumMemberType extends BscType { constructor( public enumName: string, - public memberName: string + public memberName: string, + /** + * The runtime type for this enum (i.e. what type the value will be transpiled into) + */ + public underlyingType: BscType = DynamicType.instance ) { super(`${enumName}.${memberName}`); } diff --git a/src/types/StringType.ts b/src/types/StringType.ts index d15eb94cc..1b6f843b3 100644 --- a/src/types/StringType.ts +++ b/src/types/StringType.ts @@ -1,4 +1,4 @@ -import { isDynamicType, isStringType } from '../astUtils/reflection'; +import { isDynamicType, isEnumMemberType, isEnumType, isStringType } from '../astUtils/reflection'; import { BscType } from './BscType'; import { BscTypeKind } from './BscTypeKind'; @@ -19,7 +19,11 @@ export class StringType extends BscType { public isTypeCompatible(targetType: BscType) { return ( isStringType(targetType) || - isDynamicType(targetType) + isDynamicType(targetType) || + //string enums are compatible with strings + ( + (isEnumType(targetType) || isEnumMemberType(targetType)) && isStringType(targetType.underlyingType) + ) ); }