@tilua/parser 2.3.0 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +45 -23
- package/dist/index.cjs +431 -65
- package/dist/index.d.cts +73 -23
- package/dist/index.d.ts +73 -23
- package/dist/index.js +431 -65
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -629,7 +629,8 @@ var DECLARATION_STATEMENTS = /* @__PURE__ */ new Set([
|
|
|
629
629
|
"ExportNamedStatement",
|
|
630
630
|
"ExportAllStatement",
|
|
631
631
|
"DeclareStatement",
|
|
632
|
-
"DeclareClassStatement"
|
|
632
|
+
"DeclareClassStatement",
|
|
633
|
+
"DeclareMetatableStatement"
|
|
633
634
|
]);
|
|
634
635
|
function spanFrom(start, end) {
|
|
635
636
|
return {
|
|
@@ -1247,6 +1248,9 @@ var Parser = class {
|
|
|
1247
1248
|
if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
|
|
1248
1249
|
return this.parseDeclareClassStatement();
|
|
1249
1250
|
}
|
|
1251
|
+
if (p1.type === "Identifier" && p1.value === "metatable" && !this.punctuatorAt(2, ":")) {
|
|
1252
|
+
return this.parseDeclareMetatableStatement();
|
|
1253
|
+
}
|
|
1250
1254
|
if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
|
|
1251
1255
|
return this.parseDeclareStatement();
|
|
1252
1256
|
}
|
|
@@ -1299,6 +1303,17 @@ var Parser = class {
|
|
|
1299
1303
|
}
|
|
1300
1304
|
return tokenIdentifier(first);
|
|
1301
1305
|
}
|
|
1306
|
+
// `declare metatable<T> T[]: { __index: { ... } }`
|
|
1307
|
+
parseDeclareMetatableStatement() {
|
|
1308
|
+
const start = this.current();
|
|
1309
|
+
this.advance();
|
|
1310
|
+
this.advance();
|
|
1311
|
+
const generics = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
|
|
1312
|
+
const target = this.parseType();
|
|
1313
|
+
this.expectPunctuator(":");
|
|
1314
|
+
const metatable = this.parseType();
|
|
1315
|
+
return { type: "DeclareMetatableStatement", generics, target, metatable, ...spanFrom(start, this.previous()) };
|
|
1316
|
+
}
|
|
1302
1317
|
// `declare class Name extends Base { member: T, ... }`
|
|
1303
1318
|
parseDeclareClassStatement() {
|
|
1304
1319
|
const start = this.current();
|
|
@@ -2007,7 +2022,15 @@ var Parser = class {
|
|
|
2007
2022
|
parseExpressionStatement() {
|
|
2008
2023
|
const start = this.current();
|
|
2009
2024
|
if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
|
|
2010
|
-
const
|
|
2025
|
+
const call = this.tryParse(() => {
|
|
2026
|
+
const errors = this.errors.length;
|
|
2027
|
+
const expression2 = this.parseAtom();
|
|
2028
|
+
if (expression2.type !== "MethodCallExpression" && expression2.type !== "CallExpression") throw new ParseRecover("not a call");
|
|
2029
|
+
if (this.errors.length > errors) throw new ParseRecover("not a call");
|
|
2030
|
+
return expression2;
|
|
2031
|
+
});
|
|
2032
|
+
if (call) return { type: "CallStatement", expression: call, ...spanFrom(start, this.previous()) };
|
|
2033
|
+
const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
|
|
2011
2034
|
return this.parseAssignmentRest(start, target);
|
|
2012
2035
|
}
|
|
2013
2036
|
const statementStart = this.cursor;
|
|
@@ -2328,11 +2351,9 @@ var Parser = class {
|
|
|
2328
2351
|
if (t.type === "Identifier" && t.value === "class") {
|
|
2329
2352
|
return this.parseClassExpression();
|
|
2330
2353
|
}
|
|
2331
|
-
if (t.type === "Punctuator" && t.value === "{") {
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
if (t.type === "Punctuator" && t.value === "[") {
|
|
2335
|
-
return this.parseArrayExpression();
|
|
2354
|
+
if (t.type === "Punctuator" && (t.value === "{" || t.value === "[")) {
|
|
2355
|
+
const literal2 = t.value === "{" ? this.parseTableExpression() : this.parseArrayExpression();
|
|
2356
|
+
return this.checkPunctuator(":") && this.startsMethodCall() ? this.parseSuffixes(literal2) : literal2;
|
|
2336
2357
|
}
|
|
2337
2358
|
if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
|
|
2338
2359
|
if (this.checkPunctuator("(") || this.checkOperator("<")) {
|
|
@@ -2409,6 +2430,11 @@ var Parser = class {
|
|
|
2409
2430
|
} else {
|
|
2410
2431
|
this.error("Expected identifier or '('");
|
|
2411
2432
|
}
|
|
2433
|
+
return this.parseSuffixes(base);
|
|
2434
|
+
}
|
|
2435
|
+
/** What follows a prefix expression's base: `.name`, `[key]`, `:m(...)`,
|
|
2436
|
+
* a call, and their optional forms. */
|
|
2437
|
+
parseSuffixes(base) {
|
|
2412
2438
|
while (true) {
|
|
2413
2439
|
if (this.checkPunctuator("?") && this.touchesNext()) {
|
|
2414
2440
|
const next = this.peek(1);
|
|
@@ -2562,8 +2588,8 @@ var Parser = class {
|
|
|
2562
2588
|
/** An assignment target after the first — Luau's `a, b = ...`, read only
|
|
2563
2589
|
* to be reported: a prefix expression or a destructuring pattern. */
|
|
2564
2590
|
parseAssignTarget() {
|
|
2565
|
-
if (this.checkPunctuator("{")) return this.parseObjectPattern();
|
|
2566
|
-
if (this.checkPunctuator("[")) return this.parseArrayPattern();
|
|
2591
|
+
if (this.checkPunctuator("{")) return this.parseObjectPattern(true);
|
|
2592
|
+
if (this.checkPunctuator("[")) return this.parseArrayPattern(true);
|
|
2567
2593
|
return this.parsePrefixExpression();
|
|
2568
2594
|
}
|
|
2569
2595
|
/** Does a call's argument list start here? Lua's three forms: `(`, a
|
|
@@ -2742,13 +2768,17 @@ var Parser = class {
|
|
|
2742
2768
|
/** Parses a binding target. When `topLevel`, also consumes a trailing
|
|
2743
2769
|
* `<attr>` list (identifier only) and a `: Type` annotation — these are
|
|
2744
2770
|
* only valid at the outermost level of a `local` / parameter binding,
|
|
2745
|
-
* never nested inside another pattern.
|
|
2746
|
-
|
|
2771
|
+
* never nested inside another pattern. When `assign`, the pattern is a
|
|
2772
|
+
* destructuring assignment's (`[t[i], t[j]] = ...`), and a leaf is any
|
|
2773
|
+
* place a value can be assigned to: a name, a member or an index. */
|
|
2774
|
+
parseBindingTarget(topLevel, assign = false) {
|
|
2747
2775
|
let target;
|
|
2748
2776
|
if (this.checkPunctuator("{")) {
|
|
2749
|
-
target = this.parseObjectPattern();
|
|
2777
|
+
target = this.parseObjectPattern(assign);
|
|
2750
2778
|
} else if (this.checkPunctuator("[")) {
|
|
2751
|
-
target = this.parseArrayPattern();
|
|
2779
|
+
target = this.parseArrayPattern(assign);
|
|
2780
|
+
} else if (assign) {
|
|
2781
|
+
return this.parseAssignPatternLeaf();
|
|
2752
2782
|
} else {
|
|
2753
2783
|
const nameTok = this.expectIdentifier();
|
|
2754
2784
|
let attributes;
|
|
@@ -2770,10 +2800,25 @@ var Parser = class {
|
|
|
2770
2800
|
}
|
|
2771
2801
|
return target;
|
|
2772
2802
|
}
|
|
2773
|
-
|
|
2774
|
-
|
|
2803
|
+
/** A leaf of a destructuring assignment: `a`, `a.b` or `a[i]`. */
|
|
2804
|
+
parseAssignPatternLeaf() {
|
|
2805
|
+
const expression = this.parsePrefixExpression();
|
|
2806
|
+
if (expression.type === "Identifier") {
|
|
2807
|
+
return { type: "IdentifierPattern", name: expression.name, line: expression.line, column: expression.column };
|
|
2808
|
+
}
|
|
2809
|
+
if (expression.type === "MemberExpression" || expression.type === "IndexExpression") {
|
|
2810
|
+
this.rejectOptionalTarget(expression);
|
|
2811
|
+
return expression;
|
|
2812
|
+
}
|
|
2813
|
+
const err = new ParseError("Only a name, a member or an index can be assigned to", expression.line.start, expression.column.start);
|
|
2814
|
+
if (!this.recover) throw err;
|
|
2815
|
+
this.record(err);
|
|
2816
|
+
return { type: "IdentifierPattern", name: "", line: expression.line, column: expression.column };
|
|
2817
|
+
}
|
|
2818
|
+
parseObjectPattern(assign = false) {
|
|
2819
|
+
return this.inBrackets(() => this.parseObjectPatternInner(assign));
|
|
2775
2820
|
}
|
|
2776
|
-
parseObjectPatternInner() {
|
|
2821
|
+
parseObjectPatternInner(assign) {
|
|
2777
2822
|
const start = this.current();
|
|
2778
2823
|
this.expectPunctuator("{");
|
|
2779
2824
|
const properties = [];
|
|
@@ -2781,7 +2826,7 @@ var Parser = class {
|
|
|
2781
2826
|
while (!this.checkPunctuator("}")) {
|
|
2782
2827
|
if (this.checkOperator("...")) {
|
|
2783
2828
|
this.advance();
|
|
2784
|
-
rest = this.parseBindingTarget(false);
|
|
2829
|
+
rest = this.parseBindingTarget(false, assign);
|
|
2785
2830
|
break;
|
|
2786
2831
|
}
|
|
2787
2832
|
const propStart = this.current();
|
|
@@ -2794,17 +2839,17 @@ var Parser = class {
|
|
|
2794
2839
|
key = this.parseExpression();
|
|
2795
2840
|
this.expectPunctuator("]");
|
|
2796
2841
|
this.expectPunctuator(":");
|
|
2797
|
-
value = this.parseBindingTarget(false);
|
|
2842
|
+
value = this.parseBindingTarget(false, assign);
|
|
2798
2843
|
} else if (this.checkType("Literal") && this.current().kind === "string") {
|
|
2799
2844
|
const t = this.advance();
|
|
2800
2845
|
key = { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
|
|
2801
2846
|
this.expectPunctuator(":");
|
|
2802
|
-
value = this.parseBindingTarget(false);
|
|
2847
|
+
value = this.parseBindingTarget(false, assign);
|
|
2803
2848
|
} else {
|
|
2804
2849
|
const nameTok = this.expectIdentifier();
|
|
2805
2850
|
key = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
|
|
2806
2851
|
if (this.matchPunctuator(":")) {
|
|
2807
|
-
value = this.parseBindingTarget(false);
|
|
2852
|
+
value = this.parseBindingTarget(false, assign);
|
|
2808
2853
|
} else {
|
|
2809
2854
|
shorthand = true;
|
|
2810
2855
|
value = { type: "IdentifierPattern", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
|
|
@@ -2827,10 +2872,10 @@ var Parser = class {
|
|
|
2827
2872
|
this.expectPunctuator("}");
|
|
2828
2873
|
return { type: "ObjectPattern", properties, rest, ...spanFrom(start, this.previous()) };
|
|
2829
2874
|
}
|
|
2830
|
-
parseArrayPattern() {
|
|
2831
|
-
return this.inBrackets(() => this.parseArrayPatternInner());
|
|
2875
|
+
parseArrayPattern(assign = false) {
|
|
2876
|
+
return this.inBrackets(() => this.parseArrayPatternInner(assign));
|
|
2832
2877
|
}
|
|
2833
|
-
parseArrayPatternInner() {
|
|
2878
|
+
parseArrayPatternInner(assign) {
|
|
2834
2879
|
const start = this.current();
|
|
2835
2880
|
this.expectPunctuator("[");
|
|
2836
2881
|
const elements = [];
|
|
@@ -2838,7 +2883,7 @@ var Parser = class {
|
|
|
2838
2883
|
while (!this.checkPunctuator("]")) {
|
|
2839
2884
|
if (this.checkOperator("...")) {
|
|
2840
2885
|
this.advance();
|
|
2841
|
-
rest = this.parseBindingTarget(false);
|
|
2886
|
+
rest = this.parseBindingTarget(false, assign);
|
|
2842
2887
|
break;
|
|
2843
2888
|
}
|
|
2844
2889
|
if (this.checkPunctuator(",")) {
|
|
@@ -2847,7 +2892,7 @@ var Parser = class {
|
|
|
2847
2892
|
continue;
|
|
2848
2893
|
}
|
|
2849
2894
|
const elStart = this.current();
|
|
2850
|
-
const value = this.parseBindingTarget(false);
|
|
2895
|
+
const value = this.parseBindingTarget(false, assign);
|
|
2851
2896
|
let def;
|
|
2852
2897
|
if (this.matchOperator("=")) def = this.parseExpression();
|
|
2853
2898
|
elements.push({ type: "ArrayPatternElement", value, default: def, ...spanFrom(elStart, this.previous()) });
|
|
@@ -3984,6 +4029,10 @@ var Analyzer = class {
|
|
|
3984
4029
|
this.checkConstAssign(id, t);
|
|
3985
4030
|
return;
|
|
3986
4031
|
}
|
|
4032
|
+
case "MemberExpression":
|
|
4033
|
+
case "IndexExpression":
|
|
4034
|
+
this.visitExpression(t, scope);
|
|
4035
|
+
return;
|
|
3987
4036
|
case "ObjectPattern":
|
|
3988
4037
|
for (const p of t.properties) {
|
|
3989
4038
|
if (p.computed) this.visitExpression(p.key, scope);
|
|
@@ -4019,7 +4068,9 @@ var Analyzer = class {
|
|
|
4019
4068
|
switch (stmt.type) {
|
|
4020
4069
|
case "VariableDeclaration": {
|
|
4021
4070
|
if (stmt.init) this.visitExpression(stmt.init, scope);
|
|
4022
|
-
|
|
4071
|
+
if (stmt.name.type !== "MemberExpression" && stmt.name.type !== "IndexExpression") {
|
|
4072
|
+
this.visitType(stmt.name.typeAnnotation, scope);
|
|
4073
|
+
}
|
|
4023
4074
|
this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
|
|
4024
4075
|
return;
|
|
4025
4076
|
}
|
|
@@ -4134,6 +4185,11 @@ var Analyzer = class {
|
|
|
4134
4185
|
case "DeclareClassStatement":
|
|
4135
4186
|
this.visitType(stmt.body, scope);
|
|
4136
4187
|
return;
|
|
4188
|
+
case "DeclareMetatableStatement":
|
|
4189
|
+
this.visitGenerics(stmt.generics, scope);
|
|
4190
|
+
this.visitType(stmt.target, scope);
|
|
4191
|
+
this.visitType(stmt.metatable, scope);
|
|
4192
|
+
return;
|
|
4137
4193
|
case "TypeAliasStatement":
|
|
4138
4194
|
case "ExportTypeAliasStatement":
|
|
4139
4195
|
this.visitGenerics(
|
|
@@ -4399,6 +4455,101 @@ type Mutable<T> = { -readonly [K in keyof T]: T[K] }
|
|
|
4399
4455
|
type Pick<T, K> = { [P in K]: T[P] }
|
|
4400
4456
|
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
|
|
4401
4457
|
type Record<K, V> = { [P in K]: V }
|
|
4458
|
+
|
|
4459
|
+
-- ============================================================
|
|
4460
|
+
-- The metatables of the language's own values
|
|
4461
|
+
-- ============================================================
|
|
4462
|
+
|
|
4463
|
+
-- A string's methods. The first group is Lua's string library, which a
|
|
4464
|
+
-- string really answers to; where Lua answers several values, tilua answers
|
|
4465
|
+
-- one, as the global functions do (\`find\` is an array, \`gsub\` the new
|
|
4466
|
+
-- string). The second group is written the way JavaScript writes it.
|
|
4467
|
+
--
|
|
4468
|
+
-- Positions are Lua's: \`sub\` and \`slice\` count from 1, and \`indexOf\` is
|
|
4469
|
+
-- \`nil\` when the text is not there.
|
|
4470
|
+
declare metatable string: {
|
|
4471
|
+
__index: {
|
|
4472
|
+
upper: (self: string) => string,
|
|
4473
|
+
lower: (self: string) => string,
|
|
4474
|
+
len: (self: string) => number,
|
|
4475
|
+
sub: (self: string, i: number, j?: number) => string,
|
|
4476
|
+
rep: (self: string, n: number, separator?: string) => string,
|
|
4477
|
+
reverse: (self: string) => string,
|
|
4478
|
+
format: (self: string, ...args: unknown[]) => string,
|
|
4479
|
+
byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),
|
|
4480
|
+
find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,
|
|
4481
|
+
match: (self: string, pattern: string, init?: number) => [...string[]] | nil,
|
|
4482
|
+
gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,
|
|
4483
|
+
gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,
|
|
4484
|
+
|
|
4485
|
+
trim: (self: string) => string,
|
|
4486
|
+
trimStart: (self: string) => string,
|
|
4487
|
+
trimEnd: (self: string) => string,
|
|
4488
|
+
startsWith: (self: string, text: string) => boolean,
|
|
4489
|
+
endsWith: (self: string, text: string) => boolean,
|
|
4490
|
+
includes: (self: string, text: string) => boolean,
|
|
4491
|
+
indexOf: (self: string, text: string, start?: number) => number | nil,
|
|
4492
|
+
slice: (self: string, start?: number, stop?: number) => string,
|
|
4493
|
+
replace: (self: string, text: string, replacement: string) => string,
|
|
4494
|
+
replaceAll: (self: string, text: string, replacement: string) => string,
|
|
4495
|
+
padStart: (self: string, length: number, padding?: string) => string,
|
|
4496
|
+
padEnd: (self: string, length: number, padding?: string) => string,
|
|
4497
|
+
},
|
|
4498
|
+
}
|
|
4499
|
+
|
|
4500
|
+
-- An array's methods. Lua gives an array no metatable: the compiler lowers
|
|
4501
|
+
-- each call to a plain function, so they work on any array, one a Lua
|
|
4502
|
+
-- library made included.
|
|
4503
|
+
--
|
|
4504
|
+
-- Indices are Lua's: the first element is 1, and \`indexOf\` / \`findIndex\`
|
|
4505
|
+
-- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
|
|
4506
|
+
-- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
|
|
4507
|
+
-- \`sort\` and \`reverse\` change the array; everything else returns a new one.
|
|
4508
|
+
declare metatable<T> T[]: {
|
|
4509
|
+
__index: {
|
|
4510
|
+
find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
|
|
4511
|
+
findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
|
|
4512
|
+
filter: (self: T[], test: (value: T, index: number) => boolean) => T[],
|
|
4513
|
+
map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
|
|
4514
|
+
forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
|
|
4515
|
+
some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
|
|
4516
|
+
every: (self: T[], test: (value: T, index: number) => boolean) => boolean,
|
|
4517
|
+
reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,
|
|
4518
|
+
includes: (self: T[], value: T) => boolean,
|
|
4519
|
+
indexOf: (self: T[], value: T, start?: number) => number | nil,
|
|
4520
|
+
join: (self: T[], separator?: string) => string,
|
|
4521
|
+
concat: (self: T[], ...others: T[][]) => T[],
|
|
4522
|
+
slice: (self: T[], start?: number, stop?: number) => T[],
|
|
4523
|
+
flat: (self: T[]) => T[],
|
|
4524
|
+
reverse: (self: T[]) => T[],
|
|
4525
|
+
sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],
|
|
4526
|
+
push: (self: T[], ...args: T[]) => number,
|
|
4527
|
+
pop: (self: T[]) => T | nil,
|
|
4528
|
+
shift: (self: T[]) => T | nil,
|
|
4529
|
+
unshift: (self: T[], ...args: T[]) => number,
|
|
4530
|
+
},
|
|
4531
|
+
}
|
|
4532
|
+
|
|
4533
|
+
-- A table's methods. Unlike JavaScript's \`Object.keys\`, they answer a
|
|
4534
|
+
-- tuple, in the order the table's type wrote its members:
|
|
4535
|
+
--
|
|
4536
|
+
-- const point = { x: 1, y: "up" }
|
|
4537
|
+
-- point:keys() -- ["x", "y"]
|
|
4538
|
+
-- point:values() -- [number, string]
|
|
4539
|
+
-- point:entries() -- [["x", number], ["y", string]]
|
|
4540
|
+
--
|
|
4541
|
+
-- A Lua table has no order of its own, so the compiler hands its runtime the
|
|
4542
|
+
-- keys as the type lists them. An optional member keeps its place, holding
|
|
4543
|
+
-- \`nil\` when it is missing; an indexer's keys follow the named ones. A
|
|
4544
|
+
-- member of the table's own with one of these names is what a call reaches,
|
|
4545
|
+
-- and a class instance, which has a metatable of its own, has none of them.
|
|
4546
|
+
declare metatable<T extends {}> T: {
|
|
4547
|
+
__index: {
|
|
4548
|
+
keys: (self: T) => ObjectKeys<T>,
|
|
4549
|
+
values: (self: T) => ObjectValues<T>,
|
|
4550
|
+
entries: (self: T) => ObjectEntries<T>,
|
|
4551
|
+
},
|
|
4552
|
+
}
|
|
4402
4553
|
`;
|
|
4403
4554
|
var prelude;
|
|
4404
4555
|
function preludeProgram() {
|
|
@@ -5764,6 +5915,10 @@ var TypeAnalyzer = class {
|
|
|
5764
5915
|
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
5765
5916
|
expectedTypeOf = /* @__PURE__ */ new Map();
|
|
5766
5917
|
loops = /* @__PURE__ */ new Map();
|
|
5918
|
+
/** Every `declare metatable`, libraries first, in declaration order. */
|
|
5919
|
+
metatables = [];
|
|
5920
|
+
metatableCache = /* @__PURE__ */ new WeakMap();
|
|
5921
|
+
methodSources = /* @__PURE__ */ new Map();
|
|
5767
5922
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
5768
5923
|
* `typeParam` nodes in the body). */
|
|
5769
5924
|
aliases = new LazyMap();
|
|
@@ -5830,6 +5985,9 @@ var TypeAnalyzer = class {
|
|
|
5830
5985
|
this.registerNestedClasses();
|
|
5831
5986
|
this.registerNestedAliases();
|
|
5832
5987
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
5988
|
+
this.harvestMetatables(preludeProgram().body, { origin: "language" });
|
|
5989
|
+
(this.options.libs ?? []).forEach((lib, i) => this.harvestMetatables(lib.body, { origin: "library", library: i }));
|
|
5990
|
+
this.harvestMetatables(this.program.body, { origin: "program" });
|
|
5833
5991
|
this.registerImportedTypes();
|
|
5834
5992
|
this.resolveAllAliases();
|
|
5835
5993
|
this.harvestDeclares(this.program.body, true);
|
|
@@ -5861,6 +6019,8 @@ var TypeAnalyzer = class {
|
|
|
5861
6019
|
expectedTypeOf: this.expectedTypeOf,
|
|
5862
6020
|
aliases: this.resolveDeferredAliases(),
|
|
5863
6021
|
loops: this.loops,
|
|
6022
|
+
methodSources: this.methodSources,
|
|
6023
|
+
metatableMembers: (type) => this.metatableMembers(type),
|
|
5864
6024
|
diagnostics: this.diagnostics
|
|
5865
6025
|
};
|
|
5866
6026
|
}
|
|
@@ -5918,8 +6078,7 @@ var TypeAnalyzer = class {
|
|
|
5918
6078
|
}
|
|
5919
6079
|
/** `layering` is on for the prelude and for definitions files: a second
|
|
5920
6080
|
* library that declares an alias already declared *adds* to it, the way a
|
|
5921
|
-
* second `declare` of a table's name does
|
|
5922
|
-
* `StringMethods` Luau's `split` without restating Lua's. The file being
|
|
6081
|
+
* second `declare` of a table's name does. The file being
|
|
5923
6082
|
* analysed is not a layer: its own alias replaces what the libraries
|
|
5924
6083
|
* gave, which is how a project opts out of a set. */
|
|
5925
6084
|
registerAliasDefs(block, layering = false) {
|
|
@@ -6436,6 +6595,14 @@ var TypeAnalyzer = class {
|
|
|
6436
6595
|
report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
|
|
6437
6596
|
}
|
|
6438
6597
|
}
|
|
6598
|
+
this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
|
|
6599
|
+
this.withSelfType(this.selfTypeOf(stmt), () => {
|
|
6600
|
+
for (const member of stmt.members) {
|
|
6601
|
+
if (member.type !== "ClassMethod" || !member.signatures) continue;
|
|
6602
|
+
for (const signature of member.signatures) this.checkOverload(member.func, signature);
|
|
6603
|
+
}
|
|
6604
|
+
});
|
|
6605
|
+
}));
|
|
6439
6606
|
for (const member of stmt.members) {
|
|
6440
6607
|
if (member.type === "ClassConstructor") continue;
|
|
6441
6608
|
if (CLASS_RESERVED.has(member.name.name)) {
|
|
@@ -6829,7 +6996,7 @@ var TypeAnalyzer = class {
|
|
|
6829
6996
|
if (record.type === "TypeReference" && typeof record.base === "string") {
|
|
6830
6997
|
const name = typeof record.namespace === "string" ? `${record.namespace}.${record.base}` : record.base;
|
|
6831
6998
|
const resolved = this.typeOfTypeNode.get(node);
|
|
6832
|
-
const unresolved = resolved?.kind === "genericRef" && resolved.name === name && !this.aliasDefs.has(name) && !this.importedTypes.has(name) && this.options.libTypes?.[name] === void 0 && !
|
|
6999
|
+
const unresolved = resolved?.kind === "genericRef" && resolved.name === name && !this.aliasDefs.has(name) && !this.importedTypes.has(name) && this.options.libTypes?.[name] === void 0 && !INTRINSICS.has(name) && !this.importedNames().has(name.split(".")[0]);
|
|
6833
7000
|
const at = node;
|
|
6834
7001
|
const key = `${at.line.start}:${at.column.start}`;
|
|
6835
7002
|
if (unresolved && !reported.has(key)) {
|
|
@@ -6947,7 +7114,7 @@ var TypeAnalyzer = class {
|
|
|
6947
7114
|
const tp = this.lookupTypeParam(node.base);
|
|
6948
7115
|
if (tp) return typeParam(tp.name, tp.constraint, tp.isConst);
|
|
6949
7116
|
if (node.typeArguments.length === 1 && !this.aliasDefs.has(node.base)) {
|
|
6950
|
-
const intrinsic = this.
|
|
7117
|
+
const intrinsic = this.applyIntrinsic(
|
|
6951
7118
|
node.base,
|
|
6952
7119
|
this.resolveType(node.typeArguments[0])
|
|
6953
7120
|
);
|
|
@@ -7154,7 +7321,7 @@ var TypeAnalyzer = class {
|
|
|
7154
7321
|
return difference(this.reduceType(t.base), this.reduceType(t.excluded));
|
|
7155
7322
|
case "genericRef": {
|
|
7156
7323
|
if (t.typeArguments.length !== 1 || this.aliasDefs.has(t.name)) return t;
|
|
7157
|
-
return this.
|
|
7324
|
+
return this.applyIntrinsic(t.name, t.typeArguments[0]) ?? t;
|
|
7158
7325
|
}
|
|
7159
7326
|
case "conditional":
|
|
7160
7327
|
return this.reduceConditional(t);
|
|
@@ -7267,6 +7434,41 @@ var TypeAnalyzer = class {
|
|
|
7267
7434
|
}
|
|
7268
7435
|
return union(combos.map((c) => literal(c)));
|
|
7269
7436
|
}
|
|
7437
|
+
/** A type function the analyzer supplies, since tilua cannot write it. */
|
|
7438
|
+
applyIntrinsic(name, arg) {
|
|
7439
|
+
return this.applyStringIntrinsic(name, arg) ?? this.applyObjectIntrinsic(name, arg);
|
|
7440
|
+
}
|
|
7441
|
+
/** `ObjectKeys<T>`, `ObjectValues<T>`, `ObjectEntries<T>`: an object's
|
|
7442
|
+
* members as a tuple, in the order they were written — `{ a: 1, b: "x" }`
|
|
7443
|
+
* has keys `["a", "b"]`, values `[number, string]` and entries
|
|
7444
|
+
* `[["a", number], ["b", string]]`. TypeScript answers `string[]` for
|
|
7445
|
+
* `Object.keys`, since its objects have no order; tilua's lowering hands
|
|
7446
|
+
* the runtime the written order, so the tuple is what runs. An indexer's
|
|
7447
|
+
* keys follow the named ones, as the tuple's rest. */
|
|
7448
|
+
applyObjectIntrinsic(name, arg) {
|
|
7449
|
+
if (!OBJECT_INTRINSICS.has(name)) return void 0;
|
|
7450
|
+
const t = this.expand(this.reduceType(arg));
|
|
7451
|
+
if (containsTypeParam(t)) return void 0;
|
|
7452
|
+
if (t.kind === "union") return union(t.types.map((m) => this.applyObjectIntrinsic(name, m) ?? unknownType));
|
|
7453
|
+
const parts = t.kind === "intersection" ? t.types.map((m) => this.expand(m)) : [t];
|
|
7454
|
+
if (!parts.every((m) => m.kind === "object" && !m.class)) return unknownType;
|
|
7455
|
+
const properties = /* @__PURE__ */ new Map();
|
|
7456
|
+
let indexer;
|
|
7457
|
+
for (const part of parts) {
|
|
7458
|
+
for (const [key, property] of part.properties) properties.set(key, property);
|
|
7459
|
+
indexer ??= part.indexer;
|
|
7460
|
+
}
|
|
7461
|
+
const entries = [...properties].map(([key, p]) => [literal(key), p.optional ? optional(p.type) : p.type]);
|
|
7462
|
+
const listed = (elements, rest) => !elements.length && rest ? arrayOf(rest) : tuple(elements, rest);
|
|
7463
|
+
switch (name) {
|
|
7464
|
+
case "ObjectKeys":
|
|
7465
|
+
return listed(entries.map(([key]) => key), indexer?.key);
|
|
7466
|
+
case "ObjectValues":
|
|
7467
|
+
return listed(entries.map(([, value]) => value), indexer?.value);
|
|
7468
|
+
default:
|
|
7469
|
+
return listed(entries.map((entry) => tuple([...entry])), indexer && tuple([indexer.key, indexer.value]));
|
|
7470
|
+
}
|
|
7471
|
+
}
|
|
7270
7472
|
/** TypeScript's four string intrinsics. They cannot be written in tilua —
|
|
7271
7473
|
* there is no character-level type arithmetic — so the analyzer supplies
|
|
7272
7474
|
* them, and only them, as named type functions. */
|
|
@@ -7942,7 +8144,7 @@ var TypeAnalyzer = class {
|
|
|
7942
8144
|
/** Synthesize a type from a destructuring pattern used without an
|
|
7943
8145
|
* annotation (`function f({ a, b = 1 })`). */
|
|
7944
8146
|
patternToType(target, env) {
|
|
7945
|
-
const leaf = (v, def) => v.type
|
|
8147
|
+
const leaf = (v, def) => v.type === "ObjectPattern" || v.type === "ArrayPattern" ? this.patternToType(v, env) : v.type !== "IdentifierPattern" ? anyType : v.typeAnnotation ? this.resolveType(v.typeAnnotation) : def ? widen(this.infer(def, env)) : anyType;
|
|
7946
8148
|
if (target.type === "ObjectPattern") {
|
|
7947
8149
|
const entries = [];
|
|
7948
8150
|
for (const p of target.properties) {
|
|
@@ -8348,6 +8550,7 @@ var TypeAnalyzer = class {
|
|
|
8348
8550
|
* can actually be. An annotation, a pattern or a default still wins. */
|
|
8349
8551
|
paramsFromSignatures(func, signatures) {
|
|
8350
8552
|
if (!signatures?.length) return;
|
|
8553
|
+
for (const signature of signatures) this.checkOverload(func, signature);
|
|
8351
8554
|
const resolved = signatures.map((sig) => this.signatureToFnType(sig));
|
|
8352
8555
|
func.params.forEach((param, i) => {
|
|
8353
8556
|
if (param.typeAnnotation || param.pattern || param.default) return;
|
|
@@ -8361,6 +8564,64 @@ var TypeAnalyzer = class {
|
|
|
8361
8564
|
if (candidates.length) this.contextualParams.set(param, union(candidates));
|
|
8362
8565
|
});
|
|
8363
8566
|
}
|
|
8567
|
+
/** The implementation is what runs for every signature of an overload
|
|
8568
|
+
* set, so each signature must be one it can serve: no parameter it does
|
|
8569
|
+
* not have, no fewer arguments than it requires, no argument type it does
|
|
8570
|
+
* not accept, and a return it can give. Stricter than TypeScript, which
|
|
8571
|
+
* lets a signature name parameters the implementation never reads. */
|
|
8572
|
+
checkOverload(func, signature) {
|
|
8573
|
+
if (!this.emitDiagnostics) return;
|
|
8574
|
+
const node = signature;
|
|
8575
|
+
const report = (message) => {
|
|
8576
|
+
this.diagnostics.push({ node, message });
|
|
8577
|
+
};
|
|
8578
|
+
const own = (params) => params.filter((p, i) => !p.rest && !(i === 0 && !p.typeAnnotation && (p.name === "self" || p.name === "this")));
|
|
8579
|
+
const implementation = own(func.params);
|
|
8580
|
+
const offered = own(signature.params);
|
|
8581
|
+
const implementationRest = func.params.some((p) => p.rest);
|
|
8582
|
+
const signatureRest = signature.params.some((p) => p.rest);
|
|
8583
|
+
const label = (p) => p.pattern ? "a destructured parameter" : `'${p.name}'`;
|
|
8584
|
+
if (!implementationRest) {
|
|
8585
|
+
const extra = offered[implementation.length];
|
|
8586
|
+
if (extra) {
|
|
8587
|
+
report(`This overload takes parameter ${label(extra)}, which the implementation does not have: it takes ${implementation.length} parameter${implementation.length === 1 ? "" : "s"}`);
|
|
8588
|
+
return;
|
|
8589
|
+
}
|
|
8590
|
+
if (signatureRest) {
|
|
8591
|
+
report("This overload takes a rest parameter, which the implementation does not have");
|
|
8592
|
+
return;
|
|
8593
|
+
}
|
|
8594
|
+
}
|
|
8595
|
+
if (!signatureRest) {
|
|
8596
|
+
let required = 0;
|
|
8597
|
+
implementation.forEach((p, i) => {
|
|
8598
|
+
if (!p.optional && p.default === void 0) required = i + 1;
|
|
8599
|
+
});
|
|
8600
|
+
if (offered.length < required) {
|
|
8601
|
+
report(`The implementation requires ${required} argument${required === 1 ? "" : "s"}, and this overload passes at most ${offered.length}`);
|
|
8602
|
+
return;
|
|
8603
|
+
}
|
|
8604
|
+
}
|
|
8605
|
+
if (signature.generics.length || func.generics.length) return;
|
|
8606
|
+
for (let i = 0; i < offered.length && i < implementation.length; i++) {
|
|
8607
|
+
const target = implementation[i];
|
|
8608
|
+
if (!target.typeAnnotation && target.default === void 0) continue;
|
|
8609
|
+
let accepted = this.paramType(target, /* @__PURE__ */ new Map());
|
|
8610
|
+
if (target.default !== void 0) accepted = optional(accepted);
|
|
8611
|
+
const given = this.paramType(offered[i], /* @__PURE__ */ new Map());
|
|
8612
|
+
if (!isAssignable(given, accepted)) {
|
|
8613
|
+
report(`Parameter ${label(offered[i])} of this overload is '${briefType(given)}', which the implementation's '${briefType(accepted)}' does not accept`);
|
|
8614
|
+
return;
|
|
8615
|
+
}
|
|
8616
|
+
}
|
|
8617
|
+
if (signature.returnType && func.returnType) {
|
|
8618
|
+
const promised = this.resolveType(signature.returnType);
|
|
8619
|
+
const returned = this.resolveType(func.returnType);
|
|
8620
|
+
if (!isAssignable(promised, returned) && !isAssignable(returned, promised)) {
|
|
8621
|
+
report(`This overload returns '${briefType(promised)}', which the implementation's '${briefType(returned)}' cannot be`);
|
|
8622
|
+
}
|
|
8623
|
+
}
|
|
8624
|
+
}
|
|
8364
8625
|
signatureToFnType(sig) {
|
|
8365
8626
|
const names = sig.generics.map((g) => g.name);
|
|
8366
8627
|
const record = (type) => {
|
|
@@ -8785,6 +9046,12 @@ var TypeAnalyzer = class {
|
|
|
8785
9046
|
}
|
|
8786
9047
|
return;
|
|
8787
9048
|
}
|
|
9049
|
+
case "MemberExpression":
|
|
9050
|
+
case "IndexExpression":
|
|
9051
|
+
this.infer(target, env);
|
|
9052
|
+
this.checkReadonlyAssign(target, env);
|
|
9053
|
+
this.assignToRef(target, widen(valueType), env);
|
|
9054
|
+
return;
|
|
8788
9055
|
case "ObjectPattern": {
|
|
8789
9056
|
for (const p of target.properties) {
|
|
8790
9057
|
const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
|
|
@@ -8883,28 +9150,122 @@ var TypeAnalyzer = class {
|
|
|
8883
9150
|
this.resolvingAliases.delete(t.name);
|
|
8884
9151
|
}
|
|
8885
9152
|
}
|
|
8886
|
-
|
|
8887
|
-
|
|
8888
|
-
|
|
8889
|
-
|
|
8890
|
-
|
|
8891
|
-
|
|
8892
|
-
|
|
8893
|
-
|
|
8894
|
-
|
|
8895
|
-
|
|
8896
|
-
|
|
8897
|
-
|
|
8898
|
-
|
|
8899
|
-
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
|
|
8903
|
-
|
|
8904
|
-
|
|
9153
|
+
// --------------------------------------------------------
|
|
9154
|
+
// Metatables
|
|
9155
|
+
// --------------------------------------------------------
|
|
9156
|
+
harvestMetatables(block, source) {
|
|
9157
|
+
for (const stmt of block.statements) {
|
|
9158
|
+
if (stmt.type === "DeclareMetatableStatement") this.metatables.push({ node: stmt, source });
|
|
9159
|
+
}
|
|
9160
|
+
}
|
|
9161
|
+
resolvedMetatable(entry) {
|
|
9162
|
+
entry.resolved ??= this.withTypeParams(entry.node.generics, () => ({
|
|
9163
|
+
target: this.resolveType(entry.node.target),
|
|
9164
|
+
metatable: this.resolveType(entry.node.metatable)
|
|
9165
|
+
}));
|
|
9166
|
+
return entry.resolved;
|
|
9167
|
+
}
|
|
9168
|
+
/** Does a value (`whole`, whose union members are `parts`) have the
|
|
9169
|
+
* metatable `entry` declares — and if so how specifically, and what its
|
|
9170
|
+
* type parameters stand for there. Three kinds of target:
|
|
9171
|
+
*
|
|
9172
|
+
* - `<T> T[]`: every array and tuple, `T` what they hold;
|
|
9173
|
+
* - `<T extends C> T`: every value that is a `C` other than a class
|
|
9174
|
+
* instance, which has a metatable of its own; `T` the value itself;
|
|
9175
|
+
* - a type with no parameters (`string`): every value that is one.
|
|
9176
|
+
*
|
|
9177
|
+
* A bare parameter matches the most, so it is the least specific: an
|
|
9178
|
+
* array that is also a `{}` takes an array's metatable. */
|
|
9179
|
+
matchMetatable(entry, whole, parts) {
|
|
9180
|
+
if (!parts.length || parts.some((m) => m.kind === "any" || m.kind === "unknown" || m.kind === "never")) return void 0;
|
|
9181
|
+
const { target } = this.resolvedMetatable(entry);
|
|
9182
|
+
if (target.kind === "array" && target.element.kind === "typeParam") {
|
|
9183
|
+
const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(tupleMembers(m)) : void 0);
|
|
9184
|
+
if (!elements.every((e) => e !== void 0)) return void 0;
|
|
9185
|
+
return { specificity: 2, bindings: /* @__PURE__ */ new Map([[target.element.name, union(elements)]]) };
|
|
9186
|
+
}
|
|
9187
|
+
if (target.kind === "typeParam") {
|
|
9188
|
+
const constraint = target.constraint;
|
|
9189
|
+
if (parts.some((m) => m.kind === "object" && m.class)) return void 0;
|
|
9190
|
+
if (constraint && !parts.every((m) => isAssignable(m, constraint))) return void 0;
|
|
9191
|
+
return { specificity: 1, bindings: /* @__PURE__ */ new Map([[target.name, whole]]) };
|
|
9192
|
+
}
|
|
9193
|
+
if (containsTypeParam(target)) return void 0;
|
|
9194
|
+
return parts.every((m) => isAssignable(m, target)) ? { specificity: 2, bindings: /* @__PURE__ */ new Map() } : void 0;
|
|
9195
|
+
}
|
|
9196
|
+
/** The metatables `t` has: every declaration of the most specific target
|
|
9197
|
+
* it matches, instantiated for it, in declaration order — a later one
|
|
9198
|
+
* adds to an earlier, the way a library layers on another. A type
|
|
9199
|
+
* parameter is read at its constraint. */
|
|
9200
|
+
metatablesOf(t) {
|
|
9201
|
+
if (!this.metatables.length) return [];
|
|
9202
|
+
const cached = this.metatableCache.get(t);
|
|
9203
|
+
if (cached) return cached;
|
|
9204
|
+
const expanded = this.expand(t);
|
|
9205
|
+
const parts = (expanded.kind === "union" ? expanded.types : [expanded]).map((m) => this.expand(m)).map((m) => m.kind === "typeParam" && m.constraint ? this.expand(m.constraint) : m);
|
|
9206
|
+
const whole = union(parts);
|
|
9207
|
+
const matches = this.metatables.flatMap((entry) => {
|
|
9208
|
+
const match = this.matchMetatable(entry, whole, parts);
|
|
9209
|
+
return match ? [{ entry, ...match }] : [];
|
|
9210
|
+
});
|
|
9211
|
+
const best = Math.max(0, ...matches.map((m) => m.specificity));
|
|
9212
|
+
const found = matches.filter((m) => m.specificity === best).map(({ entry, bindings }) => ({
|
|
9213
|
+
type: this.expand(this.reduceType(substitute(this.resolvedMetatable(entry).metatable, bindings))),
|
|
9214
|
+
source: entry.source
|
|
9215
|
+
}));
|
|
9216
|
+
this.metatableCache.set(t, found);
|
|
9217
|
+
return found;
|
|
9218
|
+
}
|
|
9219
|
+
/** The `__index` tables of `t`'s metatables, each with the library that
|
|
9220
|
+
* declared it, in declaration order. */
|
|
9221
|
+
metatableIndexes(t) {
|
|
9222
|
+
return this.metatablesOf(t).flatMap(({ type, source }) => {
|
|
9223
|
+
const parts = type.kind === "intersection" ? type.types.map((m) => this.expand(m)) : [type];
|
|
9224
|
+
return parts.flatMap((part) => {
|
|
9225
|
+
const index = part.kind === "object" ? part.properties.get("__index") : void 0;
|
|
9226
|
+
if (!index) return [];
|
|
9227
|
+
const table = this.expand(index.type);
|
|
9228
|
+
return (table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table]).map((m) => ({ table: m, source }));
|
|
9229
|
+
});
|
|
9230
|
+
});
|
|
9231
|
+
}
|
|
9232
|
+
/** `name` read through `t`'s metatable: `text:upper()` is `upper` in the
|
|
9233
|
+
* string metatable's `__index`. The last declaration to give the name
|
|
9234
|
+
* wins. The language's own metatables are declared in the prelude, in
|
|
9235
|
+
* tilua, like any library's; a library adds to them the same way. */
|
|
9236
|
+
metatableMember(t, name) {
|
|
9237
|
+
const indexes = this.metatableIndexes(t);
|
|
9238
|
+
for (let i = indexes.length - 1; i >= 0; i--) {
|
|
9239
|
+
const { table, source } = indexes[i];
|
|
9240
|
+
const property = table.kind === "object" ? table.properties.get(name) : void 0;
|
|
9241
|
+
if (property) return { type: property.type, source };
|
|
8905
9242
|
}
|
|
8906
9243
|
return void 0;
|
|
8907
9244
|
}
|
|
9245
|
+
metatableMethod(t, name) {
|
|
9246
|
+
return this.metatableMember(t, name)?.type;
|
|
9247
|
+
}
|
|
9248
|
+
hasMetatable(t) {
|
|
9249
|
+
return this.metatablesOf(t).length > 0;
|
|
9250
|
+
}
|
|
9251
|
+
metatableMembers(t) {
|
|
9252
|
+
const members = /* @__PURE__ */ new Map();
|
|
9253
|
+
for (const { table } of this.metatableIndexes(t)) {
|
|
9254
|
+
if (table.kind !== "object") continue;
|
|
9255
|
+
for (const [name, property] of table.properties) members.set(name, property.type);
|
|
9256
|
+
}
|
|
9257
|
+
return members;
|
|
9258
|
+
}
|
|
9259
|
+
/** `text:upper()`, `scores:keys()`: the method a value's metatable gives
|
|
9260
|
+
* it, when the value has no member of that name of its own. Called with
|
|
9261
|
+
* `:`, it answers before an object's indexer, since a metatable's
|
|
9262
|
+
* `__index` is only read for a key the table does not hold. */
|
|
9263
|
+
receiverMetatableMember(objType, name) {
|
|
9264
|
+
const t = this.expand(objType);
|
|
9265
|
+
const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
|
|
9266
|
+
if (parts.some((m) => m.kind === "object" && m.properties.has(name))) return void 0;
|
|
9267
|
+
return this.metatableMember(t, name);
|
|
9268
|
+
}
|
|
8908
9269
|
/** The most a deferred type could turn out to be. A conditional is one of
|
|
8909
9270
|
* its branches, and the true branch stands for a member of what was
|
|
8910
9271
|
* tested (`T` in `T extends U ? T : never`); an indexed access reads
|
|
@@ -8970,7 +9331,7 @@ var TypeAnalyzer = class {
|
|
|
8970
9331
|
if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
|
|
8971
9332
|
const parts = this.stringParts(object);
|
|
8972
9333
|
if (!parts) return void 0;
|
|
8973
|
-
const found = parts.map((part) => this.
|
|
9334
|
+
const found = parts.map((part) => this.metatableMethod(part, index.value));
|
|
8974
9335
|
return found.every((t) => t !== void 0) ? union(found) : void 0;
|
|
8975
9336
|
}
|
|
8976
9337
|
/** The members of `t` when every one of them is a string — `"a" | "b"` is
|
|
@@ -8991,13 +9352,13 @@ var TypeAnalyzer = class {
|
|
|
8991
9352
|
* not one of them is a mistake worth reporting, rather than the nil Lua
|
|
8992
9353
|
* would hand back. */
|
|
8993
9354
|
checkStringMember(node, object, key) {
|
|
8994
|
-
if (!this.emitDiagnostics || !this.
|
|
9355
|
+
if (!this.emitDiagnostics || !this.hasMetatable(stringType)) return;
|
|
8995
9356
|
const parts = this.stringParts(object);
|
|
8996
9357
|
if (!parts) return;
|
|
8997
9358
|
const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
|
|
8998
9359
|
if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
|
|
8999
9360
|
const names = keys.map((m) => String(m.value));
|
|
9000
|
-
if (names.some((name) => parts.some((part) => this.
|
|
9361
|
+
if (names.some((name) => parts.some((part) => this.metatableMethod(part, name)))) return;
|
|
9001
9362
|
this.diagnostics.push({
|
|
9002
9363
|
node,
|
|
9003
9364
|
message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
|
|
@@ -9029,24 +9390,24 @@ var TypeAnalyzer = class {
|
|
|
9029
9390
|
switch (part.kind) {
|
|
9030
9391
|
case "primitive":
|
|
9031
9392
|
if (part.name === "nil") continue;
|
|
9032
|
-
if (part.name === "string" && !this.
|
|
9033
|
-
if (part.name === "string" && this.
|
|
9393
|
+
if (part.name === "string" && !this.hasMetatable(part)) return false;
|
|
9394
|
+
if (part.name === "string" && this.metatableMethod(part, name)) continue;
|
|
9034
9395
|
break;
|
|
9035
9396
|
case "literal":
|
|
9036
9397
|
case "templateLiteral":
|
|
9037
9398
|
if (part.kind === "templateLiteral" || part.base === "string") {
|
|
9038
|
-
if (!this.
|
|
9399
|
+
if (!this.hasMetatable(part)) return false;
|
|
9039
9400
|
}
|
|
9040
|
-
if (this.
|
|
9401
|
+
if (this.metatableMethod(part, name)) continue;
|
|
9041
9402
|
break;
|
|
9042
9403
|
case "array":
|
|
9043
9404
|
case "tuple":
|
|
9044
|
-
if (!this.
|
|
9045
|
-
if (this.
|
|
9405
|
+
if (!this.hasMetatable(part)) return false;
|
|
9406
|
+
if (this.metatableMethod(part, name)) continue;
|
|
9046
9407
|
break;
|
|
9047
9408
|
case "object":
|
|
9048
9409
|
if (part.properties.has(name)) continue;
|
|
9049
|
-
if (part.indexer || part.properties.size === 0 && !part.class || this.
|
|
9410
|
+
if (part.indexer || part.properties.size === 0 && !part.class || this.metatableMethod(part, name)) return false;
|
|
9050
9411
|
break;
|
|
9051
9412
|
default:
|
|
9052
9413
|
return false;
|
|
@@ -9181,10 +9542,10 @@ var TypeAnalyzer = class {
|
|
|
9181
9542
|
if (p) return p.optional ? optional(p.type) : p.type;
|
|
9182
9543
|
if (t.indexer) return t.indexer.value;
|
|
9183
9544
|
}
|
|
9184
|
-
const built = this.
|
|
9545
|
+
const built = this.metatableMethod(t, name);
|
|
9185
9546
|
if (built) return built;
|
|
9186
9547
|
if (t.kind === "union") {
|
|
9187
|
-
const built2 = this.
|
|
9548
|
+
const built2 = this.metatableMethod(t, name);
|
|
9188
9549
|
if (built2) return built2;
|
|
9189
9550
|
return union(t.types.map((m) => this.propertyType(m, name)));
|
|
9190
9551
|
}
|
|
@@ -9585,7 +9946,9 @@ var TypeAnalyzer = class {
|
|
|
9585
9946
|
this.checkPrivateMember(expr.method, objType, expr.method.name);
|
|
9586
9947
|
this.checkStringMember(expr.method, objType, literal(expr.method.name));
|
|
9587
9948
|
this.checkMissingMember(expr.method, objType, expr.method.name);
|
|
9588
|
-
const
|
|
9949
|
+
const fromMetatable = this.receiverMetatableMember(objType, expr.method.name);
|
|
9950
|
+
if (fromMetatable) this.methodSources.set(expr, fromMetatable.source);
|
|
9951
|
+
const method = fromMetatable?.type ?? this.propertyType(objType, expr.method.name);
|
|
9589
9952
|
const united = this.unionSignatures(method);
|
|
9590
9953
|
const fns = united ?? this.overloadsOf(method);
|
|
9591
9954
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
@@ -10827,6 +11190,8 @@ function containsTypeQuery(node) {
|
|
|
10827
11190
|
return found;
|
|
10828
11191
|
}
|
|
10829
11192
|
var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
|
|
11193
|
+
var OBJECT_INTRINSICS = /* @__PURE__ */ new Set(["ObjectKeys", "ObjectValues", "ObjectEntries"]);
|
|
11194
|
+
var INTRINSICS = /* @__PURE__ */ new Set([...STRING_INTRINSICS, ...OBJECT_INTRINSICS]);
|
|
10830
11195
|
function briefType(t) {
|
|
10831
11196
|
if (t.kind === "union" && t.types.length > 8) {
|
|
10832
11197
|
const shown = t.types.slice(0, 6).map(formatType).join(" | ");
|
|
@@ -11034,7 +11399,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
11034
11399
|
}
|
|
11035
11400
|
addFile(entryFile);
|
|
11036
11401
|
const lowering = loweringModule(directory, host, problems, config);
|
|
11037
|
-
|
|
11402
|
+
const library = files.findIndex((file) => pathKey(file) === pathKey(entryFile));
|
|
11403
|
+
if (lowering) lowerings.push({ ...lowering, library });
|
|
11038
11404
|
};
|
|
11039
11405
|
for (const entry of config.types) {
|
|
11040
11406
|
const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);
|