@tilua/parser 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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() {
@@ -1830,11 +1881,67 @@ var Parser = class {
1830
1881
  }
1831
1882
  parseClassMember() {
1832
1883
  const start = this.current();
1833
- const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1834
- if (isStatic) this.advance();
1884
+ let accessibility;
1885
+ const modifiers = /* @__PURE__ */ new Set();
1886
+ while (this.startsClassModifier()) {
1887
+ const token = this.advance();
1888
+ const word = token.value;
1889
+ if (ACCESSIBILITIES.has(word)) {
1890
+ if (accessibility) this.problemAt(token, `A member has one accessibility: '${accessibility}' is already written`);
1891
+ accessibility = word;
1892
+ } else {
1893
+ if (modifiers.has(word)) this.problemAt(token, `'${word}' is written twice`);
1894
+ modifiers.add(word);
1895
+ }
1896
+ }
1897
+ const member = this.parseClassMemberAfterModifiers(start, modifiers);
1898
+ if (!member) return member;
1899
+ const misplaced = (message) => this.problemAt(start, message);
1900
+ if (member.type === "ClassConstructor") {
1901
+ const written = [...accessibility ? [accessibility] : [], ...modifiers];
1902
+ if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
1903
+ return member;
1904
+ }
1905
+ if (modifiers.has("readonly") && member.type !== "ClassField") misplaced("Only a field can be 'readonly'");
1906
+ if (modifiers.has("abstract") && member.type !== "ClassMethod") misplaced("Only a method can be 'abstract'");
1907
+ if (modifiers.has("abstract") && member.isStatic) misplaced("A static method cannot be 'abstract'");
1908
+ if (modifiers.has("override") && member.isStatic) misplaced("A static member cannot be 'override'");
1909
+ return {
1910
+ ...member,
1911
+ ...accessibility ? { accessibility } : {},
1912
+ ...modifiers.has("readonly") && member.type === "ClassField" ? { isReadonly: true } : {},
1913
+ ...modifiers.has("override") ? { isOverride: true } : {}
1914
+ };
1915
+ }
1916
+ /** Is the word here a member modifier, rather than a member's name? */
1917
+ startsClassModifier() {
1918
+ const t = this.current();
1919
+ if (t.type !== "Identifier" || !CLASS_MODIFIERS.has(t.value)) return false;
1920
+ const next = this.peek(1);
1921
+ return next.type === "Identifier" || next.type === "Keyword" && next.value === "function";
1922
+ }
1923
+ parseClassMemberAfterModifiers(start, modifiers) {
1924
+ const isStatic = modifiers.has("static");
1835
1925
  if (this.checkKeyword("function")) {
1836
1926
  this.advance();
1837
1927
  const memberName = this.parseIdentifier();
1928
+ if (modifiers.has("abstract")) {
1929
+ const head = this.parseFunctionHead();
1930
+ const func = {
1931
+ type: "FunctionBody",
1932
+ generics: head.generics,
1933
+ params: head.params,
1934
+ hasVarargs: head.hasVarargs,
1935
+ returnType: head.returnType,
1936
+ predicate: head.predicate,
1937
+ body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1938
+ ...spanFrom(head.start, this.previous())
1939
+ };
1940
+ if (!isStatic) bindThis(func, start);
1941
+ if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1942
+ if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1943
+ return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1944
+ }
1838
1945
  const signatures = [];
1839
1946
  let written = memberName;
1840
1947
  while (true) {
@@ -1952,12 +2059,17 @@ var Parser = class {
1952
2059
  parseReturnStatement(stopAtNewline = false) {
1953
2060
  const start = this.current();
1954
2061
  this.expectKeyword("return");
1955
- let args = [];
2062
+ let argument;
1956
2063
  const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
1957
2064
  if (!pastLine() && this.isExpressionStart()) {
1958
- 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
+ }
1959
2071
  }
1960
- return { type: "ReturnStatement", arguments: args, ...spanFrom(start, this.previous()) };
2072
+ return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
1961
2073
  }
1962
2074
  parseTypeAliasStatement() {
1963
2075
  const start = this.current();
@@ -2016,7 +2128,7 @@ var Parser = class {
2016
2128
  ...spanFrom(start, this.previous())
2017
2129
  };
2018
2130
  }
2019
- if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
2131
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
2020
2132
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
2021
2133
  }
2022
2134
  let expression = first;
@@ -2069,6 +2181,15 @@ var Parser = class {
2069
2181
  }
2070
2182
  return false;
2071
2183
  }
2184
+ /** A string written straight before a method call — `"a":upper()`, or
2185
+ * `` `{n}`:rep(2) ``. Luau only takes `("a"):upper()`; the compiler adds
2186
+ * the parentheses. Anything else after a string (`"a".x`, `"a"(1)`) is
2187
+ * not a prefix expression here either. */
2188
+ startsStringMethodCall() {
2189
+ const t = this.current();
2190
+ const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
2191
+ return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
2192
+ }
2072
2193
  /** Does the next token start right where the current one ends? */
2073
2194
  touchesNext() {
2074
2195
  const current = this.current();
@@ -2255,6 +2376,7 @@ var Parser = class {
2255
2376
  }
2256
2377
  parseAtom() {
2257
2378
  const t = this.current();
2379
+ if (this.startsStringMethodCall()) return this.parsePrefixExpression();
2258
2380
  if (t.type === "Literal") {
2259
2381
  this.advance();
2260
2382
  const lit = t;
@@ -2274,8 +2396,9 @@ var Parser = class {
2274
2396
  return this.buildInterpolatedString(t);
2275
2397
  }
2276
2398
  if (t.type === "Operator" && t.value === "...") {
2399
+ this.problem(NO_DOTS);
2277
2400
  this.advance();
2278
- return { type: "VarargExpression", ...spanFrom(t, t) };
2401
+ return { type: "ErrorExpression", ...spanFrom(t, t) };
2279
2402
  }
2280
2403
  if (t.type === "Keyword" && t.value === "function") {
2281
2404
  this.advance();
@@ -2351,12 +2474,17 @@ var Parser = class {
2351
2474
  const start = this.current();
2352
2475
  let base;
2353
2476
  if (this.startsNew()) {
2354
- base = this.parseNewExpression();
2355
- } 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()) {
2356
2481
  this.advance();
2357
2482
  base = { type: "SuperExpression", ...spanFrom(start, start) };
2358
2483
  } else if (this.checkType("Identifier")) {
2359
2484
  base = this.parseIdentifier();
2485
+ } else if (this.startsStringMethodCall()) {
2486
+ const t = this.advance();
2487
+ base = t.type === "InterpolatedString" ? this.buildInterpolatedString(t) : { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2360
2488
  } else if (this.matchPunctuator("(")) {
2361
2489
  const inner = this.inBrackets(() => this.parseExpression());
2362
2490
  this.expectPunctuator(")");
@@ -2467,9 +2595,9 @@ var Parser = class {
2467
2595
  }
2468
2596
  return base;
2469
2597
  }
2470
- /** `new` is a soft keyword: it starts a construction only when a name
2471
- * follows it, so a function or field called `new` — `Instance.new(x)`,
2472
- * 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. */
2473
2601
  startsNew() {
2474
2602
  return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2475
2603
  }
@@ -2479,25 +2607,6 @@ var Parser = class {
2479
2607
  startsSuperUse() {
2480
2608
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2481
2609
  }
2482
- /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2483
- * name reached through a module — never an arbitrary expression, so the
2484
- * arguments are unambiguously the constructor's. */
2485
- parseNewExpression() {
2486
- const start = this.current();
2487
- this.advance();
2488
- let callee = this.parseIdentifier();
2489
- while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2490
- this.advance();
2491
- const property = this.parseIdentifier();
2492
- callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2493
- }
2494
- const typeArguments = this.tryCallTypeArguments();
2495
- if (!this.checkPunctuator("(")) {
2496
- this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2497
- }
2498
- const args = this.parseCallArguments();
2499
- return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2500
- }
2501
2610
  /** After `...`, is there something to spread? Nothing following it means
2502
2611
  * the vararg pack, which is what `f(...)` has always passed on. */
2503
2612
  startsSpread() {
@@ -2539,8 +2648,8 @@ var Parser = class {
2539
2648
  const errors = this.errors.length;
2540
2649
  try {
2541
2650
  this.advance();
2542
- const list = [this.parseTypeArgument()];
2543
- while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseTypeArgument());
2651
+ const list = [this.parseType()];
2652
+ while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
2544
2653
  this.expectOperator(">");
2545
2654
  if (!this.startsCallArguments()) throw new ParseRecover("not a call");
2546
2655
  return list;
@@ -2672,7 +2781,7 @@ var Parser = class {
2672
2781
  if (this.checkOperator("...")) {
2673
2782
  const dots = this.advance();
2674
2783
  if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2675
- elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2784
+ this.problemAt(dots, NO_DOTS);
2676
2785
  } else {
2677
2786
  const argument = this.expressionOr(stop);
2678
2787
  elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
@@ -2826,35 +2935,16 @@ var Parser = class {
2826
2935
  column: p.column
2827
2936
  };
2828
2937
  }
2829
- parseTypeOrTypePackReference() {
2938
+ /** `T...` written where a type goes: a pack, which tilua does not have. */
2939
+ rejectPackReference() {
2830
2940
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2831
- const start = this.current();
2832
- 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();
2833
2946
  this.advance();
2834
- const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
2835
- return {
2836
- type: "TypePackNode",
2837
- types: [],
2838
- hasVarargs: true,
2839
- // Wrap in `VariadicTypeNode`, matching the convention used by
2840
- // `parseFunctionTypeAfterParen`'s identifier-pack-reference
2841
- // branch, so the printer can tell `A...` (name-first, this
2842
- // case) apart from `...T` (dots-first) and append rather
2843
- // than prepend the `...`.
2844
- varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
2845
- ...spanFrom(start, this.previous())
2846
- };
2847
- }
2848
- return this.parseType();
2849
- }
2850
- parseTypeArgument() {
2851
- if (this.checkOperator("...")) {
2852
- return this.parseTypePack();
2853
- }
2854
- if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2855
- return this.parseTypeOrTypePackReference();
2856
2947
  }
2857
- return this.parseType();
2858
2948
  }
2859
2949
  /** The part shared by a real function body and an overload signature:
2860
2950
  * `<generics>(params): ReturnType`, up to (but not including) the block. */
@@ -2867,7 +2957,6 @@ var Parser = class {
2867
2957
  this.expectPunctuator("(");
2868
2958
  const params = [];
2869
2959
  let hasVarargs = false;
2870
- let varargTypeAnnotation;
2871
2960
  if (!this.checkPunctuator(")")) {
2872
2961
  while (true) {
2873
2962
  if (this.checkOperator("...")) {
@@ -2875,14 +2964,14 @@ var Parser = class {
2875
2964
  hasVarargs = true;
2876
2965
  if (this.checkType("Identifier")) {
2877
2966
  const nameTok = this.expectIdentifier();
2878
- let typeAnnotation2;
2967
+ let typeAnnotation3;
2879
2968
  if (this.matchPunctuator(":")) {
2880
- typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2969
+ typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
2881
2970
  }
2882
2971
  params.push({
2883
2972
  type: "FunctionParameter",
2884
2973
  name: nameTok.value,
2885
- typeAnnotation: typeAnnotation2,
2974
+ typeAnnotation: typeAnnotation3,
2886
2975
  rest: true,
2887
2976
  ...spanFrom(dots, this.previous())
2888
2977
  });
@@ -2891,9 +2980,20 @@ var Parser = class {
2891
2980
  }
2892
2981
  break;
2893
2982
  }
2983
+ this.problemAt(dots, NO_DOTS);
2984
+ let typeAnnotation2;
2894
2985
  if (this.matchPunctuator(":")) {
2895
- varargTypeAnnotation = this.parseTypeOrTypePackReference();
2986
+ this.rejectPackReference();
2987
+ const element = this.parseType();
2988
+ typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
2896
2989
  }
2990
+ params.push({
2991
+ type: "FunctionParameter",
2992
+ name: "args",
2993
+ typeAnnotation: typeAnnotation2,
2994
+ rest: true,
2995
+ ...spanFrom(dots, this.previous())
2996
+ });
2897
2997
  break;
2898
2998
  }
2899
2999
  const paramStart = this.current();
@@ -2935,10 +3035,16 @@ var Parser = class {
2935
3035
  if (this.matchPunctuator(":")) {
2936
3036
  predicate = this.tryParseTypePredicate();
2937
3037
  if (!predicate) {
2938
- returnType = this.attempt(() => this.parseTypeOrTypePackReference(), () => false, () => void 0);
3038
+ returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
2939
3039
  }
2940
3040
  }
2941
- 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();
2942
3048
  }
2943
3049
  /** TypeScript-style type-guard return annotations, in return position only:
2944
3050
  *
@@ -3008,7 +3114,6 @@ var Parser = class {
3008
3114
  generics: [],
3009
3115
  params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
3010
3116
  hasVarargs: false,
3011
- varargTypeAnnotation: void 0,
3012
3117
  returnType: void 0,
3013
3118
  predicate: void 0
3014
3119
  };
@@ -3020,7 +3125,6 @@ var Parser = class {
3020
3125
  generics: head.generics,
3021
3126
  params: head.params,
3022
3127
  hasVarargs: head.hasVarargs,
3023
- varargTypeAnnotation: head.varargTypeAnnotation,
3024
3128
  returnType: head.returnType,
3025
3129
  predicate: head.predicate,
3026
3130
  body,
@@ -3032,7 +3136,7 @@ var Parser = class {
3032
3136
  returnOf(expression) {
3033
3137
  const statement = {
3034
3138
  type: "ReturnStatement",
3035
- arguments: [expression],
3139
+ argument: expression,
3036
3140
  ...spanFrom(expression, expression)
3037
3141
  };
3038
3142
  return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
@@ -3045,7 +3149,6 @@ var Parser = class {
3045
3149
  generics: head.generics,
3046
3150
  params: head.params,
3047
3151
  hasVarargs: head.hasVarargs,
3048
- varargTypeAnnotation: head.varargTypeAnnotation,
3049
3152
  returnType: head.returnType,
3050
3153
  predicate: head.predicate,
3051
3154
  body,
@@ -3058,7 +3161,6 @@ var Parser = class {
3058
3161
  generics: head.generics,
3059
3162
  params: head.params,
3060
3163
  hasVarargs: head.hasVarargs,
3061
- varargTypeAnnotation: head.varargTypeAnnotation,
3062
3164
  returnType: head.returnType,
3063
3165
  predicate: head.predicate,
3064
3166
  ...spanFrom(head.start, this.previous())
@@ -3076,7 +3178,6 @@ var Parser = class {
3076
3178
  generics: head.generics,
3077
3179
  params: head.params,
3078
3180
  hasVarargs: head.hasVarargs,
3079
- varargTypeAnnotation: head.varargTypeAnnotation,
3080
3181
  returnType: head.returnType,
3081
3182
  predicate: head.predicate,
3082
3183
  body,
@@ -3217,9 +3318,10 @@ var Parser = class {
3217
3318
  return this.parseFunctionTypeAfterParen(t, []);
3218
3319
  }
3219
3320
  if (t.type === "Operator" && t.value === "...") {
3321
+ this.problem("'...' in a type goes before a rest parameter, '...args: T[]', or at the end of a tuple, '[A, ...B[]]'");
3220
3322
  this.advance();
3221
- const inner = this.parseType();
3222
- return { type: "VariadicTypeNode", typeAnnotation: inner, ...spanFrom(t, this.previous()) };
3323
+ const element = this.parseType();
3324
+ return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
3223
3325
  }
3224
3326
  if (t.type === "Punctuator" && t.value === "{") {
3225
3327
  return this.parseTableType();
@@ -3227,13 +3329,22 @@ var Parser = class {
3227
3329
  if (t.type === "Punctuator" && t.value === "[") {
3228
3330
  this.advance();
3229
3331
  const elements = [];
3332
+ let rest;
3230
3333
  while (!this.checkPunctuator("]")) {
3334
+ if (this.matchOperator("...")) {
3335
+ rest = this.parseType();
3336
+ if (rest.type !== "ArrayTypeNode" && !(rest.type === "TypeReference" && rest.base !== "nil")) {
3337
+ this.problem("The rest of a tuple is an array type: '...T[]'");
3338
+ }
3339
+ this.matchPunctuator(",");
3340
+ break;
3341
+ }
3231
3342
  elements.push(this.parseType());
3232
3343
  if (this.matchPunctuator(",")) continue;
3233
3344
  break;
3234
3345
  }
3235
3346
  this.expectPunctuator("]");
3236
- return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
3347
+ return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3237
3348
  }
3238
3349
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3239
3350
  this.advance();
@@ -3280,9 +3391,9 @@ var Parser = class {
3280
3391
  if (this.checkOperator("<")) {
3281
3392
  this.advance();
3282
3393
  if (!this.checkOperator(">")) {
3283
- typeArguments.push(this.parseTypeArgument());
3394
+ typeArguments.push(this.parseType());
3284
3395
  while (this.matchPunctuator(",") && !this.checkOperator(">")) {
3285
- typeArguments.push(this.parseTypeArgument());
3396
+ typeArguments.push(this.parseType());
3286
3397
  }
3287
3398
  }
3288
3399
  this.expectOperator(">");
@@ -3293,13 +3404,12 @@ var Parser = class {
3293
3404
  }
3294
3405
  parseFunctionTypeAfterParen(start, generics) {
3295
3406
  const params = [];
3296
- let hasVarargs = false;
3297
- let varargType;
3407
+ let rested = false;
3298
3408
  if (!this.checkPunctuator(")")) {
3299
3409
  while (true) {
3300
3410
  if (this.checkOperator("...")) {
3301
3411
  const dots = this.advance();
3302
- hasVarargs = true;
3412
+ rested = true;
3303
3413
  if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3304
3414
  const nameTok2 = this.expectIdentifier();
3305
3415
  this.advance();
@@ -3313,16 +3423,21 @@ var Parser = class {
3313
3423
  });
3314
3424
  break;
3315
3425
  }
3316
- 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
+ });
3317
3436
  break;
3318
3437
  }
3319
3438
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
3320
- const packStart = this.current();
3321
- const packRef = this.parseType();
3322
- this.advance();
3323
- hasVarargs = true;
3324
- varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
3325
- break;
3439
+ this.rejectPackReference();
3440
+ continue;
3326
3441
  }
3327
3442
  let name;
3328
3443
  let optional2 = false;
@@ -3352,19 +3467,17 @@ var Parser = class {
3352
3467
  this.expectPunctuator(")");
3353
3468
  if (this.matchPunctuator("=>") || this.mistypedArrow()) {
3354
3469
  const predicate = this.tryParseTypePredicate();
3355
- const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
3470
+ const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseReturnType();
3356
3471
  return {
3357
3472
  type: "FunctionTypeNode",
3358
3473
  generics,
3359
3474
  params,
3360
- hasVarargs,
3361
- varargType,
3362
3475
  returnType,
3363
3476
  predicate,
3364
3477
  ...spanFrom(start, this.previous())
3365
3478
  };
3366
3479
  }
3367
- if (params.length === 1 && !params[0].name && !hasVarargs) {
3480
+ if (params.length === 1 && !params[0].name && !rested) {
3368
3481
  return {
3369
3482
  type: "ParenthesizedTypeNode",
3370
3483
  typeAnnotation: params[0].typeAnnotation,
@@ -3374,11 +3487,14 @@ var Parser = class {
3374
3487
  if (params.some((p) => p.name !== void 0)) {
3375
3488
  this.error("Expected '=>' for function type");
3376
3489
  }
3490
+ if (!params.length) {
3491
+ this.problemAt(start, "'()' is not a type: a function that returns nothing returns 'nil'");
3492
+ return { type: "TypeReference", base: "nil", typeArguments: [], ...spanFrom(start, this.previous()) };
3493
+ }
3494
+ this.problemAt(start, "Several types in parentheses are a pack, which tilua does not have: write a tuple, '[A, B]'");
3377
3495
  return {
3378
- type: "TypePackNode",
3379
- types: params.map((p) => p.typeAnnotation),
3380
- hasVarargs,
3381
- varargType,
3496
+ type: "TupleTypeNode",
3497
+ elements: params.map((p) => p.typeAnnotation),
3382
3498
  ...spanFrom(start, this.previous())
3383
3499
  };
3384
3500
  }
@@ -3516,40 +3632,15 @@ var Parser = class {
3516
3632
  this.expectPunctuator("}");
3517
3633
  return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
3518
3634
  }
3519
- parseTypePack() {
3520
- const start = this.current();
3521
- if (this.matchOperator("...")) {
3522
- const varargType2 = this.parseType();
3523
- return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
3524
- }
3525
- this.expectPunctuator("(");
3526
- const types = [];
3527
- let hasVarargs = false;
3528
- let varargType;
3529
- if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
3530
- while (true) {
3531
- if (this.matchOperator("...")) {
3532
- hasVarargs = true;
3533
- varargType = this.parseType();
3534
- break;
3535
- }
3536
- types.push(this.parseType());
3537
- if (this.matchPunctuator(",")) continue;
3538
- break;
3539
- }
3540
- }
3541
- this.expectPunctuator(")");
3542
- return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
3543
- }
3544
3635
  parseGenericTypeParameterList() {
3545
3636
  const list = [];
3546
3637
  this.expectOperator("<");
3547
3638
  while (true) {
3548
3639
  const isConst = this.matchKeyword("const");
3549
3640
  const nameTok = this.expectIdentifier();
3550
- let isPack = false;
3551
- if (this.matchOperator("...")) {
3552
- 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'");
3553
3644
  }
3554
3645
  let constraint;
3555
3646
  if (this.checkIdentifierValue("extends")) {
@@ -3558,17 +3649,13 @@ var Parser = class {
3558
3649
  }
3559
3650
  let def;
3560
3651
  if (this.matchOperator("=")) {
3561
- if (isPack) {
3562
- def = this.parseTypePack();
3563
- } else {
3564
- def = this.parseType();
3565
- }
3652
+ this.rejectPackReference();
3653
+ def = this.parseType();
3566
3654
  }
3567
3655
  list.push({
3568
3656
  type: "GenericTypeParameter",
3569
3657
  name: nameTok.value,
3570
3658
  id: tokenIdentifier(nameTok),
3571
- isPack,
3572
3659
  isConst: isConst || void 0,
3573
3660
  constraint,
3574
3661
  default: def,
@@ -3659,7 +3746,7 @@ function childScope(parent) {
3659
3746
  var Analyzer = class {
3660
3747
  constructor(options) {
3661
3748
  this.options = options;
3662
- for (const name of options.builtinGlobals ?? []) {
3749
+ for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
3663
3750
  const id = this.getOrCreateGlobalBinding(name);
3664
3751
  this.bindings.get(id).isBuiltin = true;
3665
3752
  }
@@ -4061,14 +4148,14 @@ var Analyzer = class {
4061
4148
  return;
4062
4149
  }
4063
4150
  case "GenericForStatement": {
4064
- for (const it of stmt.iterators) this.visitExpression(it, scope);
4151
+ this.visitExpression(stmt.iterator, scope);
4065
4152
  const bodyScope = childScope(scope);
4066
- 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");
4067
4154
  this.visitBlock(stmt.body, bodyScope);
4068
4155
  return;
4069
4156
  }
4070
4157
  case "ReturnStatement":
4071
- for (const arg of stmt.arguments) this.visitExpression(arg, scope);
4158
+ if (stmt.argument) this.visitExpression(stmt.argument, scope);
4072
4159
  return;
4073
4160
  case "BreakStatement":
4074
4161
  case "ContinueStatement":
@@ -4136,7 +4223,6 @@ var Analyzer = class {
4136
4223
  this.declare(fnScope, param.name, kind, param);
4137
4224
  }
4138
4225
  });
4139
- this.visitType(func.varargTypeAnnotation, fnScope);
4140
4226
  this.visitType(func.returnType, fnScope);
4141
4227
  this.functionDepth++;
4142
4228
  try {
@@ -4164,6 +4250,7 @@ var Analyzer = class {
4164
4250
  this.visitGenerics(node.typeParams, scope);
4165
4251
  if (node.superclass) this.reference(outer, node.superclass);
4166
4252
  for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4253
+ for (const shape of node.implements ?? []) this.visitType(shape, scope);
4167
4254
  for (const member of node.members) {
4168
4255
  switch (member.type) {
4169
4256
  case "ClassField":
@@ -4223,7 +4310,6 @@ var Analyzer = class {
4223
4310
  case "BooleanLiteral":
4224
4311
  case "NumberLiteral":
4225
4312
  case "StringLiteral":
4226
- case "VarargExpression":
4227
4313
  case "ErrorExpression":
4228
4314
  return;
4229
4315
  case "InterpolatedStringExpression":
@@ -4263,11 +4349,6 @@ var Analyzer = class {
4263
4349
  this.visitExpression(expr.callee, scope);
4264
4350
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
4265
4351
  return;
4266
- case "NewExpression":
4267
- this.visitExpression(expr.callee, scope);
4268
- for (const argument of expr.arguments) this.visitExpression(argument, scope);
4269
- for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4270
- return;
4271
4352
  case "SuperExpression":
4272
4353
  return;
4273
4354
  case "SpreadElement":
@@ -4321,6 +4402,7 @@ var Analyzer = class {
4321
4402
  }
4322
4403
  }
4323
4404
  };
4405
+ var LANGUAGE_GLOBALS = ["scriptArgs"];
4324
4406
  function analyzeScopes(program, options = {}) {
4325
4407
  return new Analyzer(options).run(program);
4326
4408
  }
@@ -4339,8 +4421,8 @@ type Exclude<T, U> = T - U
4339
4421
  type Extract<T, U> = T extends U ? T : never
4340
4422
  type NonNullable<T> = T - nil
4341
4423
 
4342
- type ReturnType<T> = T extends (...unknown) => infer R ? R : never
4343
- type Parameters<T> = T extends (...infer P) => unknown ? P : never
4424
+ type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
4425
+ type Parameters<T> = T extends (...args: infer P) => unknown ? P : never
4344
4426
 
4345
4427
  type Partial<T> = { [K in keyof T]?: T[K] }
4346
4428
  type Required<T> = { [K in keyof T]-?: T[K] }
@@ -4425,8 +4507,11 @@ function literal(value) {
4425
4507
  function arrayOf(element) {
4426
4508
  return { kind: "array", element };
4427
4509
  }
4428
- function tuple(elements, isPack) {
4429
- 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;
4430
4515
  }
4431
4516
  function objectType(entries, indexer, frozen) {
4432
4517
  return { kind: "object", properties: new Map(entries), indexer, frozen };
@@ -4450,7 +4535,7 @@ function substitute(t, subst) {
4450
4535
  case "array":
4451
4536
  return arrayOf(substitute(t.element, subst));
4452
4537
  case "tuple":
4453
- return tuple(t.elements.map((e) => substitute(e, subst)), t.isPack);
4538
+ return tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst));
4454
4539
  case "object": {
4455
4540
  const entries = [];
4456
4541
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
@@ -4474,14 +4559,20 @@ function substitute(t, subst) {
4474
4559
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
4475
4560
  let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
4476
4561
  let varargs = t.varargs && substitute(t.varargs, inner);
4477
- if (varargs?.kind === "tuple" && varargs.isPack) {
4562
+ let restIsWhole = t.restIsWhole;
4563
+ if (restIsWhole && varargs?.kind === "tuple") {
4478
4564
  params = [...params, ...varargs.elements.map((type) => ({ type }))];
4479
- 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;
4480
4570
  }
4481
4571
  return {
4482
4572
  kind: "function",
4483
4573
  params,
4484
4574
  varargs,
4575
+ ...restIsWhole ? { restIsWhole } : {},
4485
4576
  returns: substitute(t.returns, inner),
4486
4577
  typeParams: t.typeParams,
4487
4578
  typeParamDefaults: t.typeParamDefaults,
@@ -4564,11 +4655,19 @@ function unify(param, arg, vars, out) {
4564
4655
  switch (param.kind) {
4565
4656
  case "array":
4566
4657
  if (arg.kind === "array") unify(param.element, arg.element, vars, out);
4567
- else if (arg.kind === "tuple") for (const e of arg.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);
4568
4659
  return;
4569
4660
  case "tuple":
4570
- if (arg.kind === "tuple") param.elements.forEach((p, i) => arg.elements[i] && unify(p, arg.elements[i], vars, out));
4571
- 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
+ }
4572
4671
  return;
4573
4672
  case "function":
4574
4673
  if (arg.kind === "function") {
@@ -4670,7 +4769,7 @@ function widen(t) {
4670
4769
  case "array":
4671
4770
  return arrayOf(widen(t.element));
4672
4771
  case "tuple":
4673
- return tuple(t.elements.map(widen), t.isPack);
4772
+ return tuple(t.elements.map(widen), t.rest && widen(t.rest));
4674
4773
  case "object": {
4675
4774
  if (t.frozen || t.class) return t;
4676
4775
  const entries = [];
@@ -4770,13 +4869,18 @@ function isAssignableInner(a, b) {
4770
4869
  if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
4771
4870
  if (a.kind === "array") {
4772
4871
  if (b.kind === "array") return isAssignable(a.element, b.element);
4872
+ if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
4773
4873
  return false;
4774
4874
  }
4775
4875
  if (a.kind === "tuple") {
4776
4876
  if (b.kind === "tuple") {
4777
- 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));
4778
4882
  }
4779
- 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));
4780
4884
  return false;
4781
4885
  }
4782
4886
  if (a.kind === "object") {
@@ -4825,7 +4929,7 @@ function sameRefTarget(a, b) {
4825
4929
  return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
4826
4930
  }
4827
4931
  function isNoValue(t) {
4828
- return t.kind === "tuple" && t.elements.length === 0;
4932
+ return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
4829
4933
  }
4830
4934
  function equalTypes(a, b) {
4831
4935
  return formatType(a) === formatType(b);
@@ -4932,7 +5036,7 @@ function containsFreeTypeParam(t, seen, bound) {
4932
5036
  case "array":
4933
5037
  return containsTypeParam(t.element, seen, bound);
4934
5038
  case "tuple":
4935
- return t.elements.some((e) => containsTypeParam(e, seen, bound));
5039
+ return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
4936
5040
  case "union":
4937
5041
  case "intersection":
4938
5042
  return t.types.some((m) => containsTypeParam(m, seen, bound));
@@ -4977,15 +5081,19 @@ function matchInfer(arg, pattern, out) {
4977
5081
  switch (pattern.kind) {
4978
5082
  case "array":
4979
5083
  if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
4980
- if (arg.kind === "tuple") return matchInfer(union(arg.elements), pattern.element, out);
5084
+ if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
4981
5085
  return false;
4982
5086
  case "tuple":
4983
5087
  if (arg.kind !== "tuple" || arg.elements.length !== pattern.elements.length) return false;
5088
+ if (pattern.rest && !matchInfer(arg.rest ?? neverType, pattern.rest, out)) return false;
4984
5089
  return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
4985
5090
  case "function": {
4986
5091
  if (arg.kind !== "function") return false;
4987
5092
  if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
4988
- out.set(pattern.varargs.name, tuple(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
+ ));
4989
5097
  } else {
4990
5098
  for (let i = 0; i < pattern.params.length; i++) {
4991
5099
  if (!arg.params[i]) return false;
@@ -5059,7 +5167,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
5059
5167
  collectTypeParams(t.element, out, seen);
5060
5168
  return;
5061
5169
  case "tuple":
5062
- for (const e of t.elements) collectTypeParams(e, out, seen);
5170
+ for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
5063
5171
  return;
5064
5172
  case "union":
5065
5173
  case "intersection":
@@ -5106,9 +5214,8 @@ function formatTypeUncached(t) {
5106
5214
  case "array":
5107
5215
  return `${formatAtom(t.element)}[]`;
5108
5216
  case "tuple": {
5109
- if (!t.elements.length) return "()";
5110
- const inner = t.elements.map(formatType).join(", ");
5111
- return t.isPack ? `(${inner})` : `[${inner}]`;
5217
+ const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
5218
+ return `[${inner.join(", ")}]`;
5112
5219
  }
5113
5220
  case "object": {
5114
5221
  if (t.name) {
@@ -5132,7 +5239,7 @@ function formatTypeUncached(t) {
5132
5239
  return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
5133
5240
  }).join(", ")}>` : "";
5134
5241
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
5135
- if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
5242
+ if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
5136
5243
  return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
5137
5244
  }
5138
5245
  case "typeParam":
@@ -5336,6 +5443,11 @@ function moduleExports(program, scopes, types, resolveModule) {
5336
5443
  }
5337
5444
  return { values, types: exportedTypes, default: defaultType };
5338
5445
  }
5446
+ function isBelow(k, key) {
5447
+ if (!k.startsWith(key) || k.length === key.length) return false;
5448
+ const next = k[key.length];
5449
+ return next === "." || next === "#" || next === "[";
5450
+ }
5339
5451
  function bindKey(id) {
5340
5452
  return `$${id}`;
5341
5453
  }
@@ -5353,9 +5465,6 @@ function mergeEnv(a, b, base) {
5353
5465
  function mergeIndexer(a, b) {
5354
5466
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5355
5467
  }
5356
- function producesMultipleValues(e) {
5357
- return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
5358
- }
5359
5468
  function isFreshLiteralExpr(e) {
5360
5469
  if (!e) return false;
5361
5470
  switch (e.type) {
@@ -5392,11 +5501,11 @@ function collectInferNames(node) {
5392
5501
  walk(n.element);
5393
5502
  return;
5394
5503
  case "ParenthesizedTypeNode":
5395
- case "VariadicTypeNode":
5396
5504
  walk(n.typeAnnotation);
5397
5505
  return;
5398
5506
  case "TupleTypeNode":
5399
5507
  n.elements.forEach(walk);
5508
+ walk(n.rest);
5400
5509
  return;
5401
5510
  case "UnionTypeNode":
5402
5511
  case "IntersectionTypeNode":
@@ -5414,7 +5523,6 @@ function collectInferNames(node) {
5414
5523
  return;
5415
5524
  case "FunctionTypeNode":
5416
5525
  n.params.forEach((pp) => walk(pp.typeAnnotation));
5417
- walk(n.varargType);
5418
5526
  walk(n.returnType);
5419
5527
  return;
5420
5528
  case "TableTypeNode":
@@ -5459,6 +5567,75 @@ function callsSuper(block) {
5459
5567
  });
5460
5568
  return found;
5461
5569
  }
5570
+ var ALL_FIELDS = /* @__PURE__ */ new Set();
5571
+ function definitelyAssigned(block) {
5572
+ const names = /* @__PURE__ */ new Set();
5573
+ for (const statement of block.statements) {
5574
+ switch (statement.type) {
5575
+ case "AssignmentStatement":
5576
+ case "CompoundAssignmentStatement":
5577
+ for (const name of assignedFields({ ...block, statements: [statement] })) names.add(name);
5578
+ break;
5579
+ case "DoStatement": {
5580
+ const inner = definitelyAssigned(statement.body);
5581
+ if (!inner) return void 0;
5582
+ for (const name of inner) names.add(name);
5583
+ break;
5584
+ }
5585
+ case "IfStatement": {
5586
+ if (!statement.alternate) break;
5587
+ const arms = [...statement.clauses.map((c) => c.body), statement.alternate].map(definitelyAssigned);
5588
+ const finishing = arms.filter((arm) => arm !== void 0);
5589
+ if (!finishing.length) return void 0;
5590
+ for (const name of finishing[0]) {
5591
+ if (finishing.every((arm) => arm.has(name))) names.add(name);
5592
+ }
5593
+ break;
5594
+ }
5595
+ case "ReturnStatement":
5596
+ return void 0;
5597
+ case "CallStatement": {
5598
+ const call = statement.expression;
5599
+ if (call.type === "CallExpression" && call.callee.type === "Identifier" && call.callee.name === "error") {
5600
+ return void 0;
5601
+ }
5602
+ break;
5603
+ }
5604
+ }
5605
+ }
5606
+ return names;
5607
+ }
5608
+ function withoutReceiver(t) {
5609
+ if (t.kind === "intersection") return intersection(t.types.map(withoutReceiver));
5610
+ if (t.kind !== "function") return t;
5611
+ const first = t.params[0]?.name;
5612
+ if (first !== "this" && first !== "self") return t;
5613
+ return { ...t, params: t.params.slice(1) };
5614
+ }
5615
+ var METAMETHOD_OPERANDS = {
5616
+ __add: 1,
5617
+ __sub: 1,
5618
+ __mul: 1,
5619
+ __div: 1,
5620
+ __idiv: 1,
5621
+ __mod: 1,
5622
+ __pow: 1,
5623
+ __concat: 1,
5624
+ __eq: 1,
5625
+ __lt: 1,
5626
+ __le: 1,
5627
+ __unm: 0,
5628
+ __len: 0,
5629
+ __tostring: 0,
5630
+ __iter: 0,
5631
+ __call: void 0
5632
+ };
5633
+ var METAMETHOD_RETURNS = {
5634
+ __tostring: stringType,
5635
+ __eq: booleanType,
5636
+ __lt: booleanType,
5637
+ __le: booleanType
5638
+ };
5462
5639
  function assignedFields(block) {
5463
5640
  const names = /* @__PURE__ */ new Set();
5464
5641
  walkNodes(block, (node) => {
@@ -5484,7 +5661,7 @@ function walkNodes(root, visit) {
5484
5661
  if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5485
5662
  }
5486
5663
  }
5487
- var AliasMap = class extends Map {
5664
+ var LazyMap = class extends Map {
5488
5665
  pending = /* @__PURE__ */ new Map();
5489
5666
  defer(name, resolve5) {
5490
5667
  super.delete(name);
@@ -5599,14 +5776,14 @@ var TypeAnalyzer = class {
5599
5776
  scopes;
5600
5777
  options;
5601
5778
  typeOf = /* @__PURE__ */ new Map();
5602
- bindingType = /* @__PURE__ */ new Map();
5779
+ bindingType = new LazyMap();
5603
5780
  narrowedTypeOf = /* @__PURE__ */ new Map();
5604
5781
  typeOfTypeNode = /* @__PURE__ */ new Map();
5605
5782
  expectedTypeOf = /* @__PURE__ */ new Map();
5606
- iteratesValues = /* @__PURE__ */ new Set();
5783
+ loops = /* @__PURE__ */ new Map();
5607
5784
  /** Public: each alias resolved once (generic aliases keep their params as
5608
5785
  * `typeParam` nodes in the body). */
5609
- aliases = new AliasMap();
5786
+ aliases = new LazyMap();
5610
5787
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
5611
5788
  aliasDefs = /* @__PURE__ */ new Map();
5612
5789
  /** Put on every ref to one of `aliasDefs`, so a module that imports the
@@ -5620,8 +5797,12 @@ var TypeAnalyzer = class {
5620
5797
  /** Generic parameters currently in lexical scope (alias body / generic fn),
5621
5798
  * with their `extends` constraints resolved. */
5622
5799
  typeParamScope = [];
5623
- /** Global types contributed by `declare` statements (libs, then this program). */
5624
- 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();
5625
5806
  /** Declaration node -> binding, built once so `bindingIdByName` is O(1)
5626
5807
  * instead of a scan of every binding per declaration site. */
5627
5808
  bindingByDecl = /* @__PURE__ */ new Map();
@@ -5666,8 +5847,11 @@ var TypeAnalyzer = class {
5666
5847
  this.indexDeclarations();
5667
5848
  for (const [name, id] of this.scopes.globalsByName) {
5668
5849
  if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
5669
- const t = this.options.globalTypes?.[name] ?? this.libGlobalTypes.get(name) ?? anyType;
5670
- 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);
5671
5855
  }
5672
5856
  setAliasExpander((t) => this.expand(t));
5673
5857
  setDeferredBound((t) => this.deferredBound(t));
@@ -5687,7 +5871,7 @@ var TypeAnalyzer = class {
5687
5871
  typeOfTypeNode: this.typeOfTypeNode,
5688
5872
  expectedTypeOf: this.expectedTypeOf,
5689
5873
  aliases: this.resolveDeferredAliases(),
5690
- iteratesValues: this.iteratesValues,
5874
+ loops: this.loops,
5691
5875
  diagnostics: this.diagnostics
5692
5876
  };
5693
5877
  }
@@ -5896,13 +6080,18 @@ var TypeAnalyzer = class {
5896
6080
  classIdentityCount = 0;
5897
6081
  /** The class whose members are being read, so `super` knows its base. */
5898
6082
  currentClass;
6083
+ /** Every class whose body the checker is inside, innermost last — what
6084
+ * decides whether a `private` member may be reached. */
6085
+ enclosingClasses = [];
5899
6086
  withClass(stmt, fn2) {
5900
6087
  const previous = this.currentClass;
5901
6088
  this.currentClass = stmt;
6089
+ this.enclosingClasses.push(stmt);
5902
6090
  try {
5903
6091
  return fn2();
5904
6092
  } finally {
5905
6093
  this.currentClass = previous;
6094
+ this.enclosingClasses.pop();
5906
6095
  }
5907
6096
  }
5908
6097
  /** What the class is known by. A declaration is known by its name, the way
@@ -6034,28 +6223,55 @@ var TypeAnalyzer = class {
6034
6223
  type.name = `typeof ${this.className(stmt)}`;
6035
6224
  const shape = this.shapeOf(stmt);
6036
6225
  const local = this.superDecl(stmt);
6037
- const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
6226
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
6038
6227
  if (parent?.kind === "object") {
6039
6228
  for (const [key, property] of parent.properties) {
6040
6229
  if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
6041
6230
  }
6042
6231
  }
6043
6232
  for (const [key, property] of shape.statics) type.properties.set(key, property);
6044
- 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
+ }
6045
6238
  const params = this.classTypeParams(stmt);
6046
- type.properties.set("new", {
6047
- type: fn(
6048
- constructor?.params.filter((p) => p.name !== "this") ?? [],
6049
- this.selfTypeOf(stmt),
6050
- constructor?.varargs,
6051
- params.map((p) => p.name)
6052
- ),
6239
+ let settled;
6240
+ const construct = {
6053
6241
  optional: false,
6054
- readonly: true
6242
+ readonly: true,
6243
+ // Kept, so a class extending it can read what it takes; reaching
6244
+ // it from outside is the error.
6245
+ ...stmt.isAbstract ? { abstract: true } : {}
6246
+ };
6247
+ Object.defineProperty(construct, "type", {
6248
+ enumerable: true,
6249
+ get: () => {
6250
+ if (settled) return settled;
6251
+ const constructor = this.constructorType(stmt);
6252
+ const made = fn(
6253
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
6254
+ this.selfTypeOf(stmt),
6255
+ constructor?.varargs,
6256
+ params.map((p) => p.name)
6257
+ );
6258
+ if (!this.shapeOf(stmt).filling) settled = made;
6259
+ return made;
6260
+ }
6055
6261
  });
6262
+ type.properties.set("new", construct);
6056
6263
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
6057
6264
  return type;
6058
6265
  }
6266
+ /** The value side of the class `superclass` names when it is not
6267
+ * declared in this file: what the name it refers to holds — an import
6268
+ * brings the class table with it — or else a class of that name. */
6269
+ classStaticsOf(superclass) {
6270
+ const id = this.bindingIdOf(superclass);
6271
+ const bound = id !== void 0 ? this.expand(this.bindingType.get(id) ?? unknownType) : void 0;
6272
+ if (bound?.kind === "object") return bound;
6273
+ return this.classStaticsByNameType(superclass.name);
6274
+ }
6059
6275
  /** The value side of a class named by a binding rather than by a
6060
6276
  * declaration in this file — an imported one. */
6061
6277
  classStaticsByNameType(name) {
@@ -6116,17 +6332,26 @@ var TypeAnalyzer = class {
6116
6332
  } finally {
6117
6333
  this.emitDiagnostics = wasEmitting;
6118
6334
  shape.filling = false;
6335
+ for (const then of shape.filled ?? []) then();
6336
+ shape.filled = void 0;
6119
6337
  }
6120
6338
  return shape;
6121
6339
  }
6122
6340
  fillShape(stmt, shape) {
6341
+ const className = this.className(stmt);
6123
6342
  const put = (isStatic, name, property) => {
6124
- (isStatic ? shape.statics : shape.instance).set(name, 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;
6345
+ (isStatic ? shape.statics : shape.instance).set(name, marked);
6125
6346
  };
6126
6347
  for (const member of stmt.members) {
6127
6348
  if (member.type !== "ClassField") continue;
6128
6349
  const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
6129
- put(member.isStatic, member.name.name, { type, optional: false });
6350
+ put(member.isStatic, member.name.name, {
6351
+ type,
6352
+ optional: false,
6353
+ ...member.isReadonly ? { readonly: true } : {}
6354
+ });
6130
6355
  }
6131
6356
  for (const member of stmt.members) {
6132
6357
  switch (member.type) {
@@ -6135,7 +6360,11 @@ var TypeAnalyzer = class {
6135
6360
  case "ClassMethod": {
6136
6361
  this.paramsFromSignatures(member.func, member.signatures);
6137
6362
  const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
6138
- put(member.isStatic, member.name.name, { type, optional: false });
6363
+ put(member.isStatic, member.name.name, {
6364
+ type,
6365
+ optional: false,
6366
+ ...member.isAbstract ? { abstract: true } : {}
6367
+ });
6139
6368
  break;
6140
6369
  }
6141
6370
  case "ClassAccessor": {
@@ -6191,7 +6420,14 @@ var TypeAnalyzer = class {
6191
6420
  for (const signature of member.signatures ?? []) {
6192
6421
  this.checkParamOrder(signature.params, member);
6193
6422
  }
6194
- 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
+ }
6195
6431
  }
6196
6432
  });
6197
6433
  }));
@@ -6238,13 +6474,148 @@ var TypeAnalyzer = class {
6238
6474
  if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
6239
6475
  report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
6240
6476
  }
6241
- 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();
6242
6482
  for (const member of stmt.members) {
6243
6483
  if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6244
- if (assigned.has(member.name.name)) continue;
6484
+ if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
6245
6485
  const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6246
6486
  if (isAssignable(nilType, type)) continue;
6247
- report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
6487
+ report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor on every path, or let its type admit nil`);
6488
+ }
6489
+ }
6490
+ /** The class whose constructor is being checked: it, and only it, may
6491
+ * assign the `readonly` fields it declares. */
6492
+ constructing;
6493
+ /** A class that is not abstract has a `new`, so everything its instances
6494
+ * promise has to be there: no abstract member of its own, and every one
6495
+ * it inherits written. */
6496
+ checkAbstractMembers(stmt) {
6497
+ if (stmt.isAbstract) return;
6498
+ const name = this.className(stmt);
6499
+ for (const member of stmt.members) {
6500
+ if (member.type === "ClassMethod" && member.isAbstract) {
6501
+ this.diagnostics.push({
6502
+ node: member.name,
6503
+ message: `'${member.name.name}' is abstract, so '${name}' has to be an 'abstract class'`
6504
+ });
6505
+ }
6506
+ }
6507
+ const own = this.shapeOf(stmt).instance;
6508
+ const missing = [...this.instanceType(stmt).properties].filter(([key, property]) => property.abstract && !own.has(key)).map(([key]) => `'${key}'`);
6509
+ if (missing.length) {
6510
+ this.diagnostics.push({
6511
+ node: stmt.name ?? stmt,
6512
+ message: `'${name}' does not write the abstract ${missing.length === 1 ? "member" : "members"} ${missing.join(", ")} of the class it extends; write ${missing.length === 1 ? "it" : "them"}, or make '${name}' abstract too`
6513
+ });
6514
+ }
6515
+ }
6516
+ /** A member written again in a class that extends another has to fit
6517
+ * where the base's stood — anything reading an instance as the base
6518
+ * would otherwise be told wrong. `override` says a member is such a one,
6519
+ * and is wrong on a member that is not. */
6520
+ checkOverrides(stmt) {
6521
+ const base = this.baseInstance(stmt);
6522
+ const name = this.className(stmt);
6523
+ const own = this.shapeOf(stmt).instance;
6524
+ for (const member of stmt.members) {
6525
+ if (member.type === "ClassConstructor" || member.isStatic) continue;
6526
+ const key = member.name.name;
6527
+ const inherited = CLASS_LINKS.has(key) ? void 0 : base?.properties.get(key);
6528
+ if (member.isOverride && !inherited) {
6529
+ this.diagnostics.push({
6530
+ node: member.name,
6531
+ message: base ? `'${key}' is marked 'override', but '${stmt.superclass.name}' has no member of that name` : `'${key}' is marked 'override', but '${name}' does not extend another class`
6532
+ });
6533
+ continue;
6534
+ }
6535
+ const mine = own.get(key);
6536
+ if (!inherited || !mine || inherited.private && !inherited.private.protected) continue;
6537
+ const written = withoutReceiver(mine.type);
6538
+ const expected = withoutReceiver(inherited.type);
6539
+ if (isAssignable(written, expected)) continue;
6540
+ this.diagnostics.push({
6541
+ node: member.name,
6542
+ message: `'${key}' in '${name}' does not fit the '${key}' of '${stmt.superclass.name}' it replaces: '${briefType(written)}' is not assignable to '${briefType(expected)}'`
6543
+ });
6544
+ }
6545
+ }
6546
+ /** `implements Shape`: every member the shape names, of a type that fits.
6547
+ * A method's own `this` is left out of the comparison on both sides. */
6548
+ checkImplements(stmt) {
6549
+ if (!stmt.implements?.length) return;
6550
+ const name = this.className(stmt);
6551
+ const instance = this.instanceType(stmt).properties;
6552
+ for (const node of stmt.implements) {
6553
+ const shown = node.type === "TypeReference" ? node.namespace ? `${node.namespace}.${node.base}` : node.base : void 0;
6554
+ const target = this.expand(this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(node)));
6555
+ if (target.kind === "any") continue;
6556
+ if (target.kind !== "object") {
6557
+ this.diagnostics.push({
6558
+ node,
6559
+ message: `A class can only implement an object type or a class, not '${briefType(target)}'`
6560
+ });
6561
+ continue;
6562
+ }
6563
+ const label = shown ?? briefType(target);
6564
+ const missing = [];
6565
+ for (const [key, wanted] of target.properties) {
6566
+ const have = instance.get(key);
6567
+ if (!have) {
6568
+ if (!wanted.optional) missing.push(`'${key}'`);
6569
+ continue;
6570
+ }
6571
+ const written = withoutReceiver(have.type);
6572
+ const expected = withoutReceiver(wanted.optional ? optional(wanted.type) : wanted.type);
6573
+ if (isAssignable(written, expected)) continue;
6574
+ this.diagnostics.push({
6575
+ node,
6576
+ message: `'${name}' does not implement '${label}': its '${key}' is '${briefType(written)}', not '${briefType(expected)}'`
6577
+ });
6578
+ }
6579
+ if (missing.length) {
6580
+ this.diagnostics.push({
6581
+ node,
6582
+ message: `'${name}' does not implement '${label}': ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing`
6583
+ });
6584
+ }
6585
+ }
6586
+ }
6587
+ /** A method named for a metamethod *is* one — the class table is its
6588
+ * instances' metatable — so it has to take what Luau hands it and give
6589
+ * back what Luau expects. */
6590
+ checkMetamethods(stmt) {
6591
+ const instance = this.shapeOf(stmt).instance;
6592
+ for (const member of stmt.members) {
6593
+ if (member.type !== "ClassMethod" || !(member.name.name in METAMETHOD_OPERANDS)) continue;
6594
+ const key = member.name.name;
6595
+ if (member.isStatic) {
6596
+ this.diagnostics.push({
6597
+ node: member.name,
6598
+ message: `'${key}' is a metamethod of the instances; it cannot be static`
6599
+ });
6600
+ continue;
6601
+ }
6602
+ const operands = METAMETHOD_OPERANDS[key];
6603
+ const written = member.func.params.filter((p) => p.name !== "this").length;
6604
+ if (operands !== void 0 && (written !== operands || member.func.hasVarargs)) {
6605
+ this.diagnostics.push({
6606
+ node: member.name,
6607
+ message: operands === 0 ? `'${key}' takes nothing besides 'this'` : `'${key}' takes one operand besides 'this'`
6608
+ });
6609
+ continue;
6610
+ }
6611
+ const required = METAMETHOD_RETURNS[key];
6612
+ const returns = this.overloadsOf(instance.get(key)?.type ?? anyType)[0]?.returns;
6613
+ if (required && returns && !isAssignable(returns, required)) {
6614
+ this.diagnostics.push({
6615
+ node: member.name,
6616
+ message: `'${key}' has to return ${briefType(required)}, not '${briefType(returns)}'`
6617
+ });
6618
+ }
6248
6619
  }
6249
6620
  }
6250
6621
  /** `extends` must name a class, and the chain must end. */
@@ -6302,19 +6673,42 @@ var TypeAnalyzer = class {
6302
6673
  harvestDeclares(block, own = false) {
6303
6674
  for (const stmt of block.statements) {
6304
6675
  if (stmt.type !== "DeclareStatement") continue;
6305
- if (own && (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))) {
6306
6686
  this.deferredDeclares.set(stmt.name, stmt);
6307
6687
  continue;
6308
6688
  }
6309
- const t = this.resolveType(stmt.valueType);
6310
- const prev = this.libGlobalTypes.get(stmt.name);
6311
- const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6312
- this.libGlobalTypes.set(
6313
- stmt.name,
6314
- overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
6315
- );
6689
+ this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
6690
+ }
6691
+ }
6692
+ /** Every library `declare` of `name`, in order. They are written at the
6693
+ * top level of their files, so they are read as if there — outside any
6694
+ * generic the use that asked for them happens to sit in. */
6695
+ resolveLibDeclares(name) {
6696
+ const scope = this.typeParamScope.splice(0);
6697
+ try {
6698
+ let type;
6699
+ for (const stmt of this.libDeclares.get(name) ?? []) type = this.declaredOver(type, stmt);
6700
+ return type ?? anyType;
6701
+ } finally {
6702
+ this.typeParamScope.push(...scope);
6316
6703
  }
6317
6704
  }
6705
+ /** A `declare` on top of what earlier ones said about the same name: a
6706
+ * function adds an overload, a table adds members. */
6707
+ declaredOver(prev, stmt) {
6708
+ const t = this.resolveType(stmt.valueType);
6709
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6710
+ return overload ? intersection([prev, t]) : this.mergeDeclared(prev, t);
6711
+ }
6318
6712
  resolveAllAliases() {
6319
6713
  for (const [name, def] of this.aliasDefs) {
6320
6714
  if (def.class && !this.program.body.statements.includes(def.class)) {
@@ -6360,11 +6754,10 @@ var TypeAnalyzer = class {
6360
6754
  }
6361
6755
  imported;
6362
6756
  /** What a `return` gives, against what the function declared. */
6363
- checkReturn(stmt, declared, types, sources, env) {
6757
+ checkReturn(stmt, declared, actual, env) {
6364
6758
  if (!declared || !this.emitDiagnostics) return;
6365
6759
  if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
6366
- const actual = stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true);
6367
- const source = stmt.arguments.length === 1 ? sources[0] : void 0;
6760
+ const source = stmt.argument;
6368
6761
  const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
6369
6762
  if (fits) return;
6370
6763
  this.diagnostics.push({
@@ -6512,21 +6905,12 @@ var TypeAnalyzer = class {
6512
6905
  this.instantiationDepth--;
6513
6906
  }
6514
6907
  }
6515
- /** Pair written type arguments with the parameters they instantiate. A
6516
- * pack parameter (`T...`) takes every argument from its position on, as
6517
- * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
6518
- * and `Signal<()>` to the empty pack. Left out, a parameter takes its
6519
- * default (`T... = ...any` is `any`), or `unknown`. */
6908
+ /** Pair written type arguments with the parameters they instantiate.
6909
+ * Left out, a parameter takes its default, or `unknown`. */
6520
6910
  bindTypeArguments(params, args) {
6521
6911
  const subst = /* @__PURE__ */ new Map();
6522
6912
  params.forEach((p, i) => {
6523
- let arg = args[i];
6524
- if (p.isPack && i < args.length) {
6525
- const rest = args.slice(i);
6526
- const single = rest.length === 1 ? rest[0] : void 0;
6527
- arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
6528
- }
6529
- subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
6913
+ subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
6530
6914
  });
6531
6915
  return subst;
6532
6916
  }
@@ -6618,8 +7002,13 @@ var TypeAnalyzer = class {
6618
7002
  return literal(node.value);
6619
7003
  case "ArrayTypeNode":
6620
7004
  return arrayOf(this.resolveType(node.element));
6621
- case "TupleTypeNode":
6622
- 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
+ }
6623
7012
  case "UnionTypeNode":
6624
7013
  return union(node.types.map((t) => this.resolveType(t)));
6625
7014
  case "IntersectionTypeNode":
@@ -6655,15 +7044,13 @@ var TypeAnalyzer = class {
6655
7044
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
6656
7045
  optional: p.optional
6657
7046
  }));
6658
- const restParam = node.params.find((p) => p.rest);
6659
- const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
6660
- return this.withTypeParamDefaults(fn(
7047
+ return this.withTypeParamDefaults(this.withRest(fn(
6661
7048
  params,
6662
7049
  this.resolveType(node.returnType),
6663
- restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
7050
+ void 0,
6664
7051
  names,
6665
7052
  this.resolvePredicate(node.predicate, params)
6666
- ), node.generics);
7053
+ ), node.params), node.generics);
6667
7054
  });
6668
7055
  }
6669
7056
  case "TypeofTypeNode": {
@@ -6727,13 +7114,6 @@ var TypeAnalyzer = class {
6727
7114
  })
6728
7115
  );
6729
7116
  }
6730
- case "VariadicTypeNode":
6731
- return this.resolveType(node.typeAnnotation);
6732
- case "TypePackNode": {
6733
- if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
6734
- if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
6735
- return tuple(node.types.map((t) => this.resolveType(t)), true);
6736
- }
6737
7117
  }
6738
7118
  }
6739
7119
  // --------------------------------------------------------
@@ -6799,15 +7179,18 @@ var TypeAnalyzer = class {
6799
7179
  case "array":
6800
7180
  return arrayOf(this.reduceType(t.element));
6801
7181
  case "tuple":
6802
- return tuple(t.elements.map((e) => this.reduceType(e)), t.isPack);
7182
+ return tuple(t.elements.map((e) => this.reduceType(e)), t.rest && this.reduceType(t.rest));
6803
7183
  case "function":
6804
- return fn(
6805
- t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
6806
- this.reduceType(t.returns),
6807
- t.varargs && this.reduceType(t.varargs),
6808
- t.typeParams,
6809
- t.predicate
6810
- );
7184
+ return {
7185
+ ...fn(
7186
+ t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
7187
+ this.reduceType(t.returns),
7188
+ t.varargs && this.reduceType(t.varargs),
7189
+ t.typeParams,
7190
+ t.predicate
7191
+ ),
7192
+ ...t.restIsWhole ? { restIsWhole: true } : {}
7193
+ };
6811
7194
  case "object": {
6812
7195
  if (t.class) return t;
6813
7196
  const entries = [];
@@ -7022,7 +7405,8 @@ var TypeAnalyzer = class {
7022
7405
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
7023
7406
  }
7024
7407
  });
7025
- const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
7408
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7409
+ this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
7026
7410
  stmt.names.forEach((target, i) => {
7027
7411
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
7028
7412
  const source = sources[i];
@@ -7111,7 +7495,8 @@ var TypeAnalyzer = class {
7111
7495
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7112
7496
  }
7113
7497
  });
7114
- const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
7498
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7499
+ this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7115
7500
  stmt.targets.forEach((target, i) => {
7116
7501
  const vt = valueTypes[i] ?? unknownType;
7117
7502
  const source = sources[i];
@@ -7198,23 +7583,26 @@ var TypeAnalyzer = class {
7198
7583
  return;
7199
7584
  }
7200
7585
  case "GenericForStatement": {
7201
- const iterTypes = stmt.iterators.map((it) => this.infer(it, env));
7586
+ const sourceType = this.infer(stmt.iterator, env);
7202
7587
  const bodyEnv = forkEnv(env);
7203
- const rows = stmt.variables.length >= 2 ? this.iterationRows(stmt.iterators[0], iterTypes[0]) : void 0;
7588
+ const rows = this.iterationRows(stmt.iterator, sourceType);
7204
7589
  if (rows) {
7205
- const [key, value] = stmt.variables;
7206
- this.bindPattern(key, union(rows.map((r) => r[0])), bodyEnv, "keep");
7207
- this.bindPattern(value, union(rows.map((r) => r[1])), bodyEnv, "keep");
7208
- stmt.variables.slice(2).forEach((v) => this.bindPattern(v, unknownType, bodyEnv, "widen"));
7209
- const keyId = key.type === "IdentifierPattern" ? this.bindingIdByName(key.name, key) : void 0;
7210
- const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
7211
- 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 });
7212
7602
  } else {
7213
- const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
7214
- stmt.variables.forEach((v, i) => {
7215
- this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
7216
- });
7217
- if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
7603
+ const [item, form] = this.iterationOf(stmt.iterator, sourceType);
7604
+ this.bindPattern(stmt.variable, item, bodyEnv, "widen");
7605
+ this.loops.set(stmt, form);
7218
7606
  }
7219
7607
  this.visitBlock(stmt.body, bodyEnv);
7220
7608
  return;
@@ -7222,19 +7610,10 @@ var TypeAnalyzer = class {
7222
7610
  case "ReturnStatement": {
7223
7611
  const declared = this.declaredReturns[this.declaredReturns.length - 1];
7224
7612
  const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
7225
- if (wanted) {
7226
- if (stmt.arguments.length === 1) {
7227
- this.applyContext(stmt.arguments[0], wanted);
7228
- } else if (wanted.kind === "tuple" && wanted.isPack) {
7229
- stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
7230
- }
7231
- }
7232
- const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
7233
- const { types, sources } = this.valueList(stmt.arguments, env, want);
7234
- this.checkReturn(stmt, declared, types, sources, env);
7235
- if (this.returnTypes) {
7236
- this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
7237
- }
7613
+ if (wanted && stmt.argument) this.applyContext(stmt.argument, wanted);
7614
+ const actual = stmt.argument ? this.infer(stmt.argument, env) : nilType;
7615
+ this.checkReturn(stmt, declared, actual, env);
7616
+ if (this.returnTypes) this.returnTypes.push(actual);
7238
7617
  return;
7239
7618
  }
7240
7619
  case "ExportStatement":
@@ -7321,7 +7700,7 @@ var TypeAnalyzer = class {
7321
7700
  * `sources` maps each produced value back to the expression it came from
7322
7701
  * (undefined for the 2nd and later values of a multi-value call), so the
7323
7702
  * caller can still do contextual typing against the written expression. */
7324
- valueList(exprs, env, want = 0) {
7703
+ valueList(exprs, env) {
7325
7704
  const types = [];
7326
7705
  const sources = [];
7327
7706
  exprs.forEach((e, i) => {
@@ -7337,31 +7716,29 @@ var TypeAnalyzer = class {
7337
7716
  }
7338
7717
  return;
7339
7718
  }
7340
- do {
7341
- types.push(t);
7342
- sources.push(e);
7343
- } while (last && types.length < want);
7344
- return;
7345
- }
7346
- if (last && e.type === "VarargExpression") {
7347
- do {
7348
- types.push(t);
7349
- sources.push(types.length - 1 === i ? e : void 0);
7350
- } while (types.length < want);
7719
+ types.push(t);
7720
+ sources.push(e);
7351
7721
  return;
7352
7722
  }
7353
- if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
7354
- t.elements.forEach((el, j) => {
7355
- types.push(el);
7356
- sources.push(j === 0 ? e : void 0);
7357
- });
7358
- return;
7359
- }
7360
- types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
7723
+ void last;
7724
+ types.push(t);
7361
7725
  sources.push(e);
7362
7726
  });
7363
7727
  return { types, sources };
7364
7728
  }
7729
+ /** `const a, b = x, y` pairs names with values, one each. A call is one
7730
+ * value — several come back as an array — so `const a, b = f()` is a
7731
+ * name short of a value, and says how to take them apart. */
7732
+ checkValueCount(node, names, values, types) {
7733
+ if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7734
+ const last = values[values.length - 1];
7735
+ const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7736
+ const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7737
+ this.diagnostics.push({
7738
+ node,
7739
+ message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
7740
+ });
7741
+ }
7365
7742
  /** Run `visit` with a fresh place to collect `break` states, and return
7366
7743
  * what it collected. */
7367
7744
  withBreakScope(visit) {
@@ -7510,18 +7887,29 @@ var TypeAnalyzer = class {
7510
7887
  * context. */
7511
7888
  contextualArrays = /* @__PURE__ */ new WeakMap();
7512
7889
  applyArrayContext(e, expected) {
7513
- const 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];
7514
7896
  if (!target) return;
7897
+ if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
7515
7898
  if (!e.elements.length) {
7516
7899
  this.contextualArrays.set(e, target);
7517
7900
  return;
7518
7901
  }
7519
7902
  e.elements.forEach((element, i) => {
7520
7903
  if (element.type === "SpreadElement") return;
7521
- const 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;
7522
7906
  this.applyContext(element, elementType);
7523
7907
  });
7524
7908
  }
7909
+ /** Array literals written where a tuple is wanted — a `return` of a
7910
+ * function that returns one, an argument, an annotated `const` — and
7911
+ * the tuple each is to be. */
7912
+ tupleArrays = /* @__PURE__ */ new WeakMap();
7525
7913
  /** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
7526
7914
  * value gets its property's type as context. */
7527
7915
  applyTableContext(e, expected) {
@@ -7577,32 +7965,33 @@ var TypeAnalyzer = class {
7577
7965
  }
7578
7966
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
7579
7967
  }
7580
- /** What a vararg function's `...` holds, one value at a time.
7581
- *
7582
- * Written three ways, and they mean the same call: `...` says nothing,
7583
- * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7584
- * into an array the body reads by name. Only the last changes what the
7585
- * body sees — the signature is the same either way. */
7586
- varargElement(func) {
7587
- if (!func.hasVarargs) return void 0;
7588
- const rest = func.params.find((p) => p.rest);
7589
- if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7590
- const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7591
- return declared?.kind === "array" ? declared.element : unknownType;
7592
- }
7593
- /** The type of `...` in each function body being walked. */
7594
- 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
+ }
7595
7986
  /** What each function body being walked declared it returns. */
7596
7987
  declaredReturns = [];
7597
- /** Run `body` with `...` and `return` as `func` declares them. */
7988
+ /** Run `body` with `return` as `func` declares it. */
7598
7989
  withVarargs(func, body) {
7599
- this.varargs.push(this.varargElement(func));
7600
7990
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
7601
7991
  this.contextualReturns.push(this.contextualReturnOf.get(func));
7602
7992
  try {
7603
7993
  return body();
7604
7994
  } finally {
7605
- this.varargs.pop();
7606
7995
  this.declaredReturns.pop();
7607
7996
  this.contextualReturns.pop();
7608
7997
  }
@@ -7661,7 +8050,7 @@ var TypeAnalyzer = class {
7661
8050
  if (type.kind !== "function") return type;
7662
8051
  const defaults = {};
7663
8052
  for (const generic of generics) {
7664
- if (generic.default && !generic.isPack) defaults[generic.name] = this.resolveType(generic.default);
8053
+ if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
7665
8054
  }
7666
8055
  return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
7667
8056
  }
@@ -7680,7 +8069,10 @@ var TypeAnalyzer = class {
7680
8069
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
7681
8070
  unify(p.type, preserve ? arg : widen(arg), vars, subst);
7682
8071
  });
7683
- if (f.varargs) {
8072
+ if (f.varargs && f.restIsWhole) {
8073
+ const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
8074
+ unify(f.varargs, tuple(rest), vars, subst);
8075
+ } else if (f.varargs) {
7684
8076
  const keeps = keepsLiterals(f.varargs);
7685
8077
  for (let i = f.params.length; i < argTypes.length; i++) {
7686
8078
  const arg = argTypes[i];
@@ -7849,7 +8241,12 @@ var TypeAnalyzer = class {
7849
8241
  const arg = argTypes[i];
7850
8242
  const declared = declaredAt(i);
7851
8243
  if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
7852
- 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
+ }
7853
8250
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
7854
8251
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
7855
8252
  this.diagnostics.push({
@@ -7977,13 +8374,13 @@ var TypeAnalyzer = class {
7977
8374
  type: this.paramType(p, /* @__PURE__ */ new Map()),
7978
8375
  optional: p.optional || p.default !== void 0
7979
8376
  }));
7980
- return fn(
8377
+ return this.withRest(fn(
7981
8378
  params,
7982
8379
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
7983
- this.varargElement(sig),
8380
+ void 0,
7984
8381
  names,
7985
8382
  this.resolvePredicate(sig.predicate, params)
7986
- );
8383
+ ), sig.params);
7987
8384
  }));
7988
8385
  }
7989
8386
  /** `...rest: T[]` holds every argument from its position on, so its type
@@ -7991,7 +8388,7 @@ var TypeAnalyzer = class {
7991
8388
  checkRestType(p, node) {
7992
8389
  if (!this.emitDiagnostics || !p.typeAnnotation) return;
7993
8390
  const declared = this.resolveType(p.typeAnnotation);
7994
- if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
8391
+ if (declared.kind === "array" || declared.kind === "tuple" || declared.kind === "any" || declared.kind === "typeParam") return;
7995
8392
  this.diagnostics.push({
7996
8393
  node,
7997
8394
  message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
@@ -8047,13 +8444,13 @@ var TypeAnalyzer = class {
8047
8444
  return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
8048
8445
  }));
8049
8446
  }
8050
- return fn(
8447
+ return this.withRest(fn(
8051
8448
  params,
8052
8449
  returns,
8053
- this.varargElement(func),
8450
+ void 0,
8054
8451
  names,
8055
8452
  this.resolvePredicate(func.predicate, params)
8056
- );
8453
+ ), func.params);
8057
8454
  });
8058
8455
  }
8059
8456
  /** Where the return types of the function being walked are collected, so
@@ -8095,17 +8492,16 @@ var TypeAnalyzer = class {
8095
8492
  this.preVisitDepth--;
8096
8493
  }
8097
8494
  }
8098
- /** The `[key, value]` pairs iterating a record yields, one per property —
8099
- * for `pairs(t)`, `next, t` and `for k, v in t` over an object type with
8100
- * no indexer. `undefined` for anything else (an array, a dictionary, an
8101
- * iterator function), whose keys have no names to list. */
8495
+ /** The `[key, value]` items `pairs(record)` hands out, one per property.
8496
+ * `undefined` for anything else (an array, a dictionary, an iterator
8497
+ * function), whose keys have no names to list. */
8102
8498
  iterationRows(iterNode, iterType) {
8103
8499
  let source;
8104
8500
  if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8105
- if (iterNode.callee.name !== "pairs" && iterNode.callee.name !== "next") return void 0;
8501
+ if (iterNode.callee.name !== "pairs") return void 0;
8106
8502
  source = this.typeOf.get(iterNode.arguments[0]);
8107
8503
  } else {
8108
- source = iterType;
8504
+ return void 0;
8109
8505
  }
8110
8506
  const t = source && this.expand(source);
8111
8507
  if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
@@ -8246,54 +8642,69 @@ var TypeAnalyzer = class {
8246
8642
  if (ids.length < 2) return;
8247
8643
  this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
8248
8644
  }
8249
- /** `(keyType, valueType, iteratesValues)` yielded by a generic-for
8250
- * iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
8251
- * iteration (`for … in t`). `varCount` is how many loop variables were
8252
- * written. `iteratesValues` reports the one case where the first variable
8253
- * is bound to the value rather than the key, which is what lowering has
8254
- * to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
8255
- iterationTypes(iterNode, iterType, varCount) {
8256
- if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8257
- const name = iterNode.callee.name;
8258
- const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
8259
- if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
8260
- 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") {
8261
8663
  if (src.kind === "object") {
8262
- return [
8664
+ return [tuple([
8263
8665
  src.indexer?.key ?? stringType,
8264
- src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
8265
- false
8266
- ];
8666
+ src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
8667
+ ]), iteration];
8267
8668
  }
8268
- if (src.kind === "array") return [numberType, src.element, false];
8669
+ if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
8269
8670
  }
8270
8671
  }
8271
- const iterator = this.expand(iterType);
8272
- if (iterator.kind === "function") {
8273
- const returns = this.expand(iterator.returns);
8274
- const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
8275
- const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
8276
- return [at(0), at(1), false];
8277
- }
8278
- const t = this.expand(iterType);
8279
- if (t.kind === "array") {
8280
- return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
8281
- }
8672
+ const handsOut = (t2, viaIter) => {
8673
+ const x = this.expand(t2);
8674
+ if (x.kind === "function") return [withoutNil(this.expand(x.returns)), { walks: "function", viaIter }];
8675
+ const step = this.iterationTriple(x);
8676
+ if (step) return [withoutNil(this.expand(step.returns)), { walks: "iteration", viaIter }];
8677
+ return void 0;
8678
+ };
8679
+ const direct = handsOut(sourceType, false);
8680
+ if (direct) return direct;
8681
+ const values = { walks: "values", viaIter: false };
8682
+ const t = this.expand(sourceType);
8683
+ if (t.kind === "any") return [anyType, values];
8684
+ const iter = t.kind === "object" ? t.properties.get("__iter") : void 0;
8685
+ if (iter) {
8686
+ const made = this.overloadsOf(iter.type)[0]?.returns ?? unknownType;
8687
+ return handsOut(made, true) ?? [unknownType, { walks: "values", viaIter: true }];
8688
+ }
8689
+ if (t.kind === "array") return [t.element, values];
8690
+ if (t.kind === "tuple") return [union([...t.elements, ...t.rest ? [t.rest] : []]), values];
8282
8691
  if (t.kind === "object") {
8283
- const k = t.indexer?.key ?? stringType;
8284
- const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
8285
- return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
8692
+ return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
8286
8693
  }
8287
- 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];
8288
8701
  }
8289
8702
  inferReturnType(body, env) {
8290
8703
  const returns = [];
8291
8704
  const walk = (block) => {
8292
8705
  for (const s of block.statements) {
8293
8706
  if (s.type === "ReturnStatement") {
8294
- if (s.arguments.length === 0) returns.push(nilType);
8295
- else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
8296
- else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
8707
+ returns.push(s.argument ? this.infer(s.argument, env) : nilType);
8297
8708
  } else if (s.type === "IfStatement") {
8298
8709
  for (const c of s.clauses) walk(c.body);
8299
8710
  if (s.alternate) walk(s.alternate);
@@ -8315,8 +8726,9 @@ var TypeAnalyzer = class {
8315
8726
  * `infer` would have widened it to an array type). */
8316
8727
  fitsAnnotation(init, declared, inferred, env) {
8317
8728
  if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
8318
- if (init.elements.length !== declared.elements.length) return false;
8319
- 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));
8320
8732
  }
8321
8733
  if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
8322
8734
  if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
@@ -8373,7 +8785,7 @@ var TypeAnalyzer = class {
8373
8785
  target.elements.forEach((el, i) => {
8374
8786
  if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
8375
8787
  });
8376
- if (target.rest) this.reassignPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env);
8788
+ if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
8377
8789
  return;
8378
8790
  }
8379
8791
  }
@@ -8409,7 +8821,7 @@ var TypeAnalyzer = class {
8409
8821
  if (!el) return;
8410
8822
  this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
8411
8823
  });
8412
- if (target.rest) this.bindPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env, mode);
8824
+ if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
8413
8825
  return;
8414
8826
  }
8415
8827
  }
@@ -8566,7 +8978,7 @@ var TypeAnalyzer = class {
8566
8978
  * not one of them is a mistake worth reporting, rather than the nil Lua
8567
8979
  * would hand back. */
8568
8980
  checkStringMember(node, object, key) {
8569
- if (!this.emitDiagnostics) return;
8981
+ if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
8570
8982
  const parts = this.stringParts(object);
8571
8983
  if (!parts) return;
8572
8984
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
@@ -8578,6 +8990,170 @@ var TypeAnalyzer = class {
8578
8990
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8579
8991
  });
8580
8992
  }
8993
+ /** `game.Anything` on a `{ GetService: ... }`, or `a.a` on a number: a
8994
+ * value whose members are all known, and none of them is this one. Only
8995
+ * a type that is known through and through is reported — an indexer, an
8996
+ * `any`, or a part still waiting on a type parameter could each supply
8997
+ * the name. An empty `{}` is a table still being filled in, so it is left
8998
+ * alone; an array's and a string's members come from a library, so
8999
+ * without one they say nothing. A string on its own is `checkStringMember`'s. */
9000
+ checkMissingMember(node, object, name) {
9001
+ if (!this.emitDiagnostics || this.missingMemberReported.has(node)) return;
9002
+ if (!this.memberMissing(object, name)) return;
9003
+ this.missingMemberReported.add(node);
9004
+ this.diagnostics.push({
9005
+ node,
9006
+ message: `Property '${name}' does not exist on type '${briefType(withoutNil(this.expand(object)))}'`
9007
+ });
9008
+ }
9009
+ /** Is `name` certainly not a member of `object`? See `checkMissingMember`. */
9010
+ memberMissing(object, name) {
9011
+ if (this.stringParts(object)) return false;
9012
+ const raw = this.expand(object);
9013
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
9014
+ let missing = false;
9015
+ for (const part of parts) {
9016
+ switch (part.kind) {
9017
+ case "primitive":
9018
+ if (part.name === "nil") continue;
9019
+ if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
9020
+ if (part.name === "string" && this.builtInMethod(part, name)) continue;
9021
+ break;
9022
+ case "literal":
9023
+ case "templateLiteral":
9024
+ if (part.kind === "templateLiteral" || part.base === "string") {
9025
+ if (!this.aliasDefs.has("StringMethods")) return false;
9026
+ }
9027
+ if (this.builtInMethod(part, name)) continue;
9028
+ break;
9029
+ case "array":
9030
+ case "tuple":
9031
+ if (!this.aliasDefs.has("ArrayMethods")) return false;
9032
+ if (this.builtInMethod(part, name)) continue;
9033
+ break;
9034
+ case "object":
9035
+ if (part.properties.has(name)) continue;
9036
+ if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
9037
+ break;
9038
+ default:
9039
+ return false;
9040
+ }
9041
+ missing = true;
9042
+ }
9043
+ return missing;
9044
+ }
9045
+ /** `t[k]` read with a `k` that cannot be a key of `t`: an `unknown`, a
9046
+ * type none of `t`'s keys has (a number into a record, a string into an
9047
+ * array or a class), or a name `t` does not have — which is `t.name`'s
9048
+ * error. An `any` on either side says nothing, and neither does a type
9049
+ * parameter still to be decided. A plain `string` into a record is
9050
+ * allowed: it reads as one of the values or nil, and the nil has to be
9051
+ * dealt with. An empty `{}` is a table still being filled in, and is
9052
+ * left alone. Reports, and answers whether it did. */
9053
+ checkIndexKey(node, object, key) {
9054
+ if (!this.emitDiagnostics || this.badIndexReported.has(node)) return false;
9055
+ const index = this.expand(key);
9056
+ if (index.kind === "any" || index.kind === "never" || containsTypeParam(index)) return false;
9057
+ const keys = index.kind === "union" ? index.types.map((k) => this.expand(k)) : [index];
9058
+ const nameOf = (k) => k.kind === "literal" && typeof k.value === "string" ? k.value : void 0;
9059
+ const shown = briefType(withoutNil(this.expand(object)));
9060
+ const names = keys.map(nameOf);
9061
+ const others = keys.filter((_, i) => names[i] === void 0);
9062
+ let message;
9063
+ if (others.length && this.cannotIndex(object, others)) {
9064
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
9065
+ } else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
9066
+ message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
9067
+ }
9068
+ if (!message) return false;
9069
+ this.badIndexReported.add(node);
9070
+ this.diagnostics.push({ node, message });
9071
+ return true;
9072
+ }
9073
+ /** Can one of `keys` (string literals aside) not index some part of
9074
+ * `object`? `false` as soon as a part cannot be judged. */
9075
+ cannotIndex(object, keys) {
9076
+ const raw = this.expand(object);
9077
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
9078
+ const isNumber = (k) => k.kind === "primitive" && k.name === "number" || k.kind === "literal" && typeof k.value === "number";
9079
+ const isString = (k) => k.kind === "primitive" && k.name === "string" || k.kind === "templateLiteral";
9080
+ let bad = false;
9081
+ for (const part of parts) {
9082
+ switch (part.kind) {
9083
+ case "primitive":
9084
+ if (part.name === "nil") continue;
9085
+ return false;
9086
+ case "array":
9087
+ case "tuple":
9088
+ if (!keys.every(isNumber)) bad = true;
9089
+ continue;
9090
+ case "object": {
9091
+ if (!part.indexer && part.properties.size === 0 && !part.class) return false;
9092
+ const fits = (k) => part.indexer !== void 0 && isAssignable(k, part.indexer.key) || !part.indexer && !part.class && isString(k) || k.kind === "literal" && part.properties.has(String(k.value));
9093
+ if (!keys.every(fits)) bad = true;
9094
+ continue;
9095
+ }
9096
+ default:
9097
+ return false;
9098
+ }
9099
+ }
9100
+ return bad;
9101
+ }
9102
+ /** Indexes already reported: a loop body is visited more than once. */
9103
+ badIndexReported = /* @__PURE__ */ new WeakSet();
9104
+ /** Members already reported as missing: a loop body, or an assignment
9105
+ * target, is visited more than once. */
9106
+ missingMemberReported = /* @__PURE__ */ new WeakSet();
9107
+ /** A `private` member read from outside the class that declared it —
9108
+ * a subclass counts as outside, as in TypeScript. */
9109
+ checkPrivateMember(node, object, name) {
9110
+ if (!this.emitDiagnostics) return;
9111
+ const raw = this.expand(object);
9112
+ for (const part of raw.kind === "union" ? raw.types : [raw]) {
9113
+ const t = this.deferredAccess(this.expand(part));
9114
+ if (t.kind !== "object") continue;
9115
+ const access = t.properties.get(name)?.private;
9116
+ if (!access || this.insideClass(access.owner)) continue;
9117
+ if (access.protected && this.insideSubclassOf(access.owner)) continue;
9118
+ this.diagnostics.push({
9119
+ node,
9120
+ message: access.protected ? `Property '${name}' is protected and only accessible within class '${access.className}' and the classes extending it` : `Property '${name}' is private and only accessible within class '${access.className}'`
9121
+ });
9122
+ return;
9123
+ }
9124
+ }
9125
+ /** `Shape.new` on an abstract class, and `super.area` on an abstract
9126
+ * method: each reaches something with nothing behind it. */
9127
+ checkAbstractAccess(expr, object) {
9128
+ if (!this.emitDiagnostics) return;
9129
+ const t = this.expand(object);
9130
+ if (t.kind !== "object") return;
9131
+ const name = expr.property.name;
9132
+ const property = t.properties.get(name);
9133
+ if (!property?.abstract) return;
9134
+ if (name === "new" && expr.object.type !== "SuperExpression") {
9135
+ const label = expressionLabel(expr.object) ?? formatType(t);
9136
+ this.diagnostics.push({
9137
+ node: expr,
9138
+ message: `'${label}' is an abstract class: build one of the classes extending it instead`
9139
+ });
9140
+ } else if (expr.object.type === "SuperExpression") {
9141
+ this.diagnostics.push({
9142
+ node: expr.property,
9143
+ message: `'${name}' is abstract in the class this one extends: there is nothing to call`
9144
+ });
9145
+ }
9146
+ }
9147
+ /** Is the code being checked written inside `owner`'s body — its own
9148
+ * methods, or a class or function nested in them? */
9149
+ insideClass(owner) {
9150
+ return this.enclosingClasses.includes(owner);
9151
+ }
9152
+ /** Is the code being checked inside a class that extends `owner`? */
9153
+ insideSubclassOf(owner) {
9154
+ const identity = this.classIdentity(owner);
9155
+ return this.enclosingClasses.some((cls) => this.instanceType(cls).class?.ancestors.includes(identity));
9156
+ }
8581
9157
  propertyType(raw, name) {
8582
9158
  const t = this.deferredAccess(this.expand(raw));
8583
9159
  if (t.kind === "object") {
@@ -8632,8 +9208,12 @@ var TypeAnalyzer = class {
8632
9208
  }
8633
9209
  if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
8634
9210
  if (t.indexer) return t.indexer.value;
9211
+ const anyString = index.kind === "primitive" && index.name === "string" || index.kind === "templateLiteral";
9212
+ if (anyString && !t.class && t.properties.size > 0) {
9213
+ return union([...[...t.properties.values()].map((p) => p.type), nilType]);
9214
+ }
8635
9215
  }
8636
- return unknownType;
9216
+ return index.kind === "any" ? anyType : unknownType;
8637
9217
  }
8638
9218
  /** What a deferred `T[K]` can be: every property its index could name.
8639
9219
  * Reading a member of one, or calling it, sees that. */
@@ -8643,10 +9223,21 @@ var TypeAnalyzer = class {
8643
9223
  if (containsTypeParam(index)) return unknownType;
8644
9224
  return this.accessType(t.objectType, index);
8645
9225
  }
9226
+ /** `[a, ...rest]`: what `rest` holds — an array of whatever comes after
9227
+ * the first `from` places. */
9228
+ restAfter(raw, from) {
9229
+ const t = this.expand(raw);
9230
+ if (t.kind === "tuple") {
9231
+ const left = [...t.elements.slice(from), ...t.rest ? [t.rest] : []];
9232
+ return arrayOf(left.length ? union(left) : neverType);
9233
+ }
9234
+ if (t.kind === "union") return union(t.types.map((m) => this.restAfter(m, from)));
9235
+ return arrayOf(this.elementType(t, from));
9236
+ }
8646
9237
  elementType(raw, index) {
8647
9238
  const t = this.expand(raw);
8648
9239
  if (t.kind === "array") return t.element;
8649
- if (t.kind === "tuple") return t.elements[index] ?? unknownType;
9240
+ if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
8650
9241
  if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
8651
9242
  if (t.kind === "difference") return this.elementType(t.base, index);
8652
9243
  if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
@@ -8676,8 +9267,6 @@ var TypeAnalyzer = class {
8676
9267
  return stringType;
8677
9268
  }
8678
9269
  // `...` holds what the function declared it takes.
8679
- case "VarargExpression":
8680
- return this.varargs[this.varargs.length - 1] ?? anyType;
8681
9270
  // `f(a, ...rest)` — every value the array holds, one after
8682
9271
  // another. Each of them is an element, so that is what the
8683
9272
  // parameters it fills are checked against.
@@ -8710,10 +9299,8 @@ var TypeAnalyzer = class {
8710
9299
  this.checkParamOrder(expr.func.params, expr);
8711
9300
  this.visitFunctionBody(expr.func, env);
8712
9301
  return this.inferFunctionBody(expr.func, env);
8713
- case "ParenthesizedExpression": {
8714
- const inner = this.infer(expr.expression, env);
8715
- return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
8716
- }
9302
+ case "ParenthesizedExpression":
9303
+ return this.infer(expr.expression, env);
8717
9304
  case "TypeAssertionExpression": {
8718
9305
  this.infer(expr.expression, env);
8719
9306
  return this.resolveType(expr.typeAnnotation);
@@ -8746,7 +9333,7 @@ var TypeAnalyzer = class {
8746
9333
  case "not":
8747
9334
  return booleanType;
8748
9335
  case "-":
8749
- return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
9336
+ return this.operatorResult(expr, "-", arg, void 0) ?? this.arithmeticOn([arg]);
8750
9337
  case "#":
8751
9338
  return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
8752
9339
  }
@@ -8764,7 +9351,11 @@ var TypeAnalyzer = class {
8764
9351
  const left = this.infer(expr.left, env);
8765
9352
  const { whenFalse } = this.narrowFromCondition(expr.left, env);
8766
9353
  const right = this.infer(expr.right, whenFalse);
8767
- 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]);
8768
9359
  }
8769
9360
  const l = this.infer(expr.left, env);
8770
9361
  const r = this.infer(expr.right, env);
@@ -8775,12 +9366,14 @@ var TypeAnalyzer = class {
8775
9366
  switch (op) {
8776
9367
  case "..":
8777
9368
  return this.operatorResult(expr, op, l, r) ?? stringType;
8778
- case "==":
8779
- case "~=":
8780
9369
  case "<":
8781
9370
  case ">":
8782
9371
  case "<=":
8783
9372
  case ">=":
9373
+ this.checkComparison(expr, op, l, r);
9374
+ return booleanType;
9375
+ case "==":
9376
+ case "~=":
8784
9377
  return booleanType;
8785
9378
  case "+":
8786
9379
  case "-":
@@ -8789,15 +9382,18 @@ var TypeAnalyzer = class {
8789
9382
  case "//":
8790
9383
  case "%":
8791
9384
  case "^":
8792
- return this.operatorResult(expr, op, l, r) ?? numberType;
9385
+ return this.operatorResult(expr, op, l, r) ?? this.arithmeticOn([l, r]);
8793
9386
  }
8794
9387
  return union([l, r]);
8795
9388
  }
8796
9389
  case "MemberExpression": {
8797
9390
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
9391
+ this.checkPrivateMember(expr.property, obj, expr.property.name);
9392
+ this.checkAbstractAccess(expr, obj);
8798
9393
  const key = this.refKeyOf(expr);
8799
9394
  const narrowed = key === void 0 ? void 0 : env.get(key);
8800
9395
  this.checkStringMember(expr, obj, literal(expr.property.name));
9396
+ if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
8801
9397
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
8802
9398
  }
8803
9399
  case "IndexExpression": {
@@ -8806,6 +9402,7 @@ var TypeAnalyzer = class {
8806
9402
  const key = this.refKeyOf(expr);
8807
9403
  const narrowed = key === void 0 ? void 0 : env.get(key);
8808
9404
  this.checkStringMember(expr, obj, this.expand(idx));
9405
+ if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
8809
9406
  const member = this.stringMember(obj, this.expand(idx));
8810
9407
  if (member) return this.chainResult(expr, member, shortCircuits);
8811
9408
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
@@ -8815,8 +9412,6 @@ var TypeAnalyzer = class {
8815
9412
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
8816
9413
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
8817
9414
  }
8818
- case "NewExpression":
8819
- return this.inferNew(expr, env);
8820
9415
  case "ClassExpression":
8821
9416
  return this.visitClass(expr, env);
8822
9417
  case "SuperExpression": {
@@ -8853,18 +9448,14 @@ var TypeAnalyzer = class {
8853
9448
  /** `new Name(args)` is `Name.new(args)` — the same function, and the
8854
9449
  * same check. Saying so here rather than rewriting the tree keeps the
8855
9450
  * error messages pointing at what was written. */
8856
- inferNew(expr, env) {
8857
- const calleeType = this.infer(expr.callee, env);
8858
- const constructor = this.propertyType(calleeType, "new");
8859
- if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8860
- if (this.emitDiagnostics) {
8861
- const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8862
- this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8863
- }
8864
- for (const argument of expr.arguments) this.infer(argument, env);
8865
- return anyType;
8866
- }
8867
- return this.inferCall(expr, constructor, env);
9451
+ /** `__call` of a value with one, as what calling the value calls. */
9452
+ callMetamethod(raw) {
9453
+ const t = this.expand(raw);
9454
+ if (t.kind !== "object" || this.overloadsOf(t).length) return void 0;
9455
+ const method = t.properties.get("__call");
9456
+ if (!method) return void 0;
9457
+ const bound = this.overloadsOf(method.type).map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f);
9458
+ return bound.length ? intersection(bound) : void 0;
8868
9459
  }
8869
9460
  /** `super(...)` — the base constructor, run on the instance being built. */
8870
9461
  inferSuperCall(expr, env) {
@@ -8888,8 +9479,9 @@ var TypeAnalyzer = class {
8888
9479
  this.inferCall(expr, callable, env);
8889
9480
  return nilType;
8890
9481
  }
8891
- inferCall(expr, callee, env) {
9482
+ inferCall(expr, called, env) {
8892
9483
  this.checkAmbiguousCall(expr);
9484
+ const callee = this.callMetamethod(called) ?? called;
8893
9485
  const united = this.unionSignatures(callee);
8894
9486
  const fns = united ?? this.overloadsOf(callee);
8895
9487
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -8912,8 +9504,31 @@ var TypeAnalyzer = class {
8912
9504
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
8913
9505
  return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
8914
9506
  }
9507
+ this.checkCallable(expr.callee, callee);
8915
9508
  return callee.kind === "any" ? anyType : unknownType;
8916
9509
  }
9510
+ /** `a()` where `a` is `1`: a value whose type is known and has no call
9511
+ * signature. A union is reported when any member cannot be called, but
9512
+ * only when every member is decided — `unknown`, `any` and anything still
9513
+ * generic say too little. */
9514
+ checkCallable(node, callee) {
9515
+ if (!this.emitDiagnostics || this.notCallableReported.has(node)) return;
9516
+ const raw = this.expand(callee);
9517
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.expand(m));
9518
+ const notCallable = (m) => m.kind === "primitive" && m.name !== "nil" || m.kind === "literal" || m.kind === "templateLiteral" || m.kind === "array" || m.kind === "tuple" || m.kind === "object";
9519
+ const callable = (m) => this.overloadsOf(m).length > 0;
9520
+ const concrete = parts.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
9521
+ if (!concrete.some(notCallable) || !concrete.every((m) => notCallable(m) || callable(m))) return;
9522
+ this.notCallableReported.add(node);
9523
+ const label = node.type === "Identifier" ? node.name : expressionLabel(node);
9524
+ this.diagnostics.push({
9525
+ node,
9526
+ message: `This expression is not callable: ${label === void 0 ? "" : `'${label}' `}is of type '${briefType(withoutNil(raw))}'`
9527
+ });
9528
+ }
9529
+ /** Callees already reported as not callable: a loop body is visited more
9530
+ * than once. */
9531
+ notCallableReported = /* @__PURE__ */ new WeakSet();
8917
9532
  /** A `(` on a line of its own continues the statement above it:
8918
9533
  *
8919
9534
  * const value = map[key]
@@ -8930,6 +9545,9 @@ var TypeAnalyzer = class {
8930
9545
  });
8931
9546
  }
8932
9547
  inferMethodCall(expr, objType, env) {
9548
+ this.checkPrivateMember(expr.method, objType, expr.method.name);
9549
+ this.checkStringMember(expr.method, objType, literal(expr.method.name));
9550
+ this.checkMissingMember(expr.method, objType, expr.method.name);
8933
9551
  const method = this.propertyType(objType, expr.method.name);
8934
9552
  const united = this.unionSignatures(method);
8935
9553
  const fns = united ?? this.overloadsOf(method);
@@ -8963,6 +9581,7 @@ var TypeAnalyzer = class {
8963
9581
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
8964
9582
  return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
8965
9583
  }
9584
+ this.checkCallable(expr.method, method);
8966
9585
  return objType.kind === "any" ? anyType : unknownType;
8967
9586
  }
8968
9587
  // --------------------------------------------------------
@@ -9023,6 +9642,8 @@ var TypeAnalyzer = class {
9023
9642
  if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
9024
9643
  }
9025
9644
  if (!this.isReadonlyProperty(this.expand(this.infer(object, env)), name)) return;
9645
+ const constructing = this.constructing;
9646
+ if (constructing && object.type === "Identifier" && object.name === "this" && constructing.members.some((m) => m.type === "ClassField" && !m.isStatic && m.isReadonly && m.name.name === name)) return;
9026
9647
  this.diagnostics.push({
9027
9648
  node: target,
9028
9649
  message: `Cannot assign to '${name}' because it is a read-only property`
@@ -9082,6 +9703,8 @@ var TypeAnalyzer = class {
9082
9703
  inferArray(expr, env, asConst) {
9083
9704
  const contextual = this.contextualArrays.get(expr);
9084
9705
  if (contextual && !asConst) return contextual;
9706
+ const wantedTuple = this.tupleArrays.get(expr);
9707
+ if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9085
9708
  const elems = [];
9086
9709
  let hadSpread = false;
9087
9710
  for (const el of expr.elements) {
@@ -9101,6 +9724,28 @@ var TypeAnalyzer = class {
9101
9724
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
9102
9725
  })) : unknownType);
9103
9726
  }
9727
+ /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
9728
+ * tuple's, and what comes after them — more elements, or a spread — is
9729
+ * its rest. */
9730
+ inferTupleLiteral(expr, env, wanted) {
9731
+ const fixed = [];
9732
+ const tail = [];
9733
+ let spread = false;
9734
+ expr.elements.forEach((element, i) => {
9735
+ if (element.type === "SpreadElement") {
9736
+ spread = true;
9737
+ const held = this.expand(this.infer(element.argument, env));
9738
+ if (held.kind === "array") tail.push(held.element);
9739
+ else if (held.kind === "tuple") tail.push(...tupleMembers(held));
9740
+ else tail.push(unknownType);
9741
+ return;
9742
+ }
9743
+ const t = this.widenUnlessAsked(this.infer(element, env), element);
9744
+ if (!spread && i < wanted.elements.length) fixed.push(t);
9745
+ else tail.push(t);
9746
+ });
9747
+ return tuple(fixed, tail.length ? union(tail) : void 0);
9748
+ }
9104
9749
  /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
9105
9750
  * `{ n: number }` — unless the surroundings said a literal belongs there.
9106
9751
  * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
@@ -9186,7 +9831,7 @@ var TypeAnalyzer = class {
9186
9831
  case "tuple": {
9187
9832
  const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
9188
9833
  if (tupleContext?.kind === "tuple") {
9189
- return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.isPack);
9834
+ return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.rest);
9190
9835
  }
9191
9836
  const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
9192
9837
  const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
@@ -9443,7 +10088,8 @@ var TypeAnalyzer = class {
9443
10088
  for (const f of candidates) {
9444
10089
  if (!f.predicate) continue;
9445
10090
  const arg = args[f.predicate.param];
9446
- 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;
9447
10093
  if (f.typeParams?.length && f.predicate.type) {
9448
10094
  const subst = this.inferTypeArgs(f, argTypes);
9449
10095
  return {
@@ -9464,6 +10110,10 @@ var TypeAnalyzer = class {
9464
10110
  const found = this.predicateCallTarget(call, env);
9465
10111
  if (!found || !found.predicate.asserts) return;
9466
10112
  const filter = found.predicate.type;
10113
+ if (this.refKeyOf(found.arg) === void 0) {
10114
+ if (!filter) this.applyNarrowing(found.arg, env, env, forkEnv(env));
10115
+ return;
10116
+ }
9467
10117
  this.narrowRef(found.arg, env, env, forkEnv(env), (cur) => filter ? { yes: narrowTo(cur, filter), no: narrowExclude(cur, filter) } : { yes: narrowTruthy(cur), no: narrowFalsy(cur) });
9468
10118
  }
9469
10119
  // --------------------------------------------------------
@@ -9488,6 +10138,10 @@ var TypeAnalyzer = class {
9488
10138
  if (base === void 0) return void 0;
9489
10139
  if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
9490
10140
  if (expr.index.type === "NumberLiteral") return `${base}#${expr.index.value}`;
10141
+ if (expr.index.type === "Identifier") {
10142
+ const id = this.bindingIdOf(expr.index);
10143
+ return id === void 0 ? void 0 : `${base}[${bindKey(id)}]`;
10144
+ }
9491
10145
  return void 0;
9492
10146
  }
9493
10147
  default:
@@ -9583,26 +10237,45 @@ var TypeAnalyzer = class {
9583
10237
  * that copied it no longer holds that value: forget the alias. */
9584
10238
  unalias(key) {
9585
10239
  for (const k of [...this.refAliases.keys()]) {
9586
- if (k !== key && !k.startsWith(`${key}.`) && !k.startsWith(`${key}#`)) continue;
10240
+ if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
9587
10241
  for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
9588
10242
  this.refAliases.delete(k);
9589
10243
  }
9590
10244
  }
9591
- /** Drop every narrowing recorded for a path strictly under `key`. */
10245
+ /** Drop every narrowing recorded for a path strictly under `key` — and,
10246
+ * when `key` is a whole variable, for every `t[key]` indexed by it: that
10247
+ * now names a different slot. */
9592
10248
  invalidateBelow(env, key) {
10249
+ const asIndex = `[${key}]`;
9593
10250
  for (const k of [...env.keys()]) {
9594
- if (k.startsWith(`${key}.`) || k.startsWith(`${key}#`)) env.delete(k);
10251
+ if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
9595
10252
  }
9596
10253
  }
9597
10254
  /** An assignment through a reference invalidates it and everything under
9598
10255
  * it, then records the assigned type. */
9599
10256
  assignToRef(expr, value, env) {
10257
+ this.invalidateSiblings(expr, env);
9600
10258
  const key = this.refKeyOf(expr);
9601
10259
  if (key === void 0) return;
9602
10260
  this.invalidateBelow(env, key);
9603
10261
  this.unalias(key);
9604
10262
  env.set(key, value);
9605
10263
  }
10264
+ /** A write to one slot of a table can be a write to a slot narrowed under
10265
+ * another name: `t.x = nil` is `t[k] = nil` when `k` is `"x"`. So a
10266
+ * write by name forgets what is known through variable keys, and a write
10267
+ * through any computed key forgets everything known about the table's
10268
+ * slots. */
10269
+ invalidateSiblings(expr, env) {
10270
+ const inner = expr.type === "ParenthesizedExpression" ? expr.expression : expr;
10271
+ if (inner.type !== "MemberExpression" && inner.type !== "IndexExpression") return;
10272
+ const parent = this.refKeyOf(inner.object);
10273
+ if (parent === void 0) return;
10274
+ const computed = inner.type === "IndexExpression" && inner.index.type !== "StringLiteral" && inner.index.type !== "NumberLiteral";
10275
+ for (const k of [...env.keys()]) {
10276
+ if (computed ? isBelow(k, parent) : k.startsWith(`${parent}[`)) env.delete(k);
10277
+ }
10278
+ }
9606
10279
  // --------------------------------------------------------
9607
10280
  // Small helpers
9608
10281
  // --------------------------------------------------------
@@ -9617,6 +10290,35 @@ var TypeAnalyzer = class {
9617
10290
  this.selfType = saved;
9618
10291
  }
9619
10292
  }
10293
+ /** Arithmetic no declared metamethod answers: a number — unless an
10294
+ * operand is `any`, which may be a value with metamethods of its own.
10295
+ * `(a.Position - b.Position).Magnitude` on an `a` nothing typed is a
10296
+ * `Vector3` as far as anyone knows, not a number with no `Magnitude`. */
10297
+ arithmeticOn(operands) {
10298
+ return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
10299
+ }
10300
+ /** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
10301
+ * with the operands swapped for `>` and `>=`, and an instance without
10302
+ * one cannot be compared at all. */
10303
+ checkComparison(node, op, l, r) {
10304
+ if (!this.emitDiagnostics) return;
10305
+ const left = this.expand(l);
10306
+ const right = this.expand(r);
10307
+ const instance = (t) => t.kind === "object" && t.class !== void 0;
10308
+ if (!instance(left) && !instance(right)) return;
10309
+ const name = op === "<" || op === ">" ? "__lt" : "__le";
10310
+ const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
10311
+ for (const receiver of operands) {
10312
+ const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
10313
+ if (!method) continue;
10314
+ if (this.pickOverload(this.overloadsOf(method.type), operands)) return;
10315
+ break;
10316
+ }
10317
+ this.diagnostics.push({
10318
+ node,
10319
+ message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
10320
+ });
10321
+ }
9620
10322
  /** What an operator on a value with metamethods gives: `a + b` calls
9621
10323
  * `__add` on `a`, or failing that on `b` with the operands swapped — the
9622
10324
  * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
@@ -9949,6 +10651,13 @@ var nodeHost = {
9949
10651
  } catch {
9950
10652
  return void 0;
9951
10653
  }
10654
+ },
10655
+ readDirectory(path) {
10656
+ try {
10657
+ return (0, import_node_fs.readdirSync)(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => entry.name);
10658
+ } catch {
10659
+ return void 0;
10660
+ }
9952
10661
  }
9953
10662
  };
9954
10663
 
@@ -10140,13 +10849,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
10140
10849
  else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
10141
10850
  continue;
10142
10851
  }
10143
- const name = entry.startsWith("@tilua-types/") ? entry : `@tilua-types/${entry}`;
10144
- 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);
10145
10872
  if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
10146
10873
  else {
10147
10874
  problems.push({
10148
10875
  file: config.path,
10149
- message: `Cannot find type library '${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]}`,
10150
10877
  ...entryPosition(config, entry)
10151
10878
  });
10152
10879
  }
@@ -10183,6 +10910,32 @@ function findPackage(name, from, host) {
10183
10910
  directory = parent;
10184
10911
  }
10185
10912
  }
10913
+ function matchPackages(pattern, from, host) {
10914
+ if (!host.readDirectory) return void 0;
10915
+ const slash = pattern.lastIndexOf("/");
10916
+ const scope = slash < 0 ? "" : pattern.slice(0, slash);
10917
+ const last = pattern.slice(slash + 1);
10918
+ const matches = new RegExp(`^${last.split("*").map(escapeRegExp2).join(".*")}$`);
10919
+ const found = /* @__PURE__ */ new Map();
10920
+ let directory = (0, import_node_path2.resolve)(from);
10921
+ for (; ; ) {
10922
+ const folder = (0, import_node_path2.join)(directory, "node_modules", ...scope ? scope.split("/") : []);
10923
+ for (const name of host.readDirectory(folder) ?? []) {
10924
+ const full = scope ? `${scope}/${name}` : name;
10925
+ if (!matches.test(name) || found.has(full)) continue;
10926
+ const candidate = (0, import_node_path2.join)(folder, name);
10927
+ const file = packageEntry(candidate, host);
10928
+ if (file) found.set(full, { directory: candidate, file });
10929
+ }
10930
+ const parent = (0, import_node_path2.dirname)(directory);
10931
+ if (parent === directory) break;
10932
+ directory = parent;
10933
+ }
10934
+ return [...found.keys()].sort().map((name) => found.get(name));
10935
+ }
10936
+ function escapeRegExp2(text) {
10937
+ return text.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
10938
+ }
10186
10939
  function dependencyNames(directory, host) {
10187
10940
  const manifest = readJson((0, import_node_path2.join)(directory, "package.json"), host);
10188
10941
  const names = /* @__PURE__ */ new Set();
@@ -10281,7 +11034,8 @@ function sourceMapTypes(text, path, options) {
10281
11034
  for (const child of node.children ?? []) {
10282
11035
  if (!isNode(child)) continue;
10283
11036
  const childAlias = visit(child, [...segments, child.name], alias);
10284
- 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;
10285
11039
  named.add(child.name);
10286
11040
  members.push(`${child.name}: ${childAlias}`);
10287
11041
  }
@@ -10342,6 +11096,7 @@ var index_default = tilua;
10342
11096
  BinaryOperators,
10343
11097
  CONFIG_FILE_NAMES,
10344
11098
  Keywords,
11099
+ LANGUAGE_GLOBALS,
10345
11100
  LexError,
10346
11101
  Operators,
10347
11102
  PRELUDE_SOURCE,
@@ -10409,6 +11164,7 @@ var index_default = tilua;
10409
11164
  tilua,
10410
11165
  tokenize,
10411
11166
  tuple,
11167
+ tupleMembers,
10412
11168
  typeParam,
10413
11169
  unify,
10414
11170
  union,