@tilua/parser 1.1.0 → 2.0.0

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