@tilua/parser 1.1.0 → 2.1.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/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. `...xs` spreads an array into it, as in a call's
969
- * arguments; bare `...` is the vararg pack, as it always was. */
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 = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
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 (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
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.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
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.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
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
- const variables = [first];
1662
- while (this.matchPunctuator(",")) {
1663
- variables.push(this.parseBindingTarget(true));
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 iterators = this.expressionListOr(untilDo);
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
- variables,
1671
- iterators,
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
- const t = this.current();
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 { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
1816
+ return {
1817
+ type: "ClassExpression",
1818
+ name,
1819
+ superclass,
1820
+ superArguments,
1821
+ ...shapes ? { implements: shapes } : {},
1822
+ members,
1823
+ ...spanFrom(start, this.previous())
1824
+ };
1825
+ }
1826
+ /** `implements Shape, Named` — each a type, checked against the instance. */
1827
+ parseImplements() {
1828
+ if (!this.checkIdentifierValue("implements")) return void 0;
1829
+ this.advance();
1830
+ const shapes = [this.parseType()];
1831
+ while (this.matchPunctuator(",")) shapes.push(this.parseType());
1832
+ return shapes;
1782
1833
  }
1783
1834
  /** `extends Base` / `extends Box<number>`. */
1784
1835
  parseExtends() {
@@ -1831,23 +1882,66 @@ var Parser = class {
1831
1882
  parseClassMember() {
1832
1883
  const start = this.current();
1833
1884
  let accessibility;
1834
- if ((this.checkIdentifierValue("public") || this.checkIdentifierValue("private")) && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(")) {
1835
- accessibility = this.advance().value;
1885
+ const modifiers = /* @__PURE__ */ new Set();
1886
+ while (this.startsClassModifier()) {
1887
+ const token = this.advance();
1888
+ const word = token.value;
1889
+ if (ACCESSIBILITIES.has(word)) {
1890
+ if (accessibility) this.problemAt(token, `A member has one accessibility: '${accessibility}' is already written`);
1891
+ accessibility = word;
1892
+ } else {
1893
+ if (modifiers.has(word)) this.problemAt(token, `'${word}' is written twice`);
1894
+ modifiers.add(word);
1895
+ }
1836
1896
  }
1837
- const member = this.parseClassMemberAfterAccessibility(start);
1838
- if (!accessibility || !member) return member;
1897
+ const member = this.parseClassMemberAfterModifiers(start, modifiers);
1898
+ if (!member) return member;
1899
+ const misplaced = (message) => this.problemAt(start, message);
1839
1900
  if (member.type === "ClassConstructor") {
1840
- this.problem(`A constructor cannot be '${accessibility}'`);
1901
+ const written = [...accessibility ? [accessibility] : [], ...modifiers];
1902
+ if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
1841
1903
  return member;
1842
1904
  }
1843
- return { ...member, accessibility };
1905
+ if (modifiers.has("readonly") && member.type !== "ClassField") misplaced("Only a field can be 'readonly'");
1906
+ if (modifiers.has("abstract") && member.type !== "ClassMethod") misplaced("Only a method can be 'abstract'");
1907
+ if (modifiers.has("abstract") && member.isStatic) misplaced("A static method cannot be 'abstract'");
1908
+ if (modifiers.has("override") && member.isStatic) misplaced("A static member cannot be 'override'");
1909
+ return {
1910
+ ...member,
1911
+ ...accessibility ? { accessibility } : {},
1912
+ ...modifiers.has("readonly") && member.type === "ClassField" ? { isReadonly: true } : {},
1913
+ ...modifiers.has("override") ? { isOverride: true } : {}
1914
+ };
1844
1915
  }
1845
- parseClassMemberAfterAccessibility(start) {
1846
- const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1847
- if (isStatic) this.advance();
1916
+ /** Is the word here a member modifier, rather than a member's name? */
1917
+ startsClassModifier() {
1918
+ const t = this.current();
1919
+ if (t.type !== "Identifier" || !CLASS_MODIFIERS.has(t.value)) return false;
1920
+ const next = this.peek(1);
1921
+ return next.type === "Identifier" || next.type === "Keyword" && next.value === "function";
1922
+ }
1923
+ parseClassMemberAfterModifiers(start, modifiers) {
1924
+ const isStatic = modifiers.has("static");
1848
1925
  if (this.checkKeyword("function")) {
1849
1926
  this.advance();
1850
1927
  const memberName = this.parseIdentifier();
1928
+ if (modifiers.has("abstract")) {
1929
+ const head = this.parseFunctionHead();
1930
+ const func = {
1931
+ type: "FunctionBody",
1932
+ generics: head.generics,
1933
+ params: head.params,
1934
+ hasVarargs: head.hasVarargs,
1935
+ returnType: head.returnType,
1936
+ predicate: head.predicate,
1937
+ body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1938
+ ...spanFrom(head.start, this.previous())
1939
+ };
1940
+ if (!isStatic) bindThis(func, start);
1941
+ if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1942
+ if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1943
+ return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1944
+ }
1851
1945
  const signatures = [];
1852
1946
  let written = memberName;
1853
1947
  while (true) {
@@ -1965,12 +2059,17 @@ var Parser = class {
1965
2059
  parseReturnStatement(stopAtNewline = false) {
1966
2060
  const start = this.current();
1967
2061
  this.expectKeyword("return");
1968
- let args = [];
2062
+ let argument;
1969
2063
  const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
1970
2064
  if (!pastLine() && this.isExpressionStart()) {
1971
- args = this.expressionListOr(pastLine);
2065
+ const values = this.expressionListOr(pastLine);
2066
+ argument = values[0];
2067
+ if (values.length > 1) {
2068
+ this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
2069
+ argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
2070
+ }
1972
2071
  }
1973
- return { type: "ReturnStatement", arguments: args, ...spanFrom(start, this.previous()) };
2072
+ return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
1974
2073
  }
1975
2074
  parseTypeAliasStatement() {
1976
2075
  const start = this.current();
@@ -2029,7 +2128,7 @@ var Parser = class {
2029
2128
  ...spanFrom(start, this.previous())
2030
2129
  };
2031
2130
  }
2032
- if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
2131
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
2033
2132
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
2034
2133
  }
2035
2134
  let expression = first;
@@ -2082,6 +2181,15 @@ var Parser = class {
2082
2181
  }
2083
2182
  return false;
2084
2183
  }
2184
+ /** A string written straight before a method call — `"a":upper()`, or
2185
+ * `` `{n}`:rep(2) ``. Luau only takes `("a"):upper()`; the compiler adds
2186
+ * the parentheses. Anything else after a string (`"a".x`, `"a"(1)`) is
2187
+ * not a prefix expression here either. */
2188
+ startsStringMethodCall() {
2189
+ const t = this.current();
2190
+ const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
2191
+ return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
2192
+ }
2085
2193
  /** Does the next token start right where the current one ends? */
2086
2194
  touchesNext() {
2087
2195
  const current = this.current();
@@ -2268,6 +2376,7 @@ var Parser = class {
2268
2376
  }
2269
2377
  parseAtom() {
2270
2378
  const t = this.current();
2379
+ if (this.startsStringMethodCall()) return this.parsePrefixExpression();
2271
2380
  if (t.type === "Literal") {
2272
2381
  this.advance();
2273
2382
  const lit = t;
@@ -2287,8 +2396,9 @@ var Parser = class {
2287
2396
  return this.buildInterpolatedString(t);
2288
2397
  }
2289
2398
  if (t.type === "Operator" && t.value === "...") {
2399
+ this.problem(NO_DOTS);
2290
2400
  this.advance();
2291
- return { type: "VarargExpression", ...spanFrom(t, t) };
2401
+ return { type: "ErrorExpression", ...spanFrom(t, t) };
2292
2402
  }
2293
2403
  if (t.type === "Keyword" && t.value === "function") {
2294
2404
  this.advance();
@@ -2364,12 +2474,17 @@ var Parser = class {
2364
2474
  const start = this.current();
2365
2475
  let base;
2366
2476
  if (this.startsNew()) {
2367
- base = this.parseNewExpression();
2368
- } else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2477
+ this.problem("tilua has no 'new' operator: construct with 'Name.new(...)'");
2478
+ this.advance();
2479
+ }
2480
+ if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2369
2481
  this.advance();
2370
2482
  base = { type: "SuperExpression", ...spanFrom(start, start) };
2371
2483
  } else if (this.checkType("Identifier")) {
2372
2484
  base = this.parseIdentifier();
2485
+ } else if (this.startsStringMethodCall()) {
2486
+ const t = this.advance();
2487
+ base = t.type === "InterpolatedString" ? this.buildInterpolatedString(t) : { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2373
2488
  } else if (this.matchPunctuator("(")) {
2374
2489
  const inner = this.inBrackets(() => this.parseExpression());
2375
2490
  this.expectPunctuator(")");
@@ -2388,6 +2503,21 @@ var Parser = class {
2388
2503
  base = { type: "MemberExpression", object: base, property: prop, optional: true, ...spanFrom(base, prop) };
2389
2504
  continue;
2390
2505
  }
2506
+ if (punct === "." && this.punctuatorAt(2, "[")) {
2507
+ this.advance();
2508
+ this.advance();
2509
+ this.expectPunctuator("[");
2510
+ const index = this.inBrackets(() => this.parseExpression());
2511
+ this.expectPunctuator("]");
2512
+ base = {
2513
+ type: "IndexExpression",
2514
+ object: base,
2515
+ index,
2516
+ optional: true,
2517
+ ...spanFrom(base, this.previous())
2518
+ };
2519
+ continue;
2520
+ }
2391
2521
  if (punct === "." && this.punctuatorAt(2, "(")) {
2392
2522
  this.advance();
2393
2523
  this.advance();
@@ -2480,9 +2610,9 @@ var Parser = class {
2480
2610
  }
2481
2611
  return base;
2482
2612
  }
2483
- /** `new` is a soft keyword: it starts a construction only when a name
2484
- * follows it, so a function or field called `new` — `Instance.new(x)`,
2485
- * and `Vec.new(1)` itself — is untouched. */
2613
+ /** `new Name(...)`, the way other languages build an instance — only to
2614
+ * say so. A function or field called `new` — `Instance.new(x)`, and
2615
+ * `Vec.new(1)` itself — is untouched. */
2486
2616
  startsNew() {
2487
2617
  return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2488
2618
  }
@@ -2492,25 +2622,6 @@ var Parser = class {
2492
2622
  startsSuperUse() {
2493
2623
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2494
2624
  }
2495
- /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2496
- * name reached through a module — never an arbitrary expression, so the
2497
- * arguments are unambiguously the constructor's. */
2498
- parseNewExpression() {
2499
- const start = this.current();
2500
- this.advance();
2501
- let callee = this.parseIdentifier();
2502
- while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2503
- this.advance();
2504
- const property = this.parseIdentifier();
2505
- callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2506
- }
2507
- const typeArguments = this.tryCallTypeArguments();
2508
- if (!this.checkPunctuator("(")) {
2509
- this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2510
- }
2511
- const args = this.parseCallArguments();
2512
- return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2513
- }
2514
2625
  /** After `...`, is there something to spread? Nothing following it means
2515
2626
  * the vararg pack, which is what `f(...)` has always passed on. */
2516
2627
  startsSpread() {
@@ -2552,8 +2663,8 @@ var Parser = class {
2552
2663
  const errors = this.errors.length;
2553
2664
  try {
2554
2665
  this.advance();
2555
- const list = [this.parseTypeArgument()];
2556
- while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseTypeArgument());
2666
+ const list = [this.parseType()];
2667
+ while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
2557
2668
  this.expectOperator(">");
2558
2669
  if (!this.startsCallArguments()) throw new ParseRecover("not a call");
2559
2670
  return list;
@@ -2685,7 +2796,7 @@ var Parser = class {
2685
2796
  if (this.checkOperator("...")) {
2686
2797
  const dots = this.advance();
2687
2798
  if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2688
- elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2799
+ this.problemAt(dots, NO_DOTS);
2689
2800
  } else {
2690
2801
  const argument = this.expressionOr(stop);
2691
2802
  elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
@@ -2839,35 +2950,16 @@ var Parser = class {
2839
2950
  column: p.column
2840
2951
  };
2841
2952
  }
2842
- parseTypeOrTypePackReference() {
2953
+ /** `T...` written where a type goes: a pack, which tilua does not have. */
2954
+ rejectPackReference() {
2843
2955
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2844
- const start = this.current();
2845
- const base = this.expectIdentifier().value;
2956
+ this.problemAt(
2957
+ this.peek(1),
2958
+ "tilua has no type packs: several values are a tuple, '[A, B]', and a varying number an array, 'T[]'"
2959
+ );
2960
+ this.advance();
2846
2961
  this.advance();
2847
- const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
2848
- return {
2849
- type: "TypePackNode",
2850
- types: [],
2851
- hasVarargs: true,
2852
- // Wrap in `VariadicTypeNode`, matching the convention used by
2853
- // `parseFunctionTypeAfterParen`'s identifier-pack-reference
2854
- // branch, so the printer can tell `A...` (name-first, this
2855
- // case) apart from `...T` (dots-first) and append rather
2856
- // than prepend the `...`.
2857
- varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
2858
- ...spanFrom(start, this.previous())
2859
- };
2860
- }
2861
- return this.parseType();
2862
- }
2863
- parseTypeArgument() {
2864
- if (this.checkOperator("...")) {
2865
- return this.parseTypePack();
2866
- }
2867
- if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2868
- return this.parseTypeOrTypePackReference();
2869
2962
  }
2870
- return this.parseType();
2871
2963
  }
2872
2964
  /** The part shared by a real function body and an overload signature:
2873
2965
  * `<generics>(params): ReturnType`, up to (but not including) the block. */
@@ -2880,7 +2972,6 @@ var Parser = class {
2880
2972
  this.expectPunctuator("(");
2881
2973
  const params = [];
2882
2974
  let hasVarargs = false;
2883
- let varargTypeAnnotation;
2884
2975
  if (!this.checkPunctuator(")")) {
2885
2976
  while (true) {
2886
2977
  if (this.checkOperator("...")) {
@@ -2888,14 +2979,14 @@ var Parser = class {
2888
2979
  hasVarargs = true;
2889
2980
  if (this.checkType("Identifier")) {
2890
2981
  const nameTok = this.expectIdentifier();
2891
- let typeAnnotation2;
2982
+ let typeAnnotation3;
2892
2983
  if (this.matchPunctuator(":")) {
2893
- typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2984
+ typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
2894
2985
  }
2895
2986
  params.push({
2896
2987
  type: "FunctionParameter",
2897
2988
  name: nameTok.value,
2898
- typeAnnotation: typeAnnotation2,
2989
+ typeAnnotation: typeAnnotation3,
2899
2990
  rest: true,
2900
2991
  ...spanFrom(dots, this.previous())
2901
2992
  });
@@ -2904,9 +2995,20 @@ var Parser = class {
2904
2995
  }
2905
2996
  break;
2906
2997
  }
2998
+ this.problemAt(dots, NO_DOTS);
2999
+ let typeAnnotation2;
2907
3000
  if (this.matchPunctuator(":")) {
2908
- varargTypeAnnotation = this.parseTypeOrTypePackReference();
3001
+ this.rejectPackReference();
3002
+ const element = this.parseType();
3003
+ typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
2909
3004
  }
3005
+ params.push({
3006
+ type: "FunctionParameter",
3007
+ name: "args",
3008
+ typeAnnotation: typeAnnotation2,
3009
+ rest: true,
3010
+ ...spanFrom(dots, this.previous())
3011
+ });
2910
3012
  break;
2911
3013
  }
2912
3014
  const paramStart = this.current();
@@ -2948,10 +3050,16 @@ var Parser = class {
2948
3050
  if (this.matchPunctuator(":")) {
2949
3051
  predicate = this.tryParseTypePredicate();
2950
3052
  if (!predicate) {
2951
- returnType = this.attempt(() => this.parseTypeOrTypePackReference(), () => false, () => void 0);
3053
+ returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
2952
3054
  }
2953
3055
  }
2954
- return { start, generics, params, hasVarargs, varargTypeAnnotation, returnType, predicate };
3056
+ return { start, generics, params, hasVarargs, returnType, predicate };
3057
+ }
3058
+ /** What a function returns: one type. `T...` is reported as the pack it
3059
+ * is, and the type after it read. */
3060
+ parseReturnType() {
3061
+ this.rejectPackReference();
3062
+ return this.parseType();
2955
3063
  }
2956
3064
  /** TypeScript-style type-guard return annotations, in return position only:
2957
3065
  *
@@ -3021,7 +3129,6 @@ var Parser = class {
3021
3129
  generics: [],
3022
3130
  params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
3023
3131
  hasVarargs: false,
3024
- varargTypeAnnotation: void 0,
3025
3132
  returnType: void 0,
3026
3133
  predicate: void 0
3027
3134
  };
@@ -3033,7 +3140,6 @@ var Parser = class {
3033
3140
  generics: head.generics,
3034
3141
  params: head.params,
3035
3142
  hasVarargs: head.hasVarargs,
3036
- varargTypeAnnotation: head.varargTypeAnnotation,
3037
3143
  returnType: head.returnType,
3038
3144
  predicate: head.predicate,
3039
3145
  body,
@@ -3045,7 +3151,7 @@ var Parser = class {
3045
3151
  returnOf(expression) {
3046
3152
  const statement = {
3047
3153
  type: "ReturnStatement",
3048
- arguments: [expression],
3154
+ argument: expression,
3049
3155
  ...spanFrom(expression, expression)
3050
3156
  };
3051
3157
  return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
@@ -3058,7 +3164,6 @@ var Parser = class {
3058
3164
  generics: head.generics,
3059
3165
  params: head.params,
3060
3166
  hasVarargs: head.hasVarargs,
3061
- varargTypeAnnotation: head.varargTypeAnnotation,
3062
3167
  returnType: head.returnType,
3063
3168
  predicate: head.predicate,
3064
3169
  body,
@@ -3071,7 +3176,6 @@ var Parser = class {
3071
3176
  generics: head.generics,
3072
3177
  params: head.params,
3073
3178
  hasVarargs: head.hasVarargs,
3074
- varargTypeAnnotation: head.varargTypeAnnotation,
3075
3179
  returnType: head.returnType,
3076
3180
  predicate: head.predicate,
3077
3181
  ...spanFrom(head.start, this.previous())
@@ -3089,7 +3193,6 @@ var Parser = class {
3089
3193
  generics: head.generics,
3090
3194
  params: head.params,
3091
3195
  hasVarargs: head.hasVarargs,
3092
- varargTypeAnnotation: head.varargTypeAnnotation,
3093
3196
  returnType: head.returnType,
3094
3197
  predicate: head.predicate,
3095
3198
  body,
@@ -3168,8 +3271,12 @@ var Parser = class {
3168
3271
  * `name?: T` for an optional property or parameter. */
3169
3272
  parseSuffixType() {
3170
3273
  if (this.checkIdentifierValue("readonly")) {
3274
+ const start = this.current();
3171
3275
  this.advance();
3172
- return this.parseSuffixType();
3276
+ const t2 = this.parseSuffixType();
3277
+ if (t2.type === "ArrayTypeNode" || t2.type === "TupleTypeNode") return { ...t2, isReadonly: true };
3278
+ this.problemAt(start, "'readonly' belongs on an array or a tuple: a property is marked where it is written");
3279
+ return t2;
3173
3280
  }
3174
3281
  let t = this.parsePrimaryType();
3175
3282
  while (true) {
@@ -3230,9 +3337,10 @@ var Parser = class {
3230
3337
  return this.parseFunctionTypeAfterParen(t, []);
3231
3338
  }
3232
3339
  if (t.type === "Operator" && t.value === "...") {
3340
+ this.problem("'...' in a type goes before a rest parameter, '...args: T[]', or at the end of a tuple, '[A, ...B[]]'");
3233
3341
  this.advance();
3234
- const inner = this.parseType();
3235
- return { type: "VariadicTypeNode", typeAnnotation: inner, ...spanFrom(t, this.previous()) };
3342
+ const element = this.parseType();
3343
+ return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
3236
3344
  }
3237
3345
  if (t.type === "Punctuator" && t.value === "{") {
3238
3346
  return this.parseTableType();
@@ -3240,13 +3348,22 @@ var Parser = class {
3240
3348
  if (t.type === "Punctuator" && t.value === "[") {
3241
3349
  this.advance();
3242
3350
  const elements = [];
3351
+ let rest;
3243
3352
  while (!this.checkPunctuator("]")) {
3353
+ if (this.matchOperator("...")) {
3354
+ rest = this.parseType();
3355
+ if (rest.type !== "ArrayTypeNode" && !(rest.type === "TypeReference" && rest.base !== "nil")) {
3356
+ this.problem("The rest of a tuple is an array type: '...T[]'");
3357
+ }
3358
+ this.matchPunctuator(",");
3359
+ break;
3360
+ }
3244
3361
  elements.push(this.parseType());
3245
3362
  if (this.matchPunctuator(",")) continue;
3246
3363
  break;
3247
3364
  }
3248
3365
  this.expectPunctuator("]");
3249
- return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
3366
+ return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3250
3367
  }
3251
3368
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3252
3369
  this.advance();
@@ -3293,9 +3410,9 @@ var Parser = class {
3293
3410
  if (this.checkOperator("<")) {
3294
3411
  this.advance();
3295
3412
  if (!this.checkOperator(">")) {
3296
- typeArguments.push(this.parseTypeArgument());
3413
+ typeArguments.push(this.parseType());
3297
3414
  while (this.matchPunctuator(",") && !this.checkOperator(">")) {
3298
- typeArguments.push(this.parseTypeArgument());
3415
+ typeArguments.push(this.parseType());
3299
3416
  }
3300
3417
  }
3301
3418
  this.expectOperator(">");
@@ -3306,13 +3423,12 @@ var Parser = class {
3306
3423
  }
3307
3424
  parseFunctionTypeAfterParen(start, generics) {
3308
3425
  const params = [];
3309
- let hasVarargs = false;
3310
- let varargType;
3426
+ let rested = false;
3311
3427
  if (!this.checkPunctuator(")")) {
3312
3428
  while (true) {
3313
3429
  if (this.checkOperator("...")) {
3314
3430
  const dots = this.advance();
3315
- hasVarargs = true;
3431
+ rested = true;
3316
3432
  if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3317
3433
  const nameTok2 = this.expectIdentifier();
3318
3434
  this.advance();
@@ -3326,16 +3442,21 @@ var Parser = class {
3326
3442
  });
3327
3443
  break;
3328
3444
  }
3329
- varargType = this.parseType();
3445
+ this.problemAt(dots, "A function type takes the rest of its arguments as an array: '(...args: T[]) => R'");
3446
+ this.rejectPackReference();
3447
+ const element = this.parseType();
3448
+ params.push({
3449
+ type: "FunctionTypeParameter",
3450
+ name: "args",
3451
+ rest: true,
3452
+ typeAnnotation: { type: "ArrayTypeNode", element, ...spanFrom(element, element) },
3453
+ ...spanFrom(dots, this.previous())
3454
+ });
3330
3455
  break;
3331
3456
  }
3332
3457
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
3333
- const packStart = this.current();
3334
- const packRef = this.parseType();
3335
- this.advance();
3336
- hasVarargs = true;
3337
- varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
3338
- break;
3458
+ this.rejectPackReference();
3459
+ continue;
3339
3460
  }
3340
3461
  let name;
3341
3462
  let optional2 = false;
@@ -3365,19 +3486,17 @@ var Parser = class {
3365
3486
  this.expectPunctuator(")");
3366
3487
  if (this.matchPunctuator("=>") || this.mistypedArrow()) {
3367
3488
  const predicate = this.tryParseTypePredicate();
3368
- const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
3489
+ const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseReturnType();
3369
3490
  return {
3370
3491
  type: "FunctionTypeNode",
3371
3492
  generics,
3372
3493
  params,
3373
- hasVarargs,
3374
- varargType,
3375
3494
  returnType,
3376
3495
  predicate,
3377
3496
  ...spanFrom(start, this.previous())
3378
3497
  };
3379
3498
  }
3380
- if (params.length === 1 && !params[0].name && !hasVarargs) {
3499
+ if (params.length === 1 && !params[0].name && !rested) {
3381
3500
  return {
3382
3501
  type: "ParenthesizedTypeNode",
3383
3502
  typeAnnotation: params[0].typeAnnotation,
@@ -3387,11 +3506,14 @@ var Parser = class {
3387
3506
  if (params.some((p) => p.name !== void 0)) {
3388
3507
  this.error("Expected '=>' for function type");
3389
3508
  }
3509
+ if (!params.length) {
3510
+ this.problemAt(start, "'()' is not a type: a function that returns nothing returns 'nil'");
3511
+ return { type: "TypeReference", base: "nil", typeArguments: [], ...spanFrom(start, this.previous()) };
3512
+ }
3513
+ this.problemAt(start, "Several types in parentheses are a pack, which tilua does not have: write a tuple, '[A, B]'");
3390
3514
  return {
3391
- type: "TypePackNode",
3392
- types: params.map((p) => p.typeAnnotation),
3393
- hasVarargs,
3394
- varargType,
3515
+ type: "TupleTypeNode",
3516
+ elements: params.map((p) => p.typeAnnotation),
3395
3517
  ...spanFrom(start, this.previous())
3396
3518
  };
3397
3519
  }
@@ -3529,40 +3651,15 @@ var Parser = class {
3529
3651
  this.expectPunctuator("}");
3530
3652
  return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
3531
3653
  }
3532
- parseTypePack() {
3533
- const start = this.current();
3534
- if (this.matchOperator("...")) {
3535
- const varargType2 = this.parseType();
3536
- return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
3537
- }
3538
- this.expectPunctuator("(");
3539
- const types = [];
3540
- let hasVarargs = false;
3541
- let varargType;
3542
- if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
3543
- while (true) {
3544
- if (this.matchOperator("...")) {
3545
- hasVarargs = true;
3546
- varargType = this.parseType();
3547
- break;
3548
- }
3549
- types.push(this.parseType());
3550
- if (this.matchPunctuator(",")) continue;
3551
- break;
3552
- }
3553
- }
3554
- this.expectPunctuator(")");
3555
- return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
3556
- }
3557
3654
  parseGenericTypeParameterList() {
3558
3655
  const list = [];
3559
3656
  this.expectOperator("<");
3560
3657
  while (true) {
3561
3658
  const isConst = this.matchKeyword("const");
3562
3659
  const nameTok = this.expectIdentifier();
3563
- let isPack = false;
3564
- if (this.matchOperator("...")) {
3565
- isPack = true;
3660
+ const dots = this.checkOperator("...") ? this.advance() : void 0;
3661
+ if (dots) {
3662
+ this.problemAt(dots, "tilua has no pack parameters: write '<T extends unknown[]>', and take it as '...args: T'");
3566
3663
  }
3567
3664
  let constraint;
3568
3665
  if (this.checkIdentifierValue("extends")) {
@@ -3571,17 +3668,13 @@ var Parser = class {
3571
3668
  }
3572
3669
  let def;
3573
3670
  if (this.matchOperator("=")) {
3574
- if (isPack) {
3575
- def = this.parseTypePack();
3576
- } else {
3577
- def = this.parseType();
3578
- }
3671
+ this.rejectPackReference();
3672
+ def = this.parseType();
3579
3673
  }
3580
3674
  list.push({
3581
3675
  type: "GenericTypeParameter",
3582
3676
  name: nameTok.value,
3583
3677
  id: tokenIdentifier(nameTok),
3584
- isPack,
3585
3678
  isConst: isConst || void 0,
3586
3679
  constraint,
3587
3680
  default: def,
@@ -3672,7 +3765,7 @@ function childScope(parent) {
3672
3765
  var Analyzer = class {
3673
3766
  constructor(options) {
3674
3767
  this.options = options;
3675
- for (const name of options.builtinGlobals ?? []) {
3768
+ for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
3676
3769
  const id = this.getOrCreateGlobalBinding(name);
3677
3770
  this.bindings.get(id).isBuiltin = true;
3678
3771
  }
@@ -3734,31 +3827,34 @@ var Analyzer = class {
3734
3827
  * can call it, but a call straight in the block before the declaration
3735
3828
  * finds nothing there yet. At a module's top level the whole function is
3736
3829
  * hoisted, and this does not apply. */
3737
- checkUseBeforeDefine(identifier, id) {
3830
+ checkUseBeforeDefine(identifier, id, at = this.functionDepth) {
3738
3831
  const binding = this.bindings.get(id);
3739
- if (binding.declaredBy !== "function" && binding.declaredBy !== "class" || this.typeQueryDepth > 0) return;
3832
+ if (this.typeQueryDepth > 0) return;
3740
3833
  const declaration = binding.declarationNode;
3741
- const depth = declaration && this.hoisted.get(declaration);
3742
- if (depth === void 0 || depth !== this.functionDepth) return;
3834
+ if (!declaration) return;
3835
+ const hoisted = binding.declaredBy === "function" || binding.declaredBy === "class";
3836
+ const depth = hoisted ? this.hoisted.get(declaration) : this.declaredDepth.get(id);
3837
+ const readable = hoisted || binding.kind === "local";
3838
+ if (!readable || depth === void 0 || depth !== at) return;
3743
3839
  const before = identifier.line.start < declaration.line.start || identifier.line.start === declaration.line.start && identifier.column.start < declaration.column.start;
3744
3840
  if (!before) return;
3745
3841
  this.diagnostics.push({
3746
3842
  node: identifier,
3747
- message: `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run`,
3843
+ message: hoisted ? `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run` : `'${binding.name}' is used before its declaration, and holds nothing until that line has run`,
3748
3844
  kind: "use-before-define"
3749
3845
  });
3750
3846
  }
3751
3847
  noteDeferred(identifier, scope, id, assignment) {
3752
3848
  if (this.functionDepth === 0 && this.typeQueryDepth === 0) return;
3753
3849
  if (this.bindings.get(id).kind !== "global") return;
3754
- this.deferredGlobals.push({ node: identifier, scope, assignment });
3850
+ this.deferredGlobals.push({ node: identifier, scope, assignment, depth: this.functionDepth });
3755
3851
  }
3756
3852
  /** Point each deferred read of a global at the declaration of that name
3757
3853
  * that turned up later — in the module, or in any block around the code
3758
3854
  * that reads it. Such code runs after the declaration has: a closure
3759
3855
  * written inside a value reads the name the value is bound to. */
3760
3856
  resolveForwardReferences() {
3761
- for (const { node, scope, assignment } of this.deferredGlobals) {
3857
+ for (const { node, scope, assignment, depth } of this.deferredGlobals) {
3762
3858
  const localId = this.lookup(scope, node.name);
3763
3859
  const globalId = this.bindingOf.get(node);
3764
3860
  if (localId === void 0 || globalId === void 0 || localId === globalId) continue;
@@ -3773,7 +3869,10 @@ var Analyzer = class {
3773
3869
  this.bindingOf.set(node, localId);
3774
3870
  this.bindings.get(localId).references.push(node);
3775
3871
  if (assignment) this.checkConstAssign(localId, node);
3776
- else if (this.typeQueryDepth === 0) this.checkTypeOnly(localId, node);
3872
+ else if (this.typeQueryDepth === 0) {
3873
+ this.checkTypeOnly(localId, node);
3874
+ this.checkUseBeforeDefine(node, localId, depth);
3875
+ }
3777
3876
  }
3778
3877
  }
3779
3878
  /** Every read of a global nothing declares. A global assigned somewhere
@@ -3804,9 +3903,13 @@ var Analyzer = class {
3804
3903
  }
3805
3904
  const id = this.nextId++;
3806
3905
  this.bindings.set(id, { id, name, kind, declarationNode: node, references: [], isConst, declaredBy });
3906
+ this.declaredDepth.set(id, this.functionDepth);
3807
3907
  scope.declarations.set(name, id);
3808
3908
  return id;
3809
3909
  }
3910
+ /** Which function body each name was declared in. A read of a `const` from
3911
+ * the same body, but above it, is a read of nothing. */
3912
+ declaredDepth = /* @__PURE__ */ new Map();
3810
3913
  resolve(scope, name) {
3811
3914
  for (let s = scope; s; s = s.parent) {
3812
3915
  const id = s.declarations.get(name);
@@ -4074,14 +4177,14 @@ var Analyzer = class {
4074
4177
  return;
4075
4178
  }
4076
4179
  case "GenericForStatement": {
4077
- for (const it of stmt.iterators) this.visitExpression(it, scope);
4180
+ this.visitExpression(stmt.iterator, scope);
4078
4181
  const bodyScope = childScope(scope);
4079
- for (const v of stmt.variables) this.declarePattern(bodyScope, v, "for-generic", scope);
4182
+ this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
4080
4183
  this.visitBlock(stmt.body, bodyScope);
4081
4184
  return;
4082
4185
  }
4083
4186
  case "ReturnStatement":
4084
- for (const arg of stmt.arguments) this.visitExpression(arg, scope);
4187
+ if (stmt.argument) this.visitExpression(stmt.argument, scope);
4085
4188
  return;
4086
4189
  case "BreakStatement":
4087
4190
  case "ContinueStatement":
@@ -4149,7 +4252,6 @@ var Analyzer = class {
4149
4252
  this.declare(fnScope, param.name, kind, param);
4150
4253
  }
4151
4254
  });
4152
- this.visitType(func.varargTypeAnnotation, fnScope);
4153
4255
  this.visitType(func.returnType, fnScope);
4154
4256
  this.functionDepth++;
4155
4257
  try {
@@ -4177,6 +4279,7 @@ var Analyzer = class {
4177
4279
  this.visitGenerics(node.typeParams, scope);
4178
4280
  if (node.superclass) this.reference(outer, node.superclass);
4179
4281
  for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4282
+ for (const shape of node.implements ?? []) this.visitType(shape, scope);
4180
4283
  for (const member of node.members) {
4181
4284
  switch (member.type) {
4182
4285
  case "ClassField":
@@ -4236,7 +4339,6 @@ var Analyzer = class {
4236
4339
  case "BooleanLiteral":
4237
4340
  case "NumberLiteral":
4238
4341
  case "StringLiteral":
4239
- case "VarargExpression":
4240
4342
  case "ErrorExpression":
4241
4343
  return;
4242
4344
  case "InterpolatedStringExpression":
@@ -4276,11 +4378,6 @@ var Analyzer = class {
4276
4378
  this.visitExpression(expr.callee, scope);
4277
4379
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
4278
4380
  return;
4279
- case "NewExpression":
4280
- this.visitExpression(expr.callee, scope);
4281
- for (const argument of expr.arguments) this.visitExpression(argument, scope);
4282
- for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4283
- return;
4284
4381
  case "SuperExpression":
4285
4382
  return;
4286
4383
  case "SpreadElement":
@@ -4334,6 +4431,7 @@ var Analyzer = class {
4334
4431
  }
4335
4432
  }
4336
4433
  };
4434
+ var LANGUAGE_GLOBALS = ["scriptArgs"];
4337
4435
  function analyzeScopes(program, options = {}) {
4338
4436
  return new Analyzer(options).run(program);
4339
4437
  }
@@ -4352,8 +4450,8 @@ type Exclude<T, U> = T - U
4352
4450
  type Extract<T, U> = T extends U ? T : never
4353
4451
  type NonNullable<T> = T - nil
4354
4452
 
4355
- type ReturnType<T> = T extends (...unknown) => infer R ? R : never
4356
- type Parameters<T> = T extends (...infer P) => unknown ? P : never
4453
+ type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
4454
+ type Parameters<T> = T extends (...args: infer P) => unknown ? P : never
4357
4455
 
4358
4456
  type Partial<T> = { [K in keyof T]?: T[K] }
4359
4457
  type Required<T> = { [K in keyof T]-?: T[K] }
@@ -4438,8 +4536,11 @@ function literal(value) {
4438
4536
  function arrayOf(element) {
4439
4537
  return { kind: "array", element };
4440
4538
  }
4441
- function tuple(elements, isPack) {
4442
- return { kind: "tuple", elements, isPack };
4539
+ function tuple(elements, rest) {
4540
+ return rest ? { kind: "tuple", elements, rest } : { kind: "tuple", elements };
4541
+ }
4542
+ function tupleMembers(t) {
4543
+ return t.rest ? [...t.elements, t.rest] : t.elements;
4443
4544
  }
4444
4545
  function objectType(entries, indexer, frozen) {
4445
4546
  return { kind: "object", properties: new Map(entries), indexer, frozen };
@@ -4461,9 +4562,9 @@ function substitute(t, subst) {
4461
4562
  case "typeParam":
4462
4563
  return subst.get(t.name) ?? t;
4463
4564
  case "array":
4464
- return arrayOf(substitute(t.element, subst));
4565
+ return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4465
4566
  case "tuple":
4466
- return tuple(t.elements.map((e) => substitute(e, subst)), t.isPack);
4567
+ return { ...tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst)), readonly: t.readonly };
4467
4568
  case "object": {
4468
4569
  const entries = [];
4469
4570
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
@@ -4487,14 +4588,20 @@ function substitute(t, subst) {
4487
4588
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
4488
4589
  let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
4489
4590
  let varargs = t.varargs && substitute(t.varargs, inner);
4490
- if (varargs?.kind === "tuple" && varargs.isPack) {
4591
+ let restIsWhole = t.restIsWhole;
4592
+ if (restIsWhole && varargs?.kind === "tuple") {
4491
4593
  params = [...params, ...varargs.elements.map((type) => ({ type }))];
4492
- varargs = void 0;
4594
+ varargs = varargs.rest;
4595
+ restIsWhole = void 0;
4596
+ } else if (restIsWhole && varargs?.kind === "array") {
4597
+ varargs = varargs.element;
4598
+ restIsWhole = void 0;
4493
4599
  }
4494
4600
  return {
4495
4601
  kind: "function",
4496
4602
  params,
4497
4603
  varargs,
4604
+ ...restIsWhole ? { restIsWhole } : {},
4498
4605
  returns: substitute(t.returns, inner),
4499
4606
  typeParams: t.typeParams,
4500
4607
  typeParamDefaults: t.typeParamDefaults,
@@ -4577,11 +4684,19 @@ function unify(param, arg, vars, out) {
4577
4684
  switch (param.kind) {
4578
4685
  case "array":
4579
4686
  if (arg.kind === "array") unify(param.element, arg.element, vars, out);
4580
- else if (arg.kind === "tuple") for (const e of arg.elements) unify(param.element, e, vars, out);
4687
+ else if (arg.kind === "tuple") for (const e of tupleMembers(arg)) unify(param.element, e, vars, out);
4581
4688
  return;
4582
4689
  case "tuple":
4583
- if (arg.kind === "tuple") param.elements.forEach((p, i) => arg.elements[i] && unify(p, arg.elements[i], vars, out));
4584
- else if (arg.kind === "array") for (const p of param.elements) unify(p, arg.element, vars, out);
4690
+ if (arg.kind === "tuple") {
4691
+ param.elements.forEach((p, i) => {
4692
+ const a = arg.elements[i] ?? arg.rest;
4693
+ if (a) unify(p, a, vars, out);
4694
+ });
4695
+ if (param.rest) for (const a of arg.elements.slice(param.elements.length)) unify(param.rest, a, vars, out);
4696
+ if (param.rest && arg.rest) unify(param.rest, arg.rest, vars, out);
4697
+ } else if (arg.kind === "array") {
4698
+ for (const p of tupleMembers(param)) unify(p, arg.element, vars, out);
4699
+ }
4585
4700
  return;
4586
4701
  case "function":
4587
4702
  if (arg.kind === "function") {
@@ -4614,6 +4729,20 @@ function unify(param, arg, vars, out) {
4614
4729
  return;
4615
4730
  }
4616
4731
  }
4732
+ function memberKey(t) {
4733
+ switch (t.kind) {
4734
+ case "literal":
4735
+ return `l${t.base}:${String(t.value)}`;
4736
+ case "primitive":
4737
+ return `p${t.name}`;
4738
+ case "any":
4739
+ case "unknown":
4740
+ case "never":
4741
+ return t.kind;
4742
+ default:
4743
+ return formatType(t);
4744
+ }
4745
+ }
4617
4746
  function union(types) {
4618
4747
  const flat = [];
4619
4748
  for (const t of types) {
@@ -4625,7 +4754,7 @@ function union(types) {
4625
4754
  const seen = /* @__PURE__ */ new Map();
4626
4755
  for (const t of flat) {
4627
4756
  if (t.kind === "never") continue;
4628
- const key = formatType(t);
4757
+ const key = memberKey(t);
4629
4758
  if (!seen.has(key)) seen.set(key, t);
4630
4759
  }
4631
4760
  let members = reduceSubtypes([...seen.values()]);
@@ -4654,7 +4783,7 @@ function intersection(types) {
4654
4783
  const seen = /* @__PURE__ */ new Map();
4655
4784
  for (const t of flat) {
4656
4785
  if (t.kind === "unknown") continue;
4657
- seen.set(formatType(t), t);
4786
+ seen.set(memberKey(t), t);
4658
4787
  }
4659
4788
  const members = [...seen.values()];
4660
4789
  if (members.length === 0) return unknownType;
@@ -4681,9 +4810,9 @@ function widen(t) {
4681
4810
  case "literal":
4682
4811
  return primitive(t.base);
4683
4812
  case "array":
4684
- return arrayOf(widen(t.element));
4813
+ return { ...arrayOf(widen(t.element)), readonly: t.readonly };
4685
4814
  case "tuple":
4686
- return tuple(t.elements.map(widen), t.isPack);
4815
+ return { ...tuple(t.elements.map(widen), t.rest && widen(t.rest)), readonly: t.readonly };
4687
4816
  case "object": {
4688
4817
  if (t.frozen || t.class) return t;
4689
4818
  const entries = [];
@@ -4781,15 +4910,21 @@ function isAssignableInner(a, b) {
4781
4910
  return b.kind === "primitive" && b.name === "string";
4782
4911
  }
4783
4912
  if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
4913
+ if ((a.kind === "array" || a.kind === "tuple") && a.readonly && (b.kind === "array" || b.kind === "tuple") && !b.readonly) return false;
4784
4914
  if (a.kind === "array") {
4785
4915
  if (b.kind === "array") return isAssignable(a.element, b.element);
4916
+ if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
4786
4917
  return false;
4787
4918
  }
4788
4919
  if (a.kind === "tuple") {
4789
4920
  if (b.kind === "tuple") {
4790
- return a.elements.length === b.elements.length && a.elements.every((t, i) => isAssignable(t, b.elements[i]));
4921
+ if (a.elements.length < b.elements.length) return false;
4922
+ if (!b.elements.every((t, i) => isAssignable(a.elements[i], t))) return false;
4923
+ const extra = [...a.elements.slice(b.elements.length), ...a.rest ? [a.rest] : []];
4924
+ if (!b.rest) return extra.length === 0;
4925
+ return extra.every((t) => isAssignable(t, b.rest));
4791
4926
  }
4792
- if (b.kind === "array") return a.elements.every((t) => isAssignable(t, b.element));
4927
+ if (b.kind === "array") return tupleMembers(a).every((t) => isAssignable(t, b.element));
4793
4928
  return false;
4794
4929
  }
4795
4930
  if (a.kind === "object") {
@@ -4838,7 +4973,7 @@ function sameRefTarget(a, b) {
4838
4973
  return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
4839
4974
  }
4840
4975
  function isNoValue(t) {
4841
- return t.kind === "tuple" && t.elements.length === 0;
4976
+ return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
4842
4977
  }
4843
4978
  function equalTypes(a, b) {
4844
4979
  return formatType(a) === formatType(b);
@@ -4945,7 +5080,7 @@ function containsFreeTypeParam(t, seen, bound) {
4945
5080
  case "array":
4946
5081
  return containsTypeParam(t.element, seen, bound);
4947
5082
  case "tuple":
4948
- return t.elements.some((e) => containsTypeParam(e, seen, bound));
5083
+ return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
4949
5084
  case "union":
4950
5085
  case "intersection":
4951
5086
  return t.types.some((m) => containsTypeParam(m, seen, bound));
@@ -4990,15 +5125,19 @@ function matchInfer(arg, pattern, out) {
4990
5125
  switch (pattern.kind) {
4991
5126
  case "array":
4992
5127
  if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
4993
- if (arg.kind === "tuple") return matchInfer(union(arg.elements), pattern.element, out);
5128
+ if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
4994
5129
  return false;
4995
5130
  case "tuple":
4996
5131
  if (arg.kind !== "tuple" || arg.elements.length !== pattern.elements.length) return false;
5132
+ if (pattern.rest && !matchInfer(arg.rest ?? neverType, pattern.rest, out)) return false;
4997
5133
  return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
4998
5134
  case "function": {
4999
5135
  if (arg.kind !== "function") return false;
5000
5136
  if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
5001
- out.set(pattern.varargs.name, tuple(arg.params.map((p) => p.type)));
5137
+ out.set(pattern.varargs.name, tuple(
5138
+ arg.params.map((p) => p.type),
5139
+ arg.varargs && !arg.restIsWhole ? arg.varargs : void 0
5140
+ ));
5002
5141
  } else {
5003
5142
  for (let i = 0; i < pattern.params.length; i++) {
5004
5143
  if (!arg.params[i]) return false;
@@ -5072,7 +5211,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
5072
5211
  collectTypeParams(t.element, out, seen);
5073
5212
  return;
5074
5213
  case "tuple":
5075
- for (const e of t.elements) collectTypeParams(e, out, seen);
5214
+ for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
5076
5215
  return;
5077
5216
  case "union":
5078
5217
  case "intersection":
@@ -5117,11 +5256,10 @@ function formatTypeUncached(t) {
5117
5256
  case "literal":
5118
5257
  return t.base === "string" ? JSON.stringify(t.value) : String(t.value);
5119
5258
  case "array":
5120
- return `${formatAtom(t.element)}[]`;
5259
+ return `${t.readonly ? "readonly " : ""}${formatAtom(t.element)}[]`;
5121
5260
  case "tuple": {
5122
- if (!t.elements.length) return "()";
5123
- const inner = t.elements.map(formatType).join(", ");
5124
- return t.isPack ? `(${inner})` : `[${inner}]`;
5261
+ const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
5262
+ return `${t.readonly ? "readonly " : ""}[${inner.join(", ")}]`;
5125
5263
  }
5126
5264
  case "object": {
5127
5265
  if (t.name) {
@@ -5145,7 +5283,7 @@ function formatTypeUncached(t) {
5145
5283
  return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
5146
5284
  }).join(", ")}>` : "";
5147
5285
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
5148
- if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
5286
+ if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
5149
5287
  return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
5150
5288
  }
5151
5289
  case "typeParam":
@@ -5349,6 +5487,11 @@ function moduleExports(program, scopes, types, resolveModule) {
5349
5487
  }
5350
5488
  return { values, types: exportedTypes, default: defaultType };
5351
5489
  }
5490
+ function isBelow(k, key) {
5491
+ if (!k.startsWith(key) || k.length === key.length) return false;
5492
+ const next = k[key.length];
5493
+ return next === "." || next === "#" || next === "[";
5494
+ }
5352
5495
  function bindKey(id) {
5353
5496
  return `$${id}`;
5354
5497
  }
@@ -5366,9 +5509,6 @@ function mergeEnv(a, b, base) {
5366
5509
  function mergeIndexer(a, b) {
5367
5510
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5368
5511
  }
5369
- function producesMultipleValues(e) {
5370
- return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
5371
- }
5372
5512
  function isFreshLiteralExpr(e) {
5373
5513
  if (!e) return false;
5374
5514
  switch (e.type) {
@@ -5405,11 +5545,11 @@ function collectInferNames(node) {
5405
5545
  walk(n.element);
5406
5546
  return;
5407
5547
  case "ParenthesizedTypeNode":
5408
- case "VariadicTypeNode":
5409
5548
  walk(n.typeAnnotation);
5410
5549
  return;
5411
5550
  case "TupleTypeNode":
5412
5551
  n.elements.forEach(walk);
5552
+ walk(n.rest);
5413
5553
  return;
5414
5554
  case "UnionTypeNode":
5415
5555
  case "IntersectionTypeNode":
@@ -5427,7 +5567,6 @@ function collectInferNames(node) {
5427
5567
  return;
5428
5568
  case "FunctionTypeNode":
5429
5569
  n.params.forEach((pp) => walk(pp.typeAnnotation));
5430
- walk(n.varargType);
5431
5570
  walk(n.returnType);
5432
5571
  return;
5433
5572
  case "TableTypeNode":
@@ -5472,6 +5611,75 @@ function callsSuper(block) {
5472
5611
  });
5473
5612
  return found;
5474
5613
  }
5614
+ var ALL_FIELDS = /* @__PURE__ */ new Set();
5615
+ function definitelyAssigned(block) {
5616
+ const names = /* @__PURE__ */ new Set();
5617
+ for (const statement of block.statements) {
5618
+ switch (statement.type) {
5619
+ case "AssignmentStatement":
5620
+ case "CompoundAssignmentStatement":
5621
+ for (const name of assignedFields({ ...block, statements: [statement] })) names.add(name);
5622
+ break;
5623
+ case "DoStatement": {
5624
+ const inner = definitelyAssigned(statement.body);
5625
+ if (!inner) return void 0;
5626
+ for (const name of inner) names.add(name);
5627
+ break;
5628
+ }
5629
+ case "IfStatement": {
5630
+ if (!statement.alternate) break;
5631
+ const arms = [...statement.clauses.map((c) => c.body), statement.alternate].map(definitelyAssigned);
5632
+ const finishing = arms.filter((arm) => arm !== void 0);
5633
+ if (!finishing.length) return void 0;
5634
+ for (const name of finishing[0]) {
5635
+ if (finishing.every((arm) => arm.has(name))) names.add(name);
5636
+ }
5637
+ break;
5638
+ }
5639
+ case "ReturnStatement":
5640
+ return void 0;
5641
+ case "CallStatement": {
5642
+ const call = statement.expression;
5643
+ if (call.type === "CallExpression" && call.callee.type === "Identifier" && call.callee.name === "error") {
5644
+ return void 0;
5645
+ }
5646
+ break;
5647
+ }
5648
+ }
5649
+ }
5650
+ return names;
5651
+ }
5652
+ function withoutReceiver(t) {
5653
+ if (t.kind === "intersection") return intersection(t.types.map(withoutReceiver));
5654
+ if (t.kind !== "function") return t;
5655
+ const first = t.params[0]?.name;
5656
+ if (first !== "this" && first !== "self") return t;
5657
+ return { ...t, params: t.params.slice(1) };
5658
+ }
5659
+ var METAMETHOD_OPERANDS = {
5660
+ __add: 1,
5661
+ __sub: 1,
5662
+ __mul: 1,
5663
+ __div: 1,
5664
+ __idiv: 1,
5665
+ __mod: 1,
5666
+ __pow: 1,
5667
+ __concat: 1,
5668
+ __eq: 1,
5669
+ __lt: 1,
5670
+ __le: 1,
5671
+ __unm: 0,
5672
+ __len: 0,
5673
+ __tostring: 0,
5674
+ __iter: 0,
5675
+ __call: void 0
5676
+ };
5677
+ var METAMETHOD_RETURNS = {
5678
+ __tostring: stringType,
5679
+ __eq: booleanType,
5680
+ __lt: booleanType,
5681
+ __le: booleanType
5682
+ };
5475
5683
  function assignedFields(block) {
5476
5684
  const names = /* @__PURE__ */ new Set();
5477
5685
  walkNodes(block, (node) => {
@@ -5497,7 +5705,7 @@ function walkNodes(root, visit) {
5497
5705
  if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5498
5706
  }
5499
5707
  }
5500
- var AliasMap = class extends Map {
5708
+ var LazyMap = class extends Map {
5501
5709
  pending = /* @__PURE__ */ new Map();
5502
5710
  defer(name, resolve5) {
5503
5711
  super.delete(name);
@@ -5574,7 +5782,7 @@ function expressionLabel(e, depth = 0) {
5574
5782
  case "IndexExpression": {
5575
5783
  const o = expressionLabel(e.object, depth + 1);
5576
5784
  const i = e.index.type === "StringLiteral" ? JSON.stringify(e.index.value) : e.index.type === "NumberLiteral" ? e.index.raw : e.index.type === "Identifier" ? e.index.name : "...";
5577
- return o === void 0 ? void 0 : `${o}[${i}]`;
5785
+ return o === void 0 ? void 0 : `${o}${e.optional ? "?." : ""}[${i}]`;
5578
5786
  }
5579
5787
  case "ParenthesizedExpression": {
5580
5788
  const inner = expressionLabel(e.expression, depth + 1);
@@ -5612,20 +5820,21 @@ var TypeAnalyzer = class {
5612
5820
  scopes;
5613
5821
  options;
5614
5822
  typeOf = /* @__PURE__ */ new Map();
5615
- bindingType = /* @__PURE__ */ new Map();
5823
+ bindingType = new LazyMap();
5616
5824
  narrowedTypeOf = /* @__PURE__ */ new Map();
5617
5825
  typeOfTypeNode = /* @__PURE__ */ new Map();
5618
5826
  expectedTypeOf = /* @__PURE__ */ new Map();
5619
- iteratesValues = /* @__PURE__ */ new Set();
5827
+ loops = /* @__PURE__ */ new Map();
5620
5828
  /** Public: each alias resolved once (generic aliases keep their params as
5621
5829
  * `typeParam` nodes in the body). */
5622
- aliases = new AliasMap();
5830
+ aliases = new LazyMap();
5623
5831
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
5624
5832
  aliasDefs = /* @__PURE__ */ new Map();
5625
5833
  /** Put on every ref to one of `aliasDefs`, so a module that imports the
5626
5834
  * type expands the ref here rather than by its name there. */
5627
5835
  origin = { expand: (t) => this.expand(t) };
5628
- /** See `resolveClass`. */
5836
+ /** See `resolveClass`. Shared with other analyses over the same libraries
5837
+ * when none of their names is redefined here; see `shareableClasses`. */
5629
5838
  classTypes = /* @__PURE__ */ new WeakMap();
5630
5839
  /** See `instanceType` — one instance type per `class ... end`. */
5631
5840
  instanceTypes = /* @__PURE__ */ new WeakMap();
@@ -5633,8 +5842,12 @@ var TypeAnalyzer = class {
5633
5842
  /** Generic parameters currently in lexical scope (alias body / generic fn),
5634
5843
  * with their `extends` constraints resolved. */
5635
5844
  typeParamScope = [];
5636
- /** Global types contributed by `declare` statements (libs, then this program). */
5637
- libGlobalTypes = /* @__PURE__ */ new Map();
5845
+ /** Global types contributed by `declare` statements (libs, then this program).
5846
+ * A library's are resolved when the name is first used: an engine's
5847
+ * definitions declare hundreds of globals, and a script uses a few. */
5848
+ libGlobalTypes = new LazyMap();
5849
+ /** Each library `declare` of a name, in order, until it is resolved. */
5850
+ libDeclares = /* @__PURE__ */ new Map();
5638
5851
  /** Declaration node -> binding, built once so `bindingIdByName` is O(1)
5639
5852
  * instead of a scan of every binding per declaration site. */
5640
5853
  bindingByDecl = /* @__PURE__ */ new Map();
@@ -5667,6 +5880,11 @@ var TypeAnalyzer = class {
5667
5880
  /** Recursion guard for `preVisitBody`. */
5668
5881
  preVisitDepth = 0;
5669
5882
  run() {
5883
+ const shared = shareableClasses(this.options.libs ?? [], this.program);
5884
+ if (shared) {
5885
+ this.classTypes = shared.classTypes;
5886
+ this.classMembers = shared.classMembers;
5887
+ }
5670
5888
  this.registerAliasDefs(preludeProgram().body, true);
5671
5889
  for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
5672
5890
  this.registerAliasDefs(this.program.body);
@@ -5679,8 +5897,11 @@ var TypeAnalyzer = class {
5679
5897
  this.indexDeclarations();
5680
5898
  for (const [name, id] of this.scopes.globalsByName) {
5681
5899
  if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
5682
- const t = this.options.globalTypes?.[name] ?? this.libGlobalTypes.get(name) ?? anyType;
5683
- this.bindingType.set(id, t);
5900
+ const given = this.options.globalTypes?.[name];
5901
+ if (given) this.bindingType.set(id, given);
5902
+ else if (this.libGlobalTypes.has(name)) this.bindingType.defer(id, () => this.libGlobalTypes.get(name) ?? anyType);
5903
+ else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(unknownType));
5904
+ else this.bindingType.set(id, anyType);
5684
5905
  }
5685
5906
  setAliasExpander((t) => this.expand(t));
5686
5907
  setDeferredBound((t) => this.deferredBound(t));
@@ -5700,7 +5921,7 @@ var TypeAnalyzer = class {
5700
5921
  typeOfTypeNode: this.typeOfTypeNode,
5701
5922
  expectedTypeOf: this.expectedTypeOf,
5702
5923
  aliases: this.resolveDeferredAliases(),
5703
- iteratesValues: this.iteratesValues,
5924
+ loops: this.loops,
5704
5925
  diagnostics: this.diagnostics
5705
5926
  };
5706
5927
  }
@@ -6052,28 +6273,55 @@ var TypeAnalyzer = class {
6052
6273
  type.name = `typeof ${this.className(stmt)}`;
6053
6274
  const shape = this.shapeOf(stmt);
6054
6275
  const local = this.superDecl(stmt);
6055
- const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
6276
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
6056
6277
  if (parent?.kind === "object") {
6057
6278
  for (const [key, property] of parent.properties) {
6058
6279
  if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
6059
6280
  }
6060
6281
  }
6061
6282
  for (const [key, property] of shape.statics) type.properties.set(key, property);
6062
- const constructor = this.constructorType(stmt);
6283
+ if (shape.filling) {
6284
+ (shape.filled ??= []).push(() => {
6285
+ for (const [key, property] of shape.statics) type.properties.set(key, property);
6286
+ });
6287
+ }
6063
6288
  const params = this.classTypeParams(stmt);
6064
- type.properties.set("new", {
6065
- type: fn(
6066
- constructor?.params.filter((p) => p.name !== "this") ?? [],
6067
- this.selfTypeOf(stmt),
6068
- constructor?.varargs,
6069
- params.map((p) => p.name)
6070
- ),
6289
+ let settled;
6290
+ const construct = {
6071
6291
  optional: false,
6072
- readonly: true
6292
+ readonly: true,
6293
+ // Kept, so a class extending it can read what it takes; reaching
6294
+ // it from outside is the error.
6295
+ ...stmt.isAbstract ? { abstract: true } : {}
6296
+ };
6297
+ Object.defineProperty(construct, "type", {
6298
+ enumerable: true,
6299
+ get: () => {
6300
+ if (settled) return settled;
6301
+ const constructor = this.constructorType(stmt);
6302
+ const made = fn(
6303
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
6304
+ this.selfTypeOf(stmt),
6305
+ constructor?.varargs,
6306
+ params.map((p) => p.name)
6307
+ );
6308
+ if (!this.shapeOf(stmt).filling) settled = made;
6309
+ return made;
6310
+ }
6073
6311
  });
6312
+ type.properties.set("new", construct);
6074
6313
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
6075
6314
  return type;
6076
6315
  }
6316
+ /** The value side of the class `superclass` names when it is not
6317
+ * declared in this file: what the name it refers to holds — an import
6318
+ * brings the class table with it — or else a class of that name. */
6319
+ classStaticsOf(superclass) {
6320
+ const id = this.bindingIdOf(superclass);
6321
+ const bound = id !== void 0 ? this.expand(this.bindingType.get(id) ?? unknownType) : void 0;
6322
+ if (bound?.kind === "object") return bound;
6323
+ return this.classStaticsByNameType(superclass.name);
6324
+ }
6077
6325
  /** The value side of a class named by a binding rather than by a
6078
6326
  * declaration in this file — an imported one. */
6079
6327
  classStaticsByNameType(name) {
@@ -6134,20 +6382,26 @@ var TypeAnalyzer = class {
6134
6382
  } finally {
6135
6383
  this.emitDiagnostics = wasEmitting;
6136
6384
  shape.filling = false;
6385
+ for (const then of shape.filled ?? []) then();
6386
+ shape.filled = void 0;
6137
6387
  }
6138
6388
  return shape;
6139
6389
  }
6140
6390
  fillShape(stmt, shape) {
6141
6391
  const className = this.className(stmt);
6142
6392
  const put = (isStatic, name, property) => {
6143
- const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && m.accessibility === "private");
6144
- const marked = member ? { ...property, private: { owner: stmt, className } } : property;
6393
+ const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && (m.accessibility === "private" || m.accessibility === "protected"));
6394
+ const marked = member ? { ...property, private: { owner: stmt, className, ...member.accessibility === "protected" ? { protected: true } : {} } } : property;
6145
6395
  (isStatic ? shape.statics : shape.instance).set(name, marked);
6146
6396
  };
6147
6397
  for (const member of stmt.members) {
6148
6398
  if (member.type !== "ClassField") continue;
6149
6399
  const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
6150
- put(member.isStatic, member.name.name, { type, optional: false });
6400
+ put(member.isStatic, member.name.name, {
6401
+ type,
6402
+ optional: false,
6403
+ ...member.isReadonly ? { readonly: true } : {}
6404
+ });
6151
6405
  }
6152
6406
  for (const member of stmt.members) {
6153
6407
  switch (member.type) {
@@ -6156,7 +6410,11 @@ var TypeAnalyzer = class {
6156
6410
  case "ClassMethod": {
6157
6411
  this.paramsFromSignatures(member.func, member.signatures);
6158
6412
  const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
6159
- put(member.isStatic, member.name.name, { type, optional: false });
6413
+ put(member.isStatic, member.name.name, {
6414
+ type,
6415
+ optional: false,
6416
+ ...member.isAbstract ? { abstract: true } : {}
6417
+ });
6160
6418
  break;
6161
6419
  }
6162
6420
  case "ClassAccessor": {
@@ -6212,7 +6470,14 @@ var TypeAnalyzer = class {
6212
6470
  for (const signature of member.signatures ?? []) {
6213
6471
  this.checkParamOrder(signature.params, member);
6214
6472
  }
6215
- this.visitFunctionBody(member.func, env);
6473
+ if (member.type === "ClassMethod" && member.isAbstract) continue;
6474
+ const constructing = this.constructing;
6475
+ if (member.type === "ClassConstructor") this.constructing = stmt;
6476
+ try {
6477
+ this.visitFunctionBody(member.func, env);
6478
+ } finally {
6479
+ this.constructing = constructing;
6480
+ }
6216
6481
  }
6217
6482
  });
6218
6483
  }));
@@ -6259,13 +6524,148 @@ var TypeAnalyzer = class {
6259
6524
  if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
6260
6525
  report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
6261
6526
  }
6262
- const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
6527
+ this.checkAbstractMembers(stmt);
6528
+ this.checkOverrides(stmt);
6529
+ this.checkImplements(stmt);
6530
+ this.checkMetamethods(stmt);
6531
+ const assigned = constructor ? definitelyAssigned(constructor.func.body) ?? ALL_FIELDS : /* @__PURE__ */ new Set();
6263
6532
  for (const member of stmt.members) {
6264
6533
  if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6265
- if (assigned.has(member.name.name)) continue;
6534
+ if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
6266
6535
  const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6267
6536
  if (isAssignable(nilType, type)) continue;
6268
- report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
6537
+ 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`);
6538
+ }
6539
+ }
6540
+ /** The class whose constructor is being checked: it, and only it, may
6541
+ * assign the `readonly` fields it declares. */
6542
+ constructing;
6543
+ /** A class that is not abstract has a `new`, so everything its instances
6544
+ * promise has to be there: no abstract member of its own, and every one
6545
+ * it inherits written. */
6546
+ checkAbstractMembers(stmt) {
6547
+ if (stmt.isAbstract) return;
6548
+ const name = this.className(stmt);
6549
+ for (const member of stmt.members) {
6550
+ if (member.type === "ClassMethod" && member.isAbstract) {
6551
+ this.diagnostics.push({
6552
+ node: member.name,
6553
+ message: `'${member.name.name}' is abstract, so '${name}' has to be an 'abstract class'`
6554
+ });
6555
+ }
6556
+ }
6557
+ const own = this.shapeOf(stmt).instance;
6558
+ const missing = [...this.instanceType(stmt).properties].filter(([key, property]) => property.abstract && !own.has(key)).map(([key]) => `'${key}'`);
6559
+ if (missing.length) {
6560
+ this.diagnostics.push({
6561
+ node: stmt.name ?? stmt,
6562
+ 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`
6563
+ });
6564
+ }
6565
+ }
6566
+ /** A member written again in a class that extends another has to fit
6567
+ * where the base's stood — anything reading an instance as the base
6568
+ * would otherwise be told wrong. `override` says a member is such a one,
6569
+ * and is wrong on a member that is not. */
6570
+ checkOverrides(stmt) {
6571
+ const base = this.baseInstance(stmt);
6572
+ const name = this.className(stmt);
6573
+ const own = this.shapeOf(stmt).instance;
6574
+ for (const member of stmt.members) {
6575
+ if (member.type === "ClassConstructor" || member.isStatic) continue;
6576
+ const key = member.name.name;
6577
+ const inherited = CLASS_LINKS.has(key) ? void 0 : base?.properties.get(key);
6578
+ if (member.isOverride && !inherited) {
6579
+ this.diagnostics.push({
6580
+ node: member.name,
6581
+ 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`
6582
+ });
6583
+ continue;
6584
+ }
6585
+ const mine = own.get(key);
6586
+ if (!inherited || !mine || inherited.private && !inherited.private.protected) continue;
6587
+ const written = withoutReceiver(mine.type);
6588
+ const expected = withoutReceiver(inherited.type);
6589
+ if (isAssignable(written, expected)) continue;
6590
+ this.diagnostics.push({
6591
+ node: member.name,
6592
+ message: `'${key}' in '${name}' does not fit the '${key}' of '${stmt.superclass.name}' it replaces: '${briefType(written)}' is not assignable to '${briefType(expected)}'`
6593
+ });
6594
+ }
6595
+ }
6596
+ /** `implements Shape`: every member the shape names, of a type that fits.
6597
+ * A method's own `this` is left out of the comparison on both sides. */
6598
+ checkImplements(stmt) {
6599
+ if (!stmt.implements?.length) return;
6600
+ const name = this.className(stmt);
6601
+ const instance = this.instanceType(stmt).properties;
6602
+ for (const node of stmt.implements) {
6603
+ const shown = node.type === "TypeReference" ? node.namespace ? `${node.namespace}.${node.base}` : node.base : void 0;
6604
+ const target = this.expand(this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(node)));
6605
+ if (target.kind === "any") continue;
6606
+ if (target.kind !== "object") {
6607
+ this.diagnostics.push({
6608
+ node,
6609
+ message: `A class can only implement an object type or a class, not '${briefType(target)}'`
6610
+ });
6611
+ continue;
6612
+ }
6613
+ const label = shown ?? briefType(target);
6614
+ const missing = [];
6615
+ for (const [key, wanted] of target.properties) {
6616
+ const have = instance.get(key);
6617
+ if (!have) {
6618
+ if (!wanted.optional) missing.push(`'${key}'`);
6619
+ continue;
6620
+ }
6621
+ const written = withoutReceiver(have.type);
6622
+ const expected = withoutReceiver(wanted.optional ? optional(wanted.type) : wanted.type);
6623
+ if (isAssignable(written, expected)) continue;
6624
+ this.diagnostics.push({
6625
+ node,
6626
+ message: `'${name}' does not implement '${label}': its '${key}' is '${briefType(written)}', not '${briefType(expected)}'`
6627
+ });
6628
+ }
6629
+ if (missing.length) {
6630
+ this.diagnostics.push({
6631
+ node,
6632
+ message: `'${name}' does not implement '${label}': ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing`
6633
+ });
6634
+ }
6635
+ }
6636
+ }
6637
+ /** A method named for a metamethod *is* one — the class table is its
6638
+ * instances' metatable — so it has to take what Luau hands it and give
6639
+ * back what Luau expects. */
6640
+ checkMetamethods(stmt) {
6641
+ const instance = this.shapeOf(stmt).instance;
6642
+ for (const member of stmt.members) {
6643
+ if (member.type !== "ClassMethod" || !(member.name.name in METAMETHOD_OPERANDS)) continue;
6644
+ const key = member.name.name;
6645
+ if (member.isStatic) {
6646
+ this.diagnostics.push({
6647
+ node: member.name,
6648
+ message: `'${key}' is a metamethod of the instances; it cannot be static`
6649
+ });
6650
+ continue;
6651
+ }
6652
+ const operands = METAMETHOD_OPERANDS[key];
6653
+ const written = member.func.params.filter((p) => p.name !== "this").length;
6654
+ if (operands !== void 0 && (written !== operands || member.func.hasVarargs)) {
6655
+ this.diagnostics.push({
6656
+ node: member.name,
6657
+ message: operands === 0 ? `'${key}' takes nothing besides 'this'` : `'${key}' takes one operand besides 'this'`
6658
+ });
6659
+ continue;
6660
+ }
6661
+ const required = METAMETHOD_RETURNS[key];
6662
+ const returns = this.overloadsOf(instance.get(key)?.type ?? anyType)[0]?.returns;
6663
+ if (required && returns && !isAssignable(returns, required)) {
6664
+ this.diagnostics.push({
6665
+ node: member.name,
6666
+ message: `'${key}' has to return ${briefType(required)}, not '${briefType(returns)}'`
6667
+ });
6668
+ }
6269
6669
  }
6270
6670
  }
6271
6671
  /** `extends` must name a class, and the chain must end. */
@@ -6323,19 +6723,42 @@ var TypeAnalyzer = class {
6323
6723
  harvestDeclares(block, own = false) {
6324
6724
  for (const stmt of block.statements) {
6325
6725
  if (stmt.type !== "DeclareStatement") continue;
6326
- if (own && (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name)))) {
6726
+ if (!own) {
6727
+ const declares = this.libDeclares.get(stmt.name);
6728
+ if (declares) declares.push(stmt);
6729
+ else {
6730
+ this.libDeclares.set(stmt.name, [stmt]);
6731
+ this.libGlobalTypes.defer(stmt.name, () => this.resolveLibDeclares(stmt.name));
6732
+ }
6733
+ continue;
6734
+ }
6735
+ if (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name))) {
6327
6736
  this.deferredDeclares.set(stmt.name, stmt);
6328
6737
  continue;
6329
6738
  }
6330
- const t = this.resolveType(stmt.valueType);
6331
- const prev = this.libGlobalTypes.get(stmt.name);
6332
- const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6333
- this.libGlobalTypes.set(
6334
- stmt.name,
6335
- overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
6336
- );
6739
+ this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
6337
6740
  }
6338
6741
  }
6742
+ /** Every library `declare` of `name`, in order. They are written at the
6743
+ * top level of their files, so they are read as if there — outside any
6744
+ * generic the use that asked for them happens to sit in. */
6745
+ resolveLibDeclares(name) {
6746
+ const scope = this.typeParamScope.splice(0);
6747
+ try {
6748
+ let type;
6749
+ for (const stmt of this.libDeclares.get(name) ?? []) type = this.declaredOver(type, stmt);
6750
+ return type ?? anyType;
6751
+ } finally {
6752
+ this.typeParamScope.push(...scope);
6753
+ }
6754
+ }
6755
+ /** A `declare` on top of what earlier ones said about the same name: a
6756
+ * function adds an overload, a table adds members. */
6757
+ declaredOver(prev, stmt) {
6758
+ const t = this.resolveType(stmt.valueType);
6759
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6760
+ return overload ? intersection([prev, t]) : this.mergeDeclared(prev, t);
6761
+ }
6339
6762
  resolveAllAliases() {
6340
6763
  for (const [name, def] of this.aliasDefs) {
6341
6764
  if (def.class && !this.program.body.statements.includes(def.class)) {
@@ -6381,11 +6804,10 @@ var TypeAnalyzer = class {
6381
6804
  }
6382
6805
  imported;
6383
6806
  /** What a `return` gives, against what the function declared. */
6384
- checkReturn(stmt, declared, types, sources, env) {
6807
+ checkReturn(stmt, declared, actual, env) {
6385
6808
  if (!declared || !this.emitDiagnostics) return;
6386
6809
  if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
6387
- const actual = stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true);
6388
- const source = stmt.arguments.length === 1 ? sources[0] : void 0;
6810
+ const source = stmt.argument;
6389
6811
  const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
6390
6812
  if (fits) return;
6391
6813
  this.diagnostics.push({
@@ -6533,21 +6955,12 @@ var TypeAnalyzer = class {
6533
6955
  this.instantiationDepth--;
6534
6956
  }
6535
6957
  }
6536
- /** Pair written type arguments with the parameters they instantiate. A
6537
- * pack parameter (`T...`) takes every argument from its position on, as
6538
- * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
6539
- * and `Signal<()>` to the empty pack. Left out, a parameter takes its
6540
- * default (`T... = ...any` is `any`), or `unknown`. */
6958
+ /** Pair written type arguments with the parameters they instantiate.
6959
+ * Left out, a parameter takes its default, or `unknown`. */
6541
6960
  bindTypeArguments(params, args) {
6542
6961
  const subst = /* @__PURE__ */ new Map();
6543
6962
  params.forEach((p, i) => {
6544
- let arg = args[i];
6545
- if (p.isPack && i < args.length) {
6546
- const rest = args.slice(i);
6547
- const single = rest.length === 1 ? rest[0] : void 0;
6548
- arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
6549
- }
6550
- subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
6963
+ subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
6551
6964
  });
6552
6965
  return subst;
6553
6966
  }
@@ -6637,10 +7050,18 @@ var TypeAnalyzer = class {
6637
7050
  return literal(node.value);
6638
7051
  case "TypeLiteralNumber":
6639
7052
  return literal(node.value);
6640
- case "ArrayTypeNode":
6641
- return arrayOf(this.resolveType(node.element));
6642
- case "TupleTypeNode":
6643
- return tuple(node.elements.map((e) => this.resolveType(e)));
7053
+ case "ArrayTypeNode": {
7054
+ const made = arrayOf(this.resolveType(node.element));
7055
+ return node.isReadonly ? { ...made, readonly: true } : made;
7056
+ }
7057
+ case "TupleTypeNode": {
7058
+ const rest = node.rest && this.expand(this.resolveType(node.rest));
7059
+ const made = tuple(
7060
+ node.elements.map((e) => this.resolveType(e)),
7061
+ rest ? rest.kind === "array" ? rest.element : rest.kind === "any" ? anyType : unknownType : void 0
7062
+ );
7063
+ return node.isReadonly && made.kind === "tuple" ? { ...made, readonly: true } : made;
7064
+ }
6644
7065
  case "UnionTypeNode":
6645
7066
  return union(node.types.map((t) => this.resolveType(t)));
6646
7067
  case "IntersectionTypeNode":
@@ -6676,15 +7097,13 @@ var TypeAnalyzer = class {
6676
7097
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
6677
7098
  optional: p.optional
6678
7099
  }));
6679
- const restParam = node.params.find((p) => p.rest);
6680
- const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
6681
- return this.withTypeParamDefaults(fn(
7100
+ return this.withTypeParamDefaults(this.withRest(fn(
6682
7101
  params,
6683
7102
  this.resolveType(node.returnType),
6684
- restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
7103
+ void 0,
6685
7104
  names,
6686
7105
  this.resolvePredicate(node.predicate, params)
6687
- ), node.generics);
7106
+ ), node.params), node.generics);
6688
7107
  });
6689
7108
  }
6690
7109
  case "TypeofTypeNode": {
@@ -6748,13 +7167,6 @@ var TypeAnalyzer = class {
6748
7167
  })
6749
7168
  );
6750
7169
  }
6751
- case "VariadicTypeNode":
6752
- return this.resolveType(node.typeAnnotation);
6753
- case "TypePackNode": {
6754
- if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
6755
- if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
6756
- return tuple(node.types.map((t) => this.resolveType(t)), true);
6757
- }
6758
7170
  }
6759
7171
  }
6760
7172
  // --------------------------------------------------------
@@ -6820,15 +7232,18 @@ var TypeAnalyzer = class {
6820
7232
  case "array":
6821
7233
  return arrayOf(this.reduceType(t.element));
6822
7234
  case "tuple":
6823
- return tuple(t.elements.map((e) => this.reduceType(e)), t.isPack);
7235
+ return tuple(t.elements.map((e) => this.reduceType(e)), t.rest && this.reduceType(t.rest));
6824
7236
  case "function":
6825
- return fn(
6826
- t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
6827
- this.reduceType(t.returns),
6828
- t.varargs && this.reduceType(t.varargs),
6829
- t.typeParams,
6830
- t.predicate
6831
- );
7237
+ return {
7238
+ ...fn(
7239
+ t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
7240
+ this.reduceType(t.returns),
7241
+ t.varargs && this.reduceType(t.varargs),
7242
+ t.typeParams,
7243
+ t.predicate
7244
+ ),
7245
+ ...t.restIsWhole ? { restIsWhole: true } : {}
7246
+ };
6832
7247
  case "object": {
6833
7248
  if (t.class) return t;
6834
7249
  const entries = [];
@@ -7043,7 +7458,8 @@ var TypeAnalyzer = class {
7043
7458
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
7044
7459
  }
7045
7460
  });
7046
- const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
7461
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7462
+ this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
7047
7463
  stmt.names.forEach((target, i) => {
7048
7464
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
7049
7465
  const source = sources[i];
@@ -7132,7 +7548,8 @@ var TypeAnalyzer = class {
7132
7548
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7133
7549
  }
7134
7550
  });
7135
- const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
7551
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7552
+ this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7136
7553
  stmt.targets.forEach((target, i) => {
7137
7554
  const vt = valueTypes[i] ?? unknownType;
7138
7555
  const source = sources[i];
@@ -7219,23 +7636,26 @@ var TypeAnalyzer = class {
7219
7636
  return;
7220
7637
  }
7221
7638
  case "GenericForStatement": {
7222
- const iterTypes = stmt.iterators.map((it) => this.infer(it, env));
7639
+ const sourceType = this.infer(stmt.iterator, env);
7223
7640
  const bodyEnv = forkEnv(env);
7224
- const rows = stmt.variables.length >= 2 ? this.iterationRows(stmt.iterators[0], iterTypes[0]) : void 0;
7641
+ const rows = this.iterationRows(stmt.iterator, sourceType);
7225
7642
  if (rows) {
7226
- const [key, value] = stmt.variables;
7227
- this.bindPattern(key, union(rows.map((r) => r[0])), bodyEnv, "keep");
7228
- this.bindPattern(value, union(rows.map((r) => r[1])), bodyEnv, "keep");
7229
- stmt.variables.slice(2).forEach((v) => this.bindPattern(v, unknownType, bodyEnv, "widen"));
7230
- const keyId = key.type === "IdentifierPattern" ? this.bindingIdByName(key.name, key) : void 0;
7231
- const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
7232
- if (keyId !== void 0 && valueId !== void 0) this.correlateBindings(bodyEnv, [keyId, valueId], rows);
7643
+ this.bindPattern(
7644
+ stmt.variable,
7645
+ union(rows.map((row) => tuple(row))),
7646
+ bodyEnv,
7647
+ stmt.kind === "const" ? "keep" : "widen"
7648
+ );
7649
+ const parts = stmt.variable.type === "ArrayPattern" && !stmt.variable.rest ? stmt.variable.elements.slice(0, 2).map((el) => el?.value) : [];
7650
+ const ids = parts.map((p) => p?.type === "IdentifierPattern" ? this.bindingIdByName(p.name, p) : void 0);
7651
+ if (ids.length === 2 && ids.every((id) => id !== void 0)) {
7652
+ this.correlateBindings(bodyEnv, ids, rows);
7653
+ }
7654
+ this.loops.set(stmt, { walks: "iteration", viaIter: false });
7233
7655
  } else {
7234
- const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
7235
- stmt.variables.forEach((v, i) => {
7236
- this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
7237
- });
7238
- if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
7656
+ const [item, form] = this.iterationOf(stmt.iterator, sourceType);
7657
+ this.bindPattern(stmt.variable, item, bodyEnv, "widen");
7658
+ this.loops.set(stmt, form);
7239
7659
  }
7240
7660
  this.visitBlock(stmt.body, bodyEnv);
7241
7661
  return;
@@ -7243,19 +7663,10 @@ var TypeAnalyzer = class {
7243
7663
  case "ReturnStatement": {
7244
7664
  const declared = this.declaredReturns[this.declaredReturns.length - 1];
7245
7665
  const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
7246
- if (wanted) {
7247
- if (stmt.arguments.length === 1) {
7248
- this.applyContext(stmt.arguments[0], wanted);
7249
- } else if (wanted.kind === "tuple" && wanted.isPack) {
7250
- stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
7251
- }
7252
- }
7253
- const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
7254
- const { types, sources } = this.valueList(stmt.arguments, env, want);
7255
- this.checkReturn(stmt, declared, types, sources, env);
7256
- if (this.returnTypes) {
7257
- this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
7258
- }
7666
+ if (wanted && stmt.argument) this.applyContext(stmt.argument, wanted);
7667
+ const actual = stmt.argument ? this.infer(stmt.argument, env) : nilType;
7668
+ this.checkReturn(stmt, declared, actual, env);
7669
+ if (this.returnTypes) this.returnTypes.push(actual);
7259
7670
  return;
7260
7671
  }
7261
7672
  case "ExportStatement":
@@ -7342,7 +7753,7 @@ var TypeAnalyzer = class {
7342
7753
  * `sources` maps each produced value back to the expression it came from
7343
7754
  * (undefined for the 2nd and later values of a multi-value call), so the
7344
7755
  * caller can still do contextual typing against the written expression. */
7345
- valueList(exprs, env, want = 0) {
7756
+ valueList(exprs, env) {
7346
7757
  const types = [];
7347
7758
  const sources = [];
7348
7759
  exprs.forEach((e, i) => {
@@ -7358,31 +7769,29 @@ var TypeAnalyzer = class {
7358
7769
  }
7359
7770
  return;
7360
7771
  }
7361
- do {
7362
- types.push(t);
7363
- sources.push(e);
7364
- } while (last && types.length < want);
7365
- return;
7366
- }
7367
- if (last && e.type === "VarargExpression") {
7368
- do {
7369
- types.push(t);
7370
- sources.push(types.length - 1 === i ? e : void 0);
7371
- } while (types.length < want);
7372
- return;
7373
- }
7374
- if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
7375
- t.elements.forEach((el, j) => {
7376
- types.push(el);
7377
- sources.push(j === 0 ? e : void 0);
7378
- });
7772
+ types.push(t);
7773
+ sources.push(e);
7379
7774
  return;
7380
7775
  }
7381
- types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
7776
+ void last;
7777
+ types.push(t);
7382
7778
  sources.push(e);
7383
7779
  });
7384
7780
  return { types, sources };
7385
7781
  }
7782
+ /** `const a, b = x, y` pairs names with values, one each. A call is one
7783
+ * value — several come back as an array — so `const a, b = f()` is a
7784
+ * name short of a value, and says how to take them apart. */
7785
+ checkValueCount(node, names, values, types) {
7786
+ if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7787
+ const last = values[values.length - 1];
7788
+ const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7789
+ const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7790
+ this.diagnostics.push({
7791
+ node,
7792
+ 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`
7793
+ });
7794
+ }
7386
7795
  /** Run `visit` with a fresh place to collect `break` states, and return
7387
7796
  * what it collected. */
7388
7797
  withBreakScope(visit) {
@@ -7471,8 +7880,19 @@ var TypeAnalyzer = class {
7471
7880
  if (p.rest) return arrayOf(unknownType);
7472
7881
  if (p.pattern) return this.patternToType(p.pattern, env);
7473
7882
  if (p.default) return widen(this.infer(p.default, env));
7474
- return this.contextualParams.get(p) ?? anyType;
7883
+ const contextual = this.contextualParams.get(p);
7884
+ if (contextual) return contextual;
7885
+ if (this.emitDiagnostics && !receiver && p.name && p.line && !this.untypedParamReported.has(p)) {
7886
+ this.untypedParamReported.add(p);
7887
+ this.diagnostics.push({
7888
+ node: p,
7889
+ message: `Parameter '${p.name}' has no type, so it is 'any': give it one, or a default to read it from`
7890
+ });
7891
+ }
7892
+ return anyType;
7475
7893
  }
7894
+ /** Parameters already spoken about: a body is visited more than once. */
7895
+ untypedParamReported = /* @__PURE__ */ new WeakSet();
7476
7896
  /** What a function expression's unannotated parameters are, from where
7477
7897
  * it is written — see `applyContext`. */
7478
7898
  contextualParams = /* @__PURE__ */ new WeakMap();
@@ -7531,18 +7951,29 @@ var TypeAnalyzer = class {
7531
7951
  * context. */
7532
7952
  contextualArrays = /* @__PURE__ */ new WeakMap();
7533
7953
  applyArrayContext(e, expected) {
7534
- const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
7954
+ const members = this.expectedMembers(expected);
7955
+ const tuples = members.filter((m) => m.kind === "tuple");
7956
+ const spread = e.elements.some((el) => el.type === "SpreadElement");
7957
+ const length = e.elements.length;
7958
+ const fitting = tuples.find((t) => !spread && !t.rest && t.elements.length === length) ?? tuples.find((t) => t.rest !== void 0 && (spread || length >= t.elements.length));
7959
+ const target = fitting ?? members.find((m) => m.kind === "array") ?? tuples[0];
7535
7960
  if (!target) return;
7961
+ if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
7536
7962
  if (!e.elements.length) {
7537
7963
  this.contextualArrays.set(e, target);
7538
7964
  return;
7539
7965
  }
7540
7966
  e.elements.forEach((element, i) => {
7541
7967
  if (element.type === "SpreadElement") return;
7542
- const elementType = target.kind === "array" ? target.element : target.elements[i];
7968
+ const tupleTarget = target;
7969
+ const elementType = target.kind === "array" ? target.element : tupleTarget.elements[i] ?? tupleTarget.rest;
7543
7970
  this.applyContext(element, elementType);
7544
7971
  });
7545
7972
  }
7973
+ /** Array literals written where a tuple is wanted — a `return` of a
7974
+ * function that returns one, an argument, an annotated `const` — and
7975
+ * the tuple each is to be. */
7976
+ tupleArrays = /* @__PURE__ */ new WeakMap();
7546
7977
  /** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
7547
7978
  * value gets its property's type as context. */
7548
7979
  applyTableContext(e, expected) {
@@ -7598,32 +8029,33 @@ var TypeAnalyzer = class {
7598
8029
  }
7599
8030
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
7600
8031
  }
7601
- /** What a vararg function's `...` holds, one value at a time.
7602
- *
7603
- * Written three ways, and they mean the same call: `...` says nothing,
7604
- * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7605
- * into an array the body reads by name. Only the last changes what the
7606
- * body sees — the signature is the same either way. */
7607
- varargElement(func) {
7608
- if (!func.hasVarargs) return void 0;
7609
- const rest = func.params.find((p) => p.rest);
7610
- if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7611
- const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7612
- return declared?.kind === "array" ? declared.element : unknownType;
7613
- }
7614
- /** The type of `...` in each function body being walked. */
7615
- varargs = [];
8032
+ /** A function type with the rest parameter among `params` written in:
8033
+ * `...args: T[]` collects `T`s, and `...args: A` — `A` a tuple, or a
8034
+ * type parameter standing for one — is exactly `A`'s elements. */
8035
+ withRest(f, params) {
8036
+ const rest = params.find((p) => p.rest);
8037
+ if (!rest) return f;
8038
+ if (!rest.typeAnnotation) return { ...f, varargs: unknownType };
8039
+ const declared = this.resolveType(rest.typeAnnotation);
8040
+ const t = this.expand(declared);
8041
+ if (t.kind === "array") return { ...f, varargs: t.element };
8042
+ if (t.kind === "any") return { ...f, varargs: anyType };
8043
+ if (declared.kind === "infer") return { ...f, varargs: declared, restIsWhole: true };
8044
+ if (t.kind === "tuple") {
8045
+ return { ...f, params: [...f.params, ...t.elements.map((type) => ({ type }))], varargs: t.rest };
8046
+ }
8047
+ if (declared.kind === "typeParam") return { ...f, varargs: declared, restIsWhole: true };
8048
+ return { ...f, varargs: unknownType };
8049
+ }
7616
8050
  /** What each function body being walked declared it returns. */
7617
8051
  declaredReturns = [];
7618
- /** Run `body` with `...` and `return` as `func` declares them. */
8052
+ /** Run `body` with `return` as `func` declares it. */
7619
8053
  withVarargs(func, body) {
7620
- this.varargs.push(this.varargElement(func));
7621
8054
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
7622
8055
  this.contextualReturns.push(this.contextualReturnOf.get(func));
7623
8056
  try {
7624
8057
  return body();
7625
8058
  } finally {
7626
- this.varargs.pop();
7627
8059
  this.declaredReturns.pop();
7628
8060
  this.contextualReturns.pop();
7629
8061
  }
@@ -7682,7 +8114,7 @@ var TypeAnalyzer = class {
7682
8114
  if (type.kind !== "function") return type;
7683
8115
  const defaults = {};
7684
8116
  for (const generic of generics) {
7685
- if (generic.default && !generic.isPack) defaults[generic.name] = this.resolveType(generic.default);
8117
+ if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
7686
8118
  }
7687
8119
  return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
7688
8120
  }
@@ -7701,7 +8133,10 @@ var TypeAnalyzer = class {
7701
8133
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
7702
8134
  unify(p.type, preserve ? arg : widen(arg), vars, subst);
7703
8135
  });
7704
- if (f.varargs) {
8136
+ if (f.varargs && f.restIsWhole) {
8137
+ const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
8138
+ unify(f.varargs, tuple(rest), vars, subst);
8139
+ } else if (f.varargs) {
7705
8140
  const keeps = keepsLiterals(f.varargs);
7706
8141
  for (let i = f.params.length; i < argTypes.length; i++) {
7707
8142
  const arg = argTypes[i];
@@ -7752,6 +8187,11 @@ var TypeAnalyzer = class {
7752
8187
  * this returns `undefined` and the ordinary pick stands. */
7753
8188
  distributedReturn(fns, argTypes, picked, argsFor) {
7754
8189
  if (fns.length < 2) return void 0;
8190
+ if (argTypes.some((t) => this.expand(t).kind === "any")) {
8191
+ const taken = fns.filter((f) => this.overloadAccepts(f, argsFor(f, argTypes)));
8192
+ if (taken.length < 2) return void 0;
8193
+ return union(taken.map((f) => this.callReturn(f, argsFor(f, argTypes))));
8194
+ }
7755
8195
  const position = argTypes.findIndex((t) => this.expand(t).kind === "union");
7756
8196
  if (position < 0) return void 0;
7757
8197
  const members = this.expand(argTypes[position]).types;
@@ -7800,6 +8240,8 @@ var TypeAnalyzer = class {
7800
8240
  * lone argument cannot be checked against without false errors. */
7801
8241
  boundParams(f) {
7802
8242
  if (!f.typeParams?.length) return f.params.map((p) => p.type);
8243
+ const known = this.boundParamsCache.get(f);
8244
+ if (known) return known;
7803
8245
  const bounds = new Map(f.typeParams.map((name) => [name, anyType]));
7804
8246
  const seen = /* @__PURE__ */ new WeakSet();
7805
8247
  const walk = (value) => {
@@ -7817,8 +8259,11 @@ var TypeAnalyzer = class {
7817
8259
  for (const child of Object.values(value)) walk(child);
7818
8260
  };
7819
8261
  for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
7820
- return f.params.map((p) => this.reduceType(substitute(p.type, bounds)));
8262
+ const bound = f.params.map((p) => this.reduceType(substitute(p.type, bounds)));
8263
+ this.boundParamsCache.set(f, bound);
8264
+ return bound;
7821
8265
  }
8266
+ boundParamsCache = /* @__PURE__ */ new WeakMap();
7822
8267
  /** Record what each written argument is expected to be — see
7823
8268
  * `TypeAnalysis.expectedTypeOf`. */
7824
8269
  recordExpected(written, fns, selfOf, argsOf = () => []) {
@@ -7870,7 +8315,12 @@ var TypeAnalyzer = class {
7870
8315
  const arg = argTypes[i];
7871
8316
  const declared = declaredAt(i);
7872
8317
  if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
7873
- const expected = this.reduceType(substitute(declared, subst));
8318
+ let expected = this.reduceType(substitute(declared, subst));
8319
+ if (f.restIsWhole && i >= f.params.length) {
8320
+ const whole = this.expand(expected);
8321
+ expected = whole.kind === "tuple" ? whole.elements[i - f.params.length] ?? whole.rest : whole.kind === "array" ? whole.element : void 0;
8322
+ if (!expected) continue;
8323
+ }
7874
8324
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
7875
8325
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
7876
8326
  this.diagnostics.push({
@@ -7998,13 +8448,13 @@ var TypeAnalyzer = class {
7998
8448
  type: this.paramType(p, /* @__PURE__ */ new Map()),
7999
8449
  optional: p.optional || p.default !== void 0
8000
8450
  }));
8001
- return fn(
8451
+ return this.withRest(fn(
8002
8452
  params,
8003
8453
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
8004
- this.varargElement(sig),
8454
+ void 0,
8005
8455
  names,
8006
8456
  this.resolvePredicate(sig.predicate, params)
8007
- );
8457
+ ), sig.params);
8008
8458
  }));
8009
8459
  }
8010
8460
  /** `...rest: T[]` holds every argument from its position on, so its type
@@ -8012,7 +8462,7 @@ var TypeAnalyzer = class {
8012
8462
  checkRestType(p, node) {
8013
8463
  if (!this.emitDiagnostics || !p.typeAnnotation) return;
8014
8464
  const declared = this.resolveType(p.typeAnnotation);
8015
- if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
8465
+ if (declared.kind === "array" || declared.kind === "tuple" || declared.kind === "any" || declared.kind === "typeParam") return;
8016
8466
  this.diagnostics.push({
8017
8467
  node,
8018
8468
  message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
@@ -8068,13 +8518,13 @@ var TypeAnalyzer = class {
8068
8518
  return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
8069
8519
  }));
8070
8520
  }
8071
- return fn(
8521
+ return this.withRest(fn(
8072
8522
  params,
8073
8523
  returns,
8074
- this.varargElement(func),
8524
+ void 0,
8075
8525
  names,
8076
8526
  this.resolvePredicate(func.predicate, params)
8077
- );
8527
+ ), func.params);
8078
8528
  });
8079
8529
  }
8080
8530
  /** Where the return types of the function being walked are collected, so
@@ -8116,17 +8566,16 @@ var TypeAnalyzer = class {
8116
8566
  this.preVisitDepth--;
8117
8567
  }
8118
8568
  }
8119
- /** The `[key, value]` pairs iterating a record yields, one per property —
8120
- * for `pairs(t)`, `next, t` and `for k, v in t` over an object type with
8121
- * no indexer. `undefined` for anything else (an array, a dictionary, an
8122
- * iterator function), whose keys have no names to list. */
8569
+ /** The `[key, value]` items `pairs(record)` hands out, one per property.
8570
+ * `undefined` for anything else (an array, a dictionary, an iterator
8571
+ * function), whose keys have no names to list. */
8123
8572
  iterationRows(iterNode, iterType) {
8124
8573
  let source;
8125
8574
  if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8126
- if (iterNode.callee.name !== "pairs" && iterNode.callee.name !== "next") return void 0;
8575
+ if (iterNode.callee.name !== "pairs") return void 0;
8127
8576
  source = this.typeOf.get(iterNode.arguments[0]);
8128
8577
  } else {
8129
- source = iterType;
8578
+ return void 0;
8130
8579
  }
8131
8580
  const t = source && this.expand(source);
8132
8581
  if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
@@ -8267,54 +8716,76 @@ var TypeAnalyzer = class {
8267
8716
  if (ids.length < 2) return;
8268
8717
  this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
8269
8718
  }
8270
- /** `(keyType, valueType, iteratesValues)` yielded by a generic-for
8271
- * iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
8272
- * iteration (`for … in t`). `varCount` is how many loop variables were
8273
- * written. `iteratesValues` reports the one case where the first variable
8274
- * is bound to the value rather than the key, which is what lowering has
8275
- * to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
8276
- iterationTypes(iterNode, iterType, varCount) {
8277
- if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8278
- const name = iterNode.callee.name;
8279
- const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
8280
- if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
8281
- if (name === "pairs" || name === "next") {
8719
+ /** `[step, state, first]` — what a call such as `pairs(t)` answers — as
8720
+ * the `step` function, or `undefined` for an array that is just an
8721
+ * array. Three places exactly, the first a function. */
8722
+ iterationTriple(t) {
8723
+ if (t.kind !== "tuple" || t.rest || t.elements.length !== 3) return void 0;
8724
+ const step = this.expand(t.elements[0]);
8725
+ return step.kind === "function" ? step : void 0;
8726
+ }
8727
+ /** What one `for (const item in source)` hands over each time, and how it
8728
+ * walks the source. `pairs`/`ipairs` are read from the table they are
8729
+ * given, so a record's keys stay the literals they are. */
8730
+ iterationOf(source, sourceType, report = true) {
8731
+ if (source.type === "CallExpression" && source.callee.type === "Identifier" && source.arguments[0]) {
8732
+ const name = source.callee.name;
8733
+ const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
8734
+ const iteration = { walks: "iteration", viaIter: false };
8735
+ if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
8736
+ if (name === "pairs") {
8282
8737
  if (src.kind === "object") {
8283
- return [
8738
+ return [tuple([
8284
8739
  src.indexer?.key ?? stringType,
8285
- src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
8286
- false
8287
- ];
8740
+ src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
8741
+ ]), iteration];
8288
8742
  }
8289
- if (src.kind === "array") return [numberType, src.element, false];
8743
+ if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
8290
8744
  }
8291
8745
  }
8292
- const iterator = this.expand(iterType);
8293
- if (iterator.kind === "function") {
8294
- const returns = this.expand(iterator.returns);
8295
- const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
8296
- const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
8297
- return [at(0), at(1), false];
8746
+ const handsOut = (t2, viaIter) => {
8747
+ const x = this.expand(t2);
8748
+ if (x.kind === "function") return [withoutNil(this.expand(x.returns)), { walks: "function", viaIter }];
8749
+ const step = this.iterationTriple(x);
8750
+ if (step) return [withoutNil(this.expand(step.returns)), { walks: "iteration", viaIter }];
8751
+ return void 0;
8752
+ };
8753
+ const direct = handsOut(sourceType, false);
8754
+ if (direct) return direct;
8755
+ const values = { walks: "values", viaIter: false };
8756
+ const t = this.expand(sourceType);
8757
+ if (t.kind === "any") return [anyType, values];
8758
+ const iter = t.kind === "object" ? t.properties.get("__iter") : void 0;
8759
+ if (iter) {
8760
+ const made = this.overloadsOf(iter.type)[0]?.returns ?? unknownType;
8761
+ return handsOut(made, true) ?? [unknownType, { walks: "values", viaIter: true }];
8762
+ }
8763
+ if (t.kind === "array") return [t.element, values];
8764
+ if (t.kind === "tuple") return [union([...t.elements, ...t.rest ? [t.rest] : []]), values];
8765
+ if (t.kind === "object") {
8766
+ return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
8298
8767
  }
8299
- const t = this.expand(iterType);
8300
- if (t.kind === "array") {
8301
- return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
8768
+ if (t.kind === "union") {
8769
+ const each = t.types.map((member) => this.iterationOf(source, member, false));
8770
+ const [first] = each;
8771
+ if (first && each.every(([, form]) => form.walks === first[1].walks && form.viaIter === first[1].viaIter)) {
8772
+ return [union(each.map(([item]) => item)), first[1]];
8773
+ }
8302
8774
  }
8303
- if (t.kind === "object") {
8304
- const k = t.indexer?.key ?? stringType;
8305
- const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
8306
- return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
8775
+ if (t.kind !== "never" && report && this.emitDiagnostics) {
8776
+ this.diagnostics.push({
8777
+ node: source,
8778
+ message: `Cannot loop over '${formatType(sourceType)}': a loop walks an array, a table, an iterator function or an iteration such as 'pairs(t)'`
8779
+ });
8307
8780
  }
8308
- return [unknownType, unknownType, false];
8781
+ return [unknownType, values];
8309
8782
  }
8310
8783
  inferReturnType(body, env) {
8311
8784
  const returns = [];
8312
8785
  const walk = (block) => {
8313
8786
  for (const s of block.statements) {
8314
8787
  if (s.type === "ReturnStatement") {
8315
- if (s.arguments.length === 0) returns.push(nilType);
8316
- else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
8317
- else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
8788
+ returns.push(s.argument ? this.infer(s.argument, env) : nilType);
8318
8789
  } else if (s.type === "IfStatement") {
8319
8790
  for (const c of s.clauses) walk(c.body);
8320
8791
  if (s.alternate) walk(s.alternate);
@@ -8336,8 +8807,9 @@ var TypeAnalyzer = class {
8336
8807
  * `infer` would have widened it to an array type). */
8337
8808
  fitsAnnotation(init, declared, inferred, env) {
8338
8809
  if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
8339
- if (init.elements.length !== declared.elements.length) return false;
8340
- return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i]));
8810
+ const fits = declared.rest ? init.elements.length >= declared.elements.length : init.elements.length === declared.elements.length;
8811
+ if (!fits) return false;
8812
+ return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i] ?? declared.rest));
8341
8813
  }
8342
8814
  if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
8343
8815
  if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
@@ -8394,7 +8866,7 @@ var TypeAnalyzer = class {
8394
8866
  target.elements.forEach((el, i) => {
8395
8867
  if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
8396
8868
  });
8397
- if (target.rest) this.reassignPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env);
8869
+ if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
8398
8870
  return;
8399
8871
  }
8400
8872
  }
@@ -8430,7 +8902,7 @@ var TypeAnalyzer = class {
8430
8902
  if (!el) return;
8431
8903
  this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
8432
8904
  });
8433
- if (target.rest) this.bindPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env, mode);
8905
+ if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
8434
8906
  return;
8435
8907
  }
8436
8908
  }
@@ -8587,7 +9059,7 @@ var TypeAnalyzer = class {
8587
9059
  * not one of them is a mistake worth reporting, rather than the nil Lua
8588
9060
  * would hand back. */
8589
9061
  checkStringMember(node, object, key) {
8590
- if (!this.emitDiagnostics) return;
9062
+ if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
8591
9063
  const parts = this.stringParts(object);
8592
9064
  if (!parts) return;
8593
9065
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
@@ -8599,6 +9071,127 @@ var TypeAnalyzer = class {
8599
9071
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8600
9072
  });
8601
9073
  }
9074
+ /** `game.Anything` on a `{ GetService: ... }`, or `a.a` on a number: a
9075
+ * value whose members are all known, and none of them is this one. Only
9076
+ * a type that is known through and through is reported — an indexer, an
9077
+ * `any`, or a part still waiting on a type parameter could each supply
9078
+ * the name. An empty `{}` is a table still being filled in, so it is left
9079
+ * alone; an array's and a string's members come from a library, so
9080
+ * without one they say nothing. A string on its own is `checkStringMember`'s. */
9081
+ checkMissingMember(node, object, name) {
9082
+ if (!this.emitDiagnostics || this.missingMemberReported.has(node)) return;
9083
+ if (!this.memberMissing(object, name)) return;
9084
+ this.missingMemberReported.add(node);
9085
+ this.diagnostics.push({
9086
+ node,
9087
+ message: `Property '${name}' does not exist on type '${briefType(withoutNil(this.expand(object)))}'`
9088
+ });
9089
+ }
9090
+ /** Is `name` certainly not a member of `object`? See `checkMissingMember`. */
9091
+ memberMissing(object, name) {
9092
+ if (this.stringParts(object)) return false;
9093
+ const raw = this.expand(object);
9094
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
9095
+ let missing = false;
9096
+ for (const part of parts) {
9097
+ switch (part.kind) {
9098
+ case "primitive":
9099
+ if (part.name === "nil") continue;
9100
+ if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
9101
+ if (part.name === "string" && this.builtInMethod(part, name)) continue;
9102
+ break;
9103
+ case "literal":
9104
+ case "templateLiteral":
9105
+ if (part.kind === "templateLiteral" || part.base === "string") {
9106
+ if (!this.aliasDefs.has("StringMethods")) return false;
9107
+ }
9108
+ if (this.builtInMethod(part, name)) continue;
9109
+ break;
9110
+ case "array":
9111
+ case "tuple":
9112
+ if (!this.aliasDefs.has("ArrayMethods")) return false;
9113
+ if (this.builtInMethod(part, name)) continue;
9114
+ break;
9115
+ case "object":
9116
+ if (part.properties.has(name)) continue;
9117
+ if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
9118
+ break;
9119
+ default:
9120
+ return false;
9121
+ }
9122
+ missing = true;
9123
+ }
9124
+ return missing;
9125
+ }
9126
+ /** `t[k]` read with a `k` that cannot be a key of `t`: an `unknown`, a
9127
+ * type none of `t`'s keys has (a number into a record, a string into an
9128
+ * array or a class), or a name `t` does not have — which is `t.name`'s
9129
+ * error. An `any` on either side says nothing, and neither does a type
9130
+ * parameter still to be decided. A plain `string` into a record is
9131
+ * allowed: it reads as one of the values or nil, and the nil has to be
9132
+ * dealt with. An empty `{}` is a table still being filled in, and is
9133
+ * left alone. Reports, and answers whether it did. */
9134
+ checkIndexKey(node, object, key) {
9135
+ if (!this.emitDiagnostics || this.badIndexReported.has(node)) return false;
9136
+ const index = this.expand(key);
9137
+ if (index.kind === "any" || index.kind === "never" || containsTypeParam(index)) return false;
9138
+ const keys = index.kind === "union" ? index.types.map((k) => this.expand(k)) : [index];
9139
+ const nameOf = (k) => k.kind === "literal" && typeof k.value === "string" ? k.value : void 0;
9140
+ const shown = briefType(withoutNil(this.expand(object)));
9141
+ const names = keys.map(nameOf);
9142
+ const others = keys.filter((_, i) => names[i] === void 0);
9143
+ let message;
9144
+ const listOnly = (t) => {
9145
+ const x = this.expand(t);
9146
+ if (x.kind === "union") return x.types.every(listOnly);
9147
+ return x.kind === "array" || x.kind === "tuple";
9148
+ };
9149
+ if (names.some((name) => name !== void 0) && listOnly(withoutNil(this.expand(object)))) {
9150
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
9151
+ } else if (others.length && this.cannotIndex(object, others)) {
9152
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
9153
+ } else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
9154
+ message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
9155
+ }
9156
+ if (!message) return false;
9157
+ this.badIndexReported.add(node);
9158
+ this.diagnostics.push({ node, message });
9159
+ return true;
9160
+ }
9161
+ /** Can one of `keys` (string literals aside) not index some part of
9162
+ * `object`? `false` as soon as a part cannot be judged. */
9163
+ cannotIndex(object, keys) {
9164
+ const raw = this.expand(object);
9165
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
9166
+ const isNumber = (k) => k.kind === "primitive" && k.name === "number" || k.kind === "literal" && typeof k.value === "number";
9167
+ const isString = (k) => k.kind === "primitive" && k.name === "string" || k.kind === "templateLiteral";
9168
+ let bad = false;
9169
+ for (const part of parts) {
9170
+ switch (part.kind) {
9171
+ case "primitive":
9172
+ if (part.name === "nil") continue;
9173
+ return false;
9174
+ case "array":
9175
+ case "tuple":
9176
+ if (!keys.every(isNumber)) bad = true;
9177
+ continue;
9178
+ case "object": {
9179
+ if (!part.indexer && part.properties.size === 0 && !part.class) return false;
9180
+ 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));
9181
+ if (!keys.every(fits)) bad = true;
9182
+ continue;
9183
+ }
9184
+ default:
9185
+ return false;
9186
+ }
9187
+ }
9188
+ return bad;
9189
+ }
9190
+ /** Indexes already reported: a loop body is visited more than once. */
9191
+ badIndexReported = /* @__PURE__ */ new WeakSet();
9192
+ /** Members already reported as missing: a loop body, or an assignment
9193
+ * target, is visited more than once. */
9194
+ missingMemberReported = /* @__PURE__ */ new WeakSet();
8602
9195
  /** A `private` member read from outside the class that declared it —
8603
9196
  * a subclass counts as outside, as in TypeScript. */
8604
9197
  checkPrivateMember(node, object, name) {
@@ -8609,18 +9202,46 @@ var TypeAnalyzer = class {
8609
9202
  if (t.kind !== "object") continue;
8610
9203
  const access = t.properties.get(name)?.private;
8611
9204
  if (!access || this.insideClass(access.owner)) continue;
9205
+ if (access.protected && this.insideSubclassOf(access.owner)) continue;
8612
9206
  this.diagnostics.push({
8613
9207
  node,
8614
- message: `Property '${name}' is private and only accessible within class '${access.className}'`
9208
+ message: access.protected ? `Property '${name}' is protected and only accessible within class '${access.className}' and the classes extending it` : `Property '${name}' is private and only accessible within class '${access.className}'`
8615
9209
  });
8616
9210
  return;
8617
9211
  }
8618
9212
  }
9213
+ /** `Shape.new` on an abstract class, and `super.area` on an abstract
9214
+ * method: each reaches something with nothing behind it. */
9215
+ checkAbstractAccess(expr, object) {
9216
+ if (!this.emitDiagnostics) return;
9217
+ const t = this.expand(object);
9218
+ if (t.kind !== "object") return;
9219
+ const name = expr.property.name;
9220
+ const property = t.properties.get(name);
9221
+ if (!property?.abstract) return;
9222
+ if (name === "new" && expr.object.type !== "SuperExpression") {
9223
+ const label = expressionLabel(expr.object) ?? formatType(t);
9224
+ this.diagnostics.push({
9225
+ node: expr,
9226
+ message: `'${label}' is an abstract class: build one of the classes extending it instead`
9227
+ });
9228
+ } else if (expr.object.type === "SuperExpression") {
9229
+ this.diagnostics.push({
9230
+ node: expr.property,
9231
+ message: `'${name}' is abstract in the class this one extends: there is nothing to call`
9232
+ });
9233
+ }
9234
+ }
8619
9235
  /** Is the code being checked written inside `owner`'s body — its own
8620
9236
  * methods, or a class or function nested in them? */
8621
9237
  insideClass(owner) {
8622
9238
  return this.enclosingClasses.includes(owner);
8623
9239
  }
9240
+ /** Is the code being checked inside a class that extends `owner`? */
9241
+ insideSubclassOf(owner) {
9242
+ const identity = this.classIdentity(owner);
9243
+ return this.enclosingClasses.some((cls) => this.instanceType(cls).class?.ancestors.includes(identity));
9244
+ }
8624
9245
  propertyType(raw, name) {
8625
9246
  const t = this.deferredAccess(this.expand(raw));
8626
9247
  if (t.kind === "object") {
@@ -8675,8 +9296,12 @@ var TypeAnalyzer = class {
8675
9296
  }
8676
9297
  if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
8677
9298
  if (t.indexer) return t.indexer.value;
9299
+ const anyString = index.kind === "primitive" && index.name === "string" || index.kind === "templateLiteral";
9300
+ if (anyString && !t.class && t.properties.size > 0) {
9301
+ return union([...[...t.properties.values()].map((p) => p.type), nilType]);
9302
+ }
8678
9303
  }
8679
- return unknownType;
9304
+ return index.kind === "any" ? anyType : unknownType;
8680
9305
  }
8681
9306
  /** What a deferred `T[K]` can be: every property its index could name.
8682
9307
  * Reading a member of one, or calling it, sees that. */
@@ -8686,10 +9311,21 @@ var TypeAnalyzer = class {
8686
9311
  if (containsTypeParam(index)) return unknownType;
8687
9312
  return this.accessType(t.objectType, index);
8688
9313
  }
9314
+ /** `[a, ...rest]`: what `rest` holds — an array of whatever comes after
9315
+ * the first `from` places. */
9316
+ restAfter(raw, from) {
9317
+ const t = this.expand(raw);
9318
+ if (t.kind === "tuple") {
9319
+ const left = [...t.elements.slice(from), ...t.rest ? [t.rest] : []];
9320
+ return arrayOf(left.length ? union(left) : neverType);
9321
+ }
9322
+ if (t.kind === "union") return union(t.types.map((m) => this.restAfter(m, from)));
9323
+ return arrayOf(this.elementType(t, from));
9324
+ }
8689
9325
  elementType(raw, index) {
8690
9326
  const t = this.expand(raw);
8691
9327
  if (t.kind === "array") return t.element;
8692
- if (t.kind === "tuple") return t.elements[index] ?? unknownType;
9328
+ if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
8693
9329
  if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
8694
9330
  if (t.kind === "difference") return this.elementType(t.base, index);
8695
9331
  if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
@@ -8719,8 +9355,6 @@ var TypeAnalyzer = class {
8719
9355
  return stringType;
8720
9356
  }
8721
9357
  // `...` holds what the function declared it takes.
8722
- case "VarargExpression":
8723
- return this.varargs[this.varargs.length - 1] ?? anyType;
8724
9358
  // `f(a, ...rest)` — every value the array holds, one after
8725
9359
  // another. Each of them is an element, so that is what the
8726
9360
  // parameters it fills are checked against.
@@ -8753,10 +9387,8 @@ var TypeAnalyzer = class {
8753
9387
  this.checkParamOrder(expr.func.params, expr);
8754
9388
  this.visitFunctionBody(expr.func, env);
8755
9389
  return this.inferFunctionBody(expr.func, env);
8756
- case "ParenthesizedExpression": {
8757
- const inner = this.infer(expr.expression, env);
8758
- return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
8759
- }
9390
+ case "ParenthesizedExpression":
9391
+ return this.infer(expr.expression, env);
8760
9392
  case "TypeAssertionExpression": {
8761
9393
  this.infer(expr.expression, env);
8762
9394
  return this.resolveType(expr.typeAnnotation);
@@ -8788,10 +9420,18 @@ var TypeAnalyzer = class {
8788
9420
  switch (expr.operator) {
8789
9421
  case "not":
8790
9422
  return booleanType;
8791
- case "-":
8792
- return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
8793
- case "#":
8794
- return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
9423
+ case "-": {
9424
+ const answered = this.operatorResult(expr, "-", arg, void 0);
9425
+ if (answered) return answered;
9426
+ this.checkOperands(expr, "-", arg);
9427
+ return this.arithmeticOn([arg]);
9428
+ }
9429
+ case "#": {
9430
+ const answered = this.operatorResult(expr, "#", arg, void 0);
9431
+ if (answered) return answered;
9432
+ this.checkOperands(expr, "#", arg);
9433
+ return numberType;
9434
+ }
8795
9435
  }
8796
9436
  return arg;
8797
9437
  }
@@ -8807,7 +9447,11 @@ var TypeAnalyzer = class {
8807
9447
  const left = this.infer(expr.left, env);
8808
9448
  const { whenFalse } = this.narrowFromCondition(expr.left, env);
8809
9449
  const right = this.infer(expr.right, whenFalse);
8810
- return union([narrowTruthy(left), right]);
9450
+ const fallback = unwrapParens(expr.right);
9451
+ const empty = fallback.type === "TableExpression" && fallback.fields.length === 0 || fallback.type === "ArrayExpression" && fallback.elements.length === 0;
9452
+ const truthy = narrowTruthy(left);
9453
+ if (empty && truthy.kind !== "never" && truthy.kind !== "any" && isAssignable(right, truthy)) return truthy;
9454
+ return union([truthy, right]);
8811
9455
  }
8812
9456
  const l = this.infer(expr.left, env);
8813
9457
  const r = this.infer(expr.right, env);
@@ -8816,14 +9460,21 @@ var TypeAnalyzer = class {
8816
9460
  if (unwrapParens(expr.left).type === "StringLiteral") this.expectedTypeOf.set(unwrapParens(expr.left), r);
8817
9461
  }
8818
9462
  switch (op) {
8819
- case "..":
8820
- return this.operatorResult(expr, op, l, r) ?? stringType;
8821
- case "==":
8822
- case "~=":
9463
+ case "..": {
9464
+ const answered = this.operatorResult(expr, op, l, r);
9465
+ if (answered) return answered;
9466
+ this.checkOperands(expr, op, l, r);
9467
+ return stringType;
9468
+ }
8823
9469
  case "<":
8824
9470
  case ">":
8825
9471
  case "<=":
8826
9472
  case ">=":
9473
+ if (!this.checkComparison(expr, op, l, r)) this.checkOperands(expr, op, l, r);
9474
+ return booleanType;
9475
+ case "==":
9476
+ case "~=":
9477
+ this.checkOverlap(expr, op, l, r);
8827
9478
  return booleanType;
8828
9479
  case "+":
8829
9480
  case "-":
@@ -8831,17 +9482,23 @@ var TypeAnalyzer = class {
8831
9482
  case "/":
8832
9483
  case "//":
8833
9484
  case "%":
8834
- case "^":
8835
- return this.operatorResult(expr, op, l, r) ?? numberType;
9485
+ case "^": {
9486
+ const answered = this.operatorResult(expr, op, l, r);
9487
+ if (answered) return answered;
9488
+ this.checkOperands(expr, op, l, r);
9489
+ return this.arithmeticOn([l, r]);
9490
+ }
8836
9491
  }
8837
9492
  return union([l, r]);
8838
9493
  }
8839
9494
  case "MemberExpression": {
8840
9495
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
8841
9496
  this.checkPrivateMember(expr.property, obj, expr.property.name);
9497
+ this.checkAbstractAccess(expr, obj);
8842
9498
  const key = this.refKeyOf(expr);
8843
9499
  const narrowed = key === void 0 ? void 0 : env.get(key);
8844
9500
  this.checkStringMember(expr, obj, literal(expr.property.name));
9501
+ if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
8845
9502
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
8846
9503
  }
8847
9504
  case "IndexExpression": {
@@ -8850,6 +9507,7 @@ var TypeAnalyzer = class {
8850
9507
  const key = this.refKeyOf(expr);
8851
9508
  const narrowed = key === void 0 ? void 0 : env.get(key);
8852
9509
  this.checkStringMember(expr, obj, this.expand(idx));
9510
+ if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
8853
9511
  const member = this.stringMember(obj, this.expand(idx));
8854
9512
  if (member) return this.chainResult(expr, member, shortCircuits);
8855
9513
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
@@ -8859,8 +9517,6 @@ var TypeAnalyzer = class {
8859
9517
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
8860
9518
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
8861
9519
  }
8862
- case "NewExpression":
8863
- return this.inferNew(expr, env);
8864
9520
  case "ClassExpression":
8865
9521
  return this.visitClass(expr, env);
8866
9522
  case "SuperExpression": {
@@ -8897,18 +9553,14 @@ var TypeAnalyzer = class {
8897
9553
  /** `new Name(args)` is `Name.new(args)` — the same function, and the
8898
9554
  * same check. Saying so here rather than rewriting the tree keeps the
8899
9555
  * error messages pointing at what was written. */
8900
- inferNew(expr, env) {
8901
- const calleeType = this.infer(expr.callee, env);
8902
- const constructor = this.propertyType(calleeType, "new");
8903
- if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8904
- if (this.emitDiagnostics) {
8905
- const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8906
- this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8907
- }
8908
- for (const argument of expr.arguments) this.infer(argument, env);
8909
- return anyType;
8910
- }
8911
- return this.inferCall(expr, constructor, env);
9556
+ /** `__call` of a value with one, as what calling the value calls. */
9557
+ callMetamethod(raw) {
9558
+ const t = this.expand(raw);
9559
+ if (t.kind !== "object" || this.overloadsOf(t).length) return void 0;
9560
+ const method = t.properties.get("__call");
9561
+ if (!method) return void 0;
9562
+ const bound = this.overloadsOf(method.type).map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f);
9563
+ return bound.length ? intersection(bound) : void 0;
8912
9564
  }
8913
9565
  /** `super(...)` — the base constructor, run on the instance being built. */
8914
9566
  inferSuperCall(expr, env) {
@@ -8932,8 +9584,9 @@ var TypeAnalyzer = class {
8932
9584
  this.inferCall(expr, callable, env);
8933
9585
  return nilType;
8934
9586
  }
8935
- inferCall(expr, callee, env) {
9587
+ inferCall(expr, called, env) {
8936
9588
  this.checkAmbiguousCall(expr);
9589
+ const callee = this.callMetamethod(called) ?? called;
8937
9590
  const united = this.unionSignatures(callee);
8938
9591
  const fns = united ?? this.overloadsOf(callee);
8939
9592
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -8956,8 +9609,31 @@ var TypeAnalyzer = class {
8956
9609
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
8957
9610
  return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
8958
9611
  }
9612
+ this.checkCallable(expr.callee, callee);
8959
9613
  return callee.kind === "any" ? anyType : unknownType;
8960
9614
  }
9615
+ /** `a()` where `a` is `1`: a value whose type is known and has no call
9616
+ * signature. A union is reported when any member cannot be called, but
9617
+ * only when every member is decided — `unknown`, `any` and anything still
9618
+ * generic say too little. */
9619
+ checkCallable(node, callee) {
9620
+ if (!this.emitDiagnostics || this.notCallableReported.has(node)) return;
9621
+ const raw = this.expand(callee);
9622
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.expand(m));
9623
+ const notCallable = (m) => m.kind === "primitive" && m.name !== "nil" || m.kind === "literal" || m.kind === "templateLiteral" || m.kind === "array" || m.kind === "tuple" || m.kind === "object";
9624
+ const callable = (m) => this.overloadsOf(m).length > 0;
9625
+ const concrete = parts.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
9626
+ if (!concrete.some(notCallable) || !concrete.every((m) => notCallable(m) || callable(m))) return;
9627
+ this.notCallableReported.add(node);
9628
+ const label = node.type === "Identifier" ? node.name : expressionLabel(node);
9629
+ this.diagnostics.push({
9630
+ node,
9631
+ message: `This expression is not callable: ${label === void 0 ? "" : `'${label}' `}is of type '${briefType(withoutNil(raw))}'`
9632
+ });
9633
+ }
9634
+ /** Callees already reported as not callable: a loop body is visited more
9635
+ * than once. */
9636
+ notCallableReported = /* @__PURE__ */ new WeakSet();
8961
9637
  /** A `(` on a line of its own continues the statement above it:
8962
9638
  *
8963
9639
  * const value = map[key]
@@ -8975,6 +9651,8 @@ var TypeAnalyzer = class {
8975
9651
  }
8976
9652
  inferMethodCall(expr, objType, env) {
8977
9653
  this.checkPrivateMember(expr.method, objType, expr.method.name);
9654
+ this.checkStringMember(expr.method, objType, literal(expr.method.name));
9655
+ this.checkMissingMember(expr.method, objType, expr.method.name);
8978
9656
  const method = this.propertyType(objType, expr.method.name);
8979
9657
  const united = this.unionSignatures(method);
8980
9658
  const fns = united ?? this.overloadsOf(method);
@@ -9008,6 +9686,7 @@ var TypeAnalyzer = class {
9008
9686
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
9009
9687
  return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
9010
9688
  }
9689
+ this.checkCallable(expr.method, method);
9011
9690
  return objType.kind === "any" ? anyType : unknownType;
9012
9691
  }
9013
9692
  // --------------------------------------------------------
@@ -9056,6 +9735,14 @@ var TypeAnalyzer = class {
9056
9735
  name = target.property.name;
9057
9736
  object = target.object;
9058
9737
  } else if (target.type === "IndexExpression") {
9738
+ const list = this.expand(this.infer(target.object, env));
9739
+ if ((list.kind === "array" || list.kind === "tuple") && list.readonly) {
9740
+ this.diagnostics.push({
9741
+ node: target,
9742
+ message: `Cannot assign to an element of '${formatType(list)}': it is read-only`
9743
+ });
9744
+ return;
9745
+ }
9059
9746
  const index = this.expand(this.infer(target.index, env));
9060
9747
  if (index.kind !== "literal" || typeof index.value !== "string") return;
9061
9748
  name = index.value;
@@ -9068,6 +9755,8 @@ var TypeAnalyzer = class {
9068
9755
  if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
9069
9756
  }
9070
9757
  if (!this.isReadonlyProperty(this.expand(this.infer(object, env)), name)) return;
9758
+ const constructing = this.constructing;
9759
+ if (constructing && object.type === "Identifier" && object.name === "this" && constructing.members.some((m) => m.type === "ClassField" && !m.isStatic && m.isReadonly && m.name.name === name)) return;
9071
9760
  this.diagnostics.push({
9072
9761
  node: target,
9073
9762
  message: `Cannot assign to '${name}' because it is a read-only property`
@@ -9127,6 +9816,8 @@ var TypeAnalyzer = class {
9127
9816
  inferArray(expr, env, asConst) {
9128
9817
  const contextual = this.contextualArrays.get(expr);
9129
9818
  if (contextual && !asConst) return contextual;
9819
+ const wantedTuple = this.tupleArrays.get(expr);
9820
+ if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9130
9821
  const elems = [];
9131
9822
  let hadSpread = false;
9132
9823
  for (const el of expr.elements) {
@@ -9146,6 +9837,28 @@ var TypeAnalyzer = class {
9146
9837
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
9147
9838
  })) : unknownType);
9148
9839
  }
9840
+ /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
9841
+ * tuple's, and what comes after them — more elements, or a spread — is
9842
+ * its rest. */
9843
+ inferTupleLiteral(expr, env, wanted) {
9844
+ const fixed = [];
9845
+ const tail = [];
9846
+ let spread = false;
9847
+ expr.elements.forEach((element, i) => {
9848
+ if (element.type === "SpreadElement") {
9849
+ spread = true;
9850
+ const held = this.expand(this.infer(element.argument, env));
9851
+ if (held.kind === "array") tail.push(held.element);
9852
+ else if (held.kind === "tuple") tail.push(...tupleMembers(held));
9853
+ else tail.push(unknownType);
9854
+ return;
9855
+ }
9856
+ const t = this.widenUnlessAsked(this.infer(element, env), element);
9857
+ if (!spread && i < wanted.elements.length) fixed.push(t);
9858
+ else tail.push(t);
9859
+ });
9860
+ return tuple(fixed, tail.length ? union(tail) : void 0);
9861
+ }
9149
9862
  /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
9150
9863
  * `{ n: number }` — unless the surroundings said a literal belongs there.
9151
9864
  * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
@@ -9202,9 +9915,27 @@ var TypeAnalyzer = class {
9202
9915
  if (s.indexer) indexer = mergeIndexer(indexer, s.indexer);
9203
9916
  }
9204
9917
  }
9918
+ this.checkDuplicateKey(expr, field, entries);
9205
9919
  }
9206
9920
  return objectType(entries, indexer, asConst || void 0);
9207
9921
  }
9922
+ /** `{ a: 1, a: 2 }` — the second wins and the first was written for
9923
+ * nothing, which is a typo far more often than a decision. A spread is
9924
+ * not one: overriding what it brought is exactly what it is for. */
9925
+ checkDuplicateKey(expr, field, entries) {
9926
+ if (!this.emitDiagnostics || this.duplicateKeyReported.has(field)) return;
9927
+ const written = field.type === "TableFieldNamed" ? field.key.type === "Identifier" ? field.key.name : field.key.value : field.type === "TableFieldShorthand" ? field.name.name : void 0;
9928
+ if (written === void 0) return;
9929
+ const spreadBefore = expr.fields.slice(0, expr.fields.indexOf(field)).some((f) => f.type === "TableFieldSpread");
9930
+ if (spreadBefore) return;
9931
+ if (entries.filter(([k]) => k === written).length < 2) return;
9932
+ this.duplicateKeyReported.add(field);
9933
+ this.diagnostics.push({
9934
+ node: field.type === "TableFieldNamed" ? field.key : field.name,
9935
+ message: `'${written}' is given twice in this table; only the last one is kept`
9936
+ });
9937
+ }
9938
+ duplicateKeyReported = /* @__PURE__ */ new WeakSet();
9208
9939
  /** A value inferred `as const`, widened back wherever `context` does not
9209
9940
  * ask for a literal: `satisfies`' result type. A property keeps `"circle"`
9210
9941
  * when the contract's property admits string literals, and becomes
@@ -9231,7 +9962,7 @@ var TypeAnalyzer = class {
9231
9962
  case "tuple": {
9232
9963
  const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
9233
9964
  if (tupleContext?.kind === "tuple") {
9234
- return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.isPack);
9965
+ return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.rest);
9235
9966
  }
9236
9967
  const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
9237
9968
  const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
@@ -9488,7 +10219,8 @@ var TypeAnalyzer = class {
9488
10219
  for (const f of candidates) {
9489
10220
  if (!f.predicate) continue;
9490
10221
  const arg = args[f.predicate.param];
9491
- if (!arg || this.refKeyOf(arg) === void 0) continue;
10222
+ const onCondition = f.predicate.asserts && !f.predicate.type;
10223
+ if (!arg || this.refKeyOf(arg) === void 0 && !onCondition) continue;
9492
10224
  if (f.typeParams?.length && f.predicate.type) {
9493
10225
  const subst = this.inferTypeArgs(f, argTypes);
9494
10226
  return {
@@ -9509,6 +10241,10 @@ var TypeAnalyzer = class {
9509
10241
  const found = this.predicateCallTarget(call, env);
9510
10242
  if (!found || !found.predicate.asserts) return;
9511
10243
  const filter = found.predicate.type;
10244
+ if (this.refKeyOf(found.arg) === void 0) {
10245
+ if (!filter) this.applyNarrowing(found.arg, env, env, forkEnv(env));
10246
+ return;
10247
+ }
9512
10248
  this.narrowRef(found.arg, env, env, forkEnv(env), (cur) => filter ? { yes: narrowTo(cur, filter), no: narrowExclude(cur, filter) } : { yes: narrowTruthy(cur), no: narrowFalsy(cur) });
9513
10249
  }
9514
10250
  // --------------------------------------------------------
@@ -9533,6 +10269,10 @@ var TypeAnalyzer = class {
9533
10269
  if (base === void 0) return void 0;
9534
10270
  if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
9535
10271
  if (expr.index.type === "NumberLiteral") return `${base}#${expr.index.value}`;
10272
+ if (expr.index.type === "Identifier") {
10273
+ const id = this.bindingIdOf(expr.index);
10274
+ return id === void 0 ? void 0 : `${base}[${bindKey(id)}]`;
10275
+ }
9536
10276
  return void 0;
9537
10277
  }
9538
10278
  default:
@@ -9628,26 +10368,45 @@ var TypeAnalyzer = class {
9628
10368
  * that copied it no longer holds that value: forget the alias. */
9629
10369
  unalias(key) {
9630
10370
  for (const k of [...this.refAliases.keys()]) {
9631
- if (k !== key && !k.startsWith(`${key}.`) && !k.startsWith(`${key}#`)) continue;
10371
+ if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
9632
10372
  for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
9633
10373
  this.refAliases.delete(k);
9634
10374
  }
9635
10375
  }
9636
- /** Drop every narrowing recorded for a path strictly under `key`. */
10376
+ /** Drop every narrowing recorded for a path strictly under `key` — and,
10377
+ * when `key` is a whole variable, for every `t[key]` indexed by it: that
10378
+ * now names a different slot. */
9637
10379
  invalidateBelow(env, key) {
10380
+ const asIndex = `[${key}]`;
9638
10381
  for (const k of [...env.keys()]) {
9639
- if (k.startsWith(`${key}.`) || k.startsWith(`${key}#`)) env.delete(k);
10382
+ if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
9640
10383
  }
9641
10384
  }
9642
10385
  /** An assignment through a reference invalidates it and everything under
9643
10386
  * it, then records the assigned type. */
9644
10387
  assignToRef(expr, value, env) {
10388
+ this.invalidateSiblings(expr, env);
9645
10389
  const key = this.refKeyOf(expr);
9646
10390
  if (key === void 0) return;
9647
10391
  this.invalidateBelow(env, key);
9648
10392
  this.unalias(key);
9649
10393
  env.set(key, value);
9650
10394
  }
10395
+ /** A write to one slot of a table can be a write to a slot narrowed under
10396
+ * another name: `t.x = nil` is `t[k] = nil` when `k` is `"x"`. So a
10397
+ * write by name forgets what is known through variable keys, and a write
10398
+ * through any computed key forgets everything known about the table's
10399
+ * slots. */
10400
+ invalidateSiblings(expr, env) {
10401
+ const inner = expr.type === "ParenthesizedExpression" ? expr.expression : expr;
10402
+ if (inner.type !== "MemberExpression" && inner.type !== "IndexExpression") return;
10403
+ const parent = this.refKeyOf(inner.object);
10404
+ if (parent === void 0) return;
10405
+ const computed = inner.type === "IndexExpression" && inner.index.type !== "StringLiteral" && inner.index.type !== "NumberLiteral";
10406
+ for (const k of [...env.keys()]) {
10407
+ if (computed ? isBelow(k, parent) : k.startsWith(`${parent}[`)) env.delete(k);
10408
+ }
10409
+ }
9651
10410
  // --------------------------------------------------------
9652
10411
  // Small helpers
9653
10412
  // --------------------------------------------------------
@@ -9662,6 +10421,92 @@ var TypeAnalyzer = class {
9662
10421
  this.selfType = saved;
9663
10422
  }
9664
10423
  }
10424
+ /** Arithmetic no declared metamethod answers: a number — unless an
10425
+ * operand is `any`, which may be a value with metamethods of its own.
10426
+ * `(a.Position - b.Position).Magnitude` on an `a` nothing typed is a
10427
+ * `Vector3` as far as anyone knows, not a number with no `Magnitude`. */
10428
+ arithmeticOn(operands) {
10429
+ return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
10430
+ }
10431
+ /** `a == b` where nothing `a` can hold is anything `b` can: the answer is
10432
+ * always the same, so the comparison was not the one meant. Only types
10433
+ * that say what they hold take part — `any`, `unknown` and a type
10434
+ * parameter say nothing, and a table compares by identity, so two of them
10435
+ * may well be the same table. */
10436
+ checkOverlap(node, op, l, r) {
10437
+ if (!this.emitDiagnostics) return;
10438
+ const left = this.expand(l);
10439
+ const right = this.expand(r);
10440
+ const judged = (t) => {
10441
+ const parts = t.kind === "union" ? t.types.map((m) => this.expand(m)) : [t];
10442
+ return parts.every((p) => p.kind === "literal" || p.kind === "primitive");
10443
+ };
10444
+ const isNil = (t) => t.kind === "primitive" && t.name === "nil";
10445
+ if (isNil(left) || isNil(right)) return;
10446
+ if (!judged(left) || !judged(right)) return;
10447
+ if (isAssignable(left, right) || isAssignable(right, left)) return;
10448
+ this.diagnostics.push({
10449
+ node,
10450
+ message: `This comparison is always ${op === "==" ? "false" : "true"}: '${formatType(l)}' and '${formatType(r)}' have no value in common`
10451
+ });
10452
+ }
10453
+ /** What an operator takes, where no metamethod answered for it. Luau
10454
+ * raises on the rest — `n * 2` with `n` nil is an error at the line it
10455
+ * runs, and a nil `n` is exactly what `number | nil` says may happen — so
10456
+ * it is said here instead. `any` passes: nothing is known about it. */
10457
+ checkOperands(node, op, left, right) {
10458
+ if (!this.emitDiagnostics) return;
10459
+ const fits = (t) => {
10460
+ const x = this.expand(t);
10461
+ if (x.kind === "any" || x.kind === "never") return true;
10462
+ switch (op) {
10463
+ // `..` joins strings, and Luau writes a number into one.
10464
+ case "..":
10465
+ return isAssignable(x, stringType) || isAssignable(x, numberType);
10466
+ // `#` counts a string's bytes or a table's entries.
10467
+ case "#":
10468
+ return x.kind === "array" || x.kind === "tuple" || x.kind === "object" || isAssignable(x, stringType);
10469
+ default:
10470
+ return isAssignable(x, numberType);
10471
+ }
10472
+ };
10473
+ if (op === "<" || op === ">" || op === "<=" || op === ">=") {
10474
+ const both = (t) => this.expand(t);
10475
+ const [l, r] = [both(left), both(right)];
10476
+ const loose = (t) => t.kind === "any" || t.kind === "never";
10477
+ const alike = (as) => isAssignable(l, as) && isAssignable(r, as);
10478
+ if (loose(l) || loose(r) || alike(numberType) || alike(stringType)) return;
10479
+ } else if (fits(left) && (right === void 0 || fits(right))) {
10480
+ return;
10481
+ }
10482
+ this.diagnostics.push({
10483
+ node,
10484
+ message: right === void 0 ? `Operator '${op}' cannot be applied to type '${formatType(left)}'` : `Operator '${op}' cannot be applied to types '${formatType(left)}' and '${formatType(right)}'`
10485
+ });
10486
+ }
10487
+ /** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
10488
+ * with the operands swapped for `>` and `>=`, and an instance without
10489
+ * one cannot be compared at all. */
10490
+ checkComparison(node, op, l, r) {
10491
+ if (!this.emitDiagnostics) return false;
10492
+ const left = this.expand(l);
10493
+ const right = this.expand(r);
10494
+ const instance = (t) => t.kind === "object" && t.class !== void 0;
10495
+ if (!instance(left) && !instance(right)) return false;
10496
+ const name = op === "<" || op === ">" ? "__lt" : "__le";
10497
+ const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
10498
+ for (const receiver of operands) {
10499
+ const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
10500
+ if (!method) continue;
10501
+ if (this.pickOverload(this.overloadsOf(method.type), operands)) return true;
10502
+ break;
10503
+ }
10504
+ this.diagnostics.push({
10505
+ node,
10506
+ message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
10507
+ });
10508
+ return true;
10509
+ }
9665
10510
  /** What an operator on a value with metamethods gives: `a + b` calls
9666
10511
  * `__add` on `a`, or failing that on `b` with the operands swapped — the
9667
10512
  * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
@@ -9947,7 +10792,56 @@ var TypeAnalyzer = class {
9947
10792
  return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
9948
10793
  }
9949
10794
  };
9950
- function referencedTypeNames(node, out = []) {
10795
+ var libraryNames = /* @__PURE__ */ new WeakMap();
10796
+ function namesDeclaredIn(program) {
10797
+ const known = libraryNames.get(program);
10798
+ if (known) return known;
10799
+ const names = /* @__PURE__ */ new Set();
10800
+ for (const stmt of program.body.statements) {
10801
+ const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
10802
+ if (declaration.type === "TypeAliasStatement") names.add(declaration.name.name);
10803
+ else if (declaration.type === "ExportTypeAliasStatement") names.add(declaration.alias.name.name);
10804
+ else if (declaration.type === "DeclareClassStatement") names.add(declaration.name.name);
10805
+ else if (declaration.type === "ClassDeclaration" && declaration.name) names.add(declaration.name.name);
10806
+ else if (stmt.type === "ImportStatement") {
10807
+ for (const s of stmt.specifiers) names.add(s.local.name);
10808
+ if (stmt.defaultImport) names.add(stmt.defaultImport.name);
10809
+ if (stmt.namespaceImport) names.add(stmt.namespaceImport.name);
10810
+ }
10811
+ }
10812
+ libraryNames.set(program, names);
10813
+ return names;
10814
+ }
10815
+ var sharedClassesByLib = /* @__PURE__ */ new WeakMap();
10816
+ function shareableClasses(libs, program) {
10817
+ if (!libs.length) return void 0;
10818
+ const mine = namesDeclaredIn(program);
10819
+ if (mine.size) {
10820
+ for (const lib of libs) {
10821
+ for (const name of namesDeclaredIn(lib)) if (mine.has(name)) return void 0;
10822
+ }
10823
+ }
10824
+ const existing = sharedClassesByLib.get(libs[0]);
10825
+ if (existing) {
10826
+ const same = existing.libs.length === libs.length && existing.libs.every((lib, i) => lib === libs[i]);
10827
+ if (same) return existing;
10828
+ return void 0;
10829
+ }
10830
+ const store = { libs: [...libs], classTypes: /* @__PURE__ */ new WeakMap(), classMembers: /* @__PURE__ */ new WeakMap() };
10831
+ sharedClassesByLib.set(libs[0], store);
10832
+ return store;
10833
+ }
10834
+ var referencedNamesOf = /* @__PURE__ */ new WeakMap();
10835
+ var typeQueryIn = /* @__PURE__ */ new WeakMap();
10836
+ function referencedTypeNames(node, out) {
10837
+ if (out === void 0 && node && typeof node === "object") {
10838
+ const known = referencedNamesOf.get(node);
10839
+ if (known) return known;
10840
+ const found = referencedTypeNames(node, []);
10841
+ referencedNamesOf.set(node, found);
10842
+ return found;
10843
+ }
10844
+ out = out ?? [];
9951
10845
  if (!node || typeof node !== "object") return out;
9952
10846
  if (Array.isArray(node)) {
9953
10847
  for (const item of node) referencedTypeNames(item, out);
@@ -9964,9 +10858,11 @@ function referencedTypeNames(node, out = []) {
9964
10858
  }
9965
10859
  function containsTypeQuery(node) {
9966
10860
  if (!node || typeof node !== "object") return false;
9967
- if (Array.isArray(node)) return node.some(containsTypeQuery);
9968
- if (node.type === "TypeofTypeNode") return true;
9969
- return Object.values(node).some(containsTypeQuery);
10861
+ const known = typeQueryIn.get(node);
10862
+ if (known !== void 0) return known;
10863
+ const found = Array.isArray(node) ? node.some(containsTypeQuery) : node.type === "TypeofTypeNode" || Object.values(node).some(containsTypeQuery);
10864
+ typeQueryIn.set(node, found);
10865
+ return found;
9970
10866
  }
9971
10867
  var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
9972
10868
  function briefType(t) {
@@ -9994,6 +10890,13 @@ var nodeHost = {
9994
10890
  } catch {
9995
10891
  return void 0;
9996
10892
  }
10893
+ },
10894
+ readDirectory(path) {
10895
+ try {
10896
+ return (0, import_node_fs.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => entry.name);
10897
+ } catch {
10898
+ return void 0;
10899
+ }
9997
10900
  }
9998
10901
  };
9999
10902
 
@@ -10185,13 +11088,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
10185
11088
  else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
10186
11089
  continue;
10187
11090
  }
10188
- const name = entry.startsWith("@tilua-types/") ? entry : `@tilua-types/${entry}`;
10189
- const found = findPackage(name, config.directory, host);
11091
+ if (entry.includes("*")) {
11092
+ const matched = matchPackages(entry, config.directory, host);
11093
+ if (matched === void 0) {
11094
+ problems.push({
11095
+ file: config.path,
11096
+ message: `'${entry}' needs the folders listed, and this tool cannot list them; name the libraries one by one`,
11097
+ ...entryPosition(config, entry)
11098
+ });
11099
+ } else if (!matched.length) {
11100
+ problems.push({
11101
+ file: config.path,
11102
+ message: `No installed type library matches '${entry}'`,
11103
+ ...entryPosition(config, entry)
11104
+ });
11105
+ }
11106
+ for (const found2 of matched ?? []) addPackage(found2.directory, found2.file, /* @__PURE__ */ new Set());
11107
+ continue;
11108
+ }
11109
+ const names = entry.startsWith("@") ? [entry] : [`@tilua-types/${entry}`, entry];
11110
+ const found = names.map((name) => findPackage(name, config.directory, host)).find(Boolean);
10190
11111
  if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
10191
11112
  else {
10192
11113
  problems.push({
10193
11114
  file: config.path,
10194
- message: `Cannot find type library '${name}'. Install it with: npm i -D ${name}`,
11115
+ message: names.length === 1 ? `Cannot find type library '${entry}'. Install it with: npm i -D ${entry}` : `Cannot find type library '${names[0]}' or '${entry}'. Install it with: npm i -D ${names[0]}`,
10195
11116
  ...entryPosition(config, entry)
10196
11117
  });
10197
11118
  }
@@ -10228,6 +11149,32 @@ function findPackage(name, from, host) {
10228
11149
  directory = parent;
10229
11150
  }
10230
11151
  }
11152
+ function matchPackages(pattern, from, host) {
11153
+ if (!host.readDirectory) return void 0;
11154
+ const slash = pattern.lastIndexOf("/");
11155
+ const scope = slash < 0 ? "" : pattern.slice(0, slash);
11156
+ const last = pattern.slice(slash + 1);
11157
+ const matches = new RegExp(`^${last.split("*").map(escapeRegExp2).join(".*")}$`);
11158
+ const found = /* @__PURE__ */ new Map();
11159
+ let directory = (0, import_node_path2.resolve)(from);
11160
+ for (; ; ) {
11161
+ const folder = (0, import_node_path2.join)(directory, "node_modules", ...scope ? scope.split("/") : []);
11162
+ for (const name of host.readDirectory(folder) ?? []) {
11163
+ const full = scope ? `${scope}/${name}` : name;
11164
+ if (!matches.test(name) || found.has(full)) continue;
11165
+ const candidate = (0, import_node_path2.join)(folder, name);
11166
+ const file = packageEntry(candidate, host);
11167
+ if (file) found.set(full, { directory: candidate, file });
11168
+ }
11169
+ const parent = (0, import_node_path2.dirname)(directory);
11170
+ if (parent === directory) break;
11171
+ directory = parent;
11172
+ }
11173
+ return [...found.keys()].sort().map((name) => found.get(name));
11174
+ }
11175
+ function escapeRegExp2(text) {
11176
+ return text.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
11177
+ }
10231
11178
  function dependencyNames(directory, host) {
10232
11179
  const manifest = readJson((0, import_node_path2.join)(directory, "package.json"), host);
10233
11180
  const names = /* @__PURE__ */ new Set();
@@ -10326,7 +11273,8 @@ function sourceMapTypes(text, path, options) {
10326
11273
  for (const child of node.children ?? []) {
10327
11274
  if (!isNode(child)) continue;
10328
11275
  const childAlias = visit(child, [...segments, child.name], alias);
10329
- if (!IDENTIFIER.test(child.name) || taken.has(child.name) || named.has(child.name)) continue;
11276
+ const service = child.name === child.className && options.classes.has(child.className);
11277
+ if (!IDENTIFIER.test(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
10330
11278
  named.add(child.name);
10331
11279
  members.push(`${child.name}: ${childAlias}`);
10332
11280
  }
@@ -10387,6 +11335,7 @@ var index_default = tilua;
10387
11335
  BinaryOperators,
10388
11336
  CONFIG_FILE_NAMES,
10389
11337
  Keywords,
11338
+ LANGUAGE_GLOBALS,
10390
11339
  LexError,
10391
11340
  Operators,
10392
11341
  PRELUDE_SOURCE,
@@ -10454,6 +11403,7 @@ var index_default = tilua;
10454
11403
  tilua,
10455
11404
  tokenize,
10456
11405
  tuple,
11406
+ tupleMembers,
10457
11407
  typeParam,
10458
11408
  unify,
10459
11409
  union,