From c1e13d5b9947727986b23ff46e3ded58de7f902f Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Wed, 9 Mar 2022 11:41:17 -0500 Subject: [PATCH] Fix typedef auto-generated class constructor funcs --- src/astUtils/creators.ts | 4 ++-- src/files/BrsFile.spec.ts | 33 ++++++++++++++++++++++++++++++++- src/parser/Statement.ts | 24 ++++++++++++++++-------- 3 files changed, 50 insertions(+), 11 deletions(-) diff --git a/src/astUtils/creators.ts b/src/astUtils/creators.ts index a1a949cfd..e8db875a8 100644 --- a/src/astUtils/creators.ts +++ b/src/astUtils/creators.ts @@ -148,9 +148,9 @@ export function createFunctionExpression(kind: TokenKind.Sub | TokenKind.Functio ); } -export function createClassMethodStatement(name: string, kind: TokenKind.Sub | TokenKind.Function = TokenKind.Function) { +export function createClassMethodStatement(name: string, kind: TokenKind.Sub | TokenKind.Function = TokenKind.Function, accessModifier?: Token) { return new ClassMethodStatement( - createToken(TokenKind.Class), + accessModifier, createIdentifier(name), createFunctionExpression(kind), null diff --git a/src/files/BrsFile.spec.ts b/src/files/BrsFile.spec.ts index c6447e9b4..c67101ede 100644 --- a/src/files/BrsFile.spec.ts +++ b/src/files/BrsFile.spec.ts @@ -2629,11 +2629,16 @@ describe('BrsFile', () => { end class class Duck extends Bird end class - end namespace`, trim` + end namespace + `, trim` namespace AnimalKingdom class Bird + sub new() + end sub end class class Duck extends AnimalKingdom.Bird + sub new() + end sub end class end namespace `); @@ -2675,6 +2680,8 @@ describe('BrsFile', () => { function getDuck() end function class Duck + sub new() + end sub @anMember @anMember("field") private thing as dynamic @@ -2737,6 +2744,8 @@ describe('BrsFile', () => { end namespace `, trim` class Person + sub new() + end sub public name as string public age as integer public sub getAge() as integer @@ -2744,6 +2753,8 @@ describe('BrsFile', () => { end class namespace NameA.NameB class Person + sub new() + end sub public name as string public age as integer public sub getAge() as integer @@ -2753,6 +2764,18 @@ describe('BrsFile', () => { `); }); + it('creates constructor properly', () => { + testTypedef(` + class Parent + end class + `, trim` + class Parent + sub new() + end sub + end class + `); + }); + it('sets properties to dynamic when initialized to invalid', () => { testTypedef(` class Human @@ -2761,6 +2784,8 @@ describe('BrsFile', () => { end class `, trim` class Human + sub new() + end sub public firstName as dynamic public lastName as string end class @@ -2809,6 +2834,8 @@ describe('BrsFile', () => { end class `, trim` class Human + sub new() + end sub public firstName as string protected middleName as string private lastName as string @@ -2836,10 +2863,14 @@ describe('BrsFile', () => { end class `, trim` class Animal + sub new() + end sub public sub speak() end sub end class class Dog extends Animal + sub new() + end sub public override sub speak() end sub end class diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index 5e81cc61a..6fe77e0ee 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -1563,6 +1563,8 @@ export class ClassStatement extends Statement implements TypedefProvider { } getTypedef(state: BrsTranspileState) { + this.ensureConstructorFunctionExists(); + const result = [] as TranspileResult; for (let annotation of this.annotations ?? []) { result.push( @@ -1674,6 +1676,16 @@ export class ClassStatement extends Statement implements TypedefProvider { return createClassMethodStatement('new', TokenKind.Sub); } + /** + * Create an empty `new` function if class is missing it (simplifies transpile logic) + */ + private ensureConstructorFunctionExists() { + if (!this.getConstructorFunction()) { + this.memberMap.new = this.getEmptyNewFunction(); + this.body = [this.memberMap.new, ...this.body]; + } + } + /** * Determine if the specified field was declared in one of the ancestor classes */ @@ -1693,6 +1705,8 @@ export class ClassStatement extends Statement implements TypedefProvider { * without instantiating the parent constructor at that point in time. */ private getTranspiledBuilder(state: BrsTranspileState) { + this.ensureConstructorFunctionExists(); + let result = []; result.push(`function ${this.getBuilderName(this.getName(ParseMode.BrightScript))}()\n`); state.blockDepth++; @@ -1723,12 +1737,6 @@ export class ClassStatement extends Statement implements TypedefProvider { ); let parentClassIndex = this.getParentClassIndex(state); - //create empty `new` function if class is missing it (simplifies transpile logic) - if (!this.getConstructorFunction()) { - this.memberMap.new = this.getEmptyNewFunction(); - this.body = [this.memberMap.new, ...this.body]; - } - for (let statement of this.body) { //is field statement if (isClassFieldStatement(statement)) { @@ -1853,10 +1861,10 @@ export class ClassStatement extends Statement implements TypedefProvider { export class ClassMethodStatement extends FunctionStatement { constructor( - readonly accessModifier: Token, + public accessModifier: Token, name: Identifier, func: FunctionExpression, - readonly override: Token + public override: Token ) { super(name, func, undefined); this.range = util.createRangeFromPositions(