Skip to content
Merged
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
20 changes: 19 additions & 1 deletion src/transformation/visitors/access.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@ import * as lua from "../../LuaAST";
import { transformBuiltinPropertyAccessExpression } from "../builtins";
import { FunctionVisitor, TransformationContext } from "../context";
import { AnnotationKind, getTypeAnnotations } from "../utils/annotations";
import { invalidMultiReturnAccess, unsupportedOptionalCompileMembersOnly } from "../utils/diagnostics";
import {
invalidCallExtensionUse,
invalidMultiReturnAccess,
unsupportedOptionalCompileMembersOnly,
} from "../utils/diagnostics";
import { getExtensionKindForNode } from "../utils/language-extensions";
import { addToNumericExpression } from "../utils/lua-ast";
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
import { isArrayType, isNumberType, isStringType } from "../utils/typescript";
import { tryGetConstEnumValue } from "./enum";
import { transformOrderedExpressions } from "./expression-list";
import { callExtensions } from "./language-extensions/call-extension";
import { isMultiReturnCall, returnsMultiType } from "./language-extensions/multi";
import {
transformOptionalChainWithCapture,
Expand Down Expand Up @@ -143,6 +149,18 @@ export function transformPropertyAccessExpressionWithCapture(
return { expression: builtinResult };
}

if (
ts.isIdentifier(node.expression) &&
node.parent &&
(!ts.isCallExpression(node.parent) || node.parent.expression !== node)
) {
// Check if this is a method call extension that is not used as a call
const extensionType = getExtensionKindForNode(context, node);
if (extensionType && callExtensions.has(extensionType)) {
context.diagnostics.push(invalidCallExtensionUse(node));
}
}

const table = context.transformExpression(node.expression);

if (thisValueCapture) {
Expand Down
5 changes: 4 additions & 1 deletion src/transformation/visitors/identifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,10 @@ function transformNonValueIdentifier(

if (extensionKind) {
if (callExtensions.has(extensionKind)) {
context.diagnostics.push(invalidCallExtensionUse(identifier));
// Avoid putting duplicate diagnostic on the name of a variable declaration, due to the inferred type
if (!(ts.isVariableDeclaration(identifier.parent) && identifier.parent.name === identifier)) {
context.diagnostics.push(invalidCallExtensionUse(identifier));
}
// fall through
} else if (isIdentifierExtensionValue(symbol, extensionKind)) {
reportInvalidExtensionValue(context, identifier, extensionKind);
Expand Down
4 changes: 3 additions & 1 deletion test/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ expect.extend({
const message = this.isNot
? diagnosticMessages
: expected
? `Expected:\n${expected.join("\n")}\nReceived:\n${diagnostics.map(diag => diag.code).join("\n")}\n`
? `Expected:\n${expected.join("\n")}\nReceived:\n${diagnostics
.map(diag => diag.code)
.join("\n")}\n\n${diagnosticMessages}\n`
: `Received: ${this.utils.printReceived([])}\n`;

return matcherHint + "\n\n" + message;
Expand Down
48 changes: 48 additions & 0 deletions test/unit/language-extensions/__snapshots__/table.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,51 @@ exports[`LuaTableHas extension invalid use ("const foo: unknown = tableHas;"): d
exports[`LuaTableHas extension invalid use ("declare function foo(tableHas: LuaTableHas<{}, string>): void; foo(tableHas);"): code 1`] = `"foo(_G, tableHas)"`;

exports[`LuaTableHas extension invalid use ("declare function foo(tableHas: LuaTableHas<{}, string>): void; foo(tableHas);"): diagnostics 1`] = `"main.ts(3,80): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method assignment ("LuaMap<string, number>"): code 1`] = `
"____table = {}
has = ____table.has"
`;

exports[`LuaTableHas extension invalid use method assignment ("LuaMap<string, number>"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method assignment ("LuaSet<string>"): code 1`] = `
"____table = {}
has = ____table.has"
`;

exports[`LuaTableHas extension invalid use method assignment ("LuaSet<string>"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method assignment ("LuaTable<string, number>"): code 1`] = `
"____table = {}
has = ____table.has"
`;

exports[`LuaTableHas extension invalid use method assignment ("LuaTable<string, number>"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method expression ("LuaMap<string, number>"): code 1`] = `
"local ____lualib = require(\\"lualib_bundle\\")
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
____table = {}
__TS__ArrayMap({\\"a\\", \\"b\\", \\"c\\"}, ____table.has)"
`;

exports[`LuaTableHas extension invalid use method expression ("LuaMap<string, number>"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method expression ("LuaSet<string>"): code 1`] = `
"local ____lualib = require(\\"lualib_bundle\\")
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
____table = {}
__TS__ArrayMap({\\"a\\", \\"b\\", \\"c\\"}, ____table.has)"
`;

exports[`LuaTableHas extension invalid use method expression ("LuaSet<string>"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`;

exports[`LuaTableHas extension invalid use method expression ("LuaTable<string, number>"): code 1`] = `
"local ____lualib = require(\\"lualib_bundle\\")
local __TS__ArrayMap = ____lualib.__TS__ArrayMap
____table = {}
__TS__ArrayMap({\\"a\\", \\"b\\", \\"c\\"}, ____table.has)"
`;

exports[`LuaTableHas extension invalid use method expression ("LuaTable<string, number>"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`;
24 changes: 24 additions & 0 deletions test/unit/language-extensions/table.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,30 @@ describe("LuaTableHas extension", () => {
.withLanguageExtensions()
.expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]);
});

test.each(["LuaTable<string, number>", "LuaMap<string, number>", "LuaSet<string>"])(
"invalid use method assignment (%p)",
type => {
util.testModule`
const table = new ${type}();
const has = table.has;
`
.withLanguageExtensions()
.expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]);
}
);

test.each(["LuaTable<string, number>", "LuaMap<string, number>", "LuaSet<string>"])(
"invalid use method expression (%p)",
type => {
util.testModule`
const table = new ${type}();
["a", "b", "c"].map(table.has);
`
.withLanguageExtensions()
.expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]);
}
);
});

describe("LuaTableDelete extension", () => {
Expand Down