Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 44 additions & 8 deletions src/TSHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,37 +59,73 @@ export class TSHelper {
return type.symbol
&& ((type.symbol.flags & ts.SymbolFlags.Enum) !== 0)
&& type.symbol.getDocumentationComment(checker)[0] !== undefined
&& this.hasCustomDecorator(type, checker, "!CompileMembersOnly");
&& this.hasCustomDecorator(type.symbol, checker, "!CompileMembersOnly");
}

public static isPureAbstractClass(type: ts.Type, checker: ts.TypeChecker): boolean {
return type.symbol
&& ((type.symbol.flags & ts.SymbolFlags.Class) !== 0)
&& this.hasCustomDecorator(type, checker, "!PureAbstract");
&& this.hasCustomDecorator(type.symbol, checker, "!PureAbstract");
}

public static isExtensionClass(type: ts.Type, checker: ts.TypeChecker): boolean {
return type.symbol
&& ((type.symbol.flags & ts.SymbolFlags.Class) !== 0)
&& this.hasCustomDecorator(type, checker, "!Extension");
&& this.hasCustomDecorator(type.symbol, checker, "!Extension");
}

public static isPhantom(type: ts.Type, checker: ts.TypeChecker): boolean {
return type.symbol
&& ((type.symbol.flags & ts.SymbolFlags.Namespace) !== 0)
&& this.hasCustomDecorator(type, checker, "!Phantom");
&& this.hasCustomDecorator(type.symbol, checker, "!Phantom");
}

public static isTupleReturnFunction(type: ts.Type, checker: ts.TypeChecker): boolean {
return type.symbol
&& ((type.symbol.flags & ts.SymbolFlags.Function) !== 0
|| (type.symbol.flags & ts.SymbolFlags.Method) !== 0)
&& this.hasCustomDecorator(type, checker, "!TupleReturn");
&& this.hasCustomDecorator(type.symbol, checker, "!TupleReturn");
}

public static hasCustomDecorator(type: ts.Type, checker: ts.TypeChecker, decorator: string): boolean {
if (type.symbol) {
const comments = type.symbol.getDocumentationComment(checker);
public static isDotMethod(call: ts.CallExpression, checker: ts.TypeChecker): boolean {
// if we're not accessing a property on something, it doesn't matter
if (!ts.isPropertyAccessExpression(call.expression)) {
return false;
}

// check by function type
const functionType = checker.getTypeAtLocation(call.expression);

// static functions should always use dot syntax
if (functionType.symbol
&& functionType.symbol.valueDeclaration
&& functionType.symbol.valueDeclaration.modifiers) {
for (const k of functionType.symbol.valueDeclaration.modifiers || []) {
if (k.kind === ts.SyntaxKind.StaticKeyword) {
return true;
}
}
}

// check whether the function is marked as a dot method
if (this.hasCustomDecorator(functionType.symbol, checker, "!DotMethod")) {
return true;
}

// check by the container of the function
const containerType = checker.getTypeAtLocation(call.expression.expression);

// check if it's declared in a namespace
if (containerType.symbol.flags & ts.SymbolFlags.Namespace) {
return true;
}

return this.hasCustomDecorator(containerType.symbol, checker, "!DotMethod");
}

public static hasCustomDecorator(symbol: ts.Symbol | undefined, checker: ts.TypeChecker, decorator: string) {
if (symbol) {
const comments = symbol.getDocumentationComment(checker);
const decorators =
comments.filter(comment => comment.kind === "text")
.map(comment => comment.text.trim())
Expand Down
4 changes: 2 additions & 2 deletions src/Transpiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,7 @@ export class LuaTranspiler {
return this.transpileArrayCallExpression(node);
}

if (expType.symbol && (expType.symbol.flags & ts.SymbolFlags.Namespace)) {
if (tsHelper.isDotMethod(node, this.checker)) {
// Don't replace . with : for namespaces
callPath = this.transpileExpression(node.expression);
params = this.transpileArguments(node.arguments);
Expand Down Expand Up @@ -1317,7 +1317,7 @@ export class LuaTranspiler {
if (!tsHelper.isPureAbstractClass(superType, this.checker)) {
extendsType = clause.types[0];
}
noClassOr = tsHelper.hasCustomDecorator(superType, this.checker, "!NoClassOr");
noClassOr = tsHelper.hasCustomDecorator(superType.symbol, this.checker, "!NoClassOr");
}
});
}
Expand Down
8 changes: 8 additions & 0 deletions test/translation/lua/dotMethod.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
classInstance.dotMethod()
interfaceInstance.dotMethod()
MixedClass.staticMethod()
mixedInstance.dotMethod()
mixedInstance:regularMethod()
subClassInstance.dotMethod()
subClassInstance:regularMethod()
irrelevant()
36 changes: 36 additions & 0 deletions test/translation/ts/dotMethod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/** !DotMethod */
declare class TestClass {
public dotMethod(): void;
}

/** !DotMethod */
declare interface TestInterface {
dotMethod(): void;
}

declare class MixedClass {
public static staticMethod(): void;

/** !DotMethod */
public dotMethod(): void;

public regularMethod(): void;
}

declare class SubClass extends MixedClass {}

declare function irrelevant(): void;

declare const classInstance: TestClass;
declare const interfaceInstance: TestInterface;
declare const mixedInstance: MixedClass;
declare const subClassInstance: SubClass;

classInstance.dotMethod();
interfaceInstance.dotMethod();
MixedClass.staticMethod();
mixedInstance.dotMethod();
mixedInstance.regularMethod();
subClassInstance.dotMethod();
subClassInstance.regularMethod();
irrelevant();