Skip to content

Commit dc45631

Browse files
committed
Make all other errors diagnostics
1 parent 2b8303e commit dc45631

9 files changed

Lines changed: 208 additions & 57 deletions

File tree

src/transformation/utils/diagnostics.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ export const unsupportedOverloadAssignment = createDiagnosticFactory((name?: str
4141
);
4242
});
4343

44+
export const decoratorInvalidContext = createDiagnosticFactory(`Decorator function cannot have 'this: void'.`);
45+
4446
export const annotationInvalidArgumentCount = createDiagnosticFactory(
4547
(kind: AnnotationKind, got: number, expected: number) => `'@${kind}' expects ${expected} arguments, but got ${got}.`
4648
);
@@ -126,3 +128,13 @@ export const forOfUnsupportedObjectDestructuring = createDiagnosticFactory(
126128
export const invalidAmbientIdentifierName = createDiagnosticFactory(
127129
(text: string) => `Invalid ambient identifier name '${text}'. Ambient identifiers must be valid lua identifiers.`
128130
);
131+
132+
export const referencedBeforeDeclaration = createDiagnosticFactory(
133+
(text: string) =>
134+
`Identifier '${text}' was referenced before it was declared. The declaration ` +
135+
"must be moved before the identifier's use, or hoisting must be enabled."
136+
);
137+
138+
export const unresolvableRequirePath = createDiagnosticFactory(
139+
(path: string) => `Cannot create require path. Module '${path}' does not exist within --rootDir.`
140+
);

src/transformation/utils/errors.ts

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,3 @@ export class TranspileError extends Error {
66
super(message);
77
}
88
}
9-
10-
export const InvalidDecoratorContext = (node: ts.Node) =>
11-
new TranspileError(`Decorator function cannot have 'this: void'.`, node);
12-
13-
export const UnresolvableRequirePath = (node: ts.Node, reason: string, path?: string) =>
14-
new TranspileError(`${reason}. TypeScript path: ${path}.`, node);
15-
16-
export const ReferencedBeforeDeclaration = (node: ts.Identifier) =>
17-
new TranspileError(
18-
`Identifier "${node.text}" was referenced before it was declared. The declaration ` +
19-
"must be moved before the identifier's use, or hoisting must be enabled.",
20-
node
21-
);

src/transformation/utils/symbols.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as ts from "typescript";
22
import * as lua from "../../LuaAST";
33
import { getOrUpdate } from "../../utils";
44
import { TransformationContext } from "../context";
5-
import { ReferencedBeforeDeclaration } from "./errors";
5+
import { referencedBeforeDeclaration } from "./diagnostics";
66
import { markSymbolAsReferencedInCurrentScopes } from "./scope";
77
import { getFirstDeclarationInFile } from "./typescript";
88

@@ -50,7 +50,7 @@ export function trackSymbolReference(
5050
// Check for reference-before-declaration
5151
const declaration = getFirstDeclarationInFile(symbol, context.sourceFile);
5252
if (declaration && identifier.pos < declaration.pos) {
53-
throw ReferencedBeforeDeclaration(identifier);
53+
context.diagnostics.push(referencedBeforeDeclaration(identifier, identifier.text));
5454
}
5555
}
5656

