Skip to content

Commit

Permalink
Ensure enums and interfaces persist in typedefs (#757)
Browse files Browse the repository at this point in the history
  • Loading branch information
TwitchBronBron authored Dec 6, 2022
1 parent 86da48a commit 4c0e380
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 8 deletions.
29 changes: 28 additions & 1 deletion src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { DiagnosticMessages } from '../DiagnosticMessages';
import type { StandardizedFileEntry } from 'roku-deploy';
import util, { standardizePath as s } from '../util';
import PluginInterface from '../PluginInterface';
import { expectCompletionsIncludes, expectDiagnostics, expectHasDiagnostics, expectZeroDiagnostics, getTestTranspile, trim } from '../testHelpers.spec';
import { expectCompletionsIncludes, expectDiagnostics, expectHasDiagnostics, expectZeroDiagnostics, getTestGetTypedef, getTestTranspile, trim } from '../testHelpers.spec';
import { ParseMode } from '../parser/Parser';
import { Logger } from '../Logger';
import { ImportStatement } from '../parser/Statement';
Expand All @@ -34,6 +34,7 @@ describe('BrsFile', () => {
let destPath = 'source/main.brs';
let file: BrsFile;
let testTranspile = getTestTranspile(() => [program, rootDir]);
let testGetTypedef = getTestGetTypedef(() => [program, rootDir]);

beforeEach(() => {
fsExtra.emptyDirSync(tempDir);
Expand Down Expand Up @@ -3018,6 +3019,32 @@ describe('BrsFile', () => {
});

describe('typedef', () => {
it('includes enum and interface types', () => {
testGetTypedef(`
interface Foo
field as string
end interface
enum Bar
value
end enum
function baz(parameter as Foo) as Bar
return Bar.value
end function
`, `
interface Foo
field as string
end interface
enum Bar
value
end enum
function baz(parameter as Foo) as Bar
end function
`);
});

it('sets typedef path properly', () => {
expect((program.setFile<BrsFile>('source/main1.brs', '')).typedefKey).to.equal(s`${rootDir}/source/main1.d.bs`.toLowerCase());
expect((program.setFile<BrsFile>('source/main2.d.bs', '')).typedefKey).to.equal(undefined);
Expand Down
53 changes: 50 additions & 3 deletions src/parser/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,15 @@ import * as fileUrl from 'file-url';
import type { WalkOptions, WalkVisitor } from '../astUtils/visitors';
import { createVisitor, WalkMode } from '../astUtils/visitors';
import { walk, InternalWalkMode, walkArray } from '../astUtils/visitors';
import { isAALiteralExpression, isArrayLiteralExpression, isCallExpression, isCallfuncExpression, isCommentStatement, isDottedGetExpression, isEscapedCharCodeLiteralExpression, isFunctionExpression, isIntegerType, isLiteralBoolean, isLiteralExpression, isLiteralNumber, isLiteralString, isLongIntegerType, isNamespaceStatement, isStringType, isUnaryExpression, isVariableExpression } from '../astUtils/reflection';
import { isAALiteralExpression, isArrayLiteralExpression, isCallExpression, isCallfuncExpression, isCommentStatement, isDottedGetExpression, isEscapedCharCodeLiteralExpression, isFunctionExpression, isFunctionStatement, isIntegerType, isLiteralBoolean, isLiteralExpression, isLiteralNumber, isLiteralString, isLongIntegerType, isMethodStatement, isNamespaceStatement, isStringType, isUnaryExpression, isVariableExpression } from '../astUtils/reflection';
import type { TranspileResult, TypedefProvider } from '../interfaces';
import { VoidType } from '../types/VoidType';
import { DynamicType } from '../types/DynamicType';
import type { BscType } from '../types/BscType';
import { FunctionType } from '../types/FunctionType';
import { Expression } from './AstNode';
import { SymbolTable } from '../SymbolTable';
import { SourceNode } from 'source-map';

export type ExpressionVisitor = (expression: Expression, parent: Expression) => void;

Expand Down Expand Up @@ -257,8 +258,37 @@ export class FunctionExpression extends Expression implements TypedefProvider {
return results;
}

getTypedef(state: BrsTranspileState, name?: Identifier) {
return this.transpile(state, name, false);
getTypedef(state: BrsTranspileState) {
let results = [
new SourceNode(1, 0, null, [
//'function'|'sub'
this.functionType?.text,
//functionName?
...(isFunctionStatement(this.parent) || isMethodStatement(this.parent) ? [' ', this.parent.name?.text ?? ''] : []),
//leftParen
'(',
//parameters
...(
this.parameters?.map((param, i) => ([
//separating comma
i > 0 ? ', ' : '',
...param.getTypedef(state)
])) ?? []
) as any,
//right paren
')',
//as <ReturnType>
...(this.asToken ? [
' as ',
this.returnTypeToken?.text
] : []),
'\n',
state.indent(),
//'end sub'|'end function'
this.end.text
])
];
return results;
}

walk(visitor: WalkVisitor, options: WalkOptions) {
Expand Down Expand Up @@ -327,6 +357,23 @@ export class FunctionParameterExpression extends Expression {
return result;
}

public getTypedef(state: BrsTranspileState): TranspileResult {
return [
//name
this.name.text,
//default value
...(this.defaultValue ? [
' = ',
...this.defaultValue.transpile(state)
] : []),
//type declaration
...(this.asToken ? [
' as ',
this.typeToken?.text
] : [])
];
}

walk(visitor: WalkVisitor, options: WalkOptions) {
// eslint-disable-next-line no-bitwise
if (this.defaultValue && options.walkMode & InternalWalkMode.walkExpressions) {
Expand Down
12 changes: 8 additions & 4 deletions src/parser/Statement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ export class FunctionStatement extends Statement implements TypedefProvider {
}

result.push(
...this.func.getTypedef(state, this.name)
...this.func.getTypedef(state)
);
return result;
}
Expand Down Expand Up @@ -1636,8 +1636,12 @@ export class ClassStatement extends Statement implements TypedefProvider {
let body = this.body;
//inject an empty "new" method if missing
if (!this.getConstructorFunction()) {
const constructor = createMethodStatement('new', TokenKind.Sub);
constructor.parent = this;
//walk the constructor to set up parent links
constructor.link();
body = [
createMethodStatement('new', TokenKind.Sub),
constructor,
...this.body
];
}
Expand Down Expand Up @@ -1986,7 +1990,7 @@ export class MethodStatement extends FunctionStatement {
}

getTypedef(state: BrsTranspileState) {
const result = [] as string[];
const result = [] as Array<string | SourceNode>;
for (let annotation of this.annotations ?? []) {
result.push(
...annotation.getTypedef(state),
Expand All @@ -2004,7 +2008,7 @@ export class MethodStatement extends FunctionStatement {
result.push('override ');
}
result.push(
...this.func.getTypedef(state, this.name)
...this.func.getTypedef(state)
);
return result;
}
Expand Down

0 comments on commit 4c0e380

Please sign in to comment.