Skip to content

Commit 295d907

Browse files
authored
Fixed incorrect error on intersected number and string types (#786)
* Fixed incorrect error on intersected number and string types * PR feedback * Removed lua string method check * Removed undefined union return * Removed some code duplication from printMethodCallExpression print method * Removed export from isExplicitArrayType
1 parent 96c3f8d commit 295d907

File tree

9 files changed

+51
-67
lines changed

9 files changed

+51
-67
lines changed

src/LuaPrinter.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -742,7 +742,9 @@ export class LuaPrinter {
742742
public printMethodCallExpression(expression: lua.MethodCallExpression): SourceNode {
743743
const chunks = [];
744744

745-
const prefix = this.printExpression(expression.prefixExpression);
745+
const prefix = lua.isStringLiteral(expression.prefixExpression)
746+
? this.printExpression(lua.createParenthesizedExpression(expression.prefixExpression))
747+
: this.printExpression(expression.prefixExpression);
746748

747749
const name = this.printIdentifier(expression.name);
748750

src/transformation/builtins/array.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ import * as lua from "../../LuaAST";
33
import { TransformationContext } from "../context";
44
import { UnsupportedProperty } from "../utils/errors";
55
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
6-
import { isExplicitArrayType } from "../utils/typescript";
76
import { PropertyCallExpression, transformArguments } from "../visitors/call";
87

98
export function transformArrayPrototypeCall(
109
context: TransformationContext,
1110
node: PropertyCallExpression
12-
): lua.CallExpression | undefined {
11+
): lua.CallExpression {
1312
const expression = node.expression;
14-
const ownerType = context.checker.getTypeAtLocation(expression.expression);
1513
const signature = context.checker.getResolvedSignature(node);
1614
const params = transformArguments(context, node.arguments, signature);
1715
const caller = context.transformExpression(expression.expression);
@@ -79,9 +77,7 @@ export function transformArrayPrototypeCall(
7977
case "flatMap":
8078
return transformLuaLibFunction(context, LuaLibFeature.ArrayFlatMap, node, caller, ...params);
8179
default:
82-
if (isExplicitArrayType(context, ownerType)) {
83-
throw UnsupportedProperty("array", expressionName, node);
84-
}
80+
throw UnsupportedProperty("array", expressionName, node);
8581
}
8682
}
8783

src/transformation/builtins/index.ts

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,14 @@ import { assume } from "../../utils";
44
import { TransformationContext } from "../context";
55
import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib";
66
import { getIdentifierSymbolId } from "../utils/symbols";
7-
import { isArrayType, isFunctionType, isNumberType, isStandardLibraryType, isStringType } from "../utils/typescript";
7+
import {
8+
hasStandardLibrarySignature,
9+
isArrayType,
10+
isFunctionType,
11+
isNumberType,
12+
isStandardLibraryType,
13+
isStringType,
14+
} from "../utils/typescript";
815
import { PropertyCallExpression } from "../visitors/call";
916
import { checkForLuaLibType } from "../visitors/class/new";
1017
import { transformArrayProperty, transformArrayPrototypeCall } from "./array";
@@ -85,22 +92,19 @@ export function transformBuiltinCallExpression(
8592
}
8693
}
8794

88-
if (isStringType(context, ownerType)) {
95+
if (isStringType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
8996
return transformStringPrototypeCall(context, node);
9097
}
9198

92-
if (isNumberType(context, ownerType)) {
99+
if (isNumberType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
93100
return transformNumberPrototypeCall(context, node);
94101
}
95102

96-
if (isArrayType(context, ownerType)) {
97-
const result = transformArrayPrototypeCall(context, node);
98-
if (result) {
99-
return result;
100-
}
103+
if (isArrayType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
104+
return transformArrayPrototypeCall(context, node);
101105
}
102106

103-
if (isFunctionType(context, ownerType)) {
107+
if (isFunctionType(context, ownerType) && hasStandardLibrarySignature(context, node)) {
104108
return transformFunctionPrototypeCall(context, node);
105109
}
106110

src/transformation/builtins/string.ts

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { UnsupportedProperty } from "../utils/errors";
55
import { createExpressionPlusOne } from "../utils/lua-ast";
66
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
77
import { PropertyCallExpression, transformArguments } from "../visitors/call";
8-
import { transformIdentifier } from "../visitors/identifier";
98

109
function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lua.Expression[]): lua.CallExpression {
1110
const stringIdentifier = lua.createIdentifier("string");
@@ -124,37 +123,6 @@ export function transformStringPrototypeCall(
124123
return transformLuaLibFunction(context, LuaLibFeature.StringPadStart, node, caller, ...params);
125124
case "padEnd":
126125
return transformLuaLibFunction(context, LuaLibFeature.StringPadEnd, node, caller, ...params);
127-
128-
case "byte":
129-
case "char":
130-
case "dump":
131-
case "find":
132-
case "format":
133-
case "gmatch":
134-
case "gsub":
135-
case "len":
136-
case "lower":
137-
case "match":
138-
case "pack":
139-
case "packsize":
140-
case "rep":
141-
case "reverse":
142-
case "sub":
143-
case "unpack":
144-
case "upper":
145-
// Allow lua's string instance methods
146-
let stringVariable = context.transformExpression(expression.expression);
147-
if (ts.isStringLiteralLike(expression.expression)) {
148-
// "foo":method() needs to be ("foo"):method()
149-
stringVariable = lua.createParenthesizedExpression(stringVariable);
150-
}
151-
152-
return lua.createMethodCallExpression(
153-
stringVariable,
154-
transformIdentifier(context, expression.name),
155-
params,
156-
node
157-
);
158126
default:
159127
throw UnsupportedProperty("string", expressionName, node);
160128
}

src/transformation/utils/typescript/index.ts

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,15 @@ export function isFirstDeclaration(context: TransformationContext, node: ts.Vari
4343
return firstDeclaration === node;
4444
}
4545

46+
function isStandardLibraryDeclaration(context: TransformationContext, declaration: ts.Declaration): boolean {
47+
const sourceFile = declaration.getSourceFile();
48+
if (!sourceFile) {
49+
return false;
50+
}
51+
52+
return context.program.isSourceFileDefaultLibrary(sourceFile);
53+
}
54+
4655
export function isStandardLibraryType(
4756
context: TransformationContext,
4857
type: ts.Type,
@@ -59,12 +68,16 @@ export function isStandardLibraryType(
5968
return true;
6069
}
6170

62-
const sourceFile = declaration.getSourceFile();
63-
if (!sourceFile) {
64-
return false;
65-
}
71+
return isStandardLibraryDeclaration(context, declaration);
72+
}
6673

67-
return context.program.isSourceFileDefaultLibrary(sourceFile);
74+
export function hasStandardLibrarySignature(
75+
context: TransformationContext,
76+
callExpression: ts.CallExpression
77+
): boolean {
78+
const signature = context.checker.getResolvedSignature(callExpression);
79+
80+
return signature && signature.declaration ? isStandardLibraryDeclaration(context, signature.declaration) : false;
6881
}
6982

7083
export function inferAssignedType(context: TransformationContext, expression: ts.Expression): ts.Type {

src/transformation/utils/typescript/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export function isNumberType(context: TransformationContext, type: ts.Type): boo
2828
return isTypeWithFlags(context, type, ts.TypeFlags.Number | ts.TypeFlags.NumberLike | ts.TypeFlags.NumberLiteral);
2929
}
3030

31-
export function isExplicitArrayType(context: TransformationContext, type: ts.Type): boolean {
31+
function isExplicitArrayType(context: TransformationContext, type: ts.Type): boolean {
3232
if (type.symbol) {
3333
const baseConstraint = context.checker.getBaseConstraintOfType(type);
3434
if (baseConstraint && baseConstraint !== type) {

test/unit/builtins/loading.spec.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,4 @@ describe("Unknown builtin property", () => {
4444
.disableSemanticCheck()
4545
.expectToHaveDiagnosticOfError(UnsupportedProperty("Math", "unknownProperty", util.nodeStub));
4646
});
47-
48-
test("function call", () => {
49-
util.testExpression`[].unknownFunction()`
50-
.disableSemanticCheck()
51-
.expectToHaveDiagnosticOfError(UnsupportedProperty("array", "unknownFunction", util.nodeStub));
52-
});
5347
});

test/unit/builtins/numbers.spec.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,10 @@ test.each(cases)("isNaN(%p)", value => {
6767
test.each(cases)("isFinite(%p)", value => {
6868
util.testExpressionTemplate`isFinite(${value} as any)`.expectToMatchJsResult();
6969
});
70+
71+
test("number intersected method", () => {
72+
util.testFunction`
73+
type Vector = number & { normalize(): Vector };
74+
return ({ normalize: () => 3 } as Vector).normalize();
75+
`.expectToMatchJsResult();
76+
});

test/unit/builtins/string.spec.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
1-
import { UnsupportedProperty } from "../../../src/transformation/utils/errors";
21
import * as util from "../../util";
32

4-
test("Unsupported string function", () => {
5-
util.testExpression`"test".testThisIsNoMember()`
6-
.disableSemanticCheck()
7-
.expectToHaveDiagnosticOfError(UnsupportedProperty("string", "testThisIsNoMember", util.nodeStub));
8-
});
9-
103
test("Supported lua string function", () => {
114
const tsHeader = `
125
declare global {
@@ -287,3 +280,10 @@ describe.each(["trim", "trimEnd", "trimRight", "trimStart", "trimLeft"])("string
287280
util.testExpression`${util.formatCode(testString)}.${trim}()`.expectToMatchJsResult();
288281
});
289282
});
283+
284+
test("string intersected method", () => {
285+
util.testFunction`
286+
type Vector = string & { abc(): Vector };
287+
return ({ abc: () => "a" } as Vector).abc();
288+
`.expectToMatchJsResult();
289+
});

0 commit comments

Comments
 (0)