src/transformation/visitors/class/decorators.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as ts from "typescript";
22
import * as lua from "../../../LuaAST";
33
import { TransformationContext } from "../../context";
4-
import { InvalidDecoratorContext } from "../../utils/errors";
4+
import { decoratorInvalidContext } from "../../utils/diagnostics";
55
import { addExportToIdentifier } from "../../utils/export";
66
import { ContextType, getFunctionContextType } from "../../utils/function-context";
77
import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib";
@@ -26,7 +26,7 @@ export function createConstructorDecorationStatement(
2626
const type = context.checker.getTypeAtLocation(expression);
2727
const callContext = getFunctionContextType(context, type);
2828
if (callContext === ContextType.Void) {
29-
throw InvalidDecoratorContext(decorator);
29+
context.diagnostics.push(decoratorInvalidContext(decorator));
3030
}
3131

3232
return context.transformExpression(expression);

src/transformation/visitors/modules/import.ts

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,22 @@ import * as lua from "../../../LuaAST";
44
import { formatPathToLuaPath } from "../../../utils";
55
import { FunctionVisitor, TransformationContext } from "../../context";
66
import { AnnotationKind, getSymbolAnnotations, getTypeAnnotations } from "../../utils/annotations";
7-
import { UnresolvableRequirePath } from "../../utils/errors";
87
import { createDefaultExportStringLiteral } from "../../utils/export";
98
import { createHoistableVariableDeclarationStatement } from "../../utils/lua-ast";
109
import { createSafeName } from "../../utils/safe-names";
1110
import { peekScope } from "../../utils/scope";
1211
import { transformIdentifier } from "../identifier";
1312
import { transformPropertyName } from "../literal";
13+
import { unresolvableRequirePath } from "../../utils/diagnostics";
1414

1515
const getAbsoluteImportPath = (relativePath: string, directoryPath: string, options: ts.CompilerOptions): string =>
1616
relativePath[0] !== "." && options.baseUrl
1717
? path.resolve(options.baseUrl, relativePath)
1818
: path.resolve(directoryPath, relativePath);
1919

20-
function getImportPath(fileName: string, relativePath: string, node: ts.Node, options: ts.CompilerOptions): string {
20+
function getImportPath(context: TransformationContext, relativePath: string, node: ts.Node): string {
21+
const fileName = context.sourceFile.fileName;
22+
const options = context.options;
2123
const rootDir = options.rootDir ? path.resolve(options.rootDir) : path.resolve(".");
2224

2325
const absoluteImportPath = path.format(
@@ -27,11 +29,8 @@ function getImportPath(fileName: string, relativePath: string, node: ts.Node, op
2729
if (absoluteImportPath.includes(absoluteRootDirPath)) {
2830
return formatPathToLuaPath(absoluteImportPath.replace(absoluteRootDirPath, "").slice(1));
2931
} else {
30-
throw UnresolvableRequirePath(
31-
node,
32-
`Cannot create require path. Module does not exist within --rootDir`,
33-
relativePath
34-
);
32+
context.diagnostics.push(unresolvableRequirePath(node, relativePath));
33+
return relativePath;
3534
}
3635
}
3736

@@ -51,12 +50,7 @@ export function createModuleRequire(
5150
const params: lua.Expression[] = [];
5251
if (ts.isStringLiteral(moduleSpecifier)) {
5352
const modulePath = shouldResolveModulePath(context, moduleSpecifier)
54-
? getImportPath(
55-
context.sourceFile.fileName,
56-
moduleSpecifier.text.replace(/"/g, ""),
57-
moduleSpecifier,
58-
context.options
59-
)
53+
? getImportPath(context, moduleSpecifier.text.replace(/"/g, ""), moduleSpecifier)
6054
: moduleSpecifier.text;
6155

6256
params.push(lua.createStringLiteral(modulePath));
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`No Hoisting ("const foo = bar(); export function bar() { return \\"bar\\"; }"): code 1`] = `
4+
"local ____exports = {}
5+
local foo = ____exports.bar(nil)
6+
function ____exports.bar(self)
7+
return \\"bar\\"
8+
end
9+
return ____exports"
10+
`;
11+
12+
exports[`No Hoisting ("const foo = bar(); export function bar() { return \\"bar\\"; }"): diagnostics 1`] = `"main.ts(1,13): error TSTL: Identifier 'bar' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
13+
14+
exports[`No Hoisting ("const foo = bar(); function bar() { return \\"bar\\"; }"): code 1`] = `
15+
"local foo = bar(_G)
16+
function bar(self)
17+
return \\"bar\\"
18+
end"
19+
`;
20+
21+
exports[`No Hoisting ("const foo = bar(); function bar() { return \\"bar\\"; }"): diagnostics 1`] = `"main.ts(1,13): error TSTL: Identifier 'bar' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
22+
23+
exports[`No Hoisting ("export const foo = bar(); function bar() { return \\"bar\\"; }"): code 1`] = `
24+
"local ____exports = {}
25+
____exports.foo = bar(nil)
26+
local function bar(self)
27+
return \\"bar\\"
28+
end
29+
return ____exports"
30+
`;
31+
32+
exports[`No Hoisting ("export const foo = bar(); function bar() { return \\"bar\\"; }"): diagnostics 1`] = `"main.ts(1,20): error TSTL: Identifier 'bar' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
33+
34+
exports[`No Hoisting ("export namespace O { export function f() { return I.foo; } namespace I { export let foo = \\"foo\\"; } }"): code 1`] = `
35+
"local ____exports = {}
36+
____exports.O = {}
37+
local O = ____exports.O
38+
do
39+
function O.f(self)
40+
return I.foo
41+
end
42+
local I = {}
43+
do
44+
I.foo = \\"foo\\"
45+
end
46+
end
47+
return ____exports"
48+
`;
49+
50+
exports[`No Hoisting ("export namespace O { export function f() { return I.foo; } namespace I { export let foo = \\"foo\\"; } }"): diagnostics 1`] = `"main.ts(1,51): error TSTL: Identifier 'I' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
51+
52+
exports[`No Hoisting ("foo = \\"foo\\"; export var foo;"): code 1`] = `
53+
"local ____exports = {}
54+
____exports.foo = \\"foo\\"
55+
return ____exports"
56+
`;
57+
58+
exports[`No Hoisting ("foo = \\"foo\\"; export var foo;"): diagnostics 1`] = `"main.ts(1,1): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
59+
60+
exports[`No Hoisting ("foo = \\"foo\\"; var foo;"): code 1`] = `"foo = \\"foo\\""`;
61+
62+
exports[`No Hoisting ("foo = \\"foo\\"; var foo;"): diagnostics 1`] = `"main.ts(1,1): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
63+
64+
exports[`No Hoisting ("function bar() { return E.A; } enum E { A = \\"foo\\" }"): code 1`] = `
65+
"function bar(self)
66+
return E.A
67+
end
68+
E = {}
69+
E.A = \\"foo\\""
70+
`;
71+
72+
exports[`No Hoisting ("function bar() { return E.A; } enum E { A = \\"foo\\" }"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Identifier 'E' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
73+
74+
exports[`No Hoisting ("function bar() { return NS.foo; } namespace NS { export let foo = \\"foo\\"; }"): code 1`] = `
75+
"function bar(self)
76+
return NS.foo
77+
end
78+
NS = NS or {}
79+
do
80+
NS.foo = \\"foo\\"
81+
end"
82+
`;
83+
84+
exports[`No Hoisting ("function bar() { return NS.foo; } namespace NS { export let foo = \\"foo\\"; }"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Identifier 'NS' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
85+
86+
exports[`No Hoisting ("function makeFoo() { return new Foo(); } class Foo {}"): code 1`] = `
87+
"require(\\"lualib_bundle\\");
88+
function makeFoo(self)
89+
return __TS__New(Foo)
90+
end
91+
Foo = __TS__Class()
92+
Foo.name = \\"Foo\\"
93+
function Foo.prototype.____constructor(self)
94+
end"
95+
`;
96+
97+
exports[`No Hoisting ("function makeFoo() { return new Foo(); } class Foo {}"): diagnostics 1`] = `"main.ts(1,33): error TSTL: Identifier 'Foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
98+
99+
exports[`No Hoisting ("function setBar() { const bar = { foo }; } let foo = \\"foo\\";"): code 1`] = `
100+
"function setBar(self)
101+
local bar = {foo = foo}
102+
end
103+
local foo = \\"foo\\""
104+
`;
105+
106+
exports[`No Hoisting ("function setBar() { const bar = { foo }; } let foo = \\"foo\\";"): diagnostics 1`] = `"main.ts(1,35): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
107+
108+
exports[`No Hoisting ("function setBar() { const bar = foo; } const foo = \\"foo\\";"): code 1`] = `
109+
"function setBar(self)
110+
local bar = foo
111+
end
112+
local foo = \\"foo\\""
113+
`;
114+
115+
exports[`No Hoisting ("function setBar() { const bar = foo; } const foo = \\"foo\\";"): diagnostics 1`] = `"main.ts(1,33): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
116+
117+
exports[`No Hoisting ("function setBar() { const bar = foo; } export const foo = \\"foo\\";"): code 1`] = `
118+
"local ____exports = {}
119+
local function setBar(self)
120+
local bar = ____exports.foo
121+
end
122+
____exports.foo = \\"foo\\"
123+
return ____exports"
124+
`;
125+
126+
exports[`No Hoisting ("function setBar() { const bar = foo; } export const foo = \\"foo\\";"): diagnostics 1`] = `"main.ts(1,33): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
127+
128+
exports[`No Hoisting ("function setBar() { const bar = foo; } export let foo = \\"foo\\";"): code 1`] = `
129+
"local ____exports = {}
130+
local function setBar(self)
131+
local bar = ____exports.foo
132+
end
133+
____exports.foo = \\"foo\\"
134+
return ____exports"
135+
`;
136+
137+
exports[`No Hoisting ("function setBar() { const bar = foo; } export let foo = \\"foo\\";"): diagnostics 1`] = `"main.ts(1,33): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
138+
139+
exports[`No Hoisting ("function setBar() { const bar = foo; } let foo = \\"foo\\";"): code 1`] = `
140+
"function setBar(self)
141+
local bar = foo
142+
end
143+
local foo = \\"foo\\""
144+
`;
145+
146+
exports[`No Hoisting ("function setBar() { const bar = foo; } let foo = \\"foo\\";"): diagnostics 1`] = `"main.ts(1,33): error TSTL: Identifier 'foo' was referenced before it was declared. The declaration must be moved before the identifier's use, or hoisting must be enabled."`;
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`Throws error if decorator function has void context: code 1`] = `
4+
"require(\\"lualib_bundle\\");
5+
local ____exports = {}
6+
function ____exports.__main(self)
7+
local function decorator(constructor)
8+
end
9+
local TestClass = __TS__Class()
10+
TestClass.name = \\"TestClass\\"
11+
function TestClass.prototype.____constructor(self)
12+
end
13+
TestClass = __TS__Decorate({decorator}, TestClass)
14+
end
15+
return ____exports"
16+
`;
17+
18+
exports[`Throws error if decorator function has void context: diagnostics 1`] = `"main.ts(4,9): error TSTL: Decorator function cannot have 'this: void'."`;

test/unit/classes/decorators.spec.ts

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

43
test("Class decorator with no parameters", () => {
@@ -105,11 +104,11 @@ test("Class decorators are applied in order and executed in reverse order", () =
105104

106105
test("Throws error if decorator function has void context", () => {
107106
util.testFunction`
108-
function SetBool(this: void, constructor: new (...args: any[]) => {}) {}
107+
function decorator(this: void, constructor: new (...args: any[]) => {}) {}
109108
110-
@SetBool
109+
@decorator
111110
class TestClass {}
112-
`.expectToHaveDiagnosticOfError(InvalidDecoratorContext(util.nodeStub));
111+
`.expectDiagnosticsToMatchSnapshot();
113112
});
114113

115114
test("Exported class decorator", () => {

test/unit/hoisting.spec.ts

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
import * as ts from "typescript";
2-
import { ReferencedBeforeDeclaration } from "../../src/transformation/utils/errors";
31
import * as util from "../util";
42

53
test("Var Hoisting", () => {
@@ -219,27 +217,24 @@ test("Enum Hoisting", () => {
219217
});
220218

221219
test.each([
222-
{ code: `foo = "foo"; var foo;`, identifier: "foo" },
223-
{ code: `foo = "foo"; export var foo;`, identifier: "foo" },
224-
{ code: `function setBar() { const bar = foo; } let foo = "foo";`, identifier: "foo" },
225-
{ code: `function setBar() { const bar = foo; } const foo = "foo";`, identifier: "foo" },
226-
{ code: `function setBar() { const bar = foo; } export let foo = "foo";`, identifier: "foo" },
227-
{ code: `function setBar() { const bar = foo; } export const foo = "foo";`, identifier: "foo" },
228-
{ code: `const foo = bar(); function bar() { return "bar"; }`, identifier: "bar" },
229-
{ code: `export const foo = bar(); function bar() { return "bar"; }`, identifier: "bar" },
230-
{ code: `const foo = bar(); export function bar() { return "bar"; }`, identifier: "bar" },
231-
{ code: `function bar() { return NS.foo; } namespace NS { export let foo = "foo"; }`, identifier: "NS" },
232-
{
233-
code: `export namespace O { export function f() { return I.foo; } namespace I { export let foo = "foo"; } }`,
234-
identifier: "I",
235-
},
236-
{ code: `function makeFoo() { return new Foo(); } class Foo {}`, identifier: "Foo" },
237-
{ code: `function bar() { return E.A; } enum E { A = "foo" }`, identifier: "E" },
238-
{ code: `function setBar() { const bar = { foo }; } let foo = "foo";`, identifier: "foo" },
239-
])("No Hoisting (%p)", ({ code, identifier }) => {
240-
expect(() => util.transpileString(code, { noHoisting: true })).toThrowExactError(
241-
ReferencedBeforeDeclaration(ts.createIdentifier(identifier))
242-
);
220+
`foo = "foo"; var foo;`,
221+
`foo = "foo"; export var foo;`,
222+
`function setBar() { const bar = foo; } let foo = "foo";`,
223+
`function setBar() { const bar = foo; } const foo = "foo";`,
224+
`function setBar() { const bar = foo; } export let foo = "foo";`,
225+
`function setBar() { const bar = foo; } export const foo = "foo";`,
226+
`const foo = bar(); function bar() { return "bar"; }`,
227+
`export const foo = bar(); function bar() { return "bar"; }`,
228+
`const foo = bar(); export function bar() { return "bar"; }`,
229+
`function bar() { return NS.foo; } namespace NS { export let foo = "foo"; }`,
230+
`export namespace O { export function f() { return I.foo; } namespace I { export let foo = "foo"; } }`,
231+
`function makeFoo() { return new Foo(); } class Foo {}`,
232+
`function bar() { return E.A; } enum E { A = "foo" }`,
233+
`function setBar() { const bar = { foo }; } let foo = "foo";`,
234+
])("No Hoisting (%p)", (code) => {
235+
util.testModule(code)
236+
.setOptions({ noHoisting: true })
237+
.expectDiagnosticsToMatchSnapshot();
243238
});
244239

245240
test("Import hoisting (named)", () => {

0 commit comments

Comments
 (0)