@tilua/parser 1.0.2 → 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 +183 -37
- package/dist/index.cjs +1173 -417
- package/dist/index.d.cts +189 -68
- package/dist/index.d.ts +189 -68
- package/dist/index.js +1172 -418
- 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() {
|
|
@@ -1830,11 +1881,67 @@ var Parser = class {
|
|
|
1830
1881
|
}
|
|
1831
1882
|
parseClassMember() {
|
|
1832
1883
|
const start = this.current();
|
|
1833
|
-
|
|
1834
|
-
|
|
1884
|
+
let accessibility;
|
|
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
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
const member = this.parseClassMemberAfterModifiers(start, modifiers);
|
|
1898
|
+
if (!member) return member;
|
|
1899
|
+
const misplaced = (message) => this.problemAt(start, message);
|
|
1900
|
+
if (member.type === "ClassConstructor") {
|
|
1901
|
+
const written = [...accessibility ? [accessibility] : [], ...modifiers];
|
|
1902
|
+
if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
|
|
1903
|
+
return member;
|
|
1904
|
+
}
|
|
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
|
+
};
|
|
1915
|
+
}
|
|
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");
|
|
1835
1925
|
if (this.checkKeyword("function")) {
|
|
1836
1926
|
this.advance();
|
|
1837
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
|
+
}
|
|
1838
1945
|
const signatures = [];
|
|
1839
1946
|
let written = memberName;
|
|
1840
1947
|
while (true) {
|
|
@@ -1952,12 +2059,17 @@ var Parser = class {
|
|
|
1952
2059
|
parseReturnStatement(stopAtNewline = false) {
|
|
1953
2060
|
const start = this.current();
|
|
1954
2061
|
this.expectKeyword("return");
|
|
1955
|
-
let
|
|
2062
|
+
let argument;
|
|
1956
2063
|
const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
|
|
1957
2064
|
if (!pastLine() && this.isExpressionStart()) {
|
|
1958
|
-
|
|
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
|
+
}
|
|
1959
2071
|
}
|
|
1960
|
-
return { type: "ReturnStatement",
|
|
2072
|
+
return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
|
|
1961
2073
|
}
|
|
1962
2074
|
parseTypeAliasStatement() {
|
|
1963
2075
|
const start = this.current();
|
|
@@ -2016,7 +2128,7 @@ var Parser = class {
|
|
|
2016
2128
|
...spanFrom(start, this.previous())
|
|
2017
2129
|
};
|
|
2018
2130
|
}
|
|
2019
|
-
if (first.type === "CallExpression" || first.type === "MethodCallExpression"
|
|
2131
|
+
if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
|
|
2020
2132
|
return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
|
|
2021
2133
|
}
|
|
2022
2134
|
let expression = first;
|
|
@@ -2069,6 +2181,15 @@ var Parser = class {
|
|
|
2069
2181
|
}
|
|
2070
2182
|
return false;
|
|
2071
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
|
+
}
|
|
2072
2193
|
/** Does the next token start right where the current one ends? */
|
|
2073
2194
|
touchesNext() {
|
|
2074
2195
|
const current = this.current();
|
|
@@ -2255,6 +2376,7 @@ var Parser = class {
|
|
|
2255
2376
|
}
|
|
2256
2377
|
parseAtom() {
|
|
2257
2378
|
const t = this.current();
|
|
2379
|
+
if (this.startsStringMethodCall()) return this.parsePrefixExpression();
|
|
2258
2380
|
if (t.type === "Literal") {
|
|
2259
2381
|
this.advance();
|
|
2260
2382
|
const lit = t;
|
|
@@ -2274,8 +2396,9 @@ var Parser = class {
|
|
|
2274
2396
|
return this.buildInterpolatedString(t);
|
|
2275
2397
|
}
|
|
2276
2398
|
if (t.type === "Operator" && t.value === "...") {
|
|
2399
|
+
this.problem(NO_DOTS);
|
|
2277
2400
|
this.advance();
|
|
2278
|
-
return { type: "
|
|
2401
|
+
return { type: "ErrorExpression", ...spanFrom(t, t) };
|
|
2279
2402
|
}
|
|
2280
2403
|
if (t.type === "Keyword" && t.value === "function") {
|
|
2281
2404
|
this.advance();
|
|
@@ -2351,12 +2474,17 @@ var Parser = class {
|
|
|
2351
2474
|
const start = this.current();
|
|
2352
2475
|
let base;
|
|
2353
2476
|
if (this.startsNew()) {
|
|
2354
|
-
|
|
2355
|
-
|
|
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()) {
|
|
2356
2481
|
this.advance();
|
|
2357
2482
|
base = { type: "SuperExpression", ...spanFrom(start, start) };
|
|
2358
2483
|
} else if (this.checkType("Identifier")) {
|
|
2359
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) };
|
|
2360
2488
|
} else if (this.matchPunctuator("(")) {
|
|
2361
2489
|
const inner = this.inBrackets(() => this.parseExpression());
|
|
2362
2490
|
this.expectPunctuator(")");
|
|
@@ -2467,9 +2595,9 @@ var Parser = class {
|
|
|
2467
2595
|
}
|
|
2468
2596
|
return base;
|
|
2469
2597
|
}
|
|
2470
|
-
/** `new
|
|
2471
|
-
*
|
|
2472
|
-
*
|
|
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. */
|
|
2473
2601
|
startsNew() {
|
|
2474
2602
|
return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
|
|
2475
2603
|
}
|
|
@@ -2479,25 +2607,6 @@ var Parser = class {
|
|
|
2479
2607
|
startsSuperUse() {
|
|
2480
2608
|
return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
|
|
2481
2609
|
}
|
|
2482
|
-
/** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
|
|
2483
|
-
* name reached through a module — never an arbitrary expression, so the
|
|
2484
|
-
* arguments are unambiguously the constructor's. */
|
|
2485
|
-
parseNewExpression() {
|
|
2486
|
-
const start = this.current();
|
|
2487
|
-
this.advance();
|
|
2488
|
-
let callee = this.parseIdentifier();
|
|
2489
|
-
while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
2490
|
-
this.advance();
|
|
2491
|
-
const property = this.parseIdentifier();
|
|
2492
|
-
callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
|
|
2493
|
-
}
|
|
2494
|
-
const typeArguments = this.tryCallTypeArguments();
|
|
2495
|
-
if (!this.checkPunctuator("(")) {
|
|
2496
|
-
this.error("Expected '(' after the class being constructed: 'new Name(...)'");
|
|
2497
|
-
}
|
|
2498
|
-
const args = this.parseCallArguments();
|
|
2499
|
-
return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
|
|
2500
|
-
}
|
|
2501
2610
|
/** After `...`, is there something to spread? Nothing following it means
|
|
2502
2611
|
* the vararg pack, which is what `f(...)` has always passed on. */
|
|
2503
2612
|
startsSpread() {
|
|
@@ -2539,8 +2648,8 @@ var Parser = class {
|
|
|
2539
2648
|
const errors = this.errors.length;
|
|
2540
2649
|
try {
|
|
2541
2650
|
this.advance();
|
|
2542
|
-
const list = [this.
|
|
2543
|
-
while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.
|
|
2651
|
+
const list = [this.parseType()];
|
|
2652
|
+
while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
|
|
2544
2653
|
this.expectOperator(">");
|
|
2545
2654
|
if (!this.startsCallArguments()) throw new ParseRecover("not a call");
|
|
2546
2655
|
return list;
|
|
@@ -2672,7 +2781,7 @@ var Parser = class {
|
|
|
2672
2781
|
if (this.checkOperator("...")) {
|
|
2673
2782
|
const dots = this.advance();
|
|
2674
2783
|
if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
|
|
2675
|
-
|
|
2784
|
+
this.problemAt(dots, NO_DOTS);
|
|
2676
2785
|
} else {
|
|
2677
2786
|
const argument = this.expressionOr(stop);
|
|
2678
2787
|
elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
|
|
@@ -2826,35 +2935,16 @@ var Parser = class {
|
|
|
2826
2935
|
column: p.column
|
|
2827
2936
|
};
|
|
2828
2937
|
}
|
|
2829
|
-
|
|
2938
|
+
/** `T...` written where a type goes: a pack, which tilua does not have. */
|
|
2939
|
+
rejectPackReference() {
|
|
2830
2940
|
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
2831
|
-
|
|
2832
|
-
|
|
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();
|
|
2833
2946
|
this.advance();
|
|
2834
|
-
const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
|
|
2835
|
-
return {
|
|
2836
|
-
type: "TypePackNode",
|
|
2837
|
-
types: [],
|
|
2838
|
-
hasVarargs: true,
|
|
2839
|
-
// Wrap in `VariadicTypeNode`, matching the convention used by
|
|
2840
|
-
// `parseFunctionTypeAfterParen`'s identifier-pack-reference
|
|
2841
|
-
// branch, so the printer can tell `A...` (name-first, this
|
|
2842
|
-
// case) apart from `...T` (dots-first) and append rather
|
|
2843
|
-
// than prepend the `...`.
|
|
2844
|
-
varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
|
|
2845
|
-
...spanFrom(start, this.previous())
|
|
2846
|
-
};
|
|
2847
|
-
}
|
|
2848
|
-
return this.parseType();
|
|
2849
|
-
}
|
|
2850
|
-
parseTypeArgument() {
|
|
2851
|
-
if (this.checkOperator("...")) {
|
|
2852
|
-
return this.parseTypePack();
|
|
2853
|
-
}
|
|
2854
|
-
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
2855
|
-
return this.parseTypeOrTypePackReference();
|
|
2856
2947
|
}
|
|
2857
|
-
return this.parseType();
|
|
2858
2948
|
}
|
|
2859
2949
|
/** The part shared by a real function body and an overload signature:
|
|
2860
2950
|
* `<generics>(params): ReturnType`, up to (but not including) the block. */
|
|
@@ -2867,7 +2957,6 @@ var Parser = class {
|
|
|
2867
2957
|
this.expectPunctuator("(");
|
|
2868
2958
|
const params = [];
|
|
2869
2959
|
let hasVarargs = false;
|
|
2870
|
-
let varargTypeAnnotation;
|
|
2871
2960
|
if (!this.checkPunctuator(")")) {
|
|
2872
2961
|
while (true) {
|
|
2873
2962
|
if (this.checkOperator("...")) {
|
|
@@ -2875,14 +2964,14 @@ var Parser = class {
|
|
|
2875
2964
|
hasVarargs = true;
|
|
2876
2965
|
if (this.checkType("Identifier")) {
|
|
2877
2966
|
const nameTok = this.expectIdentifier();
|
|
2878
|
-
let
|
|
2967
|
+
let typeAnnotation3;
|
|
2879
2968
|
if (this.matchPunctuator(":")) {
|
|
2880
|
-
|
|
2969
|
+
typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
|
|
2881
2970
|
}
|
|
2882
2971
|
params.push({
|
|
2883
2972
|
type: "FunctionParameter",
|
|
2884
2973
|
name: nameTok.value,
|
|
2885
|
-
typeAnnotation:
|
|
2974
|
+
typeAnnotation: typeAnnotation3,
|
|
2886
2975
|
rest: true,
|
|
2887
2976
|
...spanFrom(dots, this.previous())
|
|
2888
2977
|
});
|
|
@@ -2891,9 +2980,20 @@ var Parser = class {
|
|
|
2891
2980
|
}
|
|
2892
2981
|
break;
|
|
2893
2982
|
}
|
|
2983
|
+
this.problemAt(dots, NO_DOTS);
|
|
2984
|
+
let typeAnnotation2;
|
|
2894
2985
|
if (this.matchPunctuator(":")) {
|
|
2895
|
-
|
|
2986
|
+
this.rejectPackReference();
|
|
2987
|
+
const element = this.parseType();
|
|
2988
|
+
typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
|
|
2896
2989
|
}
|
|
2990
|
+
params.push({
|
|
2991
|
+
type: "FunctionParameter",
|
|
2992
|
+
name: "args",
|
|
2993
|
+
typeAnnotation: typeAnnotation2,
|
|
2994
|
+
rest: true,
|
|
2995
|
+
...spanFrom(dots, this.previous())
|
|
2996
|
+
});
|
|
2897
2997
|
break;
|
|
2898
2998
|
}
|
|
2899
2999
|
const paramStart = this.current();
|
|
@@ -2935,10 +3035,16 @@ var Parser = class {
|
|
|
2935
3035
|
if (this.matchPunctuator(":")) {
|
|
2936
3036
|
predicate = this.tryParseTypePredicate();
|
|
2937
3037
|
if (!predicate) {
|
|
2938
|
-
returnType = this.attempt(() => this.
|
|
3038
|
+
returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
|
|
2939
3039
|
}
|
|
2940
3040
|
}
|
|
2941
|
-
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();
|
|
2942
3048
|
}
|
|
2943
3049
|
/** TypeScript-style type-guard return annotations, in return position only:
|
|
2944
3050
|
*
|
|
@@ -3008,7 +3114,6 @@ var Parser = class {
|
|
|
3008
3114
|
generics: [],
|
|
3009
3115
|
params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
|
|
3010
3116
|
hasVarargs: false,
|
|
3011
|
-
varargTypeAnnotation: void 0,
|
|
3012
3117
|
returnType: void 0,
|
|
3013
3118
|
predicate: void 0
|
|
3014
3119
|
};
|
|
@@ -3020,7 +3125,6 @@ var Parser = class {
|
|
|
3020
3125
|
generics: head.generics,
|
|
3021
3126
|
params: head.params,
|
|
3022
3127
|
hasVarargs: head.hasVarargs,
|
|
3023
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3024
3128
|
returnType: head.returnType,
|
|
3025
3129
|
predicate: head.predicate,
|
|
3026
3130
|
body,
|
|
@@ -3032,7 +3136,7 @@ var Parser = class {
|
|
|
3032
3136
|
returnOf(expression) {
|
|
3033
3137
|
const statement = {
|
|
3034
3138
|
type: "ReturnStatement",
|
|
3035
|
-
|
|
3139
|
+
argument: expression,
|
|
3036
3140
|
...spanFrom(expression, expression)
|
|
3037
3141
|
};
|
|
3038
3142
|
return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
|
|
@@ -3045,7 +3149,6 @@ var Parser = class {
|
|
|
3045
3149
|
generics: head.generics,
|
|
3046
3150
|
params: head.params,
|
|
3047
3151
|
hasVarargs: head.hasVarargs,
|
|
3048
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3049
3152
|
returnType: head.returnType,
|
|
3050
3153
|
predicate: head.predicate,
|
|
3051
3154
|
body,
|
|
@@ -3058,7 +3161,6 @@ var Parser = class {
|
|
|
3058
3161
|
generics: head.generics,
|
|
3059
3162
|
params: head.params,
|
|
3060
3163
|
hasVarargs: head.hasVarargs,
|
|
3061
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3062
3164
|
returnType: head.returnType,
|
|
3063
3165
|
predicate: head.predicate,
|
|
3064
3166
|
...spanFrom(head.start, this.previous())
|
|
@@ -3076,7 +3178,6 @@ var Parser = class {
|
|
|
3076
3178
|
generics: head.generics,
|
|
3077
3179
|
params: head.params,
|
|
3078
3180
|
hasVarargs: head.hasVarargs,
|
|
3079
|
-
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
3080
3181
|
returnType: head.returnType,
|
|
3081
3182
|
predicate: head.predicate,
|
|
3082
3183
|
body,
|
|
@@ -3217,9 +3318,10 @@ var Parser = class {
|
|
|
3217
3318
|
return this.parseFunctionTypeAfterParen(t, []);
|
|
3218
3319
|
}
|
|
3219
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[]]'");
|
|
3220
3322
|
this.advance();
|
|
3221
|
-
const
|
|
3222
|
-
return { type: "
|
|
3323
|
+
const element = this.parseType();
|
|
3324
|
+
return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
|
|
3223
3325
|
}
|
|
3224
3326
|
if (t.type === "Punctuator" && t.value === "{") {
|
|
3225
3327
|
return this.parseTableType();
|
|
@@ -3227,13 +3329,22 @@ var Parser = class {
|
|
|
3227
3329
|
if (t.type === "Punctuator" && t.value === "[") {
|
|
3228
3330
|
this.advance();
|
|
3229
3331
|
const elements = [];
|
|
3332
|
+
let rest;
|
|
3230
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
|
+
}
|
|
3231
3342
|
elements.push(this.parseType());
|
|
3232
3343
|
if (this.matchPunctuator(",")) continue;
|
|
3233
3344
|
break;
|
|
3234
3345
|
}
|
|
3235
3346
|
this.expectPunctuator("]");
|
|
3236
|
-
return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
|
|
3347
|
+
return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
|
|
3237
3348
|
}
|
|
3238
3349
|
if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
|
|
3239
3350
|
this.advance();
|
|
@@ -3280,9 +3391,9 @@ var Parser = class {
|
|
|
3280
3391
|
if (this.checkOperator("<")) {
|
|
3281
3392
|
this.advance();
|
|
3282
3393
|
if (!this.checkOperator(">")) {
|
|
3283
|
-
typeArguments.push(this.
|
|
3394
|
+
typeArguments.push(this.parseType());
|
|
3284
3395
|
while (this.matchPunctuator(",") && !this.checkOperator(">")) {
|
|
3285
|
-
typeArguments.push(this.
|
|
3396
|
+
typeArguments.push(this.parseType());
|
|
3286
3397
|
}
|
|
3287
3398
|
}
|
|
3288
3399
|
this.expectOperator(">");
|
|
@@ -3293,13 +3404,12 @@ var Parser = class {
|
|
|
3293
3404
|
}
|
|
3294
3405
|
parseFunctionTypeAfterParen(start, generics) {
|
|
3295
3406
|
const params = [];
|
|
3296
|
-
let
|
|
3297
|
-
let varargType;
|
|
3407
|
+
let rested = false;
|
|
3298
3408
|
if (!this.checkPunctuator(")")) {
|
|
3299
3409
|
while (true) {
|
|
3300
3410
|
if (this.checkOperator("...")) {
|
|
3301
3411
|
const dots = this.advance();
|
|
3302
|
-
|
|
3412
|
+
rested = true;
|
|
3303
3413
|
if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
|
|
3304
3414
|
const nameTok2 = this.expectIdentifier();
|
|
3305
3415
|
this.advance();
|
|
@@ -3313,16 +3423,21 @@ var Parser = class {
|
|
|
3313
3423
|
});
|
|
3314
3424
|
break;
|
|
3315
3425
|
}
|
|
3316
|
-
|
|
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
|
+
});
|
|
3317
3436
|
break;
|
|
3318
3437
|
}
|
|
3319
3438
|
if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
this.advance();
|
|
3323
|
-
hasVarargs = true;
|
|
3324
|
-
varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
|
|
3325
|
-
break;
|
|
3439
|
+
this.rejectPackReference();
|
|
3440
|
+
continue;
|
|
3326
3441
|
}
|
|
3327
3442
|
let name;
|
|
3328
3443
|
let optional2 = false;
|
|
@@ -3352,19 +3467,17 @@ var Parser = class {
|
|
|
3352
3467
|
this.expectPunctuator(")");
|
|
3353
3468
|
if (this.matchPunctuator("=>") || this.mistypedArrow()) {
|
|
3354
3469
|
const predicate = this.tryParseTypePredicate();
|
|
3355
|
-
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();
|
|
3356
3471
|
return {
|
|
3357
3472
|
type: "FunctionTypeNode",
|
|
3358
3473
|
generics,
|
|
3359
3474
|
params,
|
|
3360
|
-
hasVarargs,
|
|
3361
|
-
varargType,
|
|
3362
3475
|
returnType,
|
|
3363
3476
|
predicate,
|
|
3364
3477
|
...spanFrom(start, this.previous())
|
|
3365
3478
|
};
|
|
3366
3479
|
}
|
|
3367
|
-
if (params.length === 1 && !params[0].name && !
|
|
3480
|
+
if (params.length === 1 && !params[0].name && !rested) {
|
|
3368
3481
|
return {
|
|
3369
3482
|
type: "ParenthesizedTypeNode",
|
|
3370
3483
|
typeAnnotation: params[0].typeAnnotation,
|
|
@@ -3374,11 +3487,14 @@ var Parser = class {
|
|
|
3374
3487
|
if (params.some((p) => p.name !== void 0)) {
|
|
3375
3488
|
this.error("Expected '=>' for function type");
|
|
3376
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]'");
|
|
3377
3495
|
return {
|
|
3378
|
-
type: "
|
|
3379
|
-
|
|
3380
|
-
hasVarargs,
|
|
3381
|
-
varargType,
|
|
3496
|
+
type: "TupleTypeNode",
|
|
3497
|
+
elements: params.map((p) => p.typeAnnotation),
|
|
3382
3498
|
...spanFrom(start, this.previous())
|
|
3383
3499
|
};
|
|
3384
3500
|
}
|
|
@@ -3516,40 +3632,15 @@ var Parser = class {
|
|
|
3516
3632
|
this.expectPunctuator("}");
|
|
3517
3633
|
return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
|
|
3518
3634
|
}
|
|
3519
|
-
parseTypePack() {
|
|
3520
|
-
const start = this.current();
|
|
3521
|
-
if (this.matchOperator("...")) {
|
|
3522
|
-
const varargType2 = this.parseType();
|
|
3523
|
-
return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
|
|
3524
|
-
}
|
|
3525
|
-
this.expectPunctuator("(");
|
|
3526
|
-
const types = [];
|
|
3527
|
-
let hasVarargs = false;
|
|
3528
|
-
let varargType;
|
|
3529
|
-
if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
|
|
3530
|
-
while (true) {
|
|
3531
|
-
if (this.matchOperator("...")) {
|
|
3532
|
-
hasVarargs = true;
|
|
3533
|
-
varargType = this.parseType();
|
|
3534
|
-
break;
|
|
3535
|
-
}
|
|
3536
|
-
types.push(this.parseType());
|
|
3537
|
-
if (this.matchPunctuator(",")) continue;
|
|
3538
|
-
break;
|
|
3539
|
-
}
|
|
3540
|
-
}
|
|
3541
|
-
this.expectPunctuator(")");
|
|
3542
|
-
return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
|
|
3543
|
-
}
|
|
3544
3635
|
parseGenericTypeParameterList() {
|
|
3545
3636
|
const list = [];
|
|
3546
3637
|
this.expectOperator("<");
|
|
3547
3638
|
while (true) {
|
|
3548
3639
|
const isConst = this.matchKeyword("const");
|
|
3549
3640
|
const nameTok = this.expectIdentifier();
|
|
3550
|
-
|
|
3551
|
-
if (
|
|
3552
|
-
|
|
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'");
|
|
3553
3644
|
}
|
|
3554
3645
|
let constraint;
|
|
3555
3646
|
if (this.checkIdentifierValue("extends")) {
|
|
@@ -3558,17 +3649,13 @@ var Parser = class {
|
|
|
3558
3649
|
}
|
|
3559
3650
|
let def;
|
|
3560
3651
|
if (this.matchOperator("=")) {
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
} else {
|
|
3564
|
-
def = this.parseType();
|
|
3565
|
-
}
|
|
3652
|
+
this.rejectPackReference();
|
|
3653
|
+
def = this.parseType();
|
|
3566
3654
|
}
|
|
3567
3655
|
list.push({
|
|
3568
3656
|
type: "GenericTypeParameter",
|
|
3569
3657
|
name: nameTok.value,
|
|
3570
3658
|
id: tokenIdentifier(nameTok),
|
|
3571
|
-
isPack,
|
|
3572
3659
|
isConst: isConst || void 0,
|
|
3573
3660
|
constraint,
|
|
3574
3661
|
default: def,
|
|
@@ -3659,7 +3746,7 @@ function childScope(parent) {
|
|
|
3659
3746
|
var Analyzer = class {
|
|
3660
3747
|
constructor(options) {
|
|
3661
3748
|
this.options = options;
|
|
3662
|
-
for (const name of options.builtinGlobals ?? []) {
|
|
3749
|
+
for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
|
|
3663
3750
|
const id = this.getOrCreateGlobalBinding(name);
|
|
3664
3751
|
this.bindings.get(id).isBuiltin = true;
|
|
3665
3752
|
}
|
|
@@ -4061,14 +4148,14 @@ var Analyzer = class {
|
|
|
4061
4148
|
return;
|
|
4062
4149
|
}
|
|
4063
4150
|
case "GenericForStatement": {
|
|
4064
|
-
|
|
4151
|
+
this.visitExpression(stmt.iterator, scope);
|
|
4065
4152
|
const bodyScope = childScope(scope);
|
|
4066
|
-
|
|
4153
|
+
this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
|
|
4067
4154
|
this.visitBlock(stmt.body, bodyScope);
|
|
4068
4155
|
return;
|
|
4069
4156
|
}
|
|
4070
4157
|
case "ReturnStatement":
|
|
4071
|
-
|
|
4158
|
+
if (stmt.argument) this.visitExpression(stmt.argument, scope);
|
|
4072
4159
|
return;
|
|
4073
4160
|
case "BreakStatement":
|
|
4074
4161
|
case "ContinueStatement":
|
|
@@ -4136,7 +4223,6 @@ var Analyzer = class {
|
|
|
4136
4223
|
this.declare(fnScope, param.name, kind, param);
|
|
4137
4224
|
}
|
|
4138
4225
|
});
|
|
4139
|
-
this.visitType(func.varargTypeAnnotation, fnScope);
|
|
4140
4226
|
this.visitType(func.returnType, fnScope);
|
|
4141
4227
|
this.functionDepth++;
|
|
4142
4228
|
try {
|
|
@@ -4164,6 +4250,7 @@ var Analyzer = class {
|
|
|
4164
4250
|
this.visitGenerics(node.typeParams, scope);
|
|
4165
4251
|
if (node.superclass) this.reference(outer, node.superclass);
|
|
4166
4252
|
for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
|
|
4253
|
+
for (const shape of node.implements ?? []) this.visitType(shape, scope);
|
|
4167
4254
|
for (const member of node.members) {
|
|
4168
4255
|
switch (member.type) {
|
|
4169
4256
|
case "ClassField":
|
|
@@ -4223,7 +4310,6 @@ var Analyzer = class {
|
|
|
4223
4310
|
case "BooleanLiteral":
|
|
4224
4311
|
case "NumberLiteral":
|
|
4225
4312
|
case "StringLiteral":
|
|
4226
|
-
case "VarargExpression":
|
|
4227
4313
|
case "ErrorExpression":
|
|
4228
4314
|
return;
|
|
4229
4315
|
case "InterpolatedStringExpression":
|
|
@@ -4263,11 +4349,6 @@ var Analyzer = class {
|
|
|
4263
4349
|
this.visitExpression(expr.callee, scope);
|
|
4264
4350
|
for (const arg of expr.arguments) this.visitExpression(arg, scope);
|
|
4265
4351
|
return;
|
|
4266
|
-
case "NewExpression":
|
|
4267
|
-
this.visitExpression(expr.callee, scope);
|
|
4268
|
-
for (const argument of expr.arguments) this.visitExpression(argument, scope);
|
|
4269
|
-
for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
|
|
4270
|
-
return;
|
|
4271
4352
|
case "SuperExpression":
|
|
4272
4353
|
return;
|
|
4273
4354
|
case "SpreadElement":
|
|
@@ -4321,6 +4402,7 @@ var Analyzer = class {
|
|
|
4321
4402
|
}
|
|
4322
4403
|
}
|
|
4323
4404
|
};
|
|
4405
|
+
var LANGUAGE_GLOBALS = ["scriptArgs"];
|
|
4324
4406
|
function analyzeScopes(program, options = {}) {
|
|
4325
4407
|
return new Analyzer(options).run(program);
|
|
4326
4408
|
}
|
|
@@ -4339,8 +4421,8 @@ type Exclude<T, U> = T - U
|
|
|
4339
4421
|
type Extract<T, U> = T extends U ? T : never
|
|
4340
4422
|
type NonNullable<T> = T - nil
|
|
4341
4423
|
|
|
4342
|
-
type ReturnType<T> = T extends (...unknown) => infer R ? R : never
|
|
4343
|
-
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
|
|
4344
4426
|
|
|
4345
4427
|
type Partial<T> = { [K in keyof T]?: T[K] }
|
|
4346
4428
|
type Required<T> = { [K in keyof T]-?: T[K] }
|
|
@@ -4425,8 +4507,11 @@ function literal(value) {
|
|
|
4425
4507
|
function arrayOf(element) {
|
|
4426
4508
|
return { kind: "array", element };
|
|
4427
4509
|
}
|
|
4428
|
-
function tuple(elements,
|
|
4429
|
-
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;
|
|
4430
4515
|
}
|
|
4431
4516
|
function objectType(entries, indexer, frozen) {
|
|
4432
4517
|
return { kind: "object", properties: new Map(entries), indexer, frozen };
|
|
@@ -4450,7 +4535,7 @@ function substitute(t, subst) {
|
|
|
4450
4535
|
case "array":
|
|
4451
4536
|
return arrayOf(substitute(t.element, subst));
|
|
4452
4537
|
case "tuple":
|
|
4453
|
-
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));
|
|
4454
4539
|
case "object": {
|
|
4455
4540
|
const entries = [];
|
|
4456
4541
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
|
|
@@ -4474,14 +4559,20 @@ function substitute(t, subst) {
|
|
|
4474
4559
|
const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
|
|
4475
4560
|
let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
|
|
4476
4561
|
let varargs = t.varargs && substitute(t.varargs, inner);
|
|
4477
|
-
|
|
4562
|
+
let restIsWhole = t.restIsWhole;
|
|
4563
|
+
if (restIsWhole && varargs?.kind === "tuple") {
|
|
4478
4564
|
params = [...params, ...varargs.elements.map((type) => ({ type }))];
|
|
4479
|
-
varargs =
|
|
4565
|
+
varargs = varargs.rest;
|
|
4566
|
+
restIsWhole = void 0;
|
|
4567
|
+
} else if (restIsWhole && varargs?.kind === "array") {
|
|
4568
|
+
varargs = varargs.element;
|
|
4569
|
+
restIsWhole = void 0;
|
|
4480
4570
|
}
|
|
4481
4571
|
return {
|
|
4482
4572
|
kind: "function",
|
|
4483
4573
|
params,
|
|
4484
4574
|
varargs,
|
|
4575
|
+
...restIsWhole ? { restIsWhole } : {},
|
|
4485
4576
|
returns: substitute(t.returns, inner),
|
|
4486
4577
|
typeParams: t.typeParams,
|
|
4487
4578
|
typeParamDefaults: t.typeParamDefaults,
|
|
@@ -4564,11 +4655,19 @@ function unify(param, arg, vars, out) {
|
|
|
4564
4655
|
switch (param.kind) {
|
|
4565
4656
|
case "array":
|
|
4566
4657
|
if (arg.kind === "array") unify(param.element, arg.element, vars, out);
|
|
4567
|
-
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);
|
|
4568
4659
|
return;
|
|
4569
4660
|
case "tuple":
|
|
4570
|
-
if (arg.kind === "tuple")
|
|
4571
|
-
|
|
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
|
+
}
|
|
4572
4671
|
return;
|
|
4573
4672
|
case "function":
|
|
4574
4673
|
if (arg.kind === "function") {
|
|
@@ -4670,7 +4769,7 @@ function widen(t) {
|
|
|
4670
4769
|
case "array":
|
|
4671
4770
|
return arrayOf(widen(t.element));
|
|
4672
4771
|
case "tuple":
|
|
4673
|
-
return tuple(t.elements.map(widen), t.
|
|
4772
|
+
return tuple(t.elements.map(widen), t.rest && widen(t.rest));
|
|
4674
4773
|
case "object": {
|
|
4675
4774
|
if (t.frozen || t.class) return t;
|
|
4676
4775
|
const entries = [];
|
|
@@ -4770,13 +4869,18 @@ function isAssignableInner(a, b) {
|
|
|
4770
4869
|
if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
|
|
4771
4870
|
if (a.kind === "array") {
|
|
4772
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);
|
|
4773
4873
|
return false;
|
|
4774
4874
|
}
|
|
4775
4875
|
if (a.kind === "tuple") {
|
|
4776
4876
|
if (b.kind === "tuple") {
|
|
4777
|
-
|
|
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));
|
|
4778
4882
|
}
|
|
4779
|
-
if (b.kind === "array") return a.
|
|
4883
|
+
if (b.kind === "array") return tupleMembers(a).every((t) => isAssignable(t, b.element));
|
|
4780
4884
|
return false;
|
|
4781
4885
|
}
|
|
4782
4886
|
if (a.kind === "object") {
|
|
@@ -4825,7 +4929,7 @@ function sameRefTarget(a, b) {
|
|
|
4825
4929
|
return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
|
|
4826
4930
|
}
|
|
4827
4931
|
function isNoValue(t) {
|
|
4828
|
-
return t.kind === "tuple" && t.elements.length === 0;
|
|
4932
|
+
return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
|
|
4829
4933
|
}
|
|
4830
4934
|
function equalTypes(a, b) {
|
|
4831
4935
|
return formatType(a) === formatType(b);
|
|
@@ -4932,7 +5036,7 @@ function containsFreeTypeParam(t, seen, bound) {
|
|
|
4932
5036
|
case "array":
|
|
4933
5037
|
return containsTypeParam(t.element, seen, bound);
|
|
4934
5038
|
case "tuple":
|
|
4935
|
-
return t.
|
|
5039
|
+
return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
|
|
4936
5040
|
case "union":
|
|
4937
5041
|
case "intersection":
|
|
4938
5042
|
return t.types.some((m) => containsTypeParam(m, seen, bound));
|
|
@@ -4977,15 +5081,19 @@ function matchInfer(arg, pattern, out) {
|
|
|
4977
5081
|
switch (pattern.kind) {
|
|
4978
5082
|
case "array":
|
|
4979
5083
|
if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
|
|
4980
|
-
if (arg.kind === "tuple") return matchInfer(union(arg
|
|
5084
|
+
if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
|
|
4981
5085
|
return false;
|
|
4982
5086
|
case "tuple":
|
|
4983
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;
|
|
4984
5089
|
return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
|
|
4985
5090
|
case "function": {
|
|
4986
5091
|
if (arg.kind !== "function") return false;
|
|
4987
5092
|
if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
|
|
4988
|
-
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
|
+
));
|
|
4989
5097
|
} else {
|
|
4990
5098
|
for (let i = 0; i < pattern.params.length; i++) {
|
|
4991
5099
|
if (!arg.params[i]) return false;
|
|
@@ -5059,7 +5167,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
|
|
|
5059
5167
|
collectTypeParams(t.element, out, seen);
|
|
5060
5168
|
return;
|
|
5061
5169
|
case "tuple":
|
|
5062
|
-
for (const e of t
|
|
5170
|
+
for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
|
|
5063
5171
|
return;
|
|
5064
5172
|
case "union":
|
|
5065
5173
|
case "intersection":
|
|
@@ -5106,9 +5214,8 @@ function formatTypeUncached(t) {
|
|
|
5106
5214
|
case "array":
|
|
5107
5215
|
return `${formatAtom(t.element)}[]`;
|
|
5108
5216
|
case "tuple": {
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
return t.isPack ? `(${inner})` : `[${inner}]`;
|
|
5217
|
+
const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
|
|
5218
|
+
return `[${inner.join(", ")}]`;
|
|
5112
5219
|
}
|
|
5113
5220
|
case "object": {
|
|
5114
5221
|
if (t.name) {
|
|
@@ -5132,7 +5239,7 @@ function formatTypeUncached(t) {
|
|
|
5132
5239
|
return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
|
|
5133
5240
|
}).join(", ")}>` : "";
|
|
5134
5241
|
const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
|
|
5135
|
-
if (t.varargs) ps.push(
|
|
5242
|
+
if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
|
|
5136
5243
|
return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
|
|
5137
5244
|
}
|
|
5138
5245
|
case "typeParam":
|
|
@@ -5336,6 +5443,11 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5336
5443
|
}
|
|
5337
5444
|
return { values, types: exportedTypes, default: defaultType };
|
|
5338
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
|
+
}
|
|
5339
5451
|
function bindKey(id) {
|
|
5340
5452
|
return `$${id}`;
|
|
5341
5453
|
}
|
|
@@ -5353,9 +5465,6 @@ function mergeEnv(a, b, base) {
|
|
|
5353
5465
|
function mergeIndexer(a, b) {
|
|
5354
5466
|
return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
|
|
5355
5467
|
}
|
|
5356
|
-
function producesMultipleValues(e) {
|
|
5357
|
-
return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
|
|
5358
|
-
}
|
|
5359
5468
|
function isFreshLiteralExpr(e) {
|
|
5360
5469
|
if (!e) return false;
|
|
5361
5470
|
switch (e.type) {
|
|
@@ -5392,11 +5501,11 @@ function collectInferNames(node) {
|
|
|
5392
5501
|
walk(n.element);
|
|
5393
5502
|
return;
|
|
5394
5503
|
case "ParenthesizedTypeNode":
|
|
5395
|
-
case "VariadicTypeNode":
|
|
5396
5504
|
walk(n.typeAnnotation);
|
|
5397
5505
|
return;
|
|
5398
5506
|
case "TupleTypeNode":
|
|
5399
5507
|
n.elements.forEach(walk);
|
|
5508
|
+
walk(n.rest);
|
|
5400
5509
|
return;
|
|
5401
5510
|
case "UnionTypeNode":
|
|
5402
5511
|
case "IntersectionTypeNode":
|
|
@@ -5414,7 +5523,6 @@ function collectInferNames(node) {
|
|
|
5414
5523
|
return;
|
|
5415
5524
|
case "FunctionTypeNode":
|
|
5416
5525
|
n.params.forEach((pp) => walk(pp.typeAnnotation));
|
|
5417
|
-
walk(n.varargType);
|
|
5418
5526
|
walk(n.returnType);
|
|
5419
5527
|
return;
|
|
5420
5528
|
case "TableTypeNode":
|
|
@@ -5459,6 +5567,75 @@ function callsSuper(block) {
|
|
|
5459
5567
|
});
|
|
5460
5568
|
return found;
|
|
5461
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
|
+
};
|
|
5462
5639
|
function assignedFields(block) {
|
|
5463
5640
|
const names = /* @__PURE__ */ new Set();
|
|
5464
5641
|
walkNodes(block, (node) => {
|
|
@@ -5484,7 +5661,7 @@ function walkNodes(root, visit) {
|
|
|
5484
5661
|
if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
|
|
5485
5662
|
}
|
|
5486
5663
|
}
|
|
5487
|
-
var
|
|
5664
|
+
var LazyMap = class extends Map {
|
|
5488
5665
|
pending = /* @__PURE__ */ new Map();
|
|
5489
5666
|
defer(name, resolve5) {
|
|
5490
5667
|
super.delete(name);
|
|
@@ -5599,14 +5776,14 @@ var TypeAnalyzer = class {
|
|
|
5599
5776
|
scopes;
|
|
5600
5777
|
options;
|
|
5601
5778
|
typeOf = /* @__PURE__ */ new Map();
|
|
5602
|
-
bindingType =
|
|
5779
|
+
bindingType = new LazyMap();
|
|
5603
5780
|
narrowedTypeOf = /* @__PURE__ */ new Map();
|
|
5604
5781
|
typeOfTypeNode = /* @__PURE__ */ new Map();
|
|
5605
5782
|
expectedTypeOf = /* @__PURE__ */ new Map();
|
|
5606
|
-
|
|
5783
|
+
loops = /* @__PURE__ */ new Map();
|
|
5607
5784
|
/** Public: each alias resolved once (generic aliases keep their params as
|
|
5608
5785
|
* `typeParam` nodes in the body). */
|
|
5609
|
-
aliases = new
|
|
5786
|
+
aliases = new LazyMap();
|
|
5610
5787
|
/** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
|
|
5611
5788
|
aliasDefs = /* @__PURE__ */ new Map();
|
|
5612
5789
|
/** Put on every ref to one of `aliasDefs`, so a module that imports the
|
|
@@ -5620,8 +5797,12 @@ var TypeAnalyzer = class {
|
|
|
5620
5797
|
/** Generic parameters currently in lexical scope (alias body / generic fn),
|
|
5621
5798
|
* with their `extends` constraints resolved. */
|
|
5622
5799
|
typeParamScope = [];
|
|
5623
|
-
/** Global types contributed by `declare` statements (libs, then this program).
|
|
5624
|
-
|
|
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();
|
|
5625
5806
|
/** Declaration node -> binding, built once so `bindingIdByName` is O(1)
|
|
5626
5807
|
* instead of a scan of every binding per declaration site. */
|
|
5627
5808
|
bindingByDecl = /* @__PURE__ */ new Map();
|
|
@@ -5666,8 +5847,11 @@ var TypeAnalyzer = class {
|
|
|
5666
5847
|
this.indexDeclarations();
|
|
5667
5848
|
for (const [name, id] of this.scopes.globalsByName) {
|
|
5668
5849
|
if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
|
|
5669
|
-
const
|
|
5670
|
-
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);
|
|
5671
5855
|
}
|
|
5672
5856
|
setAliasExpander((t) => this.expand(t));
|
|
5673
5857
|
setDeferredBound((t) => this.deferredBound(t));
|
|
@@ -5687,7 +5871,7 @@ var TypeAnalyzer = class {
|
|
|
5687
5871
|
typeOfTypeNode: this.typeOfTypeNode,
|
|
5688
5872
|
expectedTypeOf: this.expectedTypeOf,
|
|
5689
5873
|
aliases: this.resolveDeferredAliases(),
|
|
5690
|
-
|
|
5874
|
+
loops: this.loops,
|
|
5691
5875
|
diagnostics: this.diagnostics
|
|
5692
5876
|
};
|
|
5693
5877
|
}
|
|
@@ -5896,13 +6080,18 @@ var TypeAnalyzer = class {
|
|
|
5896
6080
|
classIdentityCount = 0;
|
|
5897
6081
|
/** The class whose members are being read, so `super` knows its base. */
|
|
5898
6082
|
currentClass;
|
|
6083
|
+
/** Every class whose body the checker is inside, innermost last — what
|
|
6084
|
+
* decides whether a `private` member may be reached. */
|
|
6085
|
+
enclosingClasses = [];
|
|
5899
6086
|
withClass(stmt, fn2) {
|
|
5900
6087
|
const previous = this.currentClass;
|
|
5901
6088
|
this.currentClass = stmt;
|
|
6089
|
+
this.enclosingClasses.push(stmt);
|
|
5902
6090
|
try {
|
|
5903
6091
|
return fn2();
|
|
5904
6092
|
} finally {
|
|
5905
6093
|
this.currentClass = previous;
|
|
6094
|
+
this.enclosingClasses.pop();
|
|
5906
6095
|
}
|
|
5907
6096
|
}
|
|
5908
6097
|
/** What the class is known by. A declaration is known by its name, the way
|
|
@@ -6034,28 +6223,55 @@ var TypeAnalyzer = class {
|
|
|
6034
6223
|
type.name = `typeof ${this.className(stmt)}`;
|
|
6035
6224
|
const shape = this.shapeOf(stmt);
|
|
6036
6225
|
const local = this.superDecl(stmt);
|
|
6037
|
-
const parent = local ? this.classValueType(local) : stmt.superclass ? this.
|
|
6226
|
+
const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
|
|
6038
6227
|
if (parent?.kind === "object") {
|
|
6039
6228
|
for (const [key, property] of parent.properties) {
|
|
6040
6229
|
if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
|
|
6041
6230
|
}
|
|
6042
6231
|
}
|
|
6043
6232
|
for (const [key, property] of shape.statics) type.properties.set(key, property);
|
|
6044
|
-
|
|
6233
|
+
if (shape.filling) {
|
|
6234
|
+
(shape.filled ??= []).push(() => {
|
|
6235
|
+
for (const [key, property] of shape.statics) type.properties.set(key, property);
|
|
6236
|
+
});
|
|
6237
|
+
}
|
|
6045
6238
|
const params = this.classTypeParams(stmt);
|
|
6046
|
-
|
|
6047
|
-
|
|
6048
|
-
constructor?.params.filter((p) => p.name !== "this") ?? [],
|
|
6049
|
-
this.selfTypeOf(stmt),
|
|
6050
|
-
constructor?.varargs,
|
|
6051
|
-
params.map((p) => p.name)
|
|
6052
|
-
),
|
|
6239
|
+
let settled;
|
|
6240
|
+
const construct = {
|
|
6053
6241
|
optional: false,
|
|
6054
|
-
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
|
+
}
|
|
6055
6261
|
});
|
|
6262
|
+
type.properties.set("new", construct);
|
|
6056
6263
|
type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
|
|
6057
6264
|
return type;
|
|
6058
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
|
+
}
|
|
6059
6275
|
/** The value side of a class named by a binding rather than by a
|
|
6060
6276
|
* declaration in this file — an imported one. */
|
|
6061
6277
|
classStaticsByNameType(name) {
|
|
@@ -6116,17 +6332,26 @@ var TypeAnalyzer = class {
|
|
|
6116
6332
|
} finally {
|
|
6117
6333
|
this.emitDiagnostics = wasEmitting;
|
|
6118
6334
|
shape.filling = false;
|
|
6335
|
+
for (const then of shape.filled ?? []) then();
|
|
6336
|
+
shape.filled = void 0;
|
|
6119
6337
|
}
|
|
6120
6338
|
return shape;
|
|
6121
6339
|
}
|
|
6122
6340
|
fillShape(stmt, shape) {
|
|
6341
|
+
const className = this.className(stmt);
|
|
6123
6342
|
const put = (isStatic, name, property) => {
|
|
6124
|
-
(
|
|
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;
|
|
6345
|
+
(isStatic ? shape.statics : shape.instance).set(name, marked);
|
|
6125
6346
|
};
|
|
6126
6347
|
for (const member of stmt.members) {
|
|
6127
6348
|
if (member.type !== "ClassField") continue;
|
|
6128
6349
|
const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
|
|
6129
|
-
put(member.isStatic, member.name.name, {
|
|
6350
|
+
put(member.isStatic, member.name.name, {
|
|
6351
|
+
type,
|
|
6352
|
+
optional: false,
|
|
6353
|
+
...member.isReadonly ? { readonly: true } : {}
|
|
6354
|
+
});
|
|
6130
6355
|
}
|
|
6131
6356
|
for (const member of stmt.members) {
|
|
6132
6357
|
switch (member.type) {
|
|
@@ -6135,7 +6360,11 @@ var TypeAnalyzer = class {
|
|
|
6135
6360
|
case "ClassMethod": {
|
|
6136
6361
|
this.paramsFromSignatures(member.func, member.signatures);
|
|
6137
6362
|
const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
|
|
6138
|
-
put(member.isStatic, member.name.name, {
|
|
6363
|
+
put(member.isStatic, member.name.name, {
|
|
6364
|
+
type,
|
|
6365
|
+
optional: false,
|
|
6366
|
+
...member.isAbstract ? { abstract: true } : {}
|
|
6367
|
+
});
|
|
6139
6368
|
break;
|
|
6140
6369
|
}
|
|
6141
6370
|
case "ClassAccessor": {
|
|
@@ -6191,7 +6420,14 @@ var TypeAnalyzer = class {
|
|
|
6191
6420
|
for (const signature of member.signatures ?? []) {
|
|
6192
6421
|
this.checkParamOrder(signature.params, member);
|
|
6193
6422
|
}
|
|
6194
|
-
|
|
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
|
+
}
|
|
6195
6431
|
}
|
|
6196
6432
|
});
|
|
6197
6433
|
}));
|
|
@@ -6238,13 +6474,148 @@ var TypeAnalyzer = class {
|
|
|
6238
6474
|
if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
|
|
6239
6475
|
report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
|
|
6240
6476
|
}
|
|
6241
|
-
|
|
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();
|
|
6242
6482
|
for (const member of stmt.members) {
|
|
6243
6483
|
if (member.type !== "ClassField" || member.isStatic || member.init) continue;
|
|
6244
|
-
if (assigned.has(member.name.name)) continue;
|
|
6484
|
+
if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
|
|
6245
6485
|
const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
|
|
6246
6486
|
if (isAssignable(nilType, type)) continue;
|
|
6247
|
-
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
|
+
}
|
|
6248
6619
|
}
|
|
6249
6620
|
}
|
|
6250
6621
|
/** `extends` must name a class, and the chain must end. */
|
|
@@ -6302,19 +6673,42 @@ var TypeAnalyzer = class {
|
|
|
6302
6673
|
harvestDeclares(block, own = false) {
|
|
6303
6674
|
for (const stmt of block.statements) {
|
|
6304
6675
|
if (stmt.type !== "DeclareStatement") continue;
|
|
6305
|
-
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))) {
|
|
6306
6686
|
this.deferredDeclares.set(stmt.name, stmt);
|
|
6307
6687
|
continue;
|
|
6308
6688
|
}
|
|
6309
|
-
|
|
6310
|
-
|
|
6311
|
-
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
6315
|
-
|
|
6689
|
+
this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
|
|
6690
|
+
}
|
|
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);
|
|
6316
6703
|
}
|
|
6317
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
|
+
}
|
|
6318
6712
|
resolveAllAliases() {
|
|
6319
6713
|
for (const [name, def] of this.aliasDefs) {
|
|
6320
6714
|
if (def.class && !this.program.body.statements.includes(def.class)) {
|
|
@@ -6360,11 +6754,10 @@ var TypeAnalyzer = class {
|
|
|
6360
6754
|
}
|
|
6361
6755
|
imported;
|
|
6362
6756
|
/** What a `return` gives, against what the function declared. */
|
|
6363
|
-
checkReturn(stmt, declared,
|
|
6757
|
+
checkReturn(stmt, declared, actual, env) {
|
|
6364
6758
|
if (!declared || !this.emitDiagnostics) return;
|
|
6365
6759
|
if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
|
|
6366
|
-
const
|
|
6367
|
-
const source = stmt.arguments.length === 1 ? sources[0] : void 0;
|
|
6760
|
+
const source = stmt.argument;
|
|
6368
6761
|
const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
|
|
6369
6762
|
if (fits) return;
|
|
6370
6763
|
this.diagnostics.push({
|
|
@@ -6512,21 +6905,12 @@ var TypeAnalyzer = class {
|
|
|
6512
6905
|
this.instantiationDepth--;
|
|
6513
6906
|
}
|
|
6514
6907
|
}
|
|
6515
|
-
/** Pair written type arguments with the parameters they instantiate.
|
|
6516
|
-
*
|
|
6517
|
-
* one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
|
|
6518
|
-
* and `Signal<()>` to the empty pack. Left out, a parameter takes its
|
|
6519
|
-
* 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`. */
|
|
6520
6910
|
bindTypeArguments(params, args) {
|
|
6521
6911
|
const subst = /* @__PURE__ */ new Map();
|
|
6522
6912
|
params.forEach((p, i) => {
|
|
6523
|
-
|
|
6524
|
-
if (p.isPack && i < args.length) {
|
|
6525
|
-
const rest = args.slice(i);
|
|
6526
|
-
const single = rest.length === 1 ? rest[0] : void 0;
|
|
6527
|
-
arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
|
|
6528
|
-
}
|
|
6529
|
-
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));
|
|
6530
6914
|
});
|
|
6531
6915
|
return subst;
|
|
6532
6916
|
}
|
|
@@ -6618,8 +7002,13 @@ var TypeAnalyzer = class {
|
|
|
6618
7002
|
return literal(node.value);
|
|
6619
7003
|
case "ArrayTypeNode":
|
|
6620
7004
|
return arrayOf(this.resolveType(node.element));
|
|
6621
|
-
case "TupleTypeNode":
|
|
6622
|
-
|
|
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
|
+
}
|
|
6623
7012
|
case "UnionTypeNode":
|
|
6624
7013
|
return union(node.types.map((t) => this.resolveType(t)));
|
|
6625
7014
|
case "IntersectionTypeNode":
|
|
@@ -6655,15 +7044,13 @@ var TypeAnalyzer = class {
|
|
|
6655
7044
|
type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
|
|
6656
7045
|
optional: p.optional
|
|
6657
7046
|
}));
|
|
6658
|
-
|
|
6659
|
-
const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
|
|
6660
|
-
return this.withTypeParamDefaults(fn(
|
|
7047
|
+
return this.withTypeParamDefaults(this.withRest(fn(
|
|
6661
7048
|
params,
|
|
6662
7049
|
this.resolveType(node.returnType),
|
|
6663
|
-
|
|
7050
|
+
void 0,
|
|
6664
7051
|
names,
|
|
6665
7052
|
this.resolvePredicate(node.predicate, params)
|
|
6666
|
-
), node.generics);
|
|
7053
|
+
), node.params), node.generics);
|
|
6667
7054
|
});
|
|
6668
7055
|
}
|
|
6669
7056
|
case "TypeofTypeNode": {
|
|
@@ -6727,13 +7114,6 @@ var TypeAnalyzer = class {
|
|
|
6727
7114
|
})
|
|
6728
7115
|
);
|
|
6729
7116
|
}
|
|
6730
|
-
case "VariadicTypeNode":
|
|
6731
|
-
return this.resolveType(node.typeAnnotation);
|
|
6732
|
-
case "TypePackNode": {
|
|
6733
|
-
if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
|
|
6734
|
-
if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
|
|
6735
|
-
return tuple(node.types.map((t) => this.resolveType(t)), true);
|
|
6736
|
-
}
|
|
6737
7117
|
}
|
|
6738
7118
|
}
|
|
6739
7119
|
// --------------------------------------------------------
|
|
@@ -6799,15 +7179,18 @@ var TypeAnalyzer = class {
|
|
|
6799
7179
|
case "array":
|
|
6800
7180
|
return arrayOf(this.reduceType(t.element));
|
|
6801
7181
|
case "tuple":
|
|
6802
|
-
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));
|
|
6803
7183
|
case "function":
|
|
6804
|
-
return
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
|
|
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
|
+
};
|
|
6811
7194
|
case "object": {
|
|
6812
7195
|
if (t.class) return t;
|
|
6813
7196
|
const entries = [];
|
|
@@ -7022,7 +7405,8 @@ var TypeAnalyzer = class {
|
|
|
7022
7405
|
this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
|
|
7023
7406
|
}
|
|
7024
7407
|
});
|
|
7025
|
-
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);
|
|
7026
7410
|
stmt.names.forEach((target, i) => {
|
|
7027
7411
|
const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
|
|
7028
7412
|
const source = sources[i];
|
|
@@ -7111,7 +7495,8 @@ var TypeAnalyzer = class {
|
|
|
7111
7495
|
if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
|
|
7112
7496
|
}
|
|
7113
7497
|
});
|
|
7114
|
-
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);
|
|
7115
7500
|
stmt.targets.forEach((target, i) => {
|
|
7116
7501
|
const vt = valueTypes[i] ?? unknownType;
|
|
7117
7502
|
const source = sources[i];
|
|
@@ -7198,23 +7583,26 @@ var TypeAnalyzer = class {
|
|
|
7198
7583
|
return;
|
|
7199
7584
|
}
|
|
7200
7585
|
case "GenericForStatement": {
|
|
7201
|
-
const
|
|
7586
|
+
const sourceType = this.infer(stmt.iterator, env);
|
|
7202
7587
|
const bodyEnv = forkEnv(env);
|
|
7203
|
-
const rows =
|
|
7588
|
+
const rows = this.iterationRows(stmt.iterator, sourceType);
|
|
7204
7589
|
if (rows) {
|
|
7205
|
-
|
|
7206
|
-
|
|
7207
|
-
|
|
7208
|
-
|
|
7209
|
-
|
|
7210
|
-
|
|
7211
|
-
|
|
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 });
|
|
7212
7602
|
} else {
|
|
7213
|
-
const [
|
|
7214
|
-
stmt.
|
|
7215
|
-
|
|
7216
|
-
});
|
|
7217
|
-
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);
|
|
7218
7606
|
}
|
|
7219
7607
|
this.visitBlock(stmt.body, bodyEnv);
|
|
7220
7608
|
return;
|
|
@@ -7222,19 +7610,10 @@ var TypeAnalyzer = class {
|
|
|
7222
7610
|
case "ReturnStatement": {
|
|
7223
7611
|
const declared = this.declaredReturns[this.declaredReturns.length - 1];
|
|
7224
7612
|
const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
|
|
7225
|
-
if (wanted)
|
|
7226
|
-
|
|
7227
|
-
|
|
7228
|
-
|
|
7229
|
-
stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
|
|
7230
|
-
}
|
|
7231
|
-
}
|
|
7232
|
-
const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
|
|
7233
|
-
const { types, sources } = this.valueList(stmt.arguments, env, want);
|
|
7234
|
-
this.checkReturn(stmt, declared, types, sources, env);
|
|
7235
|
-
if (this.returnTypes) {
|
|
7236
|
-
this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
|
|
7237
|
-
}
|
|
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);
|
|
7238
7617
|
return;
|
|
7239
7618
|
}
|
|
7240
7619
|
case "ExportStatement":
|
|
@@ -7321,7 +7700,7 @@ var TypeAnalyzer = class {
|
|
|
7321
7700
|
* `sources` maps each produced value back to the expression it came from
|
|
7322
7701
|
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7323
7702
|
* caller can still do contextual typing against the written expression. */
|
|
7324
|
-
valueList(exprs, env
|
|
7703
|
+
valueList(exprs, env) {
|
|
7325
7704
|
const types = [];
|
|
7326
7705
|
const sources = [];
|
|
7327
7706
|
exprs.forEach((e, i) => {
|
|
@@ -7337,31 +7716,29 @@ var TypeAnalyzer = class {
|
|
|
7337
7716
|
}
|
|
7338
7717
|
return;
|
|
7339
7718
|
}
|
|
7340
|
-
|
|
7341
|
-
|
|
7342
|
-
sources.push(e);
|
|
7343
|
-
} while (last && types.length < want);
|
|
7344
|
-
return;
|
|
7345
|
-
}
|
|
7346
|
-
if (last && e.type === "VarargExpression") {
|
|
7347
|
-
do {
|
|
7348
|
-
types.push(t);
|
|
7349
|
-
sources.push(types.length - 1 === i ? e : void 0);
|
|
7350
|
-
} while (types.length < want);
|
|
7719
|
+
types.push(t);
|
|
7720
|
+
sources.push(e);
|
|
7351
7721
|
return;
|
|
7352
7722
|
}
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
types.push(el);
|
|
7356
|
-
sources.push(j === 0 ? e : void 0);
|
|
7357
|
-
});
|
|
7358
|
-
return;
|
|
7359
|
-
}
|
|
7360
|
-
types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
|
|
7723
|
+
void last;
|
|
7724
|
+
types.push(t);
|
|
7361
7725
|
sources.push(e);
|
|
7362
7726
|
});
|
|
7363
7727
|
return { types, sources };
|
|
7364
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
|
+
}
|
|
7365
7742
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7366
7743
|
* what it collected. */
|
|
7367
7744
|
withBreakScope(visit) {
|
|
@@ -7510,18 +7887,29 @@ var TypeAnalyzer = class {
|
|
|
7510
7887
|
* context. */
|
|
7511
7888
|
contextualArrays = /* @__PURE__ */ new WeakMap();
|
|
7512
7889
|
applyArrayContext(e, expected) {
|
|
7513
|
-
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];
|
|
7514
7896
|
if (!target) return;
|
|
7897
|
+
if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
|
|
7515
7898
|
if (!e.elements.length) {
|
|
7516
7899
|
this.contextualArrays.set(e, target);
|
|
7517
7900
|
return;
|
|
7518
7901
|
}
|
|
7519
7902
|
e.elements.forEach((element, i) => {
|
|
7520
7903
|
if (element.type === "SpreadElement") return;
|
|
7521
|
-
const
|
|
7904
|
+
const tupleTarget = target;
|
|
7905
|
+
const elementType = target.kind === "array" ? target.element : tupleTarget.elements[i] ?? tupleTarget.rest;
|
|
7522
7906
|
this.applyContext(element, elementType);
|
|
7523
7907
|
});
|
|
7524
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();
|
|
7525
7913
|
/** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
|
|
7526
7914
|
* value gets its property's type as context. */
|
|
7527
7915
|
applyTableContext(e, expected) {
|
|
@@ -7577,32 +7965,33 @@ var TypeAnalyzer = class {
|
|
|
7577
7965
|
}
|
|
7578
7966
|
return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
|
|
7579
7967
|
}
|
|
7580
|
-
/**
|
|
7581
|
-
*
|
|
7582
|
-
*
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
|
|
7586
|
-
|
|
7587
|
-
|
|
7588
|
-
const
|
|
7589
|
-
if (
|
|
7590
|
-
|
|
7591
|
-
|
|
7592
|
-
|
|
7593
|
-
|
|
7594
|
-
|
|
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
|
+
}
|
|
7595
7986
|
/** What each function body being walked declared it returns. */
|
|
7596
7987
|
declaredReturns = [];
|
|
7597
|
-
/** Run `body` with
|
|
7988
|
+
/** Run `body` with `return` as `func` declares it. */
|
|
7598
7989
|
withVarargs(func, body) {
|
|
7599
|
-
this.varargs.push(this.varargElement(func));
|
|
7600
7990
|
this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
|
|
7601
7991
|
this.contextualReturns.push(this.contextualReturnOf.get(func));
|
|
7602
7992
|
try {
|
|
7603
7993
|
return body();
|
|
7604
7994
|
} finally {
|
|
7605
|
-
this.varargs.pop();
|
|
7606
7995
|
this.declaredReturns.pop();
|
|
7607
7996
|
this.contextualReturns.pop();
|
|
7608
7997
|
}
|
|
@@ -7661,7 +8050,7 @@ var TypeAnalyzer = class {
|
|
|
7661
8050
|
if (type.kind !== "function") return type;
|
|
7662
8051
|
const defaults = {};
|
|
7663
8052
|
for (const generic of generics) {
|
|
7664
|
-
if (generic.default
|
|
8053
|
+
if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
|
|
7665
8054
|
}
|
|
7666
8055
|
return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
|
|
7667
8056
|
}
|
|
@@ -7680,7 +8069,10 @@ var TypeAnalyzer = class {
|
|
|
7680
8069
|
const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
|
|
7681
8070
|
unify(p.type, preserve ? arg : widen(arg), vars, subst);
|
|
7682
8071
|
});
|
|
7683
|
-
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) {
|
|
7684
8076
|
const keeps = keepsLiterals(f.varargs);
|
|
7685
8077
|
for (let i = f.params.length; i < argTypes.length; i++) {
|
|
7686
8078
|
const arg = argTypes[i];
|
|
@@ -7849,7 +8241,12 @@ var TypeAnalyzer = class {
|
|
|
7849
8241
|
const arg = argTypes[i];
|
|
7850
8242
|
const declared = declaredAt(i);
|
|
7851
8243
|
if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
|
|
7852
|
-
|
|
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
|
+
}
|
|
7853
8250
|
if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
|
|
7854
8251
|
if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
|
|
7855
8252
|
this.diagnostics.push({
|
|
@@ -7977,13 +8374,13 @@ var TypeAnalyzer = class {
|
|
|
7977
8374
|
type: this.paramType(p, /* @__PURE__ */ new Map()),
|
|
7978
8375
|
optional: p.optional || p.default !== void 0
|
|
7979
8376
|
}));
|
|
7980
|
-
return fn(
|
|
8377
|
+
return this.withRest(fn(
|
|
7981
8378
|
params,
|
|
7982
8379
|
sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
|
|
7983
|
-
|
|
8380
|
+
void 0,
|
|
7984
8381
|
names,
|
|
7985
8382
|
this.resolvePredicate(sig.predicate, params)
|
|
7986
|
-
);
|
|
8383
|
+
), sig.params);
|
|
7987
8384
|
}));
|
|
7988
8385
|
}
|
|
7989
8386
|
/** `...rest: T[]` holds every argument from its position on, so its type
|
|
@@ -7991,7 +8388,7 @@ var TypeAnalyzer = class {
|
|
|
7991
8388
|
checkRestType(p, node) {
|
|
7992
8389
|
if (!this.emitDiagnostics || !p.typeAnnotation) return;
|
|
7993
8390
|
const declared = this.resolveType(p.typeAnnotation);
|
|
7994
|
-
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;
|
|
7995
8392
|
this.diagnostics.push({
|
|
7996
8393
|
node,
|
|
7997
8394
|
message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
|
|
@@ -8047,13 +8444,13 @@ var TypeAnalyzer = class {
|
|
|
8047
8444
|
return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
|
|
8048
8445
|
}));
|
|
8049
8446
|
}
|
|
8050
|
-
return fn(
|
|
8447
|
+
return this.withRest(fn(
|
|
8051
8448
|
params,
|
|
8052
8449
|
returns,
|
|
8053
|
-
|
|
8450
|
+
void 0,
|
|
8054
8451
|
names,
|
|
8055
8452
|
this.resolvePredicate(func.predicate, params)
|
|
8056
|
-
);
|
|
8453
|
+
), func.params);
|
|
8057
8454
|
});
|
|
8058
8455
|
}
|
|
8059
8456
|
/** Where the return types of the function being walked are collected, so
|
|
@@ -8095,17 +8492,16 @@ var TypeAnalyzer = class {
|
|
|
8095
8492
|
this.preVisitDepth--;
|
|
8096
8493
|
}
|
|
8097
8494
|
}
|
|
8098
|
-
/** The `[key, value]` pairs
|
|
8099
|
-
*
|
|
8100
|
-
*
|
|
8101
|
-
* 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. */
|
|
8102
8498
|
iterationRows(iterNode, iterType) {
|
|
8103
8499
|
let source;
|
|
8104
8500
|
if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
|
|
8105
|
-
if (iterNode.callee.name !== "pairs"
|
|
8501
|
+
if (iterNode.callee.name !== "pairs") return void 0;
|
|
8106
8502
|
source = this.typeOf.get(iterNode.arguments[0]);
|
|
8107
8503
|
} else {
|
|
8108
|
-
|
|
8504
|
+
return void 0;
|
|
8109
8505
|
}
|
|
8110
8506
|
const t = source && this.expand(source);
|
|
8111
8507
|
if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
|
|
@@ -8246,54 +8642,69 @@ var TypeAnalyzer = class {
|
|
|
8246
8642
|
if (ids.length < 2) return;
|
|
8247
8643
|
this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
|
|
8248
8644
|
}
|
|
8249
|
-
/** `
|
|
8250
|
-
*
|
|
8251
|
-
*
|
|
8252
|
-
|
|
8253
|
-
|
|
8254
|
-
|
|
8255
|
-
|
|
8256
|
-
|
|
8257
|
-
|
|
8258
|
-
|
|
8259
|
-
|
|
8260
|
-
|
|
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") {
|
|
8261
8663
|
if (src.kind === "object") {
|
|
8262
|
-
return [
|
|
8664
|
+
return [tuple([
|
|
8263
8665
|
src.indexer?.key ?? stringType,
|
|
8264
|
-
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
|
|
8265
|
-
|
|
8266
|
-
];
|
|
8666
|
+
src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
|
|
8667
|
+
]), iteration];
|
|
8267
8668
|
}
|
|
8268
|
-
if (src.kind === "array") return [numberType, src.element,
|
|
8669
|
+
if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
|
|
8269
8670
|
}
|
|
8270
8671
|
}
|
|
8271
|
-
const
|
|
8272
|
-
|
|
8273
|
-
|
|
8274
|
-
const
|
|
8275
|
-
|
|
8276
|
-
return
|
|
8277
|
-
}
|
|
8278
|
-
const
|
|
8279
|
-
if (
|
|
8280
|
-
|
|
8281
|
-
|
|
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];
|
|
8282
8691
|
if (t.kind === "object") {
|
|
8283
|
-
|
|
8284
|
-
const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
|
|
8285
|
-
return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
|
|
8692
|
+
return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
|
|
8286
8693
|
}
|
|
8287
|
-
|
|
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];
|
|
8288
8701
|
}
|
|
8289
8702
|
inferReturnType(body, env) {
|
|
8290
8703
|
const returns = [];
|
|
8291
8704
|
const walk = (block) => {
|
|
8292
8705
|
for (const s of block.statements) {
|
|
8293
8706
|
if (s.type === "ReturnStatement") {
|
|
8294
|
-
|
|
8295
|
-
else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
|
|
8296
|
-
else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
|
|
8707
|
+
returns.push(s.argument ? this.infer(s.argument, env) : nilType);
|
|
8297
8708
|
} else if (s.type === "IfStatement") {
|
|
8298
8709
|
for (const c of s.clauses) walk(c.body);
|
|
8299
8710
|
if (s.alternate) walk(s.alternate);
|
|
@@ -8315,8 +8726,9 @@ var TypeAnalyzer = class {
|
|
|
8315
8726
|
* `infer` would have widened it to an array type). */
|
|
8316
8727
|
fitsAnnotation(init, declared, inferred, env) {
|
|
8317
8728
|
if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
|
|
8318
|
-
|
|
8319
|
-
|
|
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));
|
|
8320
8732
|
}
|
|
8321
8733
|
if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
|
|
8322
8734
|
if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
|
|
@@ -8373,7 +8785,7 @@ var TypeAnalyzer = class {
|
|
|
8373
8785
|
target.elements.forEach((el, i) => {
|
|
8374
8786
|
if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
|
|
8375
8787
|
});
|
|
8376
|
-
if (target.rest) this.reassignPattern(target.rest,
|
|
8788
|
+
if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
|
|
8377
8789
|
return;
|
|
8378
8790
|
}
|
|
8379
8791
|
}
|
|
@@ -8409,7 +8821,7 @@ var TypeAnalyzer = class {
|
|
|
8409
8821
|
if (!el) return;
|
|
8410
8822
|
this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
|
|
8411
8823
|
});
|
|
8412
|
-
if (target.rest) this.bindPattern(target.rest,
|
|
8824
|
+
if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
|
|
8413
8825
|
return;
|
|
8414
8826
|
}
|
|
8415
8827
|
}
|
|
@@ -8566,7 +8978,7 @@ var TypeAnalyzer = class {
|
|
|
8566
8978
|
* not one of them is a mistake worth reporting, rather than the nil Lua
|
|
8567
8979
|
* would hand back. */
|
|
8568
8980
|
checkStringMember(node, object, key) {
|
|
8569
|
-
if (!this.emitDiagnostics) return;
|
|
8981
|
+
if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
|
|
8570
8982
|
const parts = this.stringParts(object);
|
|
8571
8983
|
if (!parts) return;
|
|
8572
8984
|
const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
|
|
@@ -8578,6 +8990,170 @@ var TypeAnalyzer = class {
|
|
|
8578
8990
|
message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
|
|
8579
8991
|
});
|
|
8580
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();
|
|
9107
|
+
/** A `private` member read from outside the class that declared it —
|
|
9108
|
+
* a subclass counts as outside, as in TypeScript. */
|
|
9109
|
+
checkPrivateMember(node, object, name) {
|
|
9110
|
+
if (!this.emitDiagnostics) return;
|
|
9111
|
+
const raw = this.expand(object);
|
|
9112
|
+
for (const part of raw.kind === "union" ? raw.types : [raw]) {
|
|
9113
|
+
const t = this.deferredAccess(this.expand(part));
|
|
9114
|
+
if (t.kind !== "object") continue;
|
|
9115
|
+
const access = t.properties.get(name)?.private;
|
|
9116
|
+
if (!access || this.insideClass(access.owner)) continue;
|
|
9117
|
+
if (access.protected && this.insideSubclassOf(access.owner)) continue;
|
|
9118
|
+
this.diagnostics.push({
|
|
9119
|
+
node,
|
|
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}'`
|
|
9121
|
+
});
|
|
9122
|
+
return;
|
|
9123
|
+
}
|
|
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
|
+
}
|
|
9147
|
+
/** Is the code being checked written inside `owner`'s body — its own
|
|
9148
|
+
* methods, or a class or function nested in them? */
|
|
9149
|
+
insideClass(owner) {
|
|
9150
|
+
return this.enclosingClasses.includes(owner);
|
|
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
|
+
}
|
|
8581
9157
|
propertyType(raw, name) {
|
|
8582
9158
|
const t = this.deferredAccess(this.expand(raw));
|
|
8583
9159
|
if (t.kind === "object") {
|
|
@@ -8632,8 +9208,12 @@ var TypeAnalyzer = class {
|
|
|
8632
9208
|
}
|
|
8633
9209
|
if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
|
|
8634
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
|
+
}
|
|
8635
9215
|
}
|
|
8636
|
-
return unknownType;
|
|
9216
|
+
return index.kind === "any" ? anyType : unknownType;
|
|
8637
9217
|
}
|
|
8638
9218
|
/** What a deferred `T[K]` can be: every property its index could name.
|
|
8639
9219
|
* Reading a member of one, or calling it, sees that. */
|
|
@@ -8643,10 +9223,21 @@ var TypeAnalyzer = class {
|
|
|
8643
9223
|
if (containsTypeParam(index)) return unknownType;
|
|
8644
9224
|
return this.accessType(t.objectType, index);
|
|
8645
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
|
+
}
|
|
8646
9237
|
elementType(raw, index) {
|
|
8647
9238
|
const t = this.expand(raw);
|
|
8648
9239
|
if (t.kind === "array") return t.element;
|
|
8649
|
-
if (t.kind === "tuple") return t.elements[index] ?? unknownType;
|
|
9240
|
+
if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
|
|
8650
9241
|
if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
|
|
8651
9242
|
if (t.kind === "difference") return this.elementType(t.base, index);
|
|
8652
9243
|
if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
|
|
@@ -8676,8 +9267,6 @@ var TypeAnalyzer = class {
|
|
|
8676
9267
|
return stringType;
|
|
8677
9268
|
}
|
|
8678
9269
|
// `...` holds what the function declared it takes.
|
|
8679
|
-
case "VarargExpression":
|
|
8680
|
-
return this.varargs[this.varargs.length - 1] ?? anyType;
|
|
8681
9270
|
// `f(a, ...rest)` — every value the array holds, one after
|
|
8682
9271
|
// another. Each of them is an element, so that is what the
|
|
8683
9272
|
// parameters it fills are checked against.
|
|
@@ -8710,10 +9299,8 @@ var TypeAnalyzer = class {
|
|
|
8710
9299
|
this.checkParamOrder(expr.func.params, expr);
|
|
8711
9300
|
this.visitFunctionBody(expr.func, env);
|
|
8712
9301
|
return this.inferFunctionBody(expr.func, env);
|
|
8713
|
-
case "ParenthesizedExpression":
|
|
8714
|
-
|
|
8715
|
-
return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
|
|
8716
|
-
}
|
|
9302
|
+
case "ParenthesizedExpression":
|
|
9303
|
+
return this.infer(expr.expression, env);
|
|
8717
9304
|
case "TypeAssertionExpression": {
|
|
8718
9305
|
this.infer(expr.expression, env);
|
|
8719
9306
|
return this.resolveType(expr.typeAnnotation);
|
|
@@ -8746,7 +9333,7 @@ var TypeAnalyzer = class {
|
|
|
8746
9333
|
case "not":
|
|
8747
9334
|
return booleanType;
|
|
8748
9335
|
case "-":
|
|
8749
|
-
return this.operatorResult(expr, "-", arg, void 0) ??
|
|
9336
|
+
return this.operatorResult(expr, "-", arg, void 0) ?? this.arithmeticOn([arg]);
|
|
8750
9337
|
case "#":
|
|
8751
9338
|
return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
|
|
8752
9339
|
}
|
|
@@ -8764,7 +9351,11 @@ var TypeAnalyzer = class {
|
|
|
8764
9351
|
const left = this.infer(expr.left, env);
|
|
8765
9352
|
const { whenFalse } = this.narrowFromCondition(expr.left, env);
|
|
8766
9353
|
const right = this.infer(expr.right, whenFalse);
|
|
8767
|
-
|
|
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]);
|
|
8768
9359
|
}
|
|
8769
9360
|
const l = this.infer(expr.left, env);
|
|
8770
9361
|
const r = this.infer(expr.right, env);
|
|
@@ -8775,12 +9366,14 @@ var TypeAnalyzer = class {
|
|
|
8775
9366
|
switch (op) {
|
|
8776
9367
|
case "..":
|
|
8777
9368
|
return this.operatorResult(expr, op, l, r) ?? stringType;
|
|
8778
|
-
case "==":
|
|
8779
|
-
case "~=":
|
|
8780
9369
|
case "<":
|
|
8781
9370
|
case ">":
|
|
8782
9371
|
case "<=":
|
|
8783
9372
|
case ">=":
|
|
9373
|
+
this.checkComparison(expr, op, l, r);
|
|
9374
|
+
return booleanType;
|
|
9375
|
+
case "==":
|
|
9376
|
+
case "~=":
|
|
8784
9377
|
return booleanType;
|
|
8785
9378
|
case "+":
|
|
8786
9379
|
case "-":
|
|
@@ -8789,15 +9382,18 @@ var TypeAnalyzer = class {
|
|
|
8789
9382
|
case "//":
|
|
8790
9383
|
case "%":
|
|
8791
9384
|
case "^":
|
|
8792
|
-
return this.operatorResult(expr, op, l, r) ??
|
|
9385
|
+
return this.operatorResult(expr, op, l, r) ?? this.arithmeticOn([l, r]);
|
|
8793
9386
|
}
|
|
8794
9387
|
return union([l, r]);
|
|
8795
9388
|
}
|
|
8796
9389
|
case "MemberExpression": {
|
|
8797
9390
|
const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
|
|
9391
|
+
this.checkPrivateMember(expr.property, obj, expr.property.name);
|
|
9392
|
+
this.checkAbstractAccess(expr, obj);
|
|
8798
9393
|
const key = this.refKeyOf(expr);
|
|
8799
9394
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8800
9395
|
this.checkStringMember(expr, obj, literal(expr.property.name));
|
|
9396
|
+
if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
|
|
8801
9397
|
return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
|
|
8802
9398
|
}
|
|
8803
9399
|
case "IndexExpression": {
|
|
@@ -8806,6 +9402,7 @@ var TypeAnalyzer = class {
|
|
|
8806
9402
|
const key = this.refKeyOf(expr);
|
|
8807
9403
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8808
9404
|
this.checkStringMember(expr, obj, this.expand(idx));
|
|
9405
|
+
if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
|
|
8809
9406
|
const member = this.stringMember(obj, this.expand(idx));
|
|
8810
9407
|
if (member) return this.chainResult(expr, member, shortCircuits);
|
|
8811
9408
|
return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
|
|
@@ -8815,8 +9412,6 @@ var TypeAnalyzer = class {
|
|
|
8815
9412
|
const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
|
|
8816
9413
|
return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
|
|
8817
9414
|
}
|
|
8818
|
-
case "NewExpression":
|
|
8819
|
-
return this.inferNew(expr, env);
|
|
8820
9415
|
case "ClassExpression":
|
|
8821
9416
|
return this.visitClass(expr, env);
|
|
8822
9417
|
case "SuperExpression": {
|
|
@@ -8853,18 +9448,14 @@ var TypeAnalyzer = class {
|
|
|
8853
9448
|
/** `new Name(args)` is `Name.new(args)` — the same function, and the
|
|
8854
9449
|
* same check. Saying so here rather than rewriting the tree keeps the
|
|
8855
9450
|
* error messages pointing at what was written. */
|
|
8856
|
-
|
|
8857
|
-
|
|
8858
|
-
const
|
|
8859
|
-
if (
|
|
8860
|
-
|
|
8861
|
-
|
|
8862
|
-
|
|
8863
|
-
|
|
8864
|
-
for (const argument of expr.arguments) this.infer(argument, env);
|
|
8865
|
-
return anyType;
|
|
8866
|
-
}
|
|
8867
|
-
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;
|
|
8868
9459
|
}
|
|
8869
9460
|
/** `super(...)` — the base constructor, run on the instance being built. */
|
|
8870
9461
|
inferSuperCall(expr, env) {
|
|
@@ -8888,8 +9479,9 @@ var TypeAnalyzer = class {
|
|
|
8888
9479
|
this.inferCall(expr, callable, env);
|
|
8889
9480
|
return nilType;
|
|
8890
9481
|
}
|
|
8891
|
-
inferCall(expr,
|
|
9482
|
+
inferCall(expr, called, env) {
|
|
8892
9483
|
this.checkAmbiguousCall(expr);
|
|
9484
|
+
const callee = this.callMetamethod(called) ?? called;
|
|
8893
9485
|
const united = this.unionSignatures(callee);
|
|
8894
9486
|
const fns = united ?? this.overloadsOf(callee);
|
|
8895
9487
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
@@ -8912,8 +9504,31 @@ var TypeAnalyzer = class {
|
|
|
8912
9504
|
if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
|
|
8913
9505
|
return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
|
|
8914
9506
|
}
|
|
9507
|
+
this.checkCallable(expr.callee, callee);
|
|
8915
9508
|
return callee.kind === "any" ? anyType : unknownType;
|
|
8916
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();
|
|
8917
9532
|
/** A `(` on a line of its own continues the statement above it:
|
|
8918
9533
|
*
|
|
8919
9534
|
* const value = map[key]
|
|
@@ -8930,6 +9545,9 @@ var TypeAnalyzer = class {
|
|
|
8930
9545
|
});
|
|
8931
9546
|
}
|
|
8932
9547
|
inferMethodCall(expr, objType, env) {
|
|
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);
|
|
8933
9551
|
const method = this.propertyType(objType, expr.method.name);
|
|
8934
9552
|
const united = this.unionSignatures(method);
|
|
8935
9553
|
const fns = united ?? this.overloadsOf(method);
|
|
@@ -8963,6 +9581,7 @@ var TypeAnalyzer = class {
|
|
|
8963
9581
|
if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
|
|
8964
9582
|
return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
|
|
8965
9583
|
}
|
|
9584
|
+
this.checkCallable(expr.method, method);
|
|
8966
9585
|
return objType.kind === "any" ? anyType : unknownType;
|
|
8967
9586
|
}
|
|
8968
9587
|
// --------------------------------------------------------
|
|
@@ -9023,6 +9642,8 @@ var TypeAnalyzer = class {
|
|
|
9023
9642
|
if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
|
|
9024
9643
|
}
|
|
9025
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;
|
|
9026
9647
|
this.diagnostics.push({
|
|
9027
9648
|
node: target,
|
|
9028
9649
|
message: `Cannot assign to '${name}' because it is a read-only property`
|
|
@@ -9082,6 +9703,8 @@ var TypeAnalyzer = class {
|
|
|
9082
9703
|
inferArray(expr, env, asConst) {
|
|
9083
9704
|
const contextual = this.contextualArrays.get(expr);
|
|
9084
9705
|
if (contextual && !asConst) return contextual;
|
|
9706
|
+
const wantedTuple = this.tupleArrays.get(expr);
|
|
9707
|
+
if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
|
|
9085
9708
|
const elems = [];
|
|
9086
9709
|
let hadSpread = false;
|
|
9087
9710
|
for (const el of expr.elements) {
|
|
@@ -9101,6 +9724,28 @@ var TypeAnalyzer = class {
|
|
|
9101
9724
|
return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
|
|
9102
9725
|
})) : unknownType);
|
|
9103
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
|
+
}
|
|
9104
9749
|
/** A literal written inside a fresh table or array widens — `{ n = 1 }` is
|
|
9105
9750
|
* `{ n: number }` — unless the surroundings said a literal belongs there.
|
|
9106
9751
|
* `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
|
|
@@ -9186,7 +9831,7 @@ var TypeAnalyzer = class {
|
|
|
9186
9831
|
case "tuple": {
|
|
9187
9832
|
const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
|
|
9188
9833
|
if (tupleContext?.kind === "tuple") {
|
|
9189
|
-
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);
|
|
9190
9835
|
}
|
|
9191
9836
|
const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
|
|
9192
9837
|
const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
|
|
@@ -9443,7 +10088,8 @@ var TypeAnalyzer = class {
|
|
|
9443
10088
|
for (const f of candidates) {
|
|
9444
10089
|
if (!f.predicate) continue;
|
|
9445
10090
|
const arg = args[f.predicate.param];
|
|
9446
|
-
|
|
10091
|
+
const onCondition = f.predicate.asserts && !f.predicate.type;
|
|
10092
|
+
if (!arg || this.refKeyOf(arg) === void 0 && !onCondition) continue;
|
|
9447
10093
|
if (f.typeParams?.length && f.predicate.type) {
|
|
9448
10094
|
const subst = this.inferTypeArgs(f, argTypes);
|
|
9449
10095
|
return {
|
|
@@ -9464,6 +10110,10 @@ var TypeAnalyzer = class {
|
|
|
9464
10110
|
const found = this.predicateCallTarget(call, env);
|
|
9465
10111
|
if (!found || !found.predicate.asserts) return;
|
|
9466
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
|
+
}
|
|
9467
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) });
|
|
9468
10118
|
}
|
|
9469
10119
|
// --------------------------------------------------------
|
|
@@ -9488,6 +10138,10 @@ var TypeAnalyzer = class {
|
|
|
9488
10138
|
if (base === void 0) return void 0;
|
|
9489
10139
|
if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
|
|
9490
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
|
+
}
|
|
9491
10145
|
return void 0;
|
|
9492
10146
|
}
|
|
9493
10147
|
default:
|
|
@@ -9583,26 +10237,45 @@ var TypeAnalyzer = class {
|
|
|
9583
10237
|
* that copied it no longer holds that value: forget the alias. */
|
|
9584
10238
|
unalias(key) {
|
|
9585
10239
|
for (const k of [...this.refAliases.keys()]) {
|
|
9586
|
-
if (k !== key && !k
|
|
10240
|
+
if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
|
|
9587
10241
|
for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
|
|
9588
10242
|
this.refAliases.delete(k);
|
|
9589
10243
|
}
|
|
9590
10244
|
}
|
|
9591
|
-
/** 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. */
|
|
9592
10248
|
invalidateBelow(env, key) {
|
|
10249
|
+
const asIndex = `[${key}]`;
|
|
9593
10250
|
for (const k of [...env.keys()]) {
|
|
9594
|
-
if (k
|
|
10251
|
+
if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
|
|
9595
10252
|
}
|
|
9596
10253
|
}
|
|
9597
10254
|
/** An assignment through a reference invalidates it and everything under
|
|
9598
10255
|
* it, then records the assigned type. */
|
|
9599
10256
|
assignToRef(expr, value, env) {
|
|
10257
|
+
this.invalidateSiblings(expr, env);
|
|
9600
10258
|
const key = this.refKeyOf(expr);
|
|
9601
10259
|
if (key === void 0) return;
|
|
9602
10260
|
this.invalidateBelow(env, key);
|
|
9603
10261
|
this.unalias(key);
|
|
9604
10262
|
env.set(key, value);
|
|
9605
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
|
+
}
|
|
9606
10279
|
// --------------------------------------------------------
|
|
9607
10280
|
// Small helpers
|
|
9608
10281
|
// --------------------------------------------------------
|
|
@@ -9617,6 +10290,35 @@ var TypeAnalyzer = class {
|
|
|
9617
10290
|
this.selfType = saved;
|
|
9618
10291
|
}
|
|
9619
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
|
+
}
|
|
9620
10322
|
/** What an operator on a value with metamethods gives: `a + b` calls
|
|
9621
10323
|
* `__add` on `a`, or failing that on `b` with the operands swapped — the
|
|
9622
10324
|
* order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
|
|
@@ -9949,6 +10651,13 @@ var nodeHost = {
|
|
|
9949
10651
|
} catch {
|
|
9950
10652
|
return void 0;
|
|
9951
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
|
+
}
|
|
9952
10661
|
}
|
|
9953
10662
|
};
|
|
9954
10663
|
|
|
@@ -10140,13 +10849,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
|
|
|
10140
10849
|
else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
|
|
10141
10850
|
continue;
|
|
10142
10851
|
}
|
|
10143
|
-
|
|
10144
|
-
|
|
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);
|
|
10145
10872
|
if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
|
|
10146
10873
|
else {
|
|
10147
10874
|
problems.push({
|
|
10148
10875
|
file: config.path,
|
|
10149
|
-
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]}`,
|
|
10150
10877
|
...entryPosition(config, entry)
|
|
10151
10878
|
});
|
|
10152
10879
|
}
|
|
@@ -10183,6 +10910,32 @@ function findPackage(name, from, host) {
|
|
|
10183
10910
|
directory = parent;
|
|
10184
10911
|
}
|
|
10185
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
|
+
}
|
|
10186
10939
|
function dependencyNames(directory, host) {
|
|
10187
10940
|
const manifest = readJson((0, import_node_path2.join)(directory, "package.json"), host);
|
|
10188
10941
|
const names = /* @__PURE__ */ new Set();
|
|
@@ -10281,7 +11034,8 @@ function sourceMapTypes(text, path, options) {
|
|
|
10281
11034
|
for (const child of node.children ?? []) {
|
|
10282
11035
|
if (!isNode(child)) continue;
|
|
10283
11036
|
const childAlias = visit(child, [...segments, child.name], alias);
|
|
10284
|
-
|
|
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;
|
|
10285
11039
|
named.add(child.name);
|
|
10286
11040
|
members.push(`${child.name}: ${childAlias}`);
|
|
10287
11041
|
}
|
|
@@ -10342,6 +11096,7 @@ var index_default = tilua;
|
|
|
10342
11096
|
BinaryOperators,
|
|
10343
11097
|
CONFIG_FILE_NAMES,
|
|
10344
11098
|
Keywords,
|
|
11099
|
+
LANGUAGE_GLOBALS,
|
|
10345
11100
|
LexError,
|
|
10346
11101
|
Operators,
|
|
10347
11102
|
PRELUDE_SOURCE,
|
|
@@ -10409,6 +11164,7 @@ var index_default = tilua;
|
|
|
10409
11164
|
tilua,
|
|
10410
11165
|
tokenize,
|
|
10411
11166
|
tuple,
|
|
11167
|
+
tupleMembers,
|
|
10412
11168
|
typeParam,
|
|
10413
11169
|
unify,
|
|
10414
11170
|
union,
|