@tilua/parser 1.1.0 → 2.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 +170 -44
- package/dist/index.cjs +1137 -426
- package/dist/index.d.cts +98 -64
- package/dist/index.d.ts +98 -64
- package/dist/index.js +1136 -427
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -23,6 +23,7 @@ __export(index_exports, {
|
|
|
23
23
|
BinaryOperators: () => BinaryOperators,
|
|
24
24
|
CONFIG_FILE_NAMES: () => CONFIG_FILE_NAMES,
|
|
25
25
|
Keywords: () => Keywords,
|
|
26
|
+
LANGUAGE_GLOBALS: () => LANGUAGE_GLOBALS,
|
|
26
27
|
LexError: () => LexError,
|
|
27
28
|
Operators: () => Operators,
|
|
28
29
|
PRELUDE_SOURCE: () => PRELUDE_SOURCE,
|
|
@@ -91,6 +92,7 @@ __export(index_exports, {
|
|
|
91
92
|
tilua: () => tilua,
|
|
92
93
|
tokenize: () => tokenize,
|
|
93
94
|
tuple: () => tuple,
|
|
95
|
+
tupleMembers: () => tupleMembers,
|
|
94
96
|
typeParam: () => typeParam,
|
|
95
97
|
unify: () => unify,
|
|
96
98
|
union: () => union,
|
|
@@ -698,6 +700,9 @@ function applyDirectives(directives, diagnostics, lineOf) {
|
|
|
698
700
|
var UNUSED_EXPECT_ERROR = "Unused '@tilua-expect-error' directive";
|
|
699
701
|
|
|
700
702
|
// src/ast/builders.ts
|
|
703
|
+
var NO_DOTS = "tilua has no bare '...': take a rest parameter, '...args: T[]', or 'scriptArgs' for the script's own";
|
|
704
|
+
var ACCESSIBILITIES = /* @__PURE__ */ new Set(["public", "private", "protected"]);
|
|
705
|
+
var CLASS_MODIFIERS = /* @__PURE__ */ new Set([...ACCESSIBILITIES, "static", "abstract", "override", "readonly"]);
|
|
701
706
|
function bindThis(func, at) {
|
|
702
707
|
bindThisParam(func.params, at);
|
|
703
708
|
func.isMethod = true;
|
|
@@ -965,11 +970,16 @@ var Parser = class {
|
|
|
965
970
|
return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
|
|
966
971
|
}
|
|
967
972
|
/** A comma-separated list of values: a `return`'s, a declaration's, an
|
|
968
|
-
* assignment's.
|
|
969
|
-
* arguments
|
|
973
|
+
* assignment's. Each is one value; a spread belongs in a call's
|
|
974
|
+
* arguments or an array, and is reported here — then read as one, so
|
|
975
|
+
* the rest still means something. */
|
|
970
976
|
expressionListOr(stop) {
|
|
971
977
|
const until = () => stop() || this.checkPunctuator(",");
|
|
972
|
-
const item = () =>
|
|
978
|
+
const item = () => {
|
|
979
|
+
if (!(this.checkOperator("...") && this.startsSpread())) return this.expressionOr(until);
|
|
980
|
+
this.problem("A spread goes in a call's arguments or an array: take values out of an array with a destructuring, 'const [a, b] = xs'");
|
|
981
|
+
return this.parseSpreadArgument(until);
|
|
982
|
+
};
|
|
973
983
|
const list = [item()];
|
|
974
984
|
while (this.matchPunctuator(",")) list.push(item());
|
|
975
985
|
return list;
|
|
@@ -1334,7 +1344,7 @@ var Parser = class {
|
|
|
1334
1344
|
if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
|
|
1335
1345
|
return this.parseTypeAliasStatement();
|
|
1336
1346
|
}
|
|
1337
|
-
if (
|
|
1347
|
+
if (this.startsClassDeclaration()) {
|
|
1338
1348
|
return this.parseClassDeclaration();
|
|
1339
1349
|
}
|
|
1340
1350
|
if (t.type === "Identifier" && t.value === "declare") {
|
|
@@ -1371,8 +1381,6 @@ var Parser = class {
|
|
|
1371
1381
|
type: "FunctionTypeNode",
|
|
1372
1382
|
generics: head.generics,
|
|
1373
1383
|
params,
|
|
1374
|
-
hasVarargs: head.hasVarargs,
|
|
1375
|
-
varargType: head.varargTypeAnnotation,
|
|
1376
1384
|
returnType: head.returnType ?? { type: "TypeReference", base: head.predicate ? "boolean" : "unknown", typeArguments: [], ...spanFrom(start, start) },
|
|
1377
1385
|
predicate: head.predicate,
|
|
1378
1386
|
...spanFrom(start, this.previous())
|
|
@@ -1511,7 +1519,7 @@ var Parser = class {
|
|
|
1511
1519
|
this.advance();
|
|
1512
1520
|
if (this.checkIdentifierValue("default")) {
|
|
1513
1521
|
this.advance();
|
|
1514
|
-
const declaration = this.
|
|
1522
|
+
const declaration = this.startsClassDeclaration() ? this.parseClassDeclaration() : this.parseExpression(0);
|
|
1515
1523
|
return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1516
1524
|
}
|
|
1517
1525
|
if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
|
|
@@ -1522,7 +1530,7 @@ var Parser = class {
|
|
|
1522
1530
|
const declaration = this.parseVariableDeclaration();
|
|
1523
1531
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1524
1532
|
}
|
|
1525
|
-
if (this.
|
|
1533
|
+
if (this.startsClassDeclaration()) {
|
|
1526
1534
|
const declaration = this.parseClassDeclaration();
|
|
1527
1535
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1528
1536
|
}
|
|
@@ -1637,9 +1645,11 @@ var Parser = class {
|
|
|
1637
1645
|
const start = this.current();
|
|
1638
1646
|
this.expectKeyword("for");
|
|
1639
1647
|
this.expectPunctuator("(");
|
|
1648
|
+
const declared = this.checkKeyword("const") || this.checkKeyword("let") ? this.advance().value : void 0;
|
|
1640
1649
|
const first = this.parseBindingTarget(true);
|
|
1641
1650
|
const untilDo = () => this.checkPunctuator(")");
|
|
1642
1651
|
if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
|
|
1652
|
+
if (declared) this.problemAt(start, `A counting loop names its variable without '${declared}': 'for (i = 1, 10)'`);
|
|
1643
1653
|
const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
|
|
1644
1654
|
this.expectPunctuator(",");
|
|
1645
1655
|
const to = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
|
|
@@ -1658,17 +1668,24 @@ var Parser = class {
|
|
|
1658
1668
|
...spanFrom(start, this.previous())
|
|
1659
1669
|
};
|
|
1660
1670
|
}
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1671
|
+
if (this.checkPunctuator(",")) {
|
|
1672
|
+
this.problemAt(this.current(), "A loop takes one value each time: take its parts with a destructuring, 'for (const [k, v] in pairs(t))'");
|
|
1673
|
+
while (this.matchPunctuator(",")) this.parseBindingTarget(true);
|
|
1674
|
+
} else if (!declared) {
|
|
1675
|
+
this.problemAt(start, "A loop over a source names its value with 'const' or 'let': 'for (const item in source)'");
|
|
1664
1676
|
}
|
|
1665
1677
|
this.expectKeyword("in");
|
|
1666
|
-
const
|
|
1678
|
+
const iterator = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
|
|
1679
|
+
if (this.checkPunctuator(",")) {
|
|
1680
|
+
this.problemAt(this.current(), "A loop has one source: an array, a table, an iterator function or an iteration '[step, state, first]'");
|
|
1681
|
+
while (this.matchPunctuator(",")) this.expressionOr(() => untilDo() || this.checkPunctuator(","));
|
|
1682
|
+
}
|
|
1667
1683
|
const body = this.parseForBody();
|
|
1668
1684
|
return {
|
|
1669
1685
|
type: "GenericForStatement",
|
|
1670
|
-
|
|
1671
|
-
|
|
1686
|
+
kind: declared ?? "const",
|
|
1687
|
+
variable: first,
|
|
1688
|
+
iterator,
|
|
1672
1689
|
body,
|
|
1673
1690
|
...spanFrom(start, this.previous())
|
|
1674
1691
|
};
|
|
@@ -1738,7 +1755,10 @@ var Parser = class {
|
|
|
1738
1755
|
* the compiler must not accept it, and recorded inside. The message says
|
|
1739
1756
|
* what is wrong on its own — no token is appended. */
|
|
1740
1757
|
problem(message) {
|
|
1741
|
-
|
|
1758
|
+
this.problemAt(this.current(), message);
|
|
1759
|
+
}
|
|
1760
|
+
/** `problem`, pointed at `t` rather than at where parsing has got to. */
|
|
1761
|
+
problemAt(t, message) {
|
|
1742
1762
|
const error = new ParseError(message, t.line.start, t.column.start);
|
|
1743
1763
|
if (!this.recover) throw error;
|
|
1744
1764
|
this.record(error);
|
|
@@ -1751,10 +1771,13 @@ var Parser = class {
|
|
|
1751
1771
|
* like a function (`function m() ... end`). */
|
|
1752
1772
|
parseClassDeclaration() {
|
|
1753
1773
|
const start = this.current();
|
|
1774
|
+
const isAbstract = this.checkIdentifierValue("abstract");
|
|
1775
|
+
if (isAbstract) this.advance();
|
|
1754
1776
|
this.advance();
|
|
1755
1777
|
const name = this.parseIdentifier();
|
|
1756
1778
|
const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
|
|
1757
1779
|
const { superclass, superArguments } = this.parseExtends();
|
|
1780
|
+
const shapes = this.parseImplements();
|
|
1758
1781
|
const members = this.parseClassBody(start);
|
|
1759
1782
|
return {
|
|
1760
1783
|
type: "ClassDeclaration",
|
|
@@ -1762,10 +1785,21 @@ var Parser = class {
|
|
|
1762
1785
|
typeParams,
|
|
1763
1786
|
superclass,
|
|
1764
1787
|
superArguments,
|
|
1788
|
+
...shapes ? { implements: shapes } : {},
|
|
1789
|
+
...isAbstract ? { isAbstract } : {},
|
|
1765
1790
|
members,
|
|
1766
1791
|
...spanFrom(start, this.previous())
|
|
1767
1792
|
};
|
|
1768
1793
|
}
|
|
1794
|
+
/** `class Name` or `abstract class Name` — all three words soft, so a name
|
|
1795
|
+
* has to follow for either to be a declaration: `class: T`, `class = 1`
|
|
1796
|
+
* and `abstract(x)` are names. */
|
|
1797
|
+
startsClassDeclaration() {
|
|
1798
|
+
const offset = this.checkIdentifierValue("abstract") ? 1 : 0;
|
|
1799
|
+
const word = this.peek(offset);
|
|
1800
|
+
if (word.type !== "Identifier" || word.value !== "class") return false;
|
|
1801
|
+
return this.peek(offset + 1).type === "Identifier" && !this.punctuatorAt(offset + 2, ":") && !this.operatorAt(offset + 2, "=") && !this.punctuatorAt(offset + 2, "(");
|
|
1802
|
+
}
|
|
1769
1803
|
/** `class ... end` as a value. It may be named — the name is for the class
|
|
1770
1804
|
* itself, not for the scope around it — and takes no type parameters,
|
|
1771
1805
|
* since nothing could write the arguments. */
|
|
@@ -1777,8 +1811,25 @@ var Parser = class {
|
|
|
1777
1811
|
this.error("A class written as a value takes no type parameters: nothing could write the arguments");
|
|
1778
1812
|
}
|
|
1779
1813
|
const { superclass, superArguments } = this.parseExtends();
|
|
1814
|
+
const shapes = this.parseImplements();
|
|
1780
1815
|
const members = this.parseClassBody(start);
|
|
1781
|
-
return {
|
|
1816
|
+
return {
|
|
1817
|
+
type: "ClassExpression",
|
|
1818
|
+
name,
|
|
1819
|
+
superclass,
|
|
1820
|
+
superArguments,
|
|
1821
|
+
...shapes ? { implements: shapes } : {},
|
|
1822
|
+
members,
|
|
1823
|
+
...spanFrom(start, this.previous())
|
|
1824
|
+
};
|
|
1825
|
+
}
|
|
1826
|
+
/** `implements Shape, Named` — each a type, checked against the instance. */
|
|
1827
|
+
parseImplements() {
|
|
1828
|
+
if (!this.checkIdentifierValue("implements")) return void 0;
|
|
1829
|
+
this.advance();
|
|
1830
|
+
const shapes = [this.parseType()];
|
|
1831
|
+
while (this.matchPunctuator(",")) shapes.push(this.parseType());
|
|
1832
|
+
return shapes;
|
|
1782
1833
|
}
|
|
1783
1834
|
/** `extends Base` / `extends Box<number>`. */
|
|
1784
1835
|
parseExtends() {
|
|
@@ -1831,23 +1882,66 @@ var Parser = class {
|
|
|
1831
1882
|
parseClassMember() {
|
|
1832
1883
|
const start = this.current();
|
|
1833
1884
|
let accessibility;
|
|
1834
|
-
|
|
1835
|
-
|
|
1885
|
+
const modifiers = /* @__PURE__ */ new Set();
|
|
1886
|
+
while (this.startsClassModifier()) {
|
|
1887
|
+
const token = this.advance();
|
|
1888
|
+
const word = token.value;
|
|
1889
|
+
if (ACCESSIBILITIES.has(word)) {
|
|
1890
|
+
if (accessibility) this.problemAt(token, `A member has one accessibility: '${accessibility}' is already written`);
|
|
1891
|
+
accessibility = word;
|
|
1892
|
+
} else {
|
|
1893
|
+
if (modifiers.has(word)) this.problemAt(token, `'${word}' is written twice`);
|
|
1894
|
+
modifiers.add(word);
|
|
1895
|
+
}
|
|
1836
1896
|
}
|
|
1837
|
-
const member = this.
|
|
1838
|
-
if (!
|
|
1897
|
+
const member = this.parseClassMemberAfterModifiers(start, modifiers);
|
|
1898
|
+
if (!member) return member;
|
|
1899
|
+
const misplaced = (message) => this.problemAt(start, message);
|
|
1839
1900
|
if (member.type === "ClassConstructor") {
|
|
1840
|
-
|
|
1901
|
+
const written = [...accessibility ? [accessibility] : [], ...modifiers];
|
|
1902
|
+
if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
|
|
1841
1903
|
return member;
|
|
1842
1904
|
}
|
|
1843
|
-
|
|
1905
|
+
if (modifiers.has("readonly") && member.type !== "ClassField") misplaced("Only a field can be 'readonly'");
|
|
1906
|
+
if (modifiers.has("abstract") && member.type !== "ClassMethod") misplaced("Only a method can be 'abstract'");
|
|
1907
|
+
if (modifiers.has("abstract") && member.isStatic) misplaced("A static method cannot be 'abstract'");
|
|
1908
|
+
if (modifiers.has("override") && member.isStatic) misplaced("A static member cannot be 'override'");
|
|
1909
|
+
return {
|
|
1910
|
+
...member,
|
|
1911
|
+
...accessibility ? { accessibility } : {},
|
|
1912
|
+
...modifiers.has("readonly") && member.type === "ClassField" ? { isReadonly: true } : {},
|
|
1913
|
+
...modifiers.has("override") ? { isOverride: true } : {}
|
|
1914
|
+
};
|
|
1844
1915
|
}
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1916
|
+
/** Is the word here a member modifier, rather than a member's name? */
|
|
1917
|
+
startsClassModifier() {
|
|
1918
|
+
const t = this.current();
|
|
1919
|
+
if (t.type !== "Identifier" || !CLASS_MODIFIERS.has(t.value)) return false;
|
|
1920
|
+
const next = this.peek(1);
|
|
1921
|
+
return next.type === "Identifier" || next.type === "Keyword" && next.value === "function";
|
|
1922
|
+
}
|
|
1923
|
+
parseClassMemberAfterModifiers(start, modifiers) {
|
|
1924
|
+
const isStatic = modifiers.has("static");
|
|
1848
1925
|
if (this.checkKeyword("function")) {
|
|
1849
1926
|
this.advance();
|
|
1850
1927
|
const memberName = this.parseIdentifier();
|
|
1928
|
+
if (modifiers.has("abstract")) {
|
|
1929
|
+
const head = this.parseFunctionHead();
|
|
1930
|
+
const func = {
|
|
1931
|
+
type: "FunctionBody",
|
|
1932
|
+
generics: head.generics,
|
|
1933
|
+
params: head.params,
|
|
1934
|
+
hasVarargs: head.hasVarargs,
|
|
1935
|
+
returnType: head.returnType,
|
|
1936
|
+
predicate: head.predicate,
|
|
1937
|
+
body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
|
|
1938
|
+
...spanFrom(head.start, this.previous())
|
|
1939
|
+
};
|
|
1940
|
+
if (!isStatic) bindThis(func, start);
|
|
1941
|
+
if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
|
|
1942
|
+
if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
|
|
1943
|
+
return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
|
|
1944
|
+
}
|
|
1851
1945
|
const signatures = [];
|
|
1852
1946
|
let written = memberName;
|
|
1853
1947
|
while (true) {
|
|
@@ -1965,12 +2059,17 @@ var Parser = class {
|
|
|
1965
2059
|
parseReturnStatement(stopAtNewline = false) {
|
|
1966
2060
|
const start = this.current();
|
|
1967
2061
|
this.expectKeyword("return");
|
|
1968
|
-
let
|
|
2062
|
+
let argument;
|
|
1969
2063
|
const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
|
|
1970
2064
|
if (!pastLine() && this.isExpressionStart()) {
|
|
1971
|
-
|
|
2065
|
+
const values = this.expressionListOr(pastLine);
|
|
2066
|
+
argument = values[0];
|
|
2067
|
+
if (values.length > 1) {
|
|
2068
|
+
this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
|
|
2069
|
+
argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
|
|
2070
|
+
}
|
|
1972
2071
|
}
|
|
1973
|
-
return { type: "ReturnStatement",
|
|
2072
|
+
return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
|
|
1974
2073
|
}
|
|
1975
2074
|
parseTypeAliasStatement() {
|
|
1976
2075
|
const start = this.current();
|
|
@@ -2029,7 +2128,7 @@ var Parser = class {
|
|
|
2029
2128
|
...spanFrom(start, this.previous())
|
|
2030
2129
|
};
|
|
2031
2130
|
}
|
|
2032
|
-
if (first.type === "CallExpression" || first.type === "MethodCallExpression"
|
|
2131
|
+
if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
|
|
2033
2132
|
return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
|
|
2034
2133
|
}
|
|
2035
2134
|
let expression = first;
|
|
@@ -2082,6 +2181,15 @@ var Parser = class {
|
|
|
2082
2181
|
}
|
|
2083
2182
|
return false;
|
|
2084
2183
|
}
|
|
2184
|
+
/** A string written straight before a method call — `"a":upper()`, or
|
|
2185
|
+
* `` `{n}`:rep(2) ``. Luau only takes `("a"):upper()`; the compiler adds
|
|
2186
|
+
* the parentheses. Anything else after a string (`"a".x`, `"a"(1)`) is
|
|
2187
|
+
* not a prefix expression here either. */
|
|
2188
|
+
startsStringMethodCall() {
|
|
2189
|
+
const t = this.current();
|
|
2190
|
+
const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
|
|
2191
|
+
return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
|
|
2192
|
+
}
|
|
2085
2193
|
/** Does the next token start right where the current one ends? */
|
|
2086
2194
|
touchesNext() {
|
|
2087
2195
|
const current = this.current();
|
|
@@ -2268,6 +2376,7 @@ var Parser = class {
|
|
|
2268
2376
|
}
|
|
2269
2377
|
parseAtom() {
|
|
2270
2378
|
const t = this.current();
|
|
2379
|
+
if (this.startsStringMethodCall()) return this.parsePrefixExpression();
|
|
2271
2380
|
if (t.type === "Literal") {
|
|
2272
2381
|
this.advance();
|
|
2273
2382
|
const lit = t;
|
|
@@ -2287,8 +2396,9 @@ var Parser = class {
|
|
|
2287
2396
|
return this.buildInterpolatedString(t);
|
|
2288
2397
|
}
|
|
2289
2398
|
if (t.type === "Operator" && t.value === "...") {
|
|
2399
|
+
this.problem(NO_DOTS);
|
|
2290
2400
|
this.advance();
|
|
2291
|
-
return { type: "
|
|
2401
|
+
return { type: "ErrorExpression", ...spanFrom(t, t) };
|
|
2292
2402
|
}
|
|
2293
2403
|
if (t.type === "Keyword" && t.value === "function") {
|
|
2294
2404
|
this.advance();
|
|
@@ -2364,12 +2474,17 @@ var Parser = class {
|
|
|
2364
2474
|
const start = this.current();
|
|
2365
2475
|
let base;
|
|
2366
2476
|
if (this.startsNew()) {
|
|
2367
|
-
|
|
2368
|
-
|
|
2477
|
+
this.problem("tilua has no 'new' operator: construct with 'Name.new(...)'");
|
|
2478
|
+
this.advance();
|
|
2479
|
+
}
|
|
2480
|
+
if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
|
|
2369
2481
|
this.advance();
|
|
2370
2482
|
base = { type: "SuperExpression", ...spanFrom(start, start) };
|
|
2371
2483
|
} else if (this.checkType("Identifier")) {
|
|
2372
2484
|
base = this.parseIdentifier();
|
|
2485
|
+
} else if (this.startsStringMethodCall()) {
|
|
2486
|
+
const t = this.advance();
|
|
2487
|
+
base = t.type === "InterpolatedString" ? this.buildInterpolatedString(t) : { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
|
|
2373
2488
|
} else if (this.matchPunctuator("(")) {
|
|
2374
2489
|
const inner = this.inBrackets(() => this.parseExpression());
|
|
2375
2490
|
this.expectPunctuator(")");
|
|
@@ -2480,9 +2595,9 @@ var Parser = class {
|
|
|
2480
2595
|
}
|
|
2481
2596
|
return base;
|
|
2482
2597
|
}
|
|
2483
|
-
/** `new
|
|
2484
|
-
*
|
|
2485
|
-
*
|
|
2598
|
+
/** `new Name(...)`, the way other languages build an instance — only to
|
|
2599
|
+
* say so. A function or field called `new` — `Instance.new(x)`, and
|
|
2600
|
+
* `Vec.new(1)` itself — is untouched. */
|
|
2486
2601
|
startsNew() {
|
|
2487
2602
|
return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
|
|
2488
2603
|
}
|
|
@@ -2492,25 +2607,6 @@ var Parser = class {
|
|
|
2492
2607
|
startsSuperUse() {
|
|
2493
2608
|
return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
|
|
2494
2609
|
}
|
|
2495
|
-
/** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
|
|
2496
|
-
* name reached through a module — never an arbitrary expression, so the
|
|
2497
|
-
* arguments are unambiguously the constructor's. */
|
|
2498
|
-
parseNewExpression() {
|
|
2499
|
-
const start = this.current();
|
|
2500
|
-
this.advance();
|
|
2501
|
-
let callee = this.parseIdentifier();
|
|
2502
|
-
while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
2503
|
-
this.advance();
|
|
2504
|
-
const property = this.parseIdentifier();
|
|
2505
|
-
callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
|
|
2506
|
-
}
|
|
2507
|
-
const typeArguments = this.tryCallTypeArguments();
|
|
2508
|
-
if (!this.checkPunctuator("(")) {
|
|
2509
|
-
this.error("Expected '(' after the class being constructed: 'new Name(...)'");
|
|
2510
|
-
}
|
|
2511
|
-
const args = this.parseCallArguments();
|
|
2512
|
-
return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
|
|
2513
|
-
}
|
|
2514
2610
|
/** After `...`, is there something to spread? Nothing following it means
|
|
2515
2611
|
* the vararg pack, which is what `f(...)` has always passed on. */
|
|
2516
2612
|
startsSpread() {
|
|
@@ -2552,8 +2648,8 @@ var Parser = class {
|
|
|
2552
2648
|
const errors = this.errors.length;
|
|
2553
2649
|
try {
|
|
2554
2650
|
this.advance();
|
|
2555
|
-
const list = [this.
|
|
2556
|
-
while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.
|
|
2651
|
+
const list = [this.parseType()];
|
|
2652
|
+
while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
|
|
2557
2653
|
this.expectOperator(">");
|
|
2558
2654
|
if (!this.startsCallArguments()) throw new ParseRecover("not a call");
|
|
2559
2655
|
return list;
|
|
@@ -2685,7 +2781,7 @@ var Parser = class {
|
|
|
2685
2781
|
if (this.checkOperator("...")) {
|
|
2686
2782
|
const dots = this.advance();
|
|
2687
2783
|
if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
|
|
2688
|
-
|
|
2784
|
+
this.problemAt(dots, NO_DOTS);
|
|
2689
2785
|
} else {
|
|
2690
2786
|
const argument = this.expressionOr(stop);
|
|
2691
2787
|
elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
|
|
@@ -2839,35 +2935,16 @@ var Parser = class {
|
|
|
2839
2935
|
column: p.column
|
|
2840
2936
|
};
|
|
2841
2937
|
}
|
|
2842
|
-
|
|
2938
|
+
/** `T...` written where a type goes: a pack, which tilua does not have. */
|
|
2939
|
+
rejectPackReference() {
|
|
2843
2940
|
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
2844
|
-
|
|
2845
|
-
|
|
2941
|
+
this.problemAt(
|
|
2942
|
+
this.peek(1),
|
|
2943
|
+
"tilua has no type packs: several values are a tuple, '[A, B]', and a varying number an array, 'T[]'"
|
|
2944
|
+
);
|
|
2945
|
+
this.advance();
|
|
2846
2946
|
this.advance();
|
|
2847
|
-
const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
|
|
2848
|
-
return {
|
|
2849
|
-
type: "TypePackNode",
|
|
2850
|
-
types: [],
|
|
2851
|
-
hasVarargs: true,
|
|
2852
|
-
// Wrap in `VariadicTypeNode`, matching the convention used by
|
|
2853
|
-
// `parseFunctionTypeAfterParen`'s identifier-pack-reference
|
|
2854
|
-
// branch, so the printer can tell `A...` (name-first, this
|
|
2855
|
-
// case) apart from `...T` (dots-first) and append rather
|
|
2856
|
-
// than prepend the `...`.
|
|
2857
|
-
varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
|
|
2858
|
-
...spanFrom(start, this.previous())
|
|
2859
|
-
};
|
|
2860
|
-
}
|
|
2861
|
-
return this.parseType();
|
|
2862
|
-
}
|
|
2863
|
-
parseTypeArgument() {
|
|
2864
|
-
if (this.checkOperator("...")) {
|
|
2865
|
-
return this.parseTypePack();
|
|
2866
|
-
}
|
|
2867
|
-
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
2868
|
-
return this.parseTypeOrTypePackReference();
|
|
2869
2947
|
}
|
|
2870
|
-
return this.parseType();
|
|
2871
2948
|
}
|
|
2872
2949
|
/** The part shared by a real function body and an overload signature:
|
|
2873
2950
|
* `<generics>(params): ReturnType`, up to (but not including) the block. */
|
|
@@ -2880,7 +2957,6 @@ var Parser = class {
|
|
|
2880
2957
|
this.expectPunctuator("(");
|
|
2881
2958
|
const params = [];
|
|
2882
2959
|
let hasVarargs = false;
|
|
2883
|
-
let varargTypeAnnotation;
|
|
2884
2960
|
if (!this.checkPunctuator(")")) {
|
|
2885
2961
|
while (true) {
|
|
2886
2962
|
if (this.checkOperator("...")) {
|
|
@@ -2888,14 +2964,14 @@ var Parser = class {
|
|
|
2888
2964
|
hasVarargs = true;
|
|
2889
2965
|
if (this.checkType("Identifier")) {
|
|
2890
2966
|
const nameTok = this.expectIdentifier();
|
|
2891
|
-
let
|
|
2967
|
+
let typeAnnotation3;
|
|
2892
2968
|
if (this.matchPunctuator(":")) {
|
|
2893
|
-
|
|
2969
|
+
typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
|
|
2894
2970
|
}
|
|
2895
2971
|
params.push({
|
|
2896
2972
|
type: "FunctionParameter",
|
|
2897
2973
|
name: nameTok.value,
|
|
2898
|
-
typeAnnotation:
|
|
2974
|
+
typeAnnotation: typeAnnotation3,
|
|
2899
2975
|
rest: true,
|
|
2900
2976
|
...spanFrom(dots, this.previous())
|
|
2901
2977
|
});
|
|
@@ -2904,9 +2980,20 @@ var Parser = class {
|
|
|
2904
2980
|
}
|
|
2905
2981
|
break;
|
|
2906
2982
|
}
|
|
2983
|
+
this.problemAt(dots, NO_DOTS);
|
|
2984
|
+
let typeAnnotation2;
|
|
2907
2985
|
if (this.matchPunctuator(":")) {
|
|
2908
|
-
|
|
2986
|
+
this.rejectPackReference();
|
|
2987
|
+
const element = this.parseType();
|
|
2988
|
+
typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
|
|
2909
2989
|
}
|
|
2990
|
+
params.push({
|
|
2991
|
+
type: "FunctionParameter",
|
|
2992
|
+
name: "args",
|
|
2993
|
+
typeAnnotation: typeAnnotation2,
|
|
2994
|
+
rest: true,
|
|
2995
|
+
...spanFrom(dots, this.previous())
|
|
2996
|
+
});
|
|
2910
2997
|
break;
|
|
2911
2998
|
}
|
|
2912
2999
|
const paramStart = this.current();
|
|
@@ -2948,10 +3035,16 @@ var Parser = class {
|
|
|
2948
3035
|
if (this.matchPunctuator(":")) {
|
|
2949
3036
|
predicate = this.tryParseTypePredicate();
|
|
2950
3037
|
if (!predicate) {
|
|
2951
|
-
returnType = this.attempt(() => this.
|
|
3038
|
+
returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
|
|
2952
3039
|
}
|
|
2953
3040
|
}
|
|
2954
|
-
return { start, generics, params, hasVarargs,
|
|
3041
|
+
return { start, generics, params, hasVarargs, returnType, predicate };
|
|
3042
|
+
}
|
|
3043
|
+
/** What a function returns: one type. `T...` is reported as the pack it
|
|
3044
|
+
* is, and the type after it read. */
|
|
3045
|
+
parseReturnType() {
|
|
3046
|
+
this.rejectPackReference();
|
|
3047
|
+
return this.parseType();
|
|
2955
3048
|
}
|
|
2956
3049
|
/** TypeScript-style type-guard return annotations, in return position only:
|
|
2957
3050
|
*
|
|
@@ -3021,7 +3114,6 @@ var Parser = class {
|
|
|
3021
3114
|
generics: [],
|
|
3022
3115
|
params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
|
|
3023
3116
|
hasVarargs: false,
|
|
3024
|
-
varargTypeAnnotation: void 0,
|
|
3025
3117
|
returnType: void 0,
|
|
3026
3118
|
predicate: void 0
|
|
3027
3119
|
};
|
|
@@ -3033,7 +3125,6 @@ var Parser = class {
|
|
|
3033
3125
|
generics: head.generics,
|
|
3034
3126
|
params: head.params,
|
|
3035
3127
|
hasVarargs: head.hasVarargs,
|
|
3036
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3037
3128
|
returnType: head.returnType,
|
|
3038
3129
|
predicate: head.predicate,
|
|
3039
3130
|
body,
|
|
@@ -3045,7 +3136,7 @@ var Parser = class {
|
|
|
3045
3136
|
returnOf(expression) {
|
|
3046
3137
|
const statement = {
|
|
3047
3138
|
type: "ReturnStatement",
|
|
3048
|
-
|
|
3139
|
+
argument: expression,
|
|
3049
3140
|
...spanFrom(expression, expression)
|
|
3050
3141
|
};
|
|
3051
3142
|
return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
|
|
@@ -3058,7 +3149,6 @@ var Parser = class {
|
|
|
3058
3149
|
generics: head.generics,
|
|
3059
3150
|
params: head.params,
|
|
3060
3151
|
hasVarargs: head.hasVarargs,
|
|
3061
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3062
3152
|
returnType: head.returnType,
|
|
3063
3153
|
predicate: head.predicate,
|
|
3064
3154
|
body,
|
|
@@ -3071,7 +3161,6 @@ var Parser = class {
|
|
|
3071
3161
|
generics: head.generics,
|
|
3072
3162
|
params: head.params,
|
|
3073
3163
|
hasVarargs: head.hasVarargs,
|
|
3074
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3075
3164
|
returnType: head.returnType,
|
|
3076
3165
|
predicate: head.predicate,
|
|
3077
3166
|
...spanFrom(head.start, this.previous())
|
|
@@ -3089,7 +3178,6 @@ var Parser = class {
|
|
|
3089
3178
|
generics: head.generics,
|
|
3090
3179
|
params: head.params,
|
|
3091
3180
|
hasVarargs: head.hasVarargs,
|
|
3092
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3093
3181
|
returnType: head.returnType,
|
|
3094
3182
|
predicate: head.predicate,
|
|
3095
3183
|
body,
|
|
@@ -3230,9 +3318,10 @@ var Parser = class {
|
|
|
3230
3318
|
return this.parseFunctionTypeAfterParen(t, []);
|
|
3231
3319
|
}
|
|
3232
3320
|
if (t.type === "Operator" && t.value === "...") {
|
|
3321
|
+
this.problem("'...' in a type goes before a rest parameter, '...args: T[]', or at the end of a tuple, '[A, ...B[]]'");
|
|
3233
3322
|
this.advance();
|
|
3234
|
-
const
|
|
3235
|
-
return { type: "
|
|
3323
|
+
const element = this.parseType();
|
|
3324
|
+
return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
|
|
3236
3325
|
}
|
|
3237
3326
|
if (t.type === "Punctuator" && t.value === "{") {
|
|
3238
3327
|
return this.parseTableType();
|
|
@@ -3240,13 +3329,22 @@ var Parser = class {
|
|
|
3240
3329
|
if (t.type === "Punctuator" && t.value === "[") {
|
|
3241
3330
|
this.advance();
|
|
3242
3331
|
const elements = [];
|
|
3332
|
+
let rest;
|
|
3243
3333
|
while (!this.checkPunctuator("]")) {
|
|
3334
|
+
if (this.matchOperator("...")) {
|
|
3335
|
+
rest = this.parseType();
|
|
3336
|
+
if (rest.type !== "ArrayTypeNode" && !(rest.type === "TypeReference" && rest.base !== "nil")) {
|
|
3337
|
+
this.problem("The rest of a tuple is an array type: '...T[]'");
|
|
3338
|
+
}
|
|
3339
|
+
this.matchPunctuator(",");
|
|
3340
|
+
break;
|
|
3341
|
+
}
|
|
3244
3342
|
elements.push(this.parseType());
|
|
3245
3343
|
if (this.matchPunctuator(",")) continue;
|
|
3246
3344
|
break;
|
|
3247
3345
|
}
|
|
3248
3346
|
this.expectPunctuator("]");
|
|
3249
|
-
return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
|
|
3347
|
+
return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
|
|
3250
3348
|
}
|
|
3251
3349
|
if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
|
|
3252
3350
|
this.advance();
|
|
@@ -3293,9 +3391,9 @@ var Parser = class {
|
|
|
3293
3391
|
if (this.checkOperator("<")) {
|
|
3294
3392
|
this.advance();
|
|
3295
3393
|
if (!this.checkOperator(">")) {
|
|
3296
|
-
typeArguments.push(this.
|
|
3394
|
+
typeArguments.push(this.parseType());
|
|
3297
3395
|
while (this.matchPunctuator(",") && !this.checkOperator(">")) {
|
|
3298
|
-
typeArguments.push(this.
|
|
3396
|
+
typeArguments.push(this.parseType());
|
|
3299
3397
|
}
|
|
3300
3398
|
}
|
|
3301
3399
|
this.expectOperator(">");
|
|
@@ -3306,13 +3404,12 @@ var Parser = class {
|
|
|
3306
3404
|
}
|
|
3307
3405
|
parseFunctionTypeAfterParen(start, generics) {
|
|
3308
3406
|
const params = [];
|
|
3309
|
-
let
|
|
3310
|
-
let varargType;
|
|
3407
|
+
let rested = false;
|
|
3311
3408
|
if (!this.checkPunctuator(")")) {
|
|
3312
3409
|
while (true) {
|
|
3313
3410
|
if (this.checkOperator("...")) {
|
|
3314
3411
|
const dots = this.advance();
|
|
3315
|
-
|
|
3412
|
+
rested = true;
|
|
3316
3413
|
if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
|
|
3317
3414
|
const nameTok2 = this.expectIdentifier();
|
|
3318
3415
|
this.advance();
|
|
@@ -3326,16 +3423,21 @@ var Parser = class {
|
|
|
3326
3423
|
});
|
|
3327
3424
|
break;
|
|
3328
3425
|
}
|
|
3329
|
-
|
|
3426
|
+
this.problemAt(dots, "A function type takes the rest of its arguments as an array: '(...args: T[]) => R'");
|
|
3427
|
+
this.rejectPackReference();
|
|
3428
|
+
const element = this.parseType();
|
|
3429
|
+
params.push({
|
|
3430
|
+
type: "FunctionTypeParameter",
|
|
3431
|
+
name: "args",
|
|
3432
|
+
rest: true,
|
|
3433
|
+
typeAnnotation: { type: "ArrayTypeNode", element, ...spanFrom(element, element) },
|
|
3434
|
+
...spanFrom(dots, this.previous())
|
|
3435
|
+
});
|
|
3330
3436
|
break;
|
|
3331
3437
|
}
|
|
3332
3438
|
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
this.advance();
|
|
3336
|
-
hasVarargs = true;
|
|
3337
|
-
varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
|
|
3338
|
-
break;
|
|
3439
|
+
this.rejectPackReference();
|
|
3440
|
+
continue;
|
|
3339
3441
|
}
|
|
3340
3442
|
let name;
|
|
3341
3443
|
let optional2 = false;
|
|
@@ -3365,19 +3467,17 @@ var Parser = class {
|
|
|
3365
3467
|
this.expectPunctuator(")");
|
|
3366
3468
|
if (this.matchPunctuator("=>") || this.mistypedArrow()) {
|
|
3367
3469
|
const predicate = this.tryParseTypePredicate();
|
|
3368
|
-
const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.
|
|
3470
|
+
const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseReturnType();
|
|
3369
3471
|
return {
|
|
3370
3472
|
type: "FunctionTypeNode",
|
|
3371
3473
|
generics,
|
|
3372
3474
|
params,
|
|
3373
|
-
hasVarargs,
|
|
3374
|
-
varargType,
|
|
3375
3475
|
returnType,
|
|
3376
3476
|
predicate,
|
|
3377
3477
|
...spanFrom(start, this.previous())
|
|
3378
3478
|
};
|
|
3379
3479
|
}
|
|
3380
|
-
if (params.length === 1 && !params[0].name && !
|
|
3480
|
+
if (params.length === 1 && !params[0].name && !rested) {
|
|
3381
3481
|
return {
|
|
3382
3482
|
type: "ParenthesizedTypeNode",
|
|
3383
3483
|
typeAnnotation: params[0].typeAnnotation,
|
|
@@ -3387,11 +3487,14 @@ var Parser = class {
|
|
|
3387
3487
|
if (params.some((p) => p.name !== void 0)) {
|
|
3388
3488
|
this.error("Expected '=>' for function type");
|
|
3389
3489
|
}
|
|
3490
|
+
if (!params.length) {
|
|
3491
|
+
this.problemAt(start, "'()' is not a type: a function that returns nothing returns 'nil'");
|
|
3492
|
+
return { type: "TypeReference", base: "nil", typeArguments: [], ...spanFrom(start, this.previous()) };
|
|
3493
|
+
}
|
|
3494
|
+
this.problemAt(start, "Several types in parentheses are a pack, which tilua does not have: write a tuple, '[A, B]'");
|
|
3390
3495
|
return {
|
|
3391
|
-
type: "
|
|
3392
|
-
|
|
3393
|
-
hasVarargs,
|
|
3394
|
-
varargType,
|
|
3496
|
+
type: "TupleTypeNode",
|
|
3497
|
+
elements: params.map((p) => p.typeAnnotation),
|
|
3395
3498
|
...spanFrom(start, this.previous())
|
|
3396
3499
|
};
|
|
3397
3500
|
}
|
|
@@ -3529,40 +3632,15 @@ var Parser = class {
|
|
|
3529
3632
|
this.expectPunctuator("}");
|
|
3530
3633
|
return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
|
|
3531
3634
|
}
|
|
3532
|
-
parseTypePack() {
|
|
3533
|
-
const start = this.current();
|
|
3534
|
-
if (this.matchOperator("...")) {
|
|
3535
|
-
const varargType2 = this.parseType();
|
|
3536
|
-
return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
|
|
3537
|
-
}
|
|
3538
|
-
this.expectPunctuator("(");
|
|
3539
|
-
const types = [];
|
|
3540
|
-
let hasVarargs = false;
|
|
3541
|
-
let varargType;
|
|
3542
|
-
if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
|
|
3543
|
-
while (true) {
|
|
3544
|
-
if (this.matchOperator("...")) {
|
|
3545
|
-
hasVarargs = true;
|
|
3546
|
-
varargType = this.parseType();
|
|
3547
|
-
break;
|
|
3548
|
-
}
|
|
3549
|
-
types.push(this.parseType());
|
|
3550
|
-
if (this.matchPunctuator(",")) continue;
|
|
3551
|
-
break;
|
|
3552
|
-
}
|
|
3553
|
-
}
|
|
3554
|
-
this.expectPunctuator(")");
|
|
3555
|
-
return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
|
|
3556
|
-
}
|
|
3557
3635
|
parseGenericTypeParameterList() {
|
|
3558
3636
|
const list = [];
|
|
3559
3637
|
this.expectOperator("<");
|
|
3560
3638
|
while (true) {
|
|
3561
3639
|
const isConst = this.matchKeyword("const");
|
|
3562
3640
|
const nameTok = this.expectIdentifier();
|
|
3563
|
-
|
|
3564
|
-
if (
|
|
3565
|
-
|
|
3641
|
+
const dots = this.checkOperator("...") ? this.advance() : void 0;
|
|
3642
|
+
if (dots) {
|
|
3643
|
+
this.problemAt(dots, "tilua has no pack parameters: write '<T extends unknown[]>', and take it as '...args: T'");
|
|
3566
3644
|
}
|
|
3567
3645
|
let constraint;
|
|
3568
3646
|
if (this.checkIdentifierValue("extends")) {
|
|
@@ -3571,17 +3649,13 @@ var Parser = class {
|
|
|
3571
3649
|
}
|
|
3572
3650
|
let def;
|
|
3573
3651
|
if (this.matchOperator("=")) {
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
} else {
|
|
3577
|
-
def = this.parseType();
|
|
3578
|
-
}
|
|
3652
|
+
this.rejectPackReference();
|
|
3653
|
+
def = this.parseType();
|
|
3579
3654
|
}
|
|
3580
3655
|
list.push({
|
|
3581
3656
|
type: "GenericTypeParameter",
|
|
3582
3657
|
name: nameTok.value,
|
|
3583
3658
|
id: tokenIdentifier(nameTok),
|
|
3584
|
-
isPack,
|
|
3585
3659
|
isConst: isConst || void 0,
|
|
3586
3660
|
constraint,
|
|
3587
3661
|
default: def,
|
|
@@ -3672,7 +3746,7 @@ function childScope(parent) {
|
|
|
3672
3746
|
var Analyzer = class {
|
|
3673
3747
|
constructor(options) {
|
|
3674
3748
|
this.options = options;
|
|
3675
|
-
for (const name of options.builtinGlobals ?? []) {
|
|
3749
|
+
for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
|
|
3676
3750
|
const id = this.getOrCreateGlobalBinding(name);
|
|
3677
3751
|
this.bindings.get(id).isBuiltin = true;
|
|
3678
3752
|
}
|
|
@@ -4074,14 +4148,14 @@ var Analyzer = class {
|
|
|
4074
4148
|
return;
|
|
4075
4149
|
}
|
|
4076
4150
|
case "GenericForStatement": {
|
|
4077
|
-
|
|
4151
|
+
this.visitExpression(stmt.iterator, scope);
|
|
4078
4152
|
const bodyScope = childScope(scope);
|
|
4079
|
-
|
|
4153
|
+
this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
|
|
4080
4154
|
this.visitBlock(stmt.body, bodyScope);
|
|
4081
4155
|
return;
|
|
4082
4156
|
}
|
|
4083
4157
|
case "ReturnStatement":
|
|
4084
|
-
|
|
4158
|
+
if (stmt.argument) this.visitExpression(stmt.argument, scope);
|
|
4085
4159
|
return;
|
|
4086
4160
|
case "BreakStatement":
|
|
4087
4161
|
case "ContinueStatement":
|
|
@@ -4149,7 +4223,6 @@ var Analyzer = class {
|
|
|
4149
4223
|
this.declare(fnScope, param.name, kind, param);
|
|
4150
4224
|
}
|
|
4151
4225
|
});
|
|
4152
|
-
this.visitType(func.varargTypeAnnotation, fnScope);
|
|
4153
4226
|
this.visitType(func.returnType, fnScope);
|
|
4154
4227
|
this.functionDepth++;
|
|
4155
4228
|
try {
|
|
@@ -4177,6 +4250,7 @@ var Analyzer = class {
|
|
|
4177
4250
|
this.visitGenerics(node.typeParams, scope);
|
|
4178
4251
|
if (node.superclass) this.reference(outer, node.superclass);
|
|
4179
4252
|
for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
|
|
4253
|
+
for (const shape of node.implements ?? []) this.visitType(shape, scope);
|
|
4180
4254
|
for (const member of node.members) {
|
|
4181
4255
|
switch (member.type) {
|
|
4182
4256
|
case "ClassField":
|
|
@@ -4236,7 +4310,6 @@ var Analyzer = class {
|
|
|
4236
4310
|
case "BooleanLiteral":
|
|
4237
4311
|
case "NumberLiteral":
|
|
4238
4312
|
case "StringLiteral":
|
|
4239
|
-
case "VarargExpression":
|
|
4240
4313
|
case "ErrorExpression":
|
|
4241
4314
|
return;
|
|
4242
4315
|
case "InterpolatedStringExpression":
|
|
@@ -4276,11 +4349,6 @@ var Analyzer = class {
|
|
|
4276
4349
|
this.visitExpression(expr.callee, scope);
|
|
4277
4350
|
for (const arg of expr.arguments) this.visitExpression(arg, scope);
|
|
4278
4351
|
return;
|
|
4279
|
-
case "NewExpression":
|
|
4280
|
-
this.visitExpression(expr.callee, scope);
|
|
4281
|
-
for (const argument of expr.arguments) this.visitExpression(argument, scope);
|
|
4282
|
-
for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
|
|
4283
|
-
return;
|
|
4284
4352
|
case "SuperExpression":
|
|
4285
4353
|
return;
|
|
4286
4354
|
case "SpreadElement":
|
|
@@ -4334,6 +4402,7 @@ var Analyzer = class {
|
|
|
4334
4402
|
}
|
|
4335
4403
|
}
|
|
4336
4404
|
};
|
|
4405
|
+
var LANGUAGE_GLOBALS = ["scriptArgs"];
|
|
4337
4406
|
function analyzeScopes(program, options = {}) {
|
|
4338
4407
|
return new Analyzer(options).run(program);
|
|
4339
4408
|
}
|
|
@@ -4352,8 +4421,8 @@ type Exclude<T, U> = T - U
|
|
|
4352
4421
|
type Extract<T, U> = T extends U ? T : never
|
|
4353
4422
|
type NonNullable<T> = T - nil
|
|
4354
4423
|
|
|
4355
|
-
type ReturnType<T> = T extends (...unknown) => infer R ? R : never
|
|
4356
|
-
type Parameters<T> = T extends (...infer P) => unknown ? P : never
|
|
4424
|
+
type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
|
|
4425
|
+
type Parameters<T> = T extends (...args: infer P) => unknown ? P : never
|
|
4357
4426
|
|
|
4358
4427
|
type Partial<T> = { [K in keyof T]?: T[K] }
|
|
4359
4428
|
type Required<T> = { [K in keyof T]-?: T[K] }
|
|
@@ -4438,8 +4507,11 @@ function literal(value) {
|
|
|
4438
4507
|
function arrayOf(element) {
|
|
4439
4508
|
return { kind: "array", element };
|
|
4440
4509
|
}
|
|
4441
|
-
function tuple(elements,
|
|
4442
|
-
return { kind: "tuple", elements,
|
|
4510
|
+
function tuple(elements, rest) {
|
|
4511
|
+
return rest ? { kind: "tuple", elements, rest } : { kind: "tuple", elements };
|
|
4512
|
+
}
|
|
4513
|
+
function tupleMembers(t) {
|
|
4514
|
+
return t.rest ? [...t.elements, t.rest] : t.elements;
|
|
4443
4515
|
}
|
|
4444
4516
|
function objectType(entries, indexer, frozen) {
|
|
4445
4517
|
return { kind: "object", properties: new Map(entries), indexer, frozen };
|
|
@@ -4463,7 +4535,7 @@ function substitute(t, subst) {
|
|
|
4463
4535
|
case "array":
|
|
4464
4536
|
return arrayOf(substitute(t.element, subst));
|
|
4465
4537
|
case "tuple":
|
|
4466
|
-
return tuple(t.elements.map((e) => substitute(e, subst)), t.
|
|
4538
|
+
return tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst));
|
|
4467
4539
|
case "object": {
|
|
4468
4540
|
const entries = [];
|
|
4469
4541
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
|
|
@@ -4487,14 +4559,20 @@ function substitute(t, subst) {
|
|
|
4487
4559
|
const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
|
|
4488
4560
|
let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
|
|
4489
4561
|
let varargs = t.varargs && substitute(t.varargs, inner);
|
|
4490
|
-
|
|
4562
|
+
let restIsWhole = t.restIsWhole;
|
|
4563
|
+
if (restIsWhole && varargs?.kind === "tuple") {
|
|
4491
4564
|
params = [...params, ...varargs.elements.map((type) => ({ type }))];
|
|
4492
|
-
varargs =
|
|
4565
|
+
varargs = varargs.rest;
|
|
4566
|
+
restIsWhole = void 0;
|
|
4567
|
+
} else if (restIsWhole && varargs?.kind === "array") {
|
|
4568
|
+
varargs = varargs.element;
|
|
4569
|
+
restIsWhole = void 0;
|
|
4493
4570
|
}
|
|
4494
4571
|
return {
|
|
4495
4572
|
kind: "function",
|
|
4496
4573
|
params,
|
|
4497
4574
|
varargs,
|
|
4575
|
+
...restIsWhole ? { restIsWhole } : {},
|
|
4498
4576
|
returns: substitute(t.returns, inner),
|
|
4499
4577
|
typeParams: t.typeParams,
|
|
4500
4578
|
typeParamDefaults: t.typeParamDefaults,
|
|
@@ -4577,11 +4655,19 @@ function unify(param, arg, vars, out) {
|
|
|
4577
4655
|
switch (param.kind) {
|
|
4578
4656
|
case "array":
|
|
4579
4657
|
if (arg.kind === "array") unify(param.element, arg.element, vars, out);
|
|
4580
|
-
else if (arg.kind === "tuple") for (const e of arg
|
|
4658
|
+
else if (arg.kind === "tuple") for (const e of tupleMembers(arg)) unify(param.element, e, vars, out);
|
|
4581
4659
|
return;
|
|
4582
4660
|
case "tuple":
|
|
4583
|
-
if (arg.kind === "tuple")
|
|
4584
|
-
|
|
4661
|
+
if (arg.kind === "tuple") {
|
|
4662
|
+
param.elements.forEach((p, i) => {
|
|
4663
|
+
const a = arg.elements[i] ?? arg.rest;
|
|
4664
|
+
if (a) unify(p, a, vars, out);
|
|
4665
|
+
});
|
|
4666
|
+
if (param.rest) for (const a of arg.elements.slice(param.elements.length)) unify(param.rest, a, vars, out);
|
|
4667
|
+
if (param.rest && arg.rest) unify(param.rest, arg.rest, vars, out);
|
|
4668
|
+
} else if (arg.kind === "array") {
|
|
4669
|
+
for (const p of tupleMembers(param)) unify(p, arg.element, vars, out);
|
|
4670
|
+
}
|
|
4585
4671
|
return;
|
|
4586
4672
|
case "function":
|
|
4587
4673
|
if (arg.kind === "function") {
|
|
@@ -4683,7 +4769,7 @@ function widen(t) {
|
|
|
4683
4769
|
case "array":
|
|
4684
4770
|
return arrayOf(widen(t.element));
|
|
4685
4771
|
case "tuple":
|
|
4686
|
-
return tuple(t.elements.map(widen), t.
|
|
4772
|
+
return tuple(t.elements.map(widen), t.rest && widen(t.rest));
|
|
4687
4773
|
case "object": {
|
|
4688
4774
|
if (t.frozen || t.class) return t;
|
|
4689
4775
|
const entries = [];
|
|
@@ -4783,13 +4869,18 @@ function isAssignableInner(a, b) {
|
|
|
4783
4869
|
if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
|
|
4784
4870
|
if (a.kind === "array") {
|
|
4785
4871
|
if (b.kind === "array") return isAssignable(a.element, b.element);
|
|
4872
|
+
if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
|
|
4786
4873
|
return false;
|
|
4787
4874
|
}
|
|
4788
4875
|
if (a.kind === "tuple") {
|
|
4789
4876
|
if (b.kind === "tuple") {
|
|
4790
|
-
|
|
4877
|
+
if (a.elements.length < b.elements.length) return false;
|
|
4878
|
+
if (!b.elements.every((t, i) => isAssignable(a.elements[i], t))) return false;
|
|
4879
|
+
const extra = [...a.elements.slice(b.elements.length), ...a.rest ? [a.rest] : []];
|
|
4880
|
+
if (!b.rest) return extra.length === 0;
|
|
4881
|
+
return extra.every((t) => isAssignable(t, b.rest));
|
|
4791
4882
|
}
|
|
4792
|
-
if (b.kind === "array") return a.
|
|
4883
|
+
if (b.kind === "array") return tupleMembers(a).every((t) => isAssignable(t, b.element));
|
|
4793
4884
|
return false;
|
|
4794
4885
|
}
|
|
4795
4886
|
if (a.kind === "object") {
|
|
@@ -4838,7 +4929,7 @@ function sameRefTarget(a, b) {
|
|
|
4838
4929
|
return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
|
|
4839
4930
|
}
|
|
4840
4931
|
function isNoValue(t) {
|
|
4841
|
-
return t.kind === "tuple" && t.elements.length === 0;
|
|
4932
|
+
return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
|
|
4842
4933
|
}
|
|
4843
4934
|
function equalTypes(a, b) {
|
|
4844
4935
|
return formatType(a) === formatType(b);
|
|
@@ -4945,7 +5036,7 @@ function containsFreeTypeParam(t, seen, bound) {
|
|
|
4945
5036
|
case "array":
|
|
4946
5037
|
return containsTypeParam(t.element, seen, bound);
|
|
4947
5038
|
case "tuple":
|
|
4948
|
-
return t.
|
|
5039
|
+
return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
|
|
4949
5040
|
case "union":
|
|
4950
5041
|
case "intersection":
|
|
4951
5042
|
return t.types.some((m) => containsTypeParam(m, seen, bound));
|
|
@@ -4990,15 +5081,19 @@ function matchInfer(arg, pattern, out) {
|
|
|
4990
5081
|
switch (pattern.kind) {
|
|
4991
5082
|
case "array":
|
|
4992
5083
|
if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
|
|
4993
|
-
if (arg.kind === "tuple") return matchInfer(union(arg
|
|
5084
|
+
if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
|
|
4994
5085
|
return false;
|
|
4995
5086
|
case "tuple":
|
|
4996
5087
|
if (arg.kind !== "tuple" || arg.elements.length !== pattern.elements.length) return false;
|
|
5088
|
+
if (pattern.rest && !matchInfer(arg.rest ?? neverType, pattern.rest, out)) return false;
|
|
4997
5089
|
return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
|
|
4998
5090
|
case "function": {
|
|
4999
5091
|
if (arg.kind !== "function") return false;
|
|
5000
5092
|
if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
|
|
5001
|
-
out.set(pattern.varargs.name, tuple(
|
|
5093
|
+
out.set(pattern.varargs.name, tuple(
|
|
5094
|
+
arg.params.map((p) => p.type),
|
|
5095
|
+
arg.varargs && !arg.restIsWhole ? arg.varargs : void 0
|
|
5096
|
+
));
|
|
5002
5097
|
} else {
|
|
5003
5098
|
for (let i = 0; i < pattern.params.length; i++) {
|
|
5004
5099
|
if (!arg.params[i]) return false;
|
|
@@ -5072,7 +5167,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
|
|
|
5072
5167
|
collectTypeParams(t.element, out, seen);
|
|
5073
5168
|
return;
|
|
5074
5169
|
case "tuple":
|
|
5075
|
-
for (const e of t
|
|
5170
|
+
for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
|
|
5076
5171
|
return;
|
|
5077
5172
|
case "union":
|
|
5078
5173
|
case "intersection":
|
|
@@ -5119,9 +5214,8 @@ function formatTypeUncached(t) {
|
|
|
5119
5214
|
case "array":
|
|
5120
5215
|
return `${formatAtom(t.element)}[]`;
|
|
5121
5216
|
case "tuple": {
|
|
5122
|
-
|
|
5123
|
-
|
|
5124
|
-
return t.isPack ? `(${inner})` : `[${inner}]`;
|
|
5217
|
+
const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
|
|
5218
|
+
return `[${inner.join(", ")}]`;
|
|
5125
5219
|
}
|
|
5126
5220
|
case "object": {
|
|
5127
5221
|
if (t.name) {
|
|
@@ -5145,7 +5239,7 @@ function formatTypeUncached(t) {
|
|
|
5145
5239
|
return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
|
|
5146
5240
|
}).join(", ")}>` : "";
|
|
5147
5241
|
const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
|
|
5148
|
-
if (t.varargs) ps.push(
|
|
5242
|
+
if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
|
|
5149
5243
|
return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
|
|
5150
5244
|
}
|
|
5151
5245
|
case "typeParam":
|
|
@@ -5349,6 +5443,11 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5349
5443
|
}
|
|
5350
5444
|
return { values, types: exportedTypes, default: defaultType };
|
|
5351
5445
|
}
|
|
5446
|
+
function isBelow(k, key) {
|
|
5447
|
+
if (!k.startsWith(key) || k.length === key.length) return false;
|
|
5448
|
+
const next = k[key.length];
|
|
5449
|
+
return next === "." || next === "#" || next === "[";
|
|
5450
|
+
}
|
|
5352
5451
|
function bindKey(id) {
|
|
5353
5452
|
return `$${id}`;
|
|
5354
5453
|
}
|
|
@@ -5366,9 +5465,6 @@ function mergeEnv(a, b, base) {
|
|
|
5366
5465
|
function mergeIndexer(a, b) {
|
|
5367
5466
|
return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
|
|
5368
5467
|
}
|
|
5369
|
-
function producesMultipleValues(e) {
|
|
5370
|
-
return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
|
|
5371
|
-
}
|
|
5372
5468
|
function isFreshLiteralExpr(e) {
|
|
5373
5469
|
if (!e) return false;
|
|
5374
5470
|
switch (e.type) {
|
|
@@ -5405,11 +5501,11 @@ function collectInferNames(node) {
|
|
|
5405
5501
|
walk(n.element);
|
|
5406
5502
|
return;
|
|
5407
5503
|
case "ParenthesizedTypeNode":
|
|
5408
|
-
case "VariadicTypeNode":
|
|
5409
5504
|
walk(n.typeAnnotation);
|
|
5410
5505
|
return;
|
|
5411
5506
|
case "TupleTypeNode":
|
|
5412
5507
|
n.elements.forEach(walk);
|
|
5508
|
+
walk(n.rest);
|
|
5413
5509
|
return;
|
|
5414
5510
|
case "UnionTypeNode":
|
|
5415
5511
|
case "IntersectionTypeNode":
|
|
@@ -5427,7 +5523,6 @@ function collectInferNames(node) {
|
|
|
5427
5523
|
return;
|
|
5428
5524
|
case "FunctionTypeNode":
|
|
5429
5525
|
n.params.forEach((pp) => walk(pp.typeAnnotation));
|
|
5430
|
-
walk(n.varargType);
|
|
5431
5526
|
walk(n.returnType);
|
|
5432
5527
|
return;
|
|
5433
5528
|
case "TableTypeNode":
|
|
@@ -5472,6 +5567,75 @@ function callsSuper(block) {
|
|
|
5472
5567
|
});
|
|
5473
5568
|
return found;
|
|
5474
5569
|
}
|
|
5570
|
+
var ALL_FIELDS = /* @__PURE__ */ new Set();
|
|
5571
|
+
function definitelyAssigned(block) {
|
|
5572
|
+
const names = /* @__PURE__ */ new Set();
|
|
5573
|
+
for (const statement of block.statements) {
|
|
5574
|
+
switch (statement.type) {
|
|
5575
|
+
case "AssignmentStatement":
|
|
5576
|
+
case "CompoundAssignmentStatement":
|
|
5577
|
+
for (const name of assignedFields({ ...block, statements: [statement] })) names.add(name);
|
|
5578
|
+
break;
|
|
5579
|
+
case "DoStatement": {
|
|
5580
|
+
const inner = definitelyAssigned(statement.body);
|
|
5581
|
+
if (!inner) return void 0;
|
|
5582
|
+
for (const name of inner) names.add(name);
|
|
5583
|
+
break;
|
|
5584
|
+
}
|
|
5585
|
+
case "IfStatement": {
|
|
5586
|
+
if (!statement.alternate) break;
|
|
5587
|
+
const arms = [...statement.clauses.map((c) => c.body), statement.alternate].map(definitelyAssigned);
|
|
5588
|
+
const finishing = arms.filter((arm) => arm !== void 0);
|
|
5589
|
+
if (!finishing.length) return void 0;
|
|
5590
|
+
for (const name of finishing[0]) {
|
|
5591
|
+
if (finishing.every((arm) => arm.has(name))) names.add(name);
|
|
5592
|
+
}
|
|
5593
|
+
break;
|
|
5594
|
+
}
|
|
5595
|
+
case "ReturnStatement":
|
|
5596
|
+
return void 0;
|
|
5597
|
+
case "CallStatement": {
|
|
5598
|
+
const call = statement.expression;
|
|
5599
|
+
if (call.type === "CallExpression" && call.callee.type === "Identifier" && call.callee.name === "error") {
|
|
5600
|
+
return void 0;
|
|
5601
|
+
}
|
|
5602
|
+
break;
|
|
5603
|
+
}
|
|
5604
|
+
}
|
|
5605
|
+
}
|
|
5606
|
+
return names;
|
|
5607
|
+
}
|
|
5608
|
+
function withoutReceiver(t) {
|
|
5609
|
+
if (t.kind === "intersection") return intersection(t.types.map(withoutReceiver));
|
|
5610
|
+
if (t.kind !== "function") return t;
|
|
5611
|
+
const first = t.params[0]?.name;
|
|
5612
|
+
if (first !== "this" && first !== "self") return t;
|
|
5613
|
+
return { ...t, params: t.params.slice(1) };
|
|
5614
|
+
}
|
|
5615
|
+
var METAMETHOD_OPERANDS = {
|
|
5616
|
+
__add: 1,
|
|
5617
|
+
__sub: 1,
|
|
5618
|
+
__mul: 1,
|
|
5619
|
+
__div: 1,
|
|
5620
|
+
__idiv: 1,
|
|
5621
|
+
__mod: 1,
|
|
5622
|
+
__pow: 1,
|
|
5623
|
+
__concat: 1,
|
|
5624
|
+
__eq: 1,
|
|
5625
|
+
__lt: 1,
|
|
5626
|
+
__le: 1,
|
|
5627
|
+
__unm: 0,
|
|
5628
|
+
__len: 0,
|
|
5629
|
+
__tostring: 0,
|
|
5630
|
+
__iter: 0,
|
|
5631
|
+
__call: void 0
|
|
5632
|
+
};
|
|
5633
|
+
var METAMETHOD_RETURNS = {
|
|
5634
|
+
__tostring: stringType,
|
|
5635
|
+
__eq: booleanType,
|
|
5636
|
+
__lt: booleanType,
|
|
5637
|
+
__le: booleanType
|
|
5638
|
+
};
|
|
5475
5639
|
function assignedFields(block) {
|
|
5476
5640
|
const names = /* @__PURE__ */ new Set();
|
|
5477
5641
|
walkNodes(block, (node) => {
|
|
@@ -5497,7 +5661,7 @@ function walkNodes(root, visit) {
|
|
|
5497
5661
|
if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
|
|
5498
5662
|
}
|
|
5499
5663
|
}
|
|
5500
|
-
var
|
|
5664
|
+
var LazyMap = class extends Map {
|
|
5501
5665
|
pending = /* @__PURE__ */ new Map();
|
|
5502
5666
|
defer(name, resolve5) {
|
|
5503
5667
|
super.delete(name);
|
|
@@ -5612,14 +5776,14 @@ var TypeAnalyzer = class {
|
|
|
5612
5776
|
scopes;
|
|
5613
5777
|
options;
|
|
5614
5778
|
typeOf = /* @__PURE__ */ new Map();
|
|
5615
|
-
bindingType =
|
|
5779
|
+
bindingType = new LazyMap();
|
|
5616
5780
|
narrowedTypeOf = /* @__PURE__ */ new Map();
|
|
5617
5781
|
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
5618
5782
|
expectedTypeOf = /* @__PURE__ */ new Map();
|
|
5619
|
-
|
|
5783
|
+
loops = /* @__PURE__ */ new Map();
|
|
5620
5784
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
5621
5785
|
* `typeParam` nodes in the body). */
|
|
5622
|
-
aliases = new
|
|
5786
|
+
aliases = new LazyMap();
|
|
5623
5787
|
/** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
|
|
5624
5788
|
aliasDefs = /* @__PURE__ */ new Map();
|
|
5625
5789
|
/** Put on every ref to one of `aliasDefs`, so a module that imports the
|
|
@@ -5633,8 +5797,12 @@ var TypeAnalyzer = class {
|
|
|
5633
5797
|
/** Generic parameters currently in lexical scope (alias body / generic fn),
|
|
5634
5798
|
* with their `extends` constraints resolved. */
|
|
5635
5799
|
typeParamScope = [];
|
|
5636
|
-
/** Global types contributed by `declare` statements (libs, then this program).
|
|
5637
|
-
|
|
5800
|
+
/** Global types contributed by `declare` statements (libs, then this program).
|
|
5801
|
+
* A library's are resolved when the name is first used: an engine's
|
|
5802
|
+
* definitions declare hundreds of globals, and a script uses a few. */
|
|
5803
|
+
libGlobalTypes = new LazyMap();
|
|
5804
|
+
/** Each library `declare` of a name, in order, until it is resolved. */
|
|
5805
|
+
libDeclares = /* @__PURE__ */ new Map();
|
|
5638
5806
|
/** Declaration node -> binding, built once so `bindingIdByName` is O(1)
|
|
5639
5807
|
* instead of a scan of every binding per declaration site. */
|
|
5640
5808
|
bindingByDecl = /* @__PURE__ */ new Map();
|
|
@@ -5679,8 +5847,11 @@ var TypeAnalyzer = class {
|
|
|
5679
5847
|
this.indexDeclarations();
|
|
5680
5848
|
for (const [name, id] of this.scopes.globalsByName) {
|
|
5681
5849
|
if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
|
|
5682
|
-
const
|
|
5683
|
-
this.bindingType.set(id,
|
|
5850
|
+
const given = this.options.globalTypes?.[name];
|
|
5851
|
+
if (given) this.bindingType.set(id, given);
|
|
5852
|
+
else if (this.libGlobalTypes.has(name)) this.bindingType.defer(id, () => this.libGlobalTypes.get(name) ?? anyType);
|
|
5853
|
+
else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(unknownType));
|
|
5854
|
+
else this.bindingType.set(id, anyType);
|
|
5684
5855
|
}
|
|
5685
5856
|
setAliasExpander((t) => this.expand(t));
|
|
5686
5857
|
setDeferredBound((t) => this.deferredBound(t));
|
|
@@ -5700,7 +5871,7 @@ var TypeAnalyzer = class {
|
|
|
5700
5871
|
typeOfTypeNode: this.typeOfTypeNode,
|
|
5701
5872
|
expectedTypeOf: this.expectedTypeOf,
|
|
5702
5873
|
aliases: this.resolveDeferredAliases(),
|
|
5703
|
-
|
|
5874
|
+
loops: this.loops,
|
|
5704
5875
|
diagnostics: this.diagnostics
|
|
5705
5876
|
};
|
|
5706
5877
|
}
|
|
@@ -6052,28 +6223,55 @@ var TypeAnalyzer = class {
|
|
|
6052
6223
|
type.name = `typeof ${this.className(stmt)}`;
|
|
6053
6224
|
const shape = this.shapeOf(stmt);
|
|
6054
6225
|
const local = this.superDecl(stmt);
|
|
6055
|
-
const parent = local ? this.classValueType(local) : stmt.superclass ? this.
|
|
6226
|
+
const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
|
|
6056
6227
|
if (parent?.kind === "object") {
|
|
6057
6228
|
for (const [key, property] of parent.properties) {
|
|
6058
6229
|
if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
|
|
6059
6230
|
}
|
|
6060
6231
|
}
|
|
6061
6232
|
for (const [key, property] of shape.statics) type.properties.set(key, property);
|
|
6062
|
-
|
|
6233
|
+
if (shape.filling) {
|
|
6234
|
+
(shape.filled ??= []).push(() => {
|
|
6235
|
+
for (const [key, property] of shape.statics) type.properties.set(key, property);
|
|
6236
|
+
});
|
|
6237
|
+
}
|
|
6063
6238
|
const params = this.classTypeParams(stmt);
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
constructor?.params.filter((p) => p.name !== "this") ?? [],
|
|
6067
|
-
this.selfTypeOf(stmt),
|
|
6068
|
-
constructor?.varargs,
|
|
6069
|
-
params.map((p) => p.name)
|
|
6070
|
-
),
|
|
6239
|
+
let settled;
|
|
6240
|
+
const construct = {
|
|
6071
6241
|
optional: false,
|
|
6072
|
-
readonly: true
|
|
6242
|
+
readonly: true,
|
|
6243
|
+
// Kept, so a class extending it can read what it takes; reaching
|
|
6244
|
+
// it from outside is the error.
|
|
6245
|
+
...stmt.isAbstract ? { abstract: true } : {}
|
|
6246
|
+
};
|
|
6247
|
+
Object.defineProperty(construct, "type", {
|
|
6248
|
+
enumerable: true,
|
|
6249
|
+
get: () => {
|
|
6250
|
+
if (settled) return settled;
|
|
6251
|
+
const constructor = this.constructorType(stmt);
|
|
6252
|
+
const made = fn(
|
|
6253
|
+
constructor?.params.filter((p) => p.name !== "this") ?? [],
|
|
6254
|
+
this.selfTypeOf(stmt),
|
|
6255
|
+
constructor?.varargs,
|
|
6256
|
+
params.map((p) => p.name)
|
|
6257
|
+
);
|
|
6258
|
+
if (!this.shapeOf(stmt).filling) settled = made;
|
|
6259
|
+
return made;
|
|
6260
|
+
}
|
|
6073
6261
|
});
|
|
6262
|
+
type.properties.set("new", construct);
|
|
6074
6263
|
type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
|
|
6075
6264
|
return type;
|
|
6076
6265
|
}
|
|
6266
|
+
/** The value side of the class `superclass` names when it is not
|
|
6267
|
+
* declared in this file: what the name it refers to holds — an import
|
|
6268
|
+
* brings the class table with it — or else a class of that name. */
|
|
6269
|
+
classStaticsOf(superclass) {
|
|
6270
|
+
const id = this.bindingIdOf(superclass);
|
|
6271
|
+
const bound = id !== void 0 ? this.expand(this.bindingType.get(id) ?? unknownType) : void 0;
|
|
6272
|
+
if (bound?.kind === "object") return bound;
|
|
6273
|
+
return this.classStaticsByNameType(superclass.name);
|
|
6274
|
+
}
|
|
6077
6275
|
/** The value side of a class named by a binding rather than by a
|
|
6078
6276
|
* declaration in this file — an imported one. */
|
|
6079
6277
|
classStaticsByNameType(name) {
|
|
@@ -6134,20 +6332,26 @@ var TypeAnalyzer = class {
|
|
|
6134
6332
|
} finally {
|
|
6135
6333
|
this.emitDiagnostics = wasEmitting;
|
|
6136
6334
|
shape.filling = false;
|
|
6335
|
+
for (const then of shape.filled ?? []) then();
|
|
6336
|
+
shape.filled = void 0;
|
|
6137
6337
|
}
|
|
6138
6338
|
return shape;
|
|
6139
6339
|
}
|
|
6140
6340
|
fillShape(stmt, shape) {
|
|
6141
6341
|
const className = this.className(stmt);
|
|
6142
6342
|
const put = (isStatic, name, property) => {
|
|
6143
|
-
const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && m.accessibility === "private");
|
|
6144
|
-
const marked = member ? { ...property, private: { owner: stmt, className } } : property;
|
|
6343
|
+
const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && (m.accessibility === "private" || m.accessibility === "protected"));
|
|
6344
|
+
const marked = member ? { ...property, private: { owner: stmt, className, ...member.accessibility === "protected" ? { protected: true } : {} } } : property;
|
|
6145
6345
|
(isStatic ? shape.statics : shape.instance).set(name, marked);
|
|
6146
6346
|
};
|
|
6147
6347
|
for (const member of stmt.members) {
|
|
6148
6348
|
if (member.type !== "ClassField") continue;
|
|
6149
6349
|
const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
|
|
6150
|
-
put(member.isStatic, member.name.name, {
|
|
6350
|
+
put(member.isStatic, member.name.name, {
|
|
6351
|
+
type,
|
|
6352
|
+
optional: false,
|
|
6353
|
+
...member.isReadonly ? { readonly: true } : {}
|
|
6354
|
+
});
|
|
6151
6355
|
}
|
|
6152
6356
|
for (const member of stmt.members) {
|
|
6153
6357
|
switch (member.type) {
|
|
@@ -6156,7 +6360,11 @@ var TypeAnalyzer = class {
|
|
|
6156
6360
|
case "ClassMethod": {
|
|
6157
6361
|
this.paramsFromSignatures(member.func, member.signatures);
|
|
6158
6362
|
const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
|
|
6159
|
-
put(member.isStatic, member.name.name, {
|
|
6363
|
+
put(member.isStatic, member.name.name, {
|
|
6364
|
+
type,
|
|
6365
|
+
optional: false,
|
|
6366
|
+
...member.isAbstract ? { abstract: true } : {}
|
|
6367
|
+
});
|
|
6160
6368
|
break;
|
|
6161
6369
|
}
|
|
6162
6370
|
case "ClassAccessor": {
|
|
@@ -6212,7 +6420,14 @@ var TypeAnalyzer = class {
|
|
|
6212
6420
|
for (const signature of member.signatures ?? []) {
|
|
6213
6421
|
this.checkParamOrder(signature.params, member);
|
|
6214
6422
|
}
|
|
6215
|
-
|
|
6423
|
+
if (member.type === "ClassMethod" && member.isAbstract) continue;
|
|
6424
|
+
const constructing = this.constructing;
|
|
6425
|
+
if (member.type === "ClassConstructor") this.constructing = stmt;
|
|
6426
|
+
try {
|
|
6427
|
+
this.visitFunctionBody(member.func, env);
|
|
6428
|
+
} finally {
|
|
6429
|
+
this.constructing = constructing;
|
|
6430
|
+
}
|
|
6216
6431
|
}
|
|
6217
6432
|
});
|
|
6218
6433
|
}));
|
|
@@ -6259,13 +6474,148 @@ var TypeAnalyzer = class {
|
|
|
6259
6474
|
if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
|
|
6260
6475
|
report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
|
|
6261
6476
|
}
|
|
6262
|
-
|
|
6477
|
+
this.checkAbstractMembers(stmt);
|
|
6478
|
+
this.checkOverrides(stmt);
|
|
6479
|
+
this.checkImplements(stmt);
|
|
6480
|
+
this.checkMetamethods(stmt);
|
|
6481
|
+
const assigned = constructor ? definitelyAssigned(constructor.func.body) ?? ALL_FIELDS : /* @__PURE__ */ new Set();
|
|
6263
6482
|
for (const member of stmt.members) {
|
|
6264
6483
|
if (member.type !== "ClassField" || member.isStatic || member.init) continue;
|
|
6265
|
-
if (assigned.has(member.name.name)) continue;
|
|
6484
|
+
if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
|
|
6266
6485
|
const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
|
|
6267
6486
|
if (isAssignable(nilType, type)) continue;
|
|
6268
|
-
report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
|
|
6487
|
+
report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor on every path, or let its type admit nil`);
|
|
6488
|
+
}
|
|
6489
|
+
}
|
|
6490
|
+
/** The class whose constructor is being checked: it, and only it, may
|
|
6491
|
+
* assign the `readonly` fields it declares. */
|
|
6492
|
+
constructing;
|
|
6493
|
+
/** A class that is not abstract has a `new`, so everything its instances
|
|
6494
|
+
* promise has to be there: no abstract member of its own, and every one
|
|
6495
|
+
* it inherits written. */
|
|
6496
|
+
checkAbstractMembers(stmt) {
|
|
6497
|
+
if (stmt.isAbstract) return;
|
|
6498
|
+
const name = this.className(stmt);
|
|
6499
|
+
for (const member of stmt.members) {
|
|
6500
|
+
if (member.type === "ClassMethod" && member.isAbstract) {
|
|
6501
|
+
this.diagnostics.push({
|
|
6502
|
+
node: member.name,
|
|
6503
|
+
message: `'${member.name.name}' is abstract, so '${name}' has to be an 'abstract class'`
|
|
6504
|
+
});
|
|
6505
|
+
}
|
|
6506
|
+
}
|
|
6507
|
+
const own = this.shapeOf(stmt).instance;
|
|
6508
|
+
const missing = [...this.instanceType(stmt).properties].filter(([key, property]) => property.abstract && !own.has(key)).map(([key]) => `'${key}'`);
|
|
6509
|
+
if (missing.length) {
|
|
6510
|
+
this.diagnostics.push({
|
|
6511
|
+
node: stmt.name ?? stmt,
|
|
6512
|
+
message: `'${name}' does not write the abstract ${missing.length === 1 ? "member" : "members"} ${missing.join(", ")} of the class it extends; write ${missing.length === 1 ? "it" : "them"}, or make '${name}' abstract too`
|
|
6513
|
+
});
|
|
6514
|
+
}
|
|
6515
|
+
}
|
|
6516
|
+
/** A member written again in a class that extends another has to fit
|
|
6517
|
+
* where the base's stood — anything reading an instance as the base
|
|
6518
|
+
* would otherwise be told wrong. `override` says a member is such a one,
|
|
6519
|
+
* and is wrong on a member that is not. */
|
|
6520
|
+
checkOverrides(stmt) {
|
|
6521
|
+
const base = this.baseInstance(stmt);
|
|
6522
|
+
const name = this.className(stmt);
|
|
6523
|
+
const own = this.shapeOf(stmt).instance;
|
|
6524
|
+
for (const member of stmt.members) {
|
|
6525
|
+
if (member.type === "ClassConstructor" || member.isStatic) continue;
|
|
6526
|
+
const key = member.name.name;
|
|
6527
|
+
const inherited = CLASS_LINKS.has(key) ? void 0 : base?.properties.get(key);
|
|
6528
|
+
if (member.isOverride && !inherited) {
|
|
6529
|
+
this.diagnostics.push({
|
|
6530
|
+
node: member.name,
|
|
6531
|
+
message: base ? `'${key}' is marked 'override', but '${stmt.superclass.name}' has no member of that name` : `'${key}' is marked 'override', but '${name}' does not extend another class`
|
|
6532
|
+
});
|
|
6533
|
+
continue;
|
|
6534
|
+
}
|
|
6535
|
+
const mine = own.get(key);
|
|
6536
|
+
if (!inherited || !mine || inherited.private && !inherited.private.protected) continue;
|
|
6537
|
+
const written = withoutReceiver(mine.type);
|
|
6538
|
+
const expected = withoutReceiver(inherited.type);
|
|
6539
|
+
if (isAssignable(written, expected)) continue;
|
|
6540
|
+
this.diagnostics.push({
|
|
6541
|
+
node: member.name,
|
|
6542
|
+
message: `'${key}' in '${name}' does not fit the '${key}' of '${stmt.superclass.name}' it replaces: '${briefType(written)}' is not assignable to '${briefType(expected)}'`
|
|
6543
|
+
});
|
|
6544
|
+
}
|
|
6545
|
+
}
|
|
6546
|
+
/** `implements Shape`: every member the shape names, of a type that fits.
|
|
6547
|
+
* A method's own `this` is left out of the comparison on both sides. */
|
|
6548
|
+
checkImplements(stmt) {
|
|
6549
|
+
if (!stmt.implements?.length) return;
|
|
6550
|
+
const name = this.className(stmt);
|
|
6551
|
+
const instance = this.instanceType(stmt).properties;
|
|
6552
|
+
for (const node of stmt.implements) {
|
|
6553
|
+
const shown = node.type === "TypeReference" ? node.namespace ? `${node.namespace}.${node.base}` : node.base : void 0;
|
|
6554
|
+
const target = this.expand(this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(node)));
|
|
6555
|
+
if (target.kind === "any") continue;
|
|
6556
|
+
if (target.kind !== "object") {
|
|
6557
|
+
this.diagnostics.push({
|
|
6558
|
+
node,
|
|
6559
|
+
message: `A class can only implement an object type or a class, not '${briefType(target)}'`
|
|
6560
|
+
});
|
|
6561
|
+
continue;
|
|
6562
|
+
}
|
|
6563
|
+
const label = shown ?? briefType(target);
|
|
6564
|
+
const missing = [];
|
|
6565
|
+
for (const [key, wanted] of target.properties) {
|
|
6566
|
+
const have = instance.get(key);
|
|
6567
|
+
if (!have) {
|
|
6568
|
+
if (!wanted.optional) missing.push(`'${key}'`);
|
|
6569
|
+
continue;
|
|
6570
|
+
}
|
|
6571
|
+
const written = withoutReceiver(have.type);
|
|
6572
|
+
const expected = withoutReceiver(wanted.optional ? optional(wanted.type) : wanted.type);
|
|
6573
|
+
if (isAssignable(written, expected)) continue;
|
|
6574
|
+
this.diagnostics.push({
|
|
6575
|
+
node,
|
|
6576
|
+
message: `'${name}' does not implement '${label}': its '${key}' is '${briefType(written)}', not '${briefType(expected)}'`
|
|
6577
|
+
});
|
|
6578
|
+
}
|
|
6579
|
+
if (missing.length) {
|
|
6580
|
+
this.diagnostics.push({
|
|
6581
|
+
node,
|
|
6582
|
+
message: `'${name}' does not implement '${label}': ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing`
|
|
6583
|
+
});
|
|
6584
|
+
}
|
|
6585
|
+
}
|
|
6586
|
+
}
|
|
6587
|
+
/** A method named for a metamethod *is* one — the class table is its
|
|
6588
|
+
* instances' metatable — so it has to take what Luau hands it and give
|
|
6589
|
+
* back what Luau expects. */
|
|
6590
|
+
checkMetamethods(stmt) {
|
|
6591
|
+
const instance = this.shapeOf(stmt).instance;
|
|
6592
|
+
for (const member of stmt.members) {
|
|
6593
|
+
if (member.type !== "ClassMethod" || !(member.name.name in METAMETHOD_OPERANDS)) continue;
|
|
6594
|
+
const key = member.name.name;
|
|
6595
|
+
if (member.isStatic) {
|
|
6596
|
+
this.diagnostics.push({
|
|
6597
|
+
node: member.name,
|
|
6598
|
+
message: `'${key}' is a metamethod of the instances; it cannot be static`
|
|
6599
|
+
});
|
|
6600
|
+
continue;
|
|
6601
|
+
}
|
|
6602
|
+
const operands = METAMETHOD_OPERANDS[key];
|
|
6603
|
+
const written = member.func.params.filter((p) => p.name !== "this").length;
|
|
6604
|
+
if (operands !== void 0 && (written !== operands || member.func.hasVarargs)) {
|
|
6605
|
+
this.diagnostics.push({
|
|
6606
|
+
node: member.name,
|
|
6607
|
+
message: operands === 0 ? `'${key}' takes nothing besides 'this'` : `'${key}' takes one operand besides 'this'`
|
|
6608
|
+
});
|
|
6609
|
+
continue;
|
|
6610
|
+
}
|
|
6611
|
+
const required = METAMETHOD_RETURNS[key];
|
|
6612
|
+
const returns = this.overloadsOf(instance.get(key)?.type ?? anyType)[0]?.returns;
|
|
6613
|
+
if (required && returns && !isAssignable(returns, required)) {
|
|
6614
|
+
this.diagnostics.push({
|
|
6615
|
+
node: member.name,
|
|
6616
|
+
message: `'${key}' has to return ${briefType(required)}, not '${briefType(returns)}'`
|
|
6617
|
+
});
|
|
6618
|
+
}
|
|
6269
6619
|
}
|
|
6270
6620
|
}
|
|
6271
6621
|
/** `extends` must name a class, and the chain must end. */
|
|
@@ -6323,19 +6673,42 @@ var TypeAnalyzer = class {
|
|
|
6323
6673
|
harvestDeclares(block, own = false) {
|
|
6324
6674
|
for (const stmt of block.statements) {
|
|
6325
6675
|
if (stmt.type !== "DeclareStatement") continue;
|
|
6326
|
-
if (own
|
|
6676
|
+
if (!own) {
|
|
6677
|
+
const declares = this.libDeclares.get(stmt.name);
|
|
6678
|
+
if (declares) declares.push(stmt);
|
|
6679
|
+
else {
|
|
6680
|
+
this.libDeclares.set(stmt.name, [stmt]);
|
|
6681
|
+
this.libGlobalTypes.defer(stmt.name, () => this.resolveLibDeclares(stmt.name));
|
|
6682
|
+
}
|
|
6683
|
+
continue;
|
|
6684
|
+
}
|
|
6685
|
+
if (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name))) {
|
|
6327
6686
|
this.deferredDeclares.set(stmt.name, stmt);
|
|
6328
6687
|
continue;
|
|
6329
6688
|
}
|
|
6330
|
-
|
|
6331
|
-
const prev = this.libGlobalTypes.get(stmt.name);
|
|
6332
|
-
const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
|
|
6333
|
-
this.libGlobalTypes.set(
|
|
6334
|
-
stmt.name,
|
|
6335
|
-
overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
|
|
6336
|
-
);
|
|
6689
|
+
this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
|
|
6337
6690
|
}
|
|
6338
6691
|
}
|
|
6692
|
+
/** Every library `declare` of `name`, in order. They are written at the
|
|
6693
|
+
* top level of their files, so they are read as if there — outside any
|
|
6694
|
+
* generic the use that asked for them happens to sit in. */
|
|
6695
|
+
resolveLibDeclares(name) {
|
|
6696
|
+
const scope = this.typeParamScope.splice(0);
|
|
6697
|
+
try {
|
|
6698
|
+
let type;
|
|
6699
|
+
for (const stmt of this.libDeclares.get(name) ?? []) type = this.declaredOver(type, stmt);
|
|
6700
|
+
return type ?? anyType;
|
|
6701
|
+
} finally {
|
|
6702
|
+
this.typeParamScope.push(...scope);
|
|
6703
|
+
}
|
|
6704
|
+
}
|
|
6705
|
+
/** A `declare` on top of what earlier ones said about the same name: a
|
|
6706
|
+
* function adds an overload, a table adds members. */
|
|
6707
|
+
declaredOver(prev, stmt) {
|
|
6708
|
+
const t = this.resolveType(stmt.valueType);
|
|
6709
|
+
const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
|
|
6710
|
+
return overload ? intersection([prev, t]) : this.mergeDeclared(prev, t);
|
|
6711
|
+
}
|
|
6339
6712
|
resolveAllAliases() {
|
|
6340
6713
|
for (const [name, def] of this.aliasDefs) {
|
|
6341
6714
|
if (def.class && !this.program.body.statements.includes(def.class)) {
|
|
@@ -6381,11 +6754,10 @@ var TypeAnalyzer = class {
|
|
|
6381
6754
|
}
|
|
6382
6755
|
imported;
|
|
6383
6756
|
/** What a `return` gives, against what the function declared. */
|
|
6384
|
-
checkReturn(stmt, declared,
|
|
6757
|
+
checkReturn(stmt, declared, actual, env) {
|
|
6385
6758
|
if (!declared || !this.emitDiagnostics) return;
|
|
6386
6759
|
if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
|
|
6387
|
-
const
|
|
6388
|
-
const source = stmt.arguments.length === 1 ? sources[0] : void 0;
|
|
6760
|
+
const source = stmt.argument;
|
|
6389
6761
|
const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
|
|
6390
6762
|
if (fits) return;
|
|
6391
6763
|
this.diagnostics.push({
|
|
@@ -6533,21 +6905,12 @@ var TypeAnalyzer = class {
|
|
|
6533
6905
|
this.instantiationDepth--;
|
|
6534
6906
|
}
|
|
6535
6907
|
}
|
|
6536
|
-
/** Pair written type arguments with the parameters they instantiate.
|
|
6537
|
-
*
|
|
6538
|
-
* one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
|
|
6539
|
-
* and `Signal<()>` to the empty pack. Left out, a parameter takes its
|
|
6540
|
-
* default (`T... = ...any` is `any`), or `unknown`. */
|
|
6908
|
+
/** Pair written type arguments with the parameters they instantiate.
|
|
6909
|
+
* Left out, a parameter takes its default, or `unknown`. */
|
|
6541
6910
|
bindTypeArguments(params, args) {
|
|
6542
6911
|
const subst = /* @__PURE__ */ new Map();
|
|
6543
6912
|
params.forEach((p, i) => {
|
|
6544
|
-
|
|
6545
|
-
if (p.isPack && i < args.length) {
|
|
6546
|
-
const rest = args.slice(i);
|
|
6547
|
-
const single = rest.length === 1 ? rest[0] : void 0;
|
|
6548
|
-
arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
|
|
6549
|
-
}
|
|
6550
|
-
subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
|
|
6913
|
+
subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
|
|
6551
6914
|
});
|
|
6552
6915
|
return subst;
|
|
6553
6916
|
}
|
|
@@ -6639,8 +7002,13 @@ var TypeAnalyzer = class {
|
|
|
6639
7002
|
return literal(node.value);
|
|
6640
7003
|
case "ArrayTypeNode":
|
|
6641
7004
|
return arrayOf(this.resolveType(node.element));
|
|
6642
|
-
case "TupleTypeNode":
|
|
6643
|
-
|
|
7005
|
+
case "TupleTypeNode": {
|
|
7006
|
+
const rest = node.rest && this.expand(this.resolveType(node.rest));
|
|
7007
|
+
return tuple(
|
|
7008
|
+
node.elements.map((e) => this.resolveType(e)),
|
|
7009
|
+
rest ? rest.kind === "array" ? rest.element : rest.kind === "any" ? anyType : unknownType : void 0
|
|
7010
|
+
);
|
|
7011
|
+
}
|
|
6644
7012
|
case "UnionTypeNode":
|
|
6645
7013
|
return union(node.types.map((t) => this.resolveType(t)));
|
|
6646
7014
|
case "IntersectionTypeNode":
|
|
@@ -6676,15 +7044,13 @@ var TypeAnalyzer = class {
|
|
|
6676
7044
|
type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
|
|
6677
7045
|
optional: p.optional
|
|
6678
7046
|
}));
|
|
6679
|
-
|
|
6680
|
-
const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
|
|
6681
|
-
return this.withTypeParamDefaults(fn(
|
|
7047
|
+
return this.withTypeParamDefaults(this.withRest(fn(
|
|
6682
7048
|
params,
|
|
6683
7049
|
this.resolveType(node.returnType),
|
|
6684
|
-
|
|
7050
|
+
void 0,
|
|
6685
7051
|
names,
|
|
6686
7052
|
this.resolvePredicate(node.predicate, params)
|
|
6687
|
-
), node.generics);
|
|
7053
|
+
), node.params), node.generics);
|
|
6688
7054
|
});
|
|
6689
7055
|
}
|
|
6690
7056
|
case "TypeofTypeNode": {
|
|
@@ -6748,13 +7114,6 @@ var TypeAnalyzer = class {
|
|
|
6748
7114
|
})
|
|
6749
7115
|
);
|
|
6750
7116
|
}
|
|
6751
|
-
case "VariadicTypeNode":
|
|
6752
|
-
return this.resolveType(node.typeAnnotation);
|
|
6753
|
-
case "TypePackNode": {
|
|
6754
|
-
if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
|
|
6755
|
-
if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
|
|
6756
|
-
return tuple(node.types.map((t) => this.resolveType(t)), true);
|
|
6757
|
-
}
|
|
6758
7117
|
}
|
|
6759
7118
|
}
|
|
6760
7119
|
// --------------------------------------------------------
|
|
@@ -6820,15 +7179,18 @@ var TypeAnalyzer = class {
|
|
|
6820
7179
|
case "array":
|
|
6821
7180
|
return arrayOf(this.reduceType(t.element));
|
|
6822
7181
|
case "tuple":
|
|
6823
|
-
return tuple(t.elements.map((e) => this.reduceType(e)), t.
|
|
7182
|
+
return tuple(t.elements.map((e) => this.reduceType(e)), t.rest && this.reduceType(t.rest));
|
|
6824
7183
|
case "function":
|
|
6825
|
-
return
|
|
6826
|
-
|
|
6827
|
-
|
|
6828
|
-
|
|
6829
|
-
|
|
6830
|
-
|
|
6831
|
-
|
|
7184
|
+
return {
|
|
7185
|
+
...fn(
|
|
7186
|
+
t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
|
|
7187
|
+
this.reduceType(t.returns),
|
|
7188
|
+
t.varargs && this.reduceType(t.varargs),
|
|
7189
|
+
t.typeParams,
|
|
7190
|
+
t.predicate
|
|
7191
|
+
),
|
|
7192
|
+
...t.restIsWhole ? { restIsWhole: true } : {}
|
|
7193
|
+
};
|
|
6832
7194
|
case "object": {
|
|
6833
7195
|
if (t.class) return t;
|
|
6834
7196
|
const entries = [];
|
|
@@ -7043,7 +7405,8 @@ var TypeAnalyzer = class {
|
|
|
7043
7405
|
this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
|
|
7044
7406
|
}
|
|
7045
7407
|
});
|
|
7046
|
-
const { types: valueTypes, sources } = this.valueList(stmt.init, env
|
|
7408
|
+
const { types: valueTypes, sources } = this.valueList(stmt.init, env);
|
|
7409
|
+
this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
|
|
7047
7410
|
stmt.names.forEach((target, i) => {
|
|
7048
7411
|
const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
|
|
7049
7412
|
const source = sources[i];
|
|
@@ -7132,7 +7495,8 @@ var TypeAnalyzer = class {
|
|
|
7132
7495
|
if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
|
|
7133
7496
|
}
|
|
7134
7497
|
});
|
|
7135
|
-
const { types: valueTypes, sources } = this.valueList(stmt.values, env
|
|
7498
|
+
const { types: valueTypes, sources } = this.valueList(stmt.values, env);
|
|
7499
|
+
this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
|
|
7136
7500
|
stmt.targets.forEach((target, i) => {
|
|
7137
7501
|
const vt = valueTypes[i] ?? unknownType;
|
|
7138
7502
|
const source = sources[i];
|
|
@@ -7219,23 +7583,26 @@ var TypeAnalyzer = class {
|
|
|
7219
7583
|
return;
|
|
7220
7584
|
}
|
|
7221
7585
|
case "GenericForStatement": {
|
|
7222
|
-
const
|
|
7586
|
+
const sourceType = this.infer(stmt.iterator, env);
|
|
7223
7587
|
const bodyEnv = forkEnv(env);
|
|
7224
|
-
const rows =
|
|
7588
|
+
const rows = this.iterationRows(stmt.iterator, sourceType);
|
|
7225
7589
|
if (rows) {
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
|
|
7230
|
-
|
|
7231
|
-
|
|
7232
|
-
|
|
7590
|
+
this.bindPattern(
|
|
7591
|
+
stmt.variable,
|
|
7592
|
+
union(rows.map((row) => tuple(row))),
|
|
7593
|
+
bodyEnv,
|
|
7594
|
+
stmt.kind === "const" ? "keep" : "widen"
|
|
7595
|
+
);
|
|
7596
|
+
const parts = stmt.variable.type === "ArrayPattern" && !stmt.variable.rest ? stmt.variable.elements.slice(0, 2).map((el) => el?.value) : [];
|
|
7597
|
+
const ids = parts.map((p) => p?.type === "IdentifierPattern" ? this.bindingIdByName(p.name, p) : void 0);
|
|
7598
|
+
if (ids.length === 2 && ids.every((id) => id !== void 0)) {
|
|
7599
|
+
this.correlateBindings(bodyEnv, ids, rows);
|
|
7600
|
+
}
|
|
7601
|
+
this.loops.set(stmt, { walks: "iteration", viaIter: false });
|
|
7233
7602
|
} else {
|
|
7234
|
-
const [
|
|
7235
|
-
stmt.
|
|
7236
|
-
|
|
7237
|
-
});
|
|
7238
|
-
if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
|
|
7603
|
+
const [item, form] = this.iterationOf(stmt.iterator, sourceType);
|
|
7604
|
+
this.bindPattern(stmt.variable, item, bodyEnv, "widen");
|
|
7605
|
+
this.loops.set(stmt, form);
|
|
7239
7606
|
}
|
|
7240
7607
|
this.visitBlock(stmt.body, bodyEnv);
|
|
7241
7608
|
return;
|
|
@@ -7243,19 +7610,10 @@ var TypeAnalyzer = class {
|
|
|
7243
7610
|
case "ReturnStatement": {
|
|
7244
7611
|
const declared = this.declaredReturns[this.declaredReturns.length - 1];
|
|
7245
7612
|
const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
|
|
7246
|
-
if (wanted)
|
|
7247
|
-
|
|
7248
|
-
|
|
7249
|
-
|
|
7250
|
-
stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
|
|
7251
|
-
}
|
|
7252
|
-
}
|
|
7253
|
-
const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
|
|
7254
|
-
const { types, sources } = this.valueList(stmt.arguments, env, want);
|
|
7255
|
-
this.checkReturn(stmt, declared, types, sources, env);
|
|
7256
|
-
if (this.returnTypes) {
|
|
7257
|
-
this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
|
|
7258
|
-
}
|
|
7613
|
+
if (wanted && stmt.argument) this.applyContext(stmt.argument, wanted);
|
|
7614
|
+
const actual = stmt.argument ? this.infer(stmt.argument, env) : nilType;
|
|
7615
|
+
this.checkReturn(stmt, declared, actual, env);
|
|
7616
|
+
if (this.returnTypes) this.returnTypes.push(actual);
|
|
7259
7617
|
return;
|
|
7260
7618
|
}
|
|
7261
7619
|
case "ExportStatement":
|
|
@@ -7342,7 +7700,7 @@ var TypeAnalyzer = class {
|
|
|
7342
7700
|
* `sources` maps each produced value back to the expression it came from
|
|
7343
7701
|
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7344
7702
|
* caller can still do contextual typing against the written expression. */
|
|
7345
|
-
valueList(exprs, env
|
|
7703
|
+
valueList(exprs, env) {
|
|
7346
7704
|
const types = [];
|
|
7347
7705
|
const sources = [];
|
|
7348
7706
|
exprs.forEach((e, i) => {
|
|
@@ -7358,31 +7716,29 @@ var TypeAnalyzer = class {
|
|
|
7358
7716
|
}
|
|
7359
7717
|
return;
|
|
7360
7718
|
}
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
sources.push(e);
|
|
7364
|
-
} while (last && types.length < want);
|
|
7365
|
-
return;
|
|
7366
|
-
}
|
|
7367
|
-
if (last && e.type === "VarargExpression") {
|
|
7368
|
-
do {
|
|
7369
|
-
types.push(t);
|
|
7370
|
-
sources.push(types.length - 1 === i ? e : void 0);
|
|
7371
|
-
} while (types.length < want);
|
|
7719
|
+
types.push(t);
|
|
7720
|
+
sources.push(e);
|
|
7372
7721
|
return;
|
|
7373
7722
|
}
|
|
7374
|
-
|
|
7375
|
-
|
|
7376
|
-
types.push(el);
|
|
7377
|
-
sources.push(j === 0 ? e : void 0);
|
|
7378
|
-
});
|
|
7379
|
-
return;
|
|
7380
|
-
}
|
|
7381
|
-
types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
|
|
7723
|
+
void last;
|
|
7724
|
+
types.push(t);
|
|
7382
7725
|
sources.push(e);
|
|
7383
7726
|
});
|
|
7384
7727
|
return { types, sources };
|
|
7385
7728
|
}
|
|
7729
|
+
/** `const a, b = x, y` pairs names with values, one each. A call is one
|
|
7730
|
+
* value — several come back as an array — so `const a, b = f()` is a
|
|
7731
|
+
* name short of a value, and says how to take them apart. */
|
|
7732
|
+
checkValueCount(node, names, values, types) {
|
|
7733
|
+
if (!this.emitDiagnostics || !values.length || types.length >= names) return;
|
|
7734
|
+
const last = values[values.length - 1];
|
|
7735
|
+
const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
|
|
7736
|
+
const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
|
|
7737
|
+
this.diagnostics.push({
|
|
7738
|
+
node,
|
|
7739
|
+
message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
|
|
7740
|
+
});
|
|
7741
|
+
}
|
|
7386
7742
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7387
7743
|
* what it collected. */
|
|
7388
7744
|
withBreakScope(visit) {
|
|
@@ -7531,18 +7887,29 @@ var TypeAnalyzer = class {
|
|
|
7531
7887
|
* context. */
|
|
7532
7888
|
contextualArrays = /* @__PURE__ */ new WeakMap();
|
|
7533
7889
|
applyArrayContext(e, expected) {
|
|
7534
|
-
const
|
|
7890
|
+
const members = this.expectedMembers(expected);
|
|
7891
|
+
const tuples = members.filter((m) => m.kind === "tuple");
|
|
7892
|
+
const spread = e.elements.some((el) => el.type === "SpreadElement");
|
|
7893
|
+
const length = e.elements.length;
|
|
7894
|
+
const fitting = tuples.find((t) => !spread && !t.rest && t.elements.length === length) ?? tuples.find((t) => t.rest !== void 0 && (spread || length >= t.elements.length));
|
|
7895
|
+
const target = fitting ?? members.find((m) => m.kind === "array") ?? tuples[0];
|
|
7535
7896
|
if (!target) return;
|
|
7897
|
+
if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
|
|
7536
7898
|
if (!e.elements.length) {
|
|
7537
7899
|
this.contextualArrays.set(e, target);
|
|
7538
7900
|
return;
|
|
7539
7901
|
}
|
|
7540
7902
|
e.elements.forEach((element, i) => {
|
|
7541
7903
|
if (element.type === "SpreadElement") return;
|
|
7542
|
-
const
|
|
7904
|
+
const tupleTarget = target;
|
|
7905
|
+
const elementType = target.kind === "array" ? target.element : tupleTarget.elements[i] ?? tupleTarget.rest;
|
|
7543
7906
|
this.applyContext(element, elementType);
|
|
7544
7907
|
});
|
|
7545
7908
|
}
|
|
7909
|
+
/** Array literals written where a tuple is wanted — a `return` of a
|
|
7910
|
+
* function that returns one, an argument, an annotated `const` — and
|
|
7911
|
+
* the tuple each is to be. */
|
|
7912
|
+
tupleArrays = /* @__PURE__ */ new WeakMap();
|
|
7546
7913
|
/** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
|
|
7547
7914
|
* value gets its property's type as context. */
|
|
7548
7915
|
applyTableContext(e, expected) {
|
|
@@ -7598,32 +7965,33 @@ var TypeAnalyzer = class {
|
|
|
7598
7965
|
}
|
|
7599
7966
|
return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
|
|
7600
7967
|
}
|
|
7601
|
-
/**
|
|
7602
|
-
*
|
|
7603
|
-
*
|
|
7604
|
-
|
|
7605
|
-
|
|
7606
|
-
|
|
7607
|
-
|
|
7608
|
-
|
|
7609
|
-
const
|
|
7610
|
-
if (
|
|
7611
|
-
|
|
7612
|
-
|
|
7613
|
-
|
|
7614
|
-
|
|
7615
|
-
|
|
7968
|
+
/** A function type with the rest parameter among `params` written in:
|
|
7969
|
+
* `...args: T[]` collects `T`s, and `...args: A` — `A` a tuple, or a
|
|
7970
|
+
* type parameter standing for one — is exactly `A`'s elements. */
|
|
7971
|
+
withRest(f, params) {
|
|
7972
|
+
const rest = params.find((p) => p.rest);
|
|
7973
|
+
if (!rest) return f;
|
|
7974
|
+
if (!rest.typeAnnotation) return { ...f, varargs: unknownType };
|
|
7975
|
+
const declared = this.resolveType(rest.typeAnnotation);
|
|
7976
|
+
const t = this.expand(declared);
|
|
7977
|
+
if (t.kind === "array") return { ...f, varargs: t.element };
|
|
7978
|
+
if (t.kind === "any") return { ...f, varargs: anyType };
|
|
7979
|
+
if (declared.kind === "infer") return { ...f, varargs: declared, restIsWhole: true };
|
|
7980
|
+
if (t.kind === "tuple") {
|
|
7981
|
+
return { ...f, params: [...f.params, ...t.elements.map((type) => ({ type }))], varargs: t.rest };
|
|
7982
|
+
}
|
|
7983
|
+
if (declared.kind === "typeParam") return { ...f, varargs: declared, restIsWhole: true };
|
|
7984
|
+
return { ...f, varargs: unknownType };
|
|
7985
|
+
}
|
|
7616
7986
|
/** What each function body being walked declared it returns. */
|
|
7617
7987
|
declaredReturns = [];
|
|
7618
|
-
/** Run `body` with
|
|
7988
|
+
/** Run `body` with `return` as `func` declares it. */
|
|
7619
7989
|
withVarargs(func, body) {
|
|
7620
|
-
this.varargs.push(this.varargElement(func));
|
|
7621
7990
|
this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
|
|
7622
7991
|
this.contextualReturns.push(this.contextualReturnOf.get(func));
|
|
7623
7992
|
try {
|
|
7624
7993
|
return body();
|
|
7625
7994
|
} finally {
|
|
7626
|
-
this.varargs.pop();
|
|
7627
7995
|
this.declaredReturns.pop();
|
|
7628
7996
|
this.contextualReturns.pop();
|
|
7629
7997
|
}
|
|
@@ -7682,7 +8050,7 @@ var TypeAnalyzer = class {
|
|
|
7682
8050
|
if (type.kind !== "function") return type;
|
|
7683
8051
|
const defaults = {};
|
|
7684
8052
|
for (const generic of generics) {
|
|
7685
|
-
if (generic.default
|
|
8053
|
+
if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
|
|
7686
8054
|
}
|
|
7687
8055
|
return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
|
|
7688
8056
|
}
|
|
@@ -7701,7 +8069,10 @@ var TypeAnalyzer = class {
|
|
|
7701
8069
|
const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
|
|
7702
8070
|
unify(p.type, preserve ? arg : widen(arg), vars, subst);
|
|
7703
8071
|
});
|
|
7704
|
-
if (f.varargs) {
|
|
8072
|
+
if (f.varargs && f.restIsWhole) {
|
|
8073
|
+
const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
|
|
8074
|
+
unify(f.varargs, tuple(rest), vars, subst);
|
|
8075
|
+
} else if (f.varargs) {
|
|
7705
8076
|
const keeps = keepsLiterals(f.varargs);
|
|
7706
8077
|
for (let i = f.params.length; i < argTypes.length; i++) {
|
|
7707
8078
|
const arg = argTypes[i];
|
|
@@ -7870,7 +8241,12 @@ var TypeAnalyzer = class {
|
|
|
7870
8241
|
const arg = argTypes[i];
|
|
7871
8242
|
const declared = declaredAt(i);
|
|
7872
8243
|
if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
|
|
7873
|
-
|
|
8244
|
+
let expected = this.reduceType(substitute(declared, subst));
|
|
8245
|
+
if (f.restIsWhole && i >= f.params.length) {
|
|
8246
|
+
const whole = this.expand(expected);
|
|
8247
|
+
expected = whole.kind === "tuple" ? whole.elements[i - f.params.length] ?? whole.rest : whole.kind === "array" ? whole.element : void 0;
|
|
8248
|
+
if (!expected) continue;
|
|
8249
|
+
}
|
|
7874
8250
|
if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
|
|
7875
8251
|
if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
|
|
7876
8252
|
this.diagnostics.push({
|
|
@@ -7998,13 +8374,13 @@ var TypeAnalyzer = class {
|
|
|
7998
8374
|
type: this.paramType(p, /* @__PURE__ */ new Map()),
|
|
7999
8375
|
optional: p.optional || p.default !== void 0
|
|
8000
8376
|
}));
|
|
8001
|
-
return fn(
|
|
8377
|
+
return this.withRest(fn(
|
|
8002
8378
|
params,
|
|
8003
8379
|
sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
|
|
8004
|
-
|
|
8380
|
+
void 0,
|
|
8005
8381
|
names,
|
|
8006
8382
|
this.resolvePredicate(sig.predicate, params)
|
|
8007
|
-
);
|
|
8383
|
+
), sig.params);
|
|
8008
8384
|
}));
|
|
8009
8385
|
}
|
|
8010
8386
|
/** `...rest: T[]` holds every argument from its position on, so its type
|
|
@@ -8012,7 +8388,7 @@ var TypeAnalyzer = class {
|
|
|
8012
8388
|
checkRestType(p, node) {
|
|
8013
8389
|
if (!this.emitDiagnostics || !p.typeAnnotation) return;
|
|
8014
8390
|
const declared = this.resolveType(p.typeAnnotation);
|
|
8015
|
-
if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
|
|
8391
|
+
if (declared.kind === "array" || declared.kind === "tuple" || declared.kind === "any" || declared.kind === "typeParam") return;
|
|
8016
8392
|
this.diagnostics.push({
|
|
8017
8393
|
node,
|
|
8018
8394
|
message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
|
|
@@ -8068,13 +8444,13 @@ var TypeAnalyzer = class {
|
|
|
8068
8444
|
return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
|
|
8069
8445
|
}));
|
|
8070
8446
|
}
|
|
8071
|
-
return fn(
|
|
8447
|
+
return this.withRest(fn(
|
|
8072
8448
|
params,
|
|
8073
8449
|
returns,
|
|
8074
|
-
|
|
8450
|
+
void 0,
|
|
8075
8451
|
names,
|
|
8076
8452
|
this.resolvePredicate(func.predicate, params)
|
|
8077
|
-
);
|
|
8453
|
+
), func.params);
|
|
8078
8454
|
});
|
|
8079
8455
|
}
|
|
8080
8456
|
/** Where the return types of the function being walked are collected, so
|
|
@@ -8116,17 +8492,16 @@ var TypeAnalyzer = class {
|
|
|
8116
8492
|
this.preVisitDepth--;
|
|
8117
8493
|
}
|
|
8118
8494
|
}
|
|
8119
|
-
/** The `[key, value]` pairs
|
|
8120
|
-
*
|
|
8121
|
-
*
|
|
8122
|
-
* iterator function), whose keys have no names to list. */
|
|
8495
|
+
/** The `[key, value]` items `pairs(record)` hands out, one per property.
|
|
8496
|
+
* `undefined` for anything else (an array, a dictionary, an iterator
|
|
8497
|
+
* function), whose keys have no names to list. */
|
|
8123
8498
|
iterationRows(iterNode, iterType) {
|
|
8124
8499
|
let source;
|
|
8125
8500
|
if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
|
|
8126
|
-
if (iterNode.callee.name !== "pairs"
|
|
8501
|
+
if (iterNode.callee.name !== "pairs") return void 0;
|
|
8127
8502
|
source = this.typeOf.get(iterNode.arguments[0]);
|
|
8128
8503
|
} else {
|
|
8129
|
-
|
|
8504
|
+
return void 0;
|
|
8130
8505
|
}
|
|
8131
8506
|
const t = source && this.expand(source);
|
|
8132
8507
|
if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
|
|
@@ -8267,54 +8642,69 @@ var TypeAnalyzer = class {
|
|
|
8267
8642
|
if (ids.length < 2) return;
|
|
8268
8643
|
this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
|
|
8269
8644
|
}
|
|
8270
|
-
/** `
|
|
8271
|
-
*
|
|
8272
|
-
*
|
|
8273
|
-
|
|
8274
|
-
|
|
8275
|
-
|
|
8276
|
-
|
|
8277
|
-
|
|
8278
|
-
|
|
8279
|
-
|
|
8280
|
-
|
|
8281
|
-
|
|
8645
|
+
/** `[step, state, first]` — what a call such as `pairs(t)` answers — as
|
|
8646
|
+
* the `step` function, or `undefined` for an array that is just an
|
|
8647
|
+
* array. Three places exactly, the first a function. */
|
|
8648
|
+
iterationTriple(t) {
|
|
8649
|
+
if (t.kind !== "tuple" || t.rest || t.elements.length !== 3) return void 0;
|
|
8650
|
+
const step = this.expand(t.elements[0]);
|
|
8651
|
+
return step.kind === "function" ? step : void 0;
|
|
8652
|
+
}
|
|
8653
|
+
/** What one `for (const item in source)` hands over each time, and how it
|
|
8654
|
+
* walks the source. `pairs`/`ipairs` are read from the table they are
|
|
8655
|
+
* given, so a record's keys stay the literals they are. */
|
|
8656
|
+
iterationOf(source, sourceType) {
|
|
8657
|
+
if (source.type === "CallExpression" && source.callee.type === "Identifier" && source.arguments[0]) {
|
|
8658
|
+
const name = source.callee.name;
|
|
8659
|
+
const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
|
|
8660
|
+
const iteration = { walks: "iteration", viaIter: false };
|
|
8661
|
+
if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
|
|
8662
|
+
if (name === "pairs") {
|
|
8282
8663
|
if (src.kind === "object") {
|
|
8283
|
-
return [
|
|
8664
|
+
return [tuple([
|
|
8284
8665
|
src.indexer?.key ?? stringType,
|
|
8285
|
-
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
|
|
8286
|
-
|
|
8287
|
-
];
|
|
8666
|
+
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
|
|
8667
|
+
]), iteration];
|
|
8288
8668
|
}
|
|
8289
|
-
if (src.kind === "array") return [numberType, src.element,
|
|
8669
|
+
if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
|
|
8290
8670
|
}
|
|
8291
8671
|
}
|
|
8292
|
-
const
|
|
8293
|
-
|
|
8294
|
-
|
|
8295
|
-
const
|
|
8296
|
-
|
|
8297
|
-
return
|
|
8298
|
-
}
|
|
8299
|
-
const
|
|
8300
|
-
if (
|
|
8301
|
-
|
|
8302
|
-
|
|
8672
|
+
const handsOut = (t2, viaIter) => {
|
|
8673
|
+
const x = this.expand(t2);
|
|
8674
|
+
if (x.kind === "function") return [withoutNil(this.expand(x.returns)), { walks: "function", viaIter }];
|
|
8675
|
+
const step = this.iterationTriple(x);
|
|
8676
|
+
if (step) return [withoutNil(this.expand(step.returns)), { walks: "iteration", viaIter }];
|
|
8677
|
+
return void 0;
|
|
8678
|
+
};
|
|
8679
|
+
const direct = handsOut(sourceType, false);
|
|
8680
|
+
if (direct) return direct;
|
|
8681
|
+
const values = { walks: "values", viaIter: false };
|
|
8682
|
+
const t = this.expand(sourceType);
|
|
8683
|
+
if (t.kind === "any") return [anyType, values];
|
|
8684
|
+
const iter = t.kind === "object" ? t.properties.get("__iter") : void 0;
|
|
8685
|
+
if (iter) {
|
|
8686
|
+
const made = this.overloadsOf(iter.type)[0]?.returns ?? unknownType;
|
|
8687
|
+
return handsOut(made, true) ?? [unknownType, { walks: "values", viaIter: true }];
|
|
8688
|
+
}
|
|
8689
|
+
if (t.kind === "array") return [t.element, values];
|
|
8690
|
+
if (t.kind === "tuple") return [union([...t.elements, ...t.rest ? [t.rest] : []]), values];
|
|
8303
8691
|
if (t.kind === "object") {
|
|
8304
|
-
|
|
8305
|
-
const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
|
|
8306
|
-
return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
|
|
8692
|
+
return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
|
|
8307
8693
|
}
|
|
8308
|
-
|
|
8694
|
+
if (t.kind !== "never" && this.emitDiagnostics) {
|
|
8695
|
+
this.diagnostics.push({
|
|
8696
|
+
node: source,
|
|
8697
|
+
message: `Cannot loop over '${formatType(sourceType)}': a loop walks an array, a table, an iterator function or an iteration such as 'pairs(t)'`
|
|
8698
|
+
});
|
|
8699
|
+
}
|
|
8700
|
+
return [unknownType, values];
|
|
8309
8701
|
}
|
|
8310
8702
|
inferReturnType(body, env) {
|
|
8311
8703
|
const returns = [];
|
|
8312
8704
|
const walk = (block) => {
|
|
8313
8705
|
for (const s of block.statements) {
|
|
8314
8706
|
if (s.type === "ReturnStatement") {
|
|
8315
|
-
|
|
8316
|
-
else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
|
|
8317
|
-
else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
|
|
8707
|
+
returns.push(s.argument ? this.infer(s.argument, env) : nilType);
|
|
8318
8708
|
} else if (s.type === "IfStatement") {
|
|
8319
8709
|
for (const c of s.clauses) walk(c.body);
|
|
8320
8710
|
if (s.alternate) walk(s.alternate);
|
|
@@ -8336,8 +8726,9 @@ var TypeAnalyzer = class {
|
|
|
8336
8726
|
* `infer` would have widened it to an array type). */
|
|
8337
8727
|
fitsAnnotation(init, declared, inferred, env) {
|
|
8338
8728
|
if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
|
|
8339
|
-
|
|
8340
|
-
|
|
8729
|
+
const fits = declared.rest ? init.elements.length >= declared.elements.length : init.elements.length === declared.elements.length;
|
|
8730
|
+
if (!fits) return false;
|
|
8731
|
+
return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i] ?? declared.rest));
|
|
8341
8732
|
}
|
|
8342
8733
|
if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
|
|
8343
8734
|
if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
|
|
@@ -8394,7 +8785,7 @@ var TypeAnalyzer = class {
|
|
|
8394
8785
|
target.elements.forEach((el, i) => {
|
|
8395
8786
|
if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
|
|
8396
8787
|
});
|
|
8397
|
-
if (target.rest) this.reassignPattern(target.rest,
|
|
8788
|
+
if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
|
|
8398
8789
|
return;
|
|
8399
8790
|
}
|
|
8400
8791
|
}
|
|
@@ -8430,7 +8821,7 @@ var TypeAnalyzer = class {
|
|
|
8430
8821
|
if (!el) return;
|
|
8431
8822
|
this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
|
|
8432
8823
|
});
|
|
8433
|
-
if (target.rest) this.bindPattern(target.rest,
|
|
8824
|
+
if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
|
|
8434
8825
|
return;
|
|
8435
8826
|
}
|
|
8436
8827
|
}
|
|
@@ -8587,7 +8978,7 @@ var TypeAnalyzer = class {
|
|
|
8587
8978
|
* not one of them is a mistake worth reporting, rather than the nil Lua
|
|
8588
8979
|
* would hand back. */
|
|
8589
8980
|
checkStringMember(node, object, key) {
|
|
8590
|
-
if (!this.emitDiagnostics) return;
|
|
8981
|
+
if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
|
|
8591
8982
|
const parts = this.stringParts(object);
|
|
8592
8983
|
if (!parts) return;
|
|
8593
8984
|
const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
|
|
@@ -8599,6 +8990,120 @@ var TypeAnalyzer = class {
|
|
|
8599
8990
|
message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
|
|
8600
8991
|
});
|
|
8601
8992
|
}
|
|
8993
|
+
/** `game.Anything` on a `{ GetService: ... }`, or `a.a` on a number: a
|
|
8994
|
+
* value whose members are all known, and none of them is this one. Only
|
|
8995
|
+
* a type that is known through and through is reported — an indexer, an
|
|
8996
|
+
* `any`, or a part still waiting on a type parameter could each supply
|
|
8997
|
+
* the name. An empty `{}` is a table still being filled in, so it is left
|
|
8998
|
+
* alone; an array's and a string's members come from a library, so
|
|
8999
|
+
* without one they say nothing. A string on its own is `checkStringMember`'s. */
|
|
9000
|
+
checkMissingMember(node, object, name) {
|
|
9001
|
+
if (!this.emitDiagnostics || this.missingMemberReported.has(node)) return;
|
|
9002
|
+
if (!this.memberMissing(object, name)) return;
|
|
9003
|
+
this.missingMemberReported.add(node);
|
|
9004
|
+
this.diagnostics.push({
|
|
9005
|
+
node,
|
|
9006
|
+
message: `Property '${name}' does not exist on type '${briefType(withoutNil(this.expand(object)))}'`
|
|
9007
|
+
});
|
|
9008
|
+
}
|
|
9009
|
+
/** Is `name` certainly not a member of `object`? See `checkMissingMember`. */
|
|
9010
|
+
memberMissing(object, name) {
|
|
9011
|
+
if (this.stringParts(object)) return false;
|
|
9012
|
+
const raw = this.expand(object);
|
|
9013
|
+
const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
|
|
9014
|
+
let missing = false;
|
|
9015
|
+
for (const part of parts) {
|
|
9016
|
+
switch (part.kind) {
|
|
9017
|
+
case "primitive":
|
|
9018
|
+
if (part.name === "nil") continue;
|
|
9019
|
+
if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
|
|
9020
|
+
if (part.name === "string" && this.builtInMethod(part, name)) continue;
|
|
9021
|
+
break;
|
|
9022
|
+
case "literal":
|
|
9023
|
+
case "templateLiteral":
|
|
9024
|
+
if (part.kind === "templateLiteral" || part.base === "string") {
|
|
9025
|
+
if (!this.aliasDefs.has("StringMethods")) return false;
|
|
9026
|
+
}
|
|
9027
|
+
if (this.builtInMethod(part, name)) continue;
|
|
9028
|
+
break;
|
|
9029
|
+
case "array":
|
|
9030
|
+
case "tuple":
|
|
9031
|
+
if (!this.aliasDefs.has("ArrayMethods")) return false;
|
|
9032
|
+
if (this.builtInMethod(part, name)) continue;
|
|
9033
|
+
break;
|
|
9034
|
+
case "object":
|
|
9035
|
+
if (part.properties.has(name)) continue;
|
|
9036
|
+
if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
|
|
9037
|
+
break;
|
|
9038
|
+
default:
|
|
9039
|
+
return false;
|
|
9040
|
+
}
|
|
9041
|
+
missing = true;
|
|
9042
|
+
}
|
|
9043
|
+
return missing;
|
|
9044
|
+
}
|
|
9045
|
+
/** `t[k]` read with a `k` that cannot be a key of `t`: an `unknown`, a
|
|
9046
|
+
* type none of `t`'s keys has (a number into a record, a string into an
|
|
9047
|
+
* array or a class), or a name `t` does not have — which is `t.name`'s
|
|
9048
|
+
* error. An `any` on either side says nothing, and neither does a type
|
|
9049
|
+
* parameter still to be decided. A plain `string` into a record is
|
|
9050
|
+
* allowed: it reads as one of the values or nil, and the nil has to be
|
|
9051
|
+
* dealt with. An empty `{}` is a table still being filled in, and is
|
|
9052
|
+
* left alone. Reports, and answers whether it did. */
|
|
9053
|
+
checkIndexKey(node, object, key) {
|
|
9054
|
+
if (!this.emitDiagnostics || this.badIndexReported.has(node)) return false;
|
|
9055
|
+
const index = this.expand(key);
|
|
9056
|
+
if (index.kind === "any" || index.kind === "never" || containsTypeParam(index)) return false;
|
|
9057
|
+
const keys = index.kind === "union" ? index.types.map((k) => this.expand(k)) : [index];
|
|
9058
|
+
const nameOf = (k) => k.kind === "literal" && typeof k.value === "string" ? k.value : void 0;
|
|
9059
|
+
const shown = briefType(withoutNil(this.expand(object)));
|
|
9060
|
+
const names = keys.map(nameOf);
|
|
9061
|
+
const others = keys.filter((_, i) => names[i] === void 0);
|
|
9062
|
+
let message;
|
|
9063
|
+
if (others.length && this.cannotIndex(object, others)) {
|
|
9064
|
+
message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
|
|
9065
|
+
} else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
|
|
9066
|
+
message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
|
|
9067
|
+
}
|
|
9068
|
+
if (!message) return false;
|
|
9069
|
+
this.badIndexReported.add(node);
|
|
9070
|
+
this.diagnostics.push({ node, message });
|
|
9071
|
+
return true;
|
|
9072
|
+
}
|
|
9073
|
+
/** Can one of `keys` (string literals aside) not index some part of
|
|
9074
|
+
* `object`? `false` as soon as a part cannot be judged. */
|
|
9075
|
+
cannotIndex(object, keys) {
|
|
9076
|
+
const raw = this.expand(object);
|
|
9077
|
+
const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
|
|
9078
|
+
const isNumber = (k) => k.kind === "primitive" && k.name === "number" || k.kind === "literal" && typeof k.value === "number";
|
|
9079
|
+
const isString = (k) => k.kind === "primitive" && k.name === "string" || k.kind === "templateLiteral";
|
|
9080
|
+
let bad = false;
|
|
9081
|
+
for (const part of parts) {
|
|
9082
|
+
switch (part.kind) {
|
|
9083
|
+
case "primitive":
|
|
9084
|
+
if (part.name === "nil") continue;
|
|
9085
|
+
return false;
|
|
9086
|
+
case "array":
|
|
9087
|
+
case "tuple":
|
|
9088
|
+
if (!keys.every(isNumber)) bad = true;
|
|
9089
|
+
continue;
|
|
9090
|
+
case "object": {
|
|
9091
|
+
if (!part.indexer && part.properties.size === 0 && !part.class) return false;
|
|
9092
|
+
const fits = (k) => part.indexer !== void 0 && isAssignable(k, part.indexer.key) || !part.indexer && !part.class && isString(k) || k.kind === "literal" && part.properties.has(String(k.value));
|
|
9093
|
+
if (!keys.every(fits)) bad = true;
|
|
9094
|
+
continue;
|
|
9095
|
+
}
|
|
9096
|
+
default:
|
|
9097
|
+
return false;
|
|
9098
|
+
}
|
|
9099
|
+
}
|
|
9100
|
+
return bad;
|
|
9101
|
+
}
|
|
9102
|
+
/** Indexes already reported: a loop body is visited more than once. */
|
|
9103
|
+
badIndexReported = /* @__PURE__ */ new WeakSet();
|
|
9104
|
+
/** Members already reported as missing: a loop body, or an assignment
|
|
9105
|
+
* target, is visited more than once. */
|
|
9106
|
+
missingMemberReported = /* @__PURE__ */ new WeakSet();
|
|
8602
9107
|
/** A `private` member read from outside the class that declared it —
|
|
8603
9108
|
* a subclass counts as outside, as in TypeScript. */
|
|
8604
9109
|
checkPrivateMember(node, object, name) {
|
|
@@ -8609,18 +9114,46 @@ var TypeAnalyzer = class {
|
|
|
8609
9114
|
if (t.kind !== "object") continue;
|
|
8610
9115
|
const access = t.properties.get(name)?.private;
|
|
8611
9116
|
if (!access || this.insideClass(access.owner)) continue;
|
|
9117
|
+
if (access.protected && this.insideSubclassOf(access.owner)) continue;
|
|
8612
9118
|
this.diagnostics.push({
|
|
8613
9119
|
node,
|
|
8614
|
-
message: `Property '${name}' is private and only accessible within class '${access.className}'`
|
|
9120
|
+
message: access.protected ? `Property '${name}' is protected and only accessible within class '${access.className}' and the classes extending it` : `Property '${name}' is private and only accessible within class '${access.className}'`
|
|
8615
9121
|
});
|
|
8616
9122
|
return;
|
|
8617
9123
|
}
|
|
8618
9124
|
}
|
|
9125
|
+
/** `Shape.new` on an abstract class, and `super.area` on an abstract
|
|
9126
|
+
* method: each reaches something with nothing behind it. */
|
|
9127
|
+
checkAbstractAccess(expr, object) {
|
|
9128
|
+
if (!this.emitDiagnostics) return;
|
|
9129
|
+
const t = this.expand(object);
|
|
9130
|
+
if (t.kind !== "object") return;
|
|
9131
|
+
const name = expr.property.name;
|
|
9132
|
+
const property = t.properties.get(name);
|
|
9133
|
+
if (!property?.abstract) return;
|
|
9134
|
+
if (name === "new" && expr.object.type !== "SuperExpression") {
|
|
9135
|
+
const label = expressionLabel(expr.object) ?? formatType(t);
|
|
9136
|
+
this.diagnostics.push({
|
|
9137
|
+
node: expr,
|
|
9138
|
+
message: `'${label}' is an abstract class: build one of the classes extending it instead`
|
|
9139
|
+
});
|
|
9140
|
+
} else if (expr.object.type === "SuperExpression") {
|
|
9141
|
+
this.diagnostics.push({
|
|
9142
|
+
node: expr.property,
|
|
9143
|
+
message: `'${name}' is abstract in the class this one extends: there is nothing to call`
|
|
9144
|
+
});
|
|
9145
|
+
}
|
|
9146
|
+
}
|
|
8619
9147
|
/** Is the code being checked written inside `owner`'s body — its own
|
|
8620
9148
|
* methods, or a class or function nested in them? */
|
|
8621
9149
|
insideClass(owner) {
|
|
8622
9150
|
return this.enclosingClasses.includes(owner);
|
|
8623
9151
|
}
|
|
9152
|
+
/** Is the code being checked inside a class that extends `owner`? */
|
|
9153
|
+
insideSubclassOf(owner) {
|
|
9154
|
+
const identity = this.classIdentity(owner);
|
|
9155
|
+
return this.enclosingClasses.some((cls) => this.instanceType(cls).class?.ancestors.includes(identity));
|
|
9156
|
+
}
|
|
8624
9157
|
propertyType(raw, name) {
|
|
8625
9158
|
const t = this.deferredAccess(this.expand(raw));
|
|
8626
9159
|
if (t.kind === "object") {
|
|
@@ -8675,8 +9208,12 @@ var TypeAnalyzer = class {
|
|
|
8675
9208
|
}
|
|
8676
9209
|
if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
|
|
8677
9210
|
if (t.indexer) return t.indexer.value;
|
|
9211
|
+
const anyString = index.kind === "primitive" && index.name === "string" || index.kind === "templateLiteral";
|
|
9212
|
+
if (anyString && !t.class && t.properties.size > 0) {
|
|
9213
|
+
return union([...[...t.properties.values()].map((p) => p.type), nilType]);
|
|
9214
|
+
}
|
|
8678
9215
|
}
|
|
8679
|
-
return unknownType;
|
|
9216
|
+
return index.kind === "any" ? anyType : unknownType;
|
|
8680
9217
|
}
|
|
8681
9218
|
/** What a deferred `T[K]` can be: every property its index could name.
|
|
8682
9219
|
* Reading a member of one, or calling it, sees that. */
|
|
@@ -8686,10 +9223,21 @@ var TypeAnalyzer = class {
|
|
|
8686
9223
|
if (containsTypeParam(index)) return unknownType;
|
|
8687
9224
|
return this.accessType(t.objectType, index);
|
|
8688
9225
|
}
|
|
9226
|
+
/** `[a, ...rest]`: what `rest` holds — an array of whatever comes after
|
|
9227
|
+
* the first `from` places. */
|
|
9228
|
+
restAfter(raw, from) {
|
|
9229
|
+
const t = this.expand(raw);
|
|
9230
|
+
if (t.kind === "tuple") {
|
|
9231
|
+
const left = [...t.elements.slice(from), ...t.rest ? [t.rest] : []];
|
|
9232
|
+
return arrayOf(left.length ? union(left) : neverType);
|
|
9233
|
+
}
|
|
9234
|
+
if (t.kind === "union") return union(t.types.map((m) => this.restAfter(m, from)));
|
|
9235
|
+
return arrayOf(this.elementType(t, from));
|
|
9236
|
+
}
|
|
8689
9237
|
elementType(raw, index) {
|
|
8690
9238
|
const t = this.expand(raw);
|
|
8691
9239
|
if (t.kind === "array") return t.element;
|
|
8692
|
-
if (t.kind === "tuple") return t.elements[index] ?? unknownType;
|
|
9240
|
+
if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
|
|
8693
9241
|
if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
|
|
8694
9242
|
if (t.kind === "difference") return this.elementType(t.base, index);
|
|
8695
9243
|
if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
|
|
@@ -8719,8 +9267,6 @@ var TypeAnalyzer = class {
|
|
|
8719
9267
|
return stringType;
|
|
8720
9268
|
}
|
|
8721
9269
|
// `...` holds what the function declared it takes.
|
|
8722
|
-
case "VarargExpression":
|
|
8723
|
-
return this.varargs[this.varargs.length - 1] ?? anyType;
|
|
8724
9270
|
// `f(a, ...rest)` — every value the array holds, one after
|
|
8725
9271
|
// another. Each of them is an element, so that is what the
|
|
8726
9272
|
// parameters it fills are checked against.
|
|
@@ -8753,10 +9299,8 @@ var TypeAnalyzer = class {
|
|
|
8753
9299
|
this.checkParamOrder(expr.func.params, expr);
|
|
8754
9300
|
this.visitFunctionBody(expr.func, env);
|
|
8755
9301
|
return this.inferFunctionBody(expr.func, env);
|
|
8756
|
-
case "ParenthesizedExpression":
|
|
8757
|
-
|
|
8758
|
-
return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
|
|
8759
|
-
}
|
|
9302
|
+
case "ParenthesizedExpression":
|
|
9303
|
+
return this.infer(expr.expression, env);
|
|
8760
9304
|
case "TypeAssertionExpression": {
|
|
8761
9305
|
this.infer(expr.expression, env);
|
|
8762
9306
|
return this.resolveType(expr.typeAnnotation);
|
|
@@ -8789,7 +9333,7 @@ var TypeAnalyzer = class {
|
|
|
8789
9333
|
case "not":
|
|
8790
9334
|
return booleanType;
|
|
8791
9335
|
case "-":
|
|
8792
|
-
return this.operatorResult(expr, "-", arg, void 0) ??
|
|
9336
|
+
return this.operatorResult(expr, "-", arg, void 0) ?? this.arithmeticOn([arg]);
|
|
8793
9337
|
case "#":
|
|
8794
9338
|
return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
|
|
8795
9339
|
}
|
|
@@ -8807,7 +9351,11 @@ var TypeAnalyzer = class {
|
|
|
8807
9351
|
const left = this.infer(expr.left, env);
|
|
8808
9352
|
const { whenFalse } = this.narrowFromCondition(expr.left, env);
|
|
8809
9353
|
const right = this.infer(expr.right, whenFalse);
|
|
8810
|
-
|
|
9354
|
+
const fallback = unwrapParens(expr.right);
|
|
9355
|
+
const empty = fallback.type === "TableExpression" && fallback.fields.length === 0 || fallback.type === "ArrayExpression" && fallback.elements.length === 0;
|
|
9356
|
+
const truthy = narrowTruthy(left);
|
|
9357
|
+
if (empty && truthy.kind !== "never" && truthy.kind !== "any" && isAssignable(right, truthy)) return truthy;
|
|
9358
|
+
return union([truthy, right]);
|
|
8811
9359
|
}
|
|
8812
9360
|
const l = this.infer(expr.left, env);
|
|
8813
9361
|
const r = this.infer(expr.right, env);
|
|
@@ -8818,12 +9366,14 @@ var TypeAnalyzer = class {
|
|
|
8818
9366
|
switch (op) {
|
|
8819
9367
|
case "..":
|
|
8820
9368
|
return this.operatorResult(expr, op, l, r) ?? stringType;
|
|
8821
|
-
case "==":
|
|
8822
|
-
case "~=":
|
|
8823
9369
|
case "<":
|
|
8824
9370
|
case ">":
|
|
8825
9371
|
case "<=":
|
|
8826
9372
|
case ">=":
|
|
9373
|
+
this.checkComparison(expr, op, l, r);
|
|
9374
|
+
return booleanType;
|
|
9375
|
+
case "==":
|
|
9376
|
+
case "~=":
|
|
8827
9377
|
return booleanType;
|
|
8828
9378
|
case "+":
|
|
8829
9379
|
case "-":
|
|
@@ -8832,16 +9382,18 @@ var TypeAnalyzer = class {
|
|
|
8832
9382
|
case "//":
|
|
8833
9383
|
case "%":
|
|
8834
9384
|
case "^":
|
|
8835
|
-
return this.operatorResult(expr, op, l, r) ??
|
|
9385
|
+
return this.operatorResult(expr, op, l, r) ?? this.arithmeticOn([l, r]);
|
|
8836
9386
|
}
|
|
8837
9387
|
return union([l, r]);
|
|
8838
9388
|
}
|
|
8839
9389
|
case "MemberExpression": {
|
|
8840
9390
|
const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
|
|
8841
9391
|
this.checkPrivateMember(expr.property, obj, expr.property.name);
|
|
9392
|
+
this.checkAbstractAccess(expr, obj);
|
|
8842
9393
|
const key = this.refKeyOf(expr);
|
|
8843
9394
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8844
9395
|
this.checkStringMember(expr, obj, literal(expr.property.name));
|
|
9396
|
+
if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
|
|
8845
9397
|
return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
|
|
8846
9398
|
}
|
|
8847
9399
|
case "IndexExpression": {
|
|
@@ -8850,6 +9402,7 @@ var TypeAnalyzer = class {
|
|
|
8850
9402
|
const key = this.refKeyOf(expr);
|
|
8851
9403
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8852
9404
|
this.checkStringMember(expr, obj, this.expand(idx));
|
|
9405
|
+
if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
|
|
8853
9406
|
const member = this.stringMember(obj, this.expand(idx));
|
|
8854
9407
|
if (member) return this.chainResult(expr, member, shortCircuits);
|
|
8855
9408
|
return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
|
|
@@ -8859,8 +9412,6 @@ var TypeAnalyzer = class {
|
|
|
8859
9412
|
const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
|
|
8860
9413
|
return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
|
|
8861
9414
|
}
|
|
8862
|
-
case "NewExpression":
|
|
8863
|
-
return this.inferNew(expr, env);
|
|
8864
9415
|
case "ClassExpression":
|
|
8865
9416
|
return this.visitClass(expr, env);
|
|
8866
9417
|
case "SuperExpression": {
|
|
@@ -8897,18 +9448,14 @@ var TypeAnalyzer = class {
|
|
|
8897
9448
|
/** `new Name(args)` is `Name.new(args)` — the same function, and the
|
|
8898
9449
|
* same check. Saying so here rather than rewriting the tree keeps the
|
|
8899
9450
|
* error messages pointing at what was written. */
|
|
8900
|
-
|
|
8901
|
-
|
|
8902
|
-
const
|
|
8903
|
-
if (
|
|
8904
|
-
|
|
8905
|
-
|
|
8906
|
-
|
|
8907
|
-
|
|
8908
|
-
for (const argument of expr.arguments) this.infer(argument, env);
|
|
8909
|
-
return anyType;
|
|
8910
|
-
}
|
|
8911
|
-
return this.inferCall(expr, constructor, env);
|
|
9451
|
+
/** `__call` of a value with one, as what calling the value calls. */
|
|
9452
|
+
callMetamethod(raw) {
|
|
9453
|
+
const t = this.expand(raw);
|
|
9454
|
+
if (t.kind !== "object" || this.overloadsOf(t).length) return void 0;
|
|
9455
|
+
const method = t.properties.get("__call");
|
|
9456
|
+
if (!method) return void 0;
|
|
9457
|
+
const bound = this.overloadsOf(method.type).map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f);
|
|
9458
|
+
return bound.length ? intersection(bound) : void 0;
|
|
8912
9459
|
}
|
|
8913
9460
|
/** `super(...)` — the base constructor, run on the instance being built. */
|
|
8914
9461
|
inferSuperCall(expr, env) {
|
|
@@ -8932,8 +9479,9 @@ var TypeAnalyzer = class {
|
|
|
8932
9479
|
this.inferCall(expr, callable, env);
|
|
8933
9480
|
return nilType;
|
|
8934
9481
|
}
|
|
8935
|
-
inferCall(expr,
|
|
9482
|
+
inferCall(expr, called, env) {
|
|
8936
9483
|
this.checkAmbiguousCall(expr);
|
|
9484
|
+
const callee = this.callMetamethod(called) ?? called;
|
|
8937
9485
|
const united = this.unionSignatures(callee);
|
|
8938
9486
|
const fns = united ?? this.overloadsOf(callee);
|
|
8939
9487
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
@@ -8956,8 +9504,31 @@ var TypeAnalyzer = class {
|
|
|
8956
9504
|
if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
|
|
8957
9505
|
return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
|
|
8958
9506
|
}
|
|
9507
|
+
this.checkCallable(expr.callee, callee);
|
|
8959
9508
|
return callee.kind === "any" ? anyType : unknownType;
|
|
8960
9509
|
}
|
|
9510
|
+
/** `a()` where `a` is `1`: a value whose type is known and has no call
|
|
9511
|
+
* signature. A union is reported when any member cannot be called, but
|
|
9512
|
+
* only when every member is decided — `unknown`, `any` and anything still
|
|
9513
|
+
* generic say too little. */
|
|
9514
|
+
checkCallable(node, callee) {
|
|
9515
|
+
if (!this.emitDiagnostics || this.notCallableReported.has(node)) return;
|
|
9516
|
+
const raw = this.expand(callee);
|
|
9517
|
+
const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.expand(m));
|
|
9518
|
+
const notCallable = (m) => m.kind === "primitive" && m.name !== "nil" || m.kind === "literal" || m.kind === "templateLiteral" || m.kind === "array" || m.kind === "tuple" || m.kind === "object";
|
|
9519
|
+
const callable = (m) => this.overloadsOf(m).length > 0;
|
|
9520
|
+
const concrete = parts.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
|
|
9521
|
+
if (!concrete.some(notCallable) || !concrete.every((m) => notCallable(m) || callable(m))) return;
|
|
9522
|
+
this.notCallableReported.add(node);
|
|
9523
|
+
const label = node.type === "Identifier" ? node.name : expressionLabel(node);
|
|
9524
|
+
this.diagnostics.push({
|
|
9525
|
+
node,
|
|
9526
|
+
message: `This expression is not callable: ${label === void 0 ? "" : `'${label}' `}is of type '${briefType(withoutNil(raw))}'`
|
|
9527
|
+
});
|
|
9528
|
+
}
|
|
9529
|
+
/** Callees already reported as not callable: a loop body is visited more
|
|
9530
|
+
* than once. */
|
|
9531
|
+
notCallableReported = /* @__PURE__ */ new WeakSet();
|
|
8961
9532
|
/** A `(` on a line of its own continues the statement above it:
|
|
8962
9533
|
*
|
|
8963
9534
|
* const value = map[key]
|
|
@@ -8975,6 +9546,8 @@ var TypeAnalyzer = class {
|
|
|
8975
9546
|
}
|
|
8976
9547
|
inferMethodCall(expr, objType, env) {
|
|
8977
9548
|
this.checkPrivateMember(expr.method, objType, expr.method.name);
|
|
9549
|
+
this.checkStringMember(expr.method, objType, literal(expr.method.name));
|
|
9550
|
+
this.checkMissingMember(expr.method, objType, expr.method.name);
|
|
8978
9551
|
const method = this.propertyType(objType, expr.method.name);
|
|
8979
9552
|
const united = this.unionSignatures(method);
|
|
8980
9553
|
const fns = united ?? this.overloadsOf(method);
|
|
@@ -9008,6 +9581,7 @@ var TypeAnalyzer = class {
|
|
|
9008
9581
|
if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
|
|
9009
9582
|
return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
|
|
9010
9583
|
}
|
|
9584
|
+
this.checkCallable(expr.method, method);
|
|
9011
9585
|
return objType.kind === "any" ? anyType : unknownType;
|
|
9012
9586
|
}
|
|
9013
9587
|
// --------------------------------------------------------
|
|
@@ -9068,6 +9642,8 @@ var TypeAnalyzer = class {
|
|
|
9068
9642
|
if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
|
|
9069
9643
|
}
|
|
9070
9644
|
if (!this.isReadonlyProperty(this.expand(this.infer(object, env)), name)) return;
|
|
9645
|
+
const constructing = this.constructing;
|
|
9646
|
+
if (constructing && object.type === "Identifier" && object.name === "this" && constructing.members.some((m) => m.type === "ClassField" && !m.isStatic && m.isReadonly && m.name.name === name)) return;
|
|
9071
9647
|
this.diagnostics.push({
|
|
9072
9648
|
node: target,
|
|
9073
9649
|
message: `Cannot assign to '${name}' because it is a read-only property`
|
|
@@ -9127,6 +9703,8 @@ var TypeAnalyzer = class {
|
|
|
9127
9703
|
inferArray(expr, env, asConst) {
|
|
9128
9704
|
const contextual = this.contextualArrays.get(expr);
|
|
9129
9705
|
if (contextual && !asConst) return contextual;
|
|
9706
|
+
const wantedTuple = this.tupleArrays.get(expr);
|
|
9707
|
+
if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
|
|
9130
9708
|
const elems = [];
|
|
9131
9709
|
let hadSpread = false;
|
|
9132
9710
|
for (const el of expr.elements) {
|
|
@@ -9146,6 +9724,28 @@ var TypeAnalyzer = class {
|
|
|
9146
9724
|
return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
|
|
9147
9725
|
})) : unknownType);
|
|
9148
9726
|
}
|
|
9727
|
+
/** `[a, b, ...more]` where a tuple is wanted: the places it names are the
|
|
9728
|
+
* tuple's, and what comes after them — more elements, or a spread — is
|
|
9729
|
+
* its rest. */
|
|
9730
|
+
inferTupleLiteral(expr, env, wanted) {
|
|
9731
|
+
const fixed = [];
|
|
9732
|
+
const tail = [];
|
|
9733
|
+
let spread = false;
|
|
9734
|
+
expr.elements.forEach((element, i) => {
|
|
9735
|
+
if (element.type === "SpreadElement") {
|
|
9736
|
+
spread = true;
|
|
9737
|
+
const held = this.expand(this.infer(element.argument, env));
|
|
9738
|
+
if (held.kind === "array") tail.push(held.element);
|
|
9739
|
+
else if (held.kind === "tuple") tail.push(...tupleMembers(held));
|
|
9740
|
+
else tail.push(unknownType);
|
|
9741
|
+
return;
|
|
9742
|
+
}
|
|
9743
|
+
const t = this.widenUnlessAsked(this.infer(element, env), element);
|
|
9744
|
+
if (!spread && i < wanted.elements.length) fixed.push(t);
|
|
9745
|
+
else tail.push(t);
|
|
9746
|
+
});
|
|
9747
|
+
return tuple(fixed, tail.length ? union(tail) : void 0);
|
|
9748
|
+
}
|
|
9149
9749
|
/** A literal written inside a fresh table or array widens — `{ n = 1 }` is
|
|
9150
9750
|
* `{ n: number }` — unless the surroundings said a literal belongs there.
|
|
9151
9751
|
* `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
|
|
@@ -9231,7 +9831,7 @@ var TypeAnalyzer = class {
|
|
|
9231
9831
|
case "tuple": {
|
|
9232
9832
|
const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
|
|
9233
9833
|
if (tupleContext?.kind === "tuple") {
|
|
9234
|
-
return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.
|
|
9834
|
+
return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.rest);
|
|
9235
9835
|
}
|
|
9236
9836
|
const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
|
|
9237
9837
|
const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
|
|
@@ -9488,7 +10088,8 @@ var TypeAnalyzer = class {
|
|
|
9488
10088
|
for (const f of candidates) {
|
|
9489
10089
|
if (!f.predicate) continue;
|
|
9490
10090
|
const arg = args[f.predicate.param];
|
|
9491
|
-
|
|
10091
|
+
const onCondition = f.predicate.asserts && !f.predicate.type;
|
|
10092
|
+
if (!arg || this.refKeyOf(arg) === void 0 && !onCondition) continue;
|
|
9492
10093
|
if (f.typeParams?.length && f.predicate.type) {
|
|
9493
10094
|
const subst = this.inferTypeArgs(f, argTypes);
|
|
9494
10095
|
return {
|
|
@@ -9509,6 +10110,10 @@ var TypeAnalyzer = class {
|
|
|
9509
10110
|
const found = this.predicateCallTarget(call, env);
|
|
9510
10111
|
if (!found || !found.predicate.asserts) return;
|
|
9511
10112
|
const filter = found.predicate.type;
|
|
10113
|
+
if (this.refKeyOf(found.arg) === void 0) {
|
|
10114
|
+
if (!filter) this.applyNarrowing(found.arg, env, env, forkEnv(env));
|
|
10115
|
+
return;
|
|
10116
|
+
}
|
|
9512
10117
|
this.narrowRef(found.arg, env, env, forkEnv(env), (cur) => filter ? { yes: narrowTo(cur, filter), no: narrowExclude(cur, filter) } : { yes: narrowTruthy(cur), no: narrowFalsy(cur) });
|
|
9513
10118
|
}
|
|
9514
10119
|
// --------------------------------------------------------
|
|
@@ -9533,6 +10138,10 @@ var TypeAnalyzer = class {
|
|
|
9533
10138
|
if (base === void 0) return void 0;
|
|
9534
10139
|
if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
|
|
9535
10140
|
if (expr.index.type === "NumberLiteral") return `${base}#${expr.index.value}`;
|
|
10141
|
+
if (expr.index.type === "Identifier") {
|
|
10142
|
+
const id = this.bindingIdOf(expr.index);
|
|
10143
|
+
return id === void 0 ? void 0 : `${base}[${bindKey(id)}]`;
|
|
10144
|
+
}
|
|
9536
10145
|
return void 0;
|
|
9537
10146
|
}
|
|
9538
10147
|
default:
|
|
@@ -9628,26 +10237,45 @@ var TypeAnalyzer = class {
|
|
|
9628
10237
|
* that copied it no longer holds that value: forget the alias. */
|
|
9629
10238
|
unalias(key) {
|
|
9630
10239
|
for (const k of [...this.refAliases.keys()]) {
|
|
9631
|
-
if (k !== key && !k
|
|
10240
|
+
if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
|
|
9632
10241
|
for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
|
|
9633
10242
|
this.refAliases.delete(k);
|
|
9634
10243
|
}
|
|
9635
10244
|
}
|
|
9636
|
-
/** Drop every narrowing recorded for a path strictly under `key
|
|
10245
|
+
/** Drop every narrowing recorded for a path strictly under `key` — and,
|
|
10246
|
+
* when `key` is a whole variable, for every `t[key]` indexed by it: that
|
|
10247
|
+
* now names a different slot. */
|
|
9637
10248
|
invalidateBelow(env, key) {
|
|
10249
|
+
const asIndex = `[${key}]`;
|
|
9638
10250
|
for (const k of [...env.keys()]) {
|
|
9639
|
-
if (k
|
|
10251
|
+
if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
|
|
9640
10252
|
}
|
|
9641
10253
|
}
|
|
9642
10254
|
/** An assignment through a reference invalidates it and everything under
|
|
9643
10255
|
* it, then records the assigned type. */
|
|
9644
10256
|
assignToRef(expr, value, env) {
|
|
10257
|
+
this.invalidateSiblings(expr, env);
|
|
9645
10258
|
const key = this.refKeyOf(expr);
|
|
9646
10259
|
if (key === void 0) return;
|
|
9647
10260
|
this.invalidateBelow(env, key);
|
|
9648
10261
|
this.unalias(key);
|
|
9649
10262
|
env.set(key, value);
|
|
9650
10263
|
}
|
|
10264
|
+
/** A write to one slot of a table can be a write to a slot narrowed under
|
|
10265
|
+
* another name: `t.x = nil` is `t[k] = nil` when `k` is `"x"`. So a
|
|
10266
|
+
* write by name forgets what is known through variable keys, and a write
|
|
10267
|
+
* through any computed key forgets everything known about the table's
|
|
10268
|
+
* slots. */
|
|
10269
|
+
invalidateSiblings(expr, env) {
|
|
10270
|
+
const inner = expr.type === "ParenthesizedExpression" ? expr.expression : expr;
|
|
10271
|
+
if (inner.type !== "MemberExpression" && inner.type !== "IndexExpression") return;
|
|
10272
|
+
const parent = this.refKeyOf(inner.object);
|
|
10273
|
+
if (parent === void 0) return;
|
|
10274
|
+
const computed = inner.type === "IndexExpression" && inner.index.type !== "StringLiteral" && inner.index.type !== "NumberLiteral";
|
|
10275
|
+
for (const k of [...env.keys()]) {
|
|
10276
|
+
if (computed ? isBelow(k, parent) : k.startsWith(`${parent}[`)) env.delete(k);
|
|
10277
|
+
}
|
|
10278
|
+
}
|
|
9651
10279
|
// --------------------------------------------------------
|
|
9652
10280
|
// Small helpers
|
|
9653
10281
|
// --------------------------------------------------------
|
|
@@ -9662,6 +10290,35 @@ var TypeAnalyzer = class {
|
|
|
9662
10290
|
this.selfType = saved;
|
|
9663
10291
|
}
|
|
9664
10292
|
}
|
|
10293
|
+
/** Arithmetic no declared metamethod answers: a number — unless an
|
|
10294
|
+
* operand is `any`, which may be a value with metamethods of its own.
|
|
10295
|
+
* `(a.Position - b.Position).Magnitude` on an `a` nothing typed is a
|
|
10296
|
+
* `Vector3` as far as anyone knows, not a number with no `Magnitude`. */
|
|
10297
|
+
arithmeticOn(operands) {
|
|
10298
|
+
return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
|
|
10299
|
+
}
|
|
10300
|
+
/** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
|
|
10301
|
+
* with the operands swapped for `>` and `>=`, and an instance without
|
|
10302
|
+
* one cannot be compared at all. */
|
|
10303
|
+
checkComparison(node, op, l, r) {
|
|
10304
|
+
if (!this.emitDiagnostics) return;
|
|
10305
|
+
const left = this.expand(l);
|
|
10306
|
+
const right = this.expand(r);
|
|
10307
|
+
const instance = (t) => t.kind === "object" && t.class !== void 0;
|
|
10308
|
+
if (!instance(left) && !instance(right)) return;
|
|
10309
|
+
const name = op === "<" || op === ">" ? "__lt" : "__le";
|
|
10310
|
+
const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
|
|
10311
|
+
for (const receiver of operands) {
|
|
10312
|
+
const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
|
|
10313
|
+
if (!method) continue;
|
|
10314
|
+
if (this.pickOverload(this.overloadsOf(method.type), operands)) return;
|
|
10315
|
+
break;
|
|
10316
|
+
}
|
|
10317
|
+
this.diagnostics.push({
|
|
10318
|
+
node,
|
|
10319
|
+
message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
|
|
10320
|
+
});
|
|
10321
|
+
}
|
|
9665
10322
|
/** What an operator on a value with metamethods gives: `a + b` calls
|
|
9666
10323
|
* `__add` on `a`, or failing that on `b` with the operands swapped — the
|
|
9667
10324
|
* order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
|
|
@@ -9994,6 +10651,13 @@ var nodeHost = {
|
|
|
9994
10651
|
} catch {
|
|
9995
10652
|
return void 0;
|
|
9996
10653
|
}
|
|
10654
|
+
},
|
|
10655
|
+
readDirectory(path) {
|
|
10656
|
+
try {
|
|
10657
|
+
return (0, import_node_fs.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => entry.name);
|
|
10658
|
+
} catch {
|
|
10659
|
+
return void 0;
|
|
10660
|
+
}
|
|
9997
10661
|
}
|
|
9998
10662
|
};
|
|
9999
10663
|
|
|
@@ -10185,13 +10849,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
10185
10849
|
else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
|
|
10186
10850
|
continue;
|
|
10187
10851
|
}
|
|
10188
|
-
|
|
10189
|
-
|
|
10852
|
+
if (entry.includes("*")) {
|
|
10853
|
+
const matched = matchPackages(entry, config.directory, host);
|
|
10854
|
+
if (matched === void 0) {
|
|
10855
|
+
problems.push({
|
|
10856
|
+
file: config.path,
|
|
10857
|
+
message: `'${entry}' needs the folders listed, and this tool cannot list them; name the libraries one by one`,
|
|
10858
|
+
...entryPosition(config, entry)
|
|
10859
|
+
});
|
|
10860
|
+
} else if (!matched.length) {
|
|
10861
|
+
problems.push({
|
|
10862
|
+
file: config.path,
|
|
10863
|
+
message: `No installed type library matches '${entry}'`,
|
|
10864
|
+
...entryPosition(config, entry)
|
|
10865
|
+
});
|
|
10866
|
+
}
|
|
10867
|
+
for (const found2 of matched ?? []) addPackage(found2.directory, found2.file, /* @__PURE__ */ new Set());
|
|
10868
|
+
continue;
|
|
10869
|
+
}
|
|
10870
|
+
const names = entry.startsWith("@") ? [entry] : [`@tilua-types/${entry}`, entry];
|
|
10871
|
+
const found = names.map((name) => findPackage(name, config.directory, host)).find(Boolean);
|
|
10190
10872
|
if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
|
|
10191
10873
|
else {
|
|
10192
10874
|
problems.push({
|
|
10193
10875
|
file: config.path,
|
|
10194
|
-
message: `Cannot find type library '${
|
|
10876
|
+
message: names.length === 1 ? `Cannot find type library '${entry}'. Install it with: npm i -D ${entry}` : `Cannot find type library '${names[0]}' or '${entry}'. Install it with: npm i -D ${names[0]}`,
|
|
10195
10877
|
...entryPosition(config, entry)
|
|
10196
10878
|
});
|
|
10197
10879
|
}
|
|
@@ -10228,6 +10910,32 @@ function findPackage(name, from, host) {
|
|
|
10228
10910
|
directory = parent;
|
|
10229
10911
|
}
|
|
10230
10912
|
}
|
|
10913
|
+
function matchPackages(pattern, from, host) {
|
|
10914
|
+
if (!host.readDirectory) return void 0;
|
|
10915
|
+
const slash = pattern.lastIndexOf("/");
|
|
10916
|
+
const scope = slash < 0 ? "" : pattern.slice(0, slash);
|
|
10917
|
+
const last = pattern.slice(slash + 1);
|
|
10918
|
+
const matches = new RegExp(`^${last.split("*").map(escapeRegExp2).join(".*")}$`);
|
|
10919
|
+
const found = /* @__PURE__ */ new Map();
|
|
10920
|
+
let directory = (0, import_node_path2.resolve)(from);
|
|
10921
|
+
for (; ; ) {
|
|
10922
|
+
const folder = (0, import_node_path2.join)(directory, "node_modules", ...scope ? scope.split("/") : []);
|
|
10923
|
+
for (const name of host.readDirectory(folder) ?? []) {
|
|
10924
|
+
const full = scope ? `${scope}/${name}` : name;
|
|
10925
|
+
if (!matches.test(name) || found.has(full)) continue;
|
|
10926
|
+
const candidate = (0, import_node_path2.join)(folder, name);
|
|
10927
|
+
const file = packageEntry(candidate, host);
|
|
10928
|
+
if (file) found.set(full, { directory: candidate, file });
|
|
10929
|
+
}
|
|
10930
|
+
const parent = (0, import_node_path2.dirname)(directory);
|
|
10931
|
+
if (parent === directory) break;
|
|
10932
|
+
directory = parent;
|
|
10933
|
+
}
|
|
10934
|
+
return [...found.keys()].sort().map((name) => found.get(name));
|
|
10935
|
+
}
|
|
10936
|
+
function escapeRegExp2(text) {
|
|
10937
|
+
return text.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
10938
|
+
}
|
|
10231
10939
|
function dependencyNames(directory, host) {
|
|
10232
10940
|
const manifest = readJson((0, import_node_path2.join)(directory, "package.json"), host);
|
|
10233
10941
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -10326,7 +11034,8 @@ function sourceMapTypes(text, path, options) {
|
|
|
10326
11034
|
for (const child of node.children ?? []) {
|
|
10327
11035
|
if (!isNode(child)) continue;
|
|
10328
11036
|
const childAlias = visit(child, [...segments, child.name], alias);
|
|
10329
|
-
|
|
11037
|
+
const service = child.name === child.className && options.classes.has(child.className);
|
|
11038
|
+
if (!IDENTIFIER.test(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
|
|
10330
11039
|
named.add(child.name);
|
|
10331
11040
|
members.push(`${child.name}: ${childAlias}`);
|
|
10332
11041
|
}
|
|
@@ -10387,6 +11096,7 @@ var index_default = tilua;
|
|
|
10387
11096
|
BinaryOperators,
|
|
10388
11097
|
CONFIG_FILE_NAMES,
|
|
10389
11098
|
Keywords,
|
|
11099
|
+
LANGUAGE_GLOBALS,
|
|
10390
11100
|
LexError,
|
|
10391
11101
|
Operators,
|
|
10392
11102
|
PRELUDE_SOURCE,
|
|
@@ -10454,6 +11164,7 @@ var index_default = tilua;
|
|
|
10454
11164
|
tilua,
|
|
10455
11165
|
tokenize,
|
|
10456
11166
|
tuple,
|
|
11167
|
+
tupleMembers,
|
|
10457
11168
|
typeParam,
|
|
10458
11169
|
unify,
|
|
10459
11170
|
union,
|