Skip to content

Commit 786c45f

Browse files
committed
2 parents 521f102 + c3dd228 commit 786c45f

15 files changed

Lines changed: 194 additions & 14 deletions

File tree

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
# Changelog
22

3+
## 1.9.0
4+
5+
- Added a warning when trying to use a type in a condition that can never be false in Lua, such as numbers or strings. (Only when `strictNullChecks` is enabled.)
6+
- Fixed some missing and misplaced errors when trying to reference LuaTable/LuaMap/LuaSet functions without calling them.
7+
- Fixed a bug in the `get()` type of `ReadOnlyLuaMap`. It is now typed the same as `LuaMap`, i.e. it can return `undefined`.
8+
- Fixed an issue in bundling that could sometimes lead to invalid bundle entry requires.
9+
- Added a warning when using `paths` without specifying `baseUrl`.
10+
- Fixed exception while checking for standard library types.
11+
312
## 1.8.0
413

514
- Added support for the [tsconfig.json paths](https://www.typescriptlang.org/tsconfig#paths) configuration option.

language-extensions/index.d.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ declare const LuaMap: (new <K extends AnyNotNil = AnyNotNil, V = any>() => LuaMa
631631
* @param V The type of the values stored in the table.
632632
*/
633633
declare interface ReadonlyLuaMap<K extends AnyNotNil = AnyNotNil, V = any> extends LuaPairsIterable<K, V> {
634-
get: LuaTableGetMethod<K, V>;
634+
get: LuaTableGetMethod<K, V | undefined>;
635635
has: LuaTableHasMethod<K>;
636636
}
637637

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "typescript-to-lua",
3-
"version": "1.8.2",
3+
"version": "1.9.0",
44
"description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!",
55
"repository": "https://github.com/TypeScriptToLua/TypeScriptToLua",
66
"homepage": "https://typescripttolua.github.io/",

src/transformation/utils/diagnostics.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,10 @@ export const annotationDeprecated = createWarningDiagnosticFactory(
147147
`See https://typescripttolua.github.io/docs/advanced/compiler-annotations#${kind.toLowerCase()} for more information.`
148148
);
149149

150+
export const truthyOnlyConditionalValue = createWarningDiagnosticFactory(
151+
"Numbers and strings will always evaluate to true in Lua. Explicitly check the value with ===."
152+
);
153+
150154
export const notAllowedOptionalAssignment = createErrorDiagnosticFactory(
151155
"The left-hand side of an assignment expression may not be an optional property access."
152156
);

src/transformation/visitors/access.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,18 @@ import * as lua from "../../LuaAST";
33
import { transformBuiltinPropertyAccessExpression } from "../builtins";
44
import { FunctionVisitor, TransformationContext } from "../context";
55
import { AnnotationKind, getTypeAnnotations } from "../utils/annotations";
6-
import { invalidMultiReturnAccess, unsupportedOptionalCompileMembersOnly } from "../utils/diagnostics";
6+
import {
7+
invalidCallExtensionUse,
8+
invalidMultiReturnAccess,
9+
unsupportedOptionalCompileMembersOnly,
10+
} from "../utils/diagnostics";
11+
import { getExtensionKindForNode } from "../utils/language-extensions";
712
import { addToNumericExpression } from "../utils/lua-ast";
813
import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib";
914
import { isArrayType, isNumberType, isStringType } from "../utils/typescript";
1015
import { tryGetConstEnumValue } from "./enum";
1116
import { transformOrderedExpressions } from "./expression-list";
17+
import { callExtensions } from "./language-extensions/call-extension";
1218
import { isMultiReturnCall, returnsMultiType } from "./language-extensions/multi";
1319
import {
1420
transformOptionalChainWithCapture,
@@ -143,6 +149,18 @@ export function transformPropertyAccessExpressionWithCapture(
143149
return { expression: builtinResult };
144150
}
145151

152+
if (
153+
ts.isIdentifier(node.expression) &&
154+
node.parent &&
155+
(!ts.isCallExpression(node.parent) || node.parent.expression !== node)
156+
) {
157+
// Check if this is a method call extension that is not used as a call
158+
const extensionType = getExtensionKindForNode(context, node);
159+
if (extensionType && callExtensions.has(extensionType)) {
160+
context.diagnostics.push(invalidCallExtensionUse(node));
161+
}
162+
}
163+
146164
const table = context.transformExpression(node.expression);
147165

148166
if (thisValueCapture) {

src/transformation/visitors/conditional.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { transformInPrecedingStatementScope } from "../utils/preceding-statement
55
import { performHoisting, ScopeType } from "../utils/scope";
66
import { transformBlockOrStatement } from "./block";
77
import { canBeFalsy } from "../utils/typescript";
8+
import { truthyOnlyConditionalValue } from "../utils/diagnostics";
89

910
type EvaluatedExpression = [precedingStatemens: lua.Statement[], value: lua.Expression];
1011

@@ -39,6 +40,9 @@ function transformProtectedConditionalExpression(
3940
}
4041

4142
export const transformConditionalExpression: FunctionVisitor<ts.ConditionalExpression> = (expression, context) => {
43+
// Check if we need to add diagnostic about Lua truthiness
44+
checkOnlyTruthyCondition(expression.condition, context);
45+
4246
const condition = transformInPrecedingStatementScope(context, () =>
4347
context.transformExpression(expression.condition)
4448
);
@@ -64,6 +68,10 @@ export const transformConditionalExpression: FunctionVisitor<ts.ConditionalExpre
6468

6569
export function transformIfStatement(statement: ts.IfStatement, context: TransformationContext): lua.IfStatement {
6670
context.pushScope(ScopeType.Conditional);
71+
72+
// Check if we need to add diagnostic about Lua truthiness
73+
checkOnlyTruthyCondition(statement.expression, context);
74+
6775
const condition = context.transformExpression(statement.expression);
6876
const statements = performHoisting(context, transformBlockOrStatement(context, statement.thenStatement));
6977
context.popScope();
@@ -103,3 +111,9 @@ export function transformIfStatement(statement: ts.IfStatement, context: Transfo
103111

104112
return lua.createIfStatement(condition, ifBlock);
105113
}
114+
115+
export function checkOnlyTruthyCondition(condition: ts.Expression, context: TransformationContext) {
116+
if (!canBeFalsy(context, context.checker.getTypeAtLocation(condition))) {
117+
context.diagnostics.push(truthyOnlyConditionalValue(condition));
118+
}
119+
}

src/transformation/visitors/identifier.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,10 @@ function transformNonValueIdentifier(
3131

3232
if (extensionKind) {
3333
if (callExtensions.has(extensionKind)) {
34-
context.diagnostics.push(invalidCallExtensionUse(identifier));
34+
// Avoid putting duplicate diagnostic on the name of a variable declaration, due to the inferred type
35+
if (!(ts.isVariableDeclaration(identifier.parent) && identifier.parent.name === identifier)) {
36+
context.diagnostics.push(invalidCallExtensionUse(identifier));
37+
}
3538
// fall through
3639
} else if (isIdentifierExtensionValue(symbol, extensionKind)) {
3740
reportInvalidExtensionValue(context, identifier, extensionKind);

src/transformation/visitors/loops/do-while.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@ import * as ts from "typescript";
22
import * as lua from "../../../LuaAST";
33
import { FunctionVisitor } from "../../context";
44
import { transformInPrecedingStatementScope } from "../../utils/preceding-statements";
5+
import { checkOnlyTruthyCondition } from "../conditional";
56
import { invertCondition, transformLoopBody } from "./utils";
67

78
export const transformWhileStatement: FunctionVisitor<ts.WhileStatement> = (statement, context) => {
9+
// Check if we need to add diagnostic about Lua truthiness
10+
checkOnlyTruthyCondition(statement.expression, context);
11+
812
const body = transformLoopBody(context, statement);
913

1014
let [conditionPrecedingStatements, condition] = transformInPrecedingStatementScope(context, () =>
@@ -37,6 +41,9 @@ export const transformWhileStatement: FunctionVisitor<ts.WhileStatement> = (stat
3741
};
3842

3943
export const transformDoStatement: FunctionVisitor<ts.DoStatement> = (statement, context) => {
44+
// Check if we need to add diagnostic about Lua truthiness
45+
checkOnlyTruthyCondition(statement.expression, context);
46+
4047
const body = lua.createDoStatement(transformLoopBody(context, statement));
4148

4249
let [conditionPrecedingStatements, condition] = transformInPrecedingStatementScope(context, () =>

src/transpilation/bundle.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CompilerOptions } from "../CompilerOptions";
55
import { escapeString, tstlHeader } from "../LuaPrinter";
66
import { cast, formatPathToLuaPath, isNonNull, trimExtension } from "../utils";
77
import { couldNotFindBundleEntryPoint } from "./diagnostics";
8-
import { getEmitOutDir, getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler";
8+
import { getEmitOutDir, getEmitPathRelativeToOutDir, getProjectRoot } from "./transpiler";
99
import { EmitFile, ProcessedFile } from "./utils";
1010

1111
const createModulePath = (pathToResolve: string, program: ts.Program) =>
@@ -85,10 +85,12 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t
8585
const entryModule = cast(options.luaBundleEntry, isNonNull);
8686

8787
// Resolve project settings relative to project file.
88-
const resolvedEntryModule = path.resolve(getSourceDir(program), entryModule);
88+
const resolvedEntryModule = path.resolve(getProjectRoot(program), entryModule);
8989
const outputPath = path.resolve(getEmitOutDir(program), bundleFile);
90+
const entryModuleFilePath =
91+
program.getSourceFile(entryModule)?.fileName ?? program.getSourceFile(resolvedEntryModule)?.fileName;
9092

91-
if (program.getSourceFile(resolvedEntryModule) === undefined && program.getSourceFile(entryModule) === undefined) {
93+
if (entryModuleFilePath === undefined) {
9294
diagnostics.push(couldNotFindBundleEntryPoint(entryModule));
9395
}
9496

@@ -99,7 +101,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t
99101
const moduleTable = createModuleTableNode(moduleTableEntries);
100102

101103
// return require("<entry module path>")
102-
const entryPoint = `return require(${createModulePath(entryModule, program)}, ...)\n`;
104+
const entryPoint = `return require(${createModulePath(entryModuleFilePath ?? entryModule, program)}, ...)\n`;
103105

104106
const footers: string[] = [];
105107
if (options.sourceMapTraceback) {

0 commit comments

Comments
 (0)