@tilua/parser 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -595,6 +595,9 @@ function applyDirectives(directives, diagnostics, lineOf) {
595
595
  var UNUSED_EXPECT_ERROR = "Unused '@tilua-expect-error' directive";
596
596
 
597
597
  // src/ast/builders.ts
598
+ var NO_DOTS = "tilua has no bare '...': take a rest parameter, '...args: T[]', or 'scriptArgs' for the script's own";
599
+ var ACCESSIBILITIES = /* @__PURE__ */ new Set(["public", "private", "protected"]);
600
+ var CLASS_MODIFIERS = /* @__PURE__ */ new Set([...ACCESSIBILITIES, "static", "abstract", "override", "readonly"]);
598
601
  function bindThis(func, at) {
599
602
  bindThisParam(func.params, at);
600
603
  func.isMethod = true;
@@ -862,11 +865,16 @@ var Parser = class {
862
865
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
863
866
  }
864
867
  /** A comma-separated list of values: a `return`'s, a declaration's, an
865
- * assignment's. `...xs` spreads an array into it, as in a call's
866
- * arguments; bare `...` is the vararg pack, as it always was. */
868
+ * assignment's. Each is one value; a spread belongs in a call's
869
+ * arguments or an array, and is reported here — then read as one, so
870
+ * the rest still means something. */
867
871
  expressionListOr(stop) {
868
872
  const until = () => stop() || this.checkPunctuator(",");
869
- const item = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
873
+ const item = () => {
874
+ if (!(this.checkOperator("...") && this.startsSpread())) return this.expressionOr(until);
875
+ 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'");
876
+ return this.parseSpreadArgument(until);
877
+ };
870
878
  const list = [item()];
871
879
  while (this.matchPunctuator(",")) list.push(item());
872
880
  return list;
@@ -1231,7 +1239,7 @@ var Parser = class {
1231
1239
  if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
1232
1240
  return this.parseTypeAliasStatement();
1233
1241
  }
1234
- if (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
1242
+ if (this.startsClassDeclaration()) {
1235
1243
  return this.parseClassDeclaration();
1236
1244
  }
1237
1245
  if (t.type === "Identifier" && t.value === "declare") {
@@ -1268,8 +1276,6 @@ var Parser = class {
1268
1276
  type: "FunctionTypeNode",
1269
1277
  generics: head.generics,
1270
1278
  params,
1271
- hasVarargs: head.hasVarargs,
1272
- varargType: head.varargTypeAnnotation,
1273
1279
  returnType: head.returnType ?? { type: "TypeReference", base: head.predicate ? "boolean" : "unknown", typeArguments: [], ...spanFrom(start, start) },
1274
1280
  predicate: head.predicate,
1275
1281
  ...spanFrom(start, this.previous())
@@ -1408,7 +1414,7 @@ var Parser = class {
1408
1414
  this.advance();
1409
1415
  if (this.checkIdentifierValue("default")) {
1410
1416
  this.advance();
1411
- const declaration = this.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
1417
+ const declaration = this.startsClassDeclaration() ? this.parseClassDeclaration() : this.parseExpression(0);
1412
1418
  return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
1413
1419
  }
1414
1420
  if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
@@ -1419,7 +1425,7 @@ var Parser = class {
1419
1425
  const declaration = this.parseVariableDeclaration();
1420
1426
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1421
1427
  }
1422
- if (this.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
1428
+ if (this.startsClassDeclaration()) {
1423
1429
  const declaration = this.parseClassDeclaration();
1424
1430
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1425
1431
  }
@@ -1534,9 +1540,11 @@ var Parser = class {
1534
1540
  const start = this.current();
1535
1541
  this.expectKeyword("for");
1536
1542
  this.expectPunctuator("(");
1543
+ const declared = this.checkKeyword("const") || this.checkKeyword("let") ? this.advance().value : void 0;
1537
1544
  const first = this.parseBindingTarget(true);
1538
1545
  const untilDo = () => this.checkPunctuator(")");
1539
1546
  if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
1547
+ if (declared) this.problemAt(start, `A counting loop names its variable without '${declared}': 'for (i = 1, 10)'`);
1540
1548
  const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1541
1549
  this.expectPunctuator(",");
1542
1550
  const to = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
@@ -1555,17 +1563,24 @@ var Parser = class {
1555
1563
  ...spanFrom(start, this.previous())
1556
1564
  };
1557
1565
  }
1558
- const variables = [first];
1559
- while (this.matchPunctuator(",")) {
1560
- variables.push(this.parseBindingTarget(true));
1566
+ if (this.checkPunctuator(",")) {
1567
+ this.problemAt(this.current(), "A loop takes one value each time: take its parts with a destructuring, 'for (const [k, v] in pairs(t))'");
1568
+ while (this.matchPunctuator(",")) this.parseBindingTarget(true);
1569
+ } else if (!declared) {
1570
+ this.problemAt(start, "A loop over a source names its value with 'const' or 'let': 'for (const item in source)'");
1561
1571
  }
1562
1572
  this.expectKeyword("in");
1563
- const iterators = this.expressionListOr(untilDo);
1573
+ const iterator = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1574
+ if (this.checkPunctuator(",")) {
1575
+ this.problemAt(this.current(), "A loop has one source: an array, a table, an iterator function or an iteration '[step, state, first]'");
1576
+ while (this.matchPunctuator(",")) this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1577
+ }
1564
1578
  const body = this.parseForBody();
1565
1579
  return {
1566
1580
  type: "GenericForStatement",
1567
- variables,
1568
- iterators,
1581
+ kind: declared ?? "const",
1582
+ variable: first,
1583
+ iterator,
1569
1584
  body,
1570
1585
  ...spanFrom(start, this.previous())
1571
1586
  };
@@ -1635,7 +1650,10 @@ var Parser = class {
1635
1650
  * the compiler must not accept it, and recorded inside. The message says
1636
1651
  * what is wrong on its own — no token is appended. */
1637
1652
  problem(message) {
1638
- const t = this.current();
1653
+ this.problemAt(this.current(), message);
1654
+ }
1655
+ /** `problem`, pointed at `t` rather than at where parsing has got to. */
1656
+ problemAt(t, message) {
1639
1657
  const error = new ParseError(message, t.line.start, t.column.start);
1640
1658
  if (!this.recover) throw error;
1641
1659
  this.record(error);
@@ -1648,10 +1666,13 @@ var Parser = class {
1648
1666
  * like a function (`function m() ... end`). */
1649
1667
  parseClassDeclaration() {
1650
1668
  const start = this.current();
1669
+ const isAbstract = this.checkIdentifierValue("abstract");
1670
+ if (isAbstract) this.advance();
1651
1671
  this.advance();
1652
1672
  const name = this.parseIdentifier();
1653
1673
  const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1654
1674
  const { superclass, superArguments } = this.parseExtends();
1675
+ const shapes = this.parseImplements();
1655
1676
  const members = this.parseClassBody(start);
1656
1677
  return {
1657
1678
  type: "ClassDeclaration",
@@ -1659,10 +1680,21 @@ var Parser = class {
1659
1680
  typeParams,
1660
1681
  superclass,
1661
1682
  superArguments,
1683
+ ...shapes ? { implements: shapes } : {},
1684
+ ...isAbstract ? { isAbstract } : {},
1662
1685
  members,
1663
1686
  ...spanFrom(start, this.previous())
1664
1687
  };
1665
1688
  }
1689
+ /** `class Name` or `abstract class Name` — all three words soft, so a name
1690
+ * has to follow for either to be a declaration: `class: T`, `class = 1`
1691
+ * and `abstract(x)` are names. */
1692
+ startsClassDeclaration() {
1693
+ const offset = this.checkIdentifierValue("abstract") ? 1 : 0;
1694
+ const word = this.peek(offset);
1695
+ if (word.type !== "Identifier" || word.value !== "class") return false;
1696
+ return this.peek(offset + 1).type === "Identifier" && !this.punctuatorAt(offset + 2, ":") && !this.operatorAt(offset + 2, "=") && !this.punctuatorAt(offset + 2, "(");
1697
+ }
1666
1698
  /** `class ... end` as a value. It may be named — the name is for the class
1667
1699
  * itself, not for the scope around it — and takes no type parameters,
1668
1700
  * since nothing could write the arguments. */
@@ -1674,8 +1706,25 @@ var Parser = class {
1674
1706
  this.error("A class written as a value takes no type parameters: nothing could write the arguments");
1675
1707
  }
1676
1708
  const { superclass, superArguments } = this.parseExtends();
1709
+ const shapes = this.parseImplements();
1677
1710
  const members = this.parseClassBody(start);
1678
- return { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
1711
+ return {
1712
+ type: "ClassExpression",
1713
+ name,
1714
+ superclass,
1715
+ superArguments,
1716
+ ...shapes ? { implements: shapes } : {},
1717
+ members,
1718
+ ...spanFrom(start, this.previous())
1719
+ };
1720
+ }
1721
+ /** `implements Shape, Named` — each a type, checked against the instance. */
1722
+ parseImplements() {
1723
+ if (!this.checkIdentifierValue("implements")) return void 0;
1724
+ this.advance();
1725
+ const shapes = [this.parseType()];
1726
+ while (this.matchPunctuator(",")) shapes.push(this.parseType());
1727
+ return shapes;
1679
1728
  }
1680
1729
  /** `extends Base` / `extends Box<number>`. */
1681
1730
  parseExtends() {
@@ -1728,23 +1777,66 @@ var Parser = class {
1728
1777
  parseClassMember() {
1729
1778
  const start = this.current();
1730
1779
  let accessibility;
1731
- if ((this.checkIdentifierValue("public") || this.checkIdentifierValue("private")) && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(")) {
1732
- accessibility = this.advance().value;
1780
+ const modifiers = /* @__PURE__ */ new Set();
1781
+ while (this.startsClassModifier()) {
1782
+ const token = this.advance();
1783
+ const word = token.value;
1784
+ if (ACCESSIBILITIES.has(word)) {
1785
+ if (accessibility) this.problemAt(token, `A member has one accessibility: '${accessibility}' is already written`);
1786
+ accessibility = word;
1787
+ } else {
1788
+ if (modifiers.has(word)) this.problemAt(token, `'${word}' is written twice`);
1789
+ modifiers.add(word);
1790
+ }
1733
1791
  }
1734
- const member = this.parseClassMemberAfterAccessibility(start);
1735
- if (!accessibility || !member) return member;
1792
+ const member = this.parseClassMemberAfterModifiers(start, modifiers);
1793
+ if (!member) return member;
1794
+ const misplaced = (message) => this.problemAt(start, message);
1736
1795
  if (member.type === "ClassConstructor") {
1737
- this.problem(`A constructor cannot be '${accessibility}'`);
1796
+ const written = [...accessibility ? [accessibility] : [], ...modifiers];
1797
+ if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
1738
1798
  return member;
1739
1799
  }
1740
- return { ...member, accessibility };
1800
+ if (modifiers.has("readonly") && member.type !== "ClassField") misplaced("Only a field can be 'readonly'");
1801
+ if (modifiers.has("abstract") && member.type !== "ClassMethod") misplaced("Only a method can be 'abstract'");
1802
+ if (modifiers.has("abstract") && member.isStatic) misplaced("A static method cannot be 'abstract'");
1803
+ if (modifiers.has("override") && member.isStatic) misplaced("A static member cannot be 'override'");
1804
+ return {
1805
+ ...member,
1806
+ ...accessibility ? { accessibility } : {},
1807
+ ...modifiers.has("readonly") && member.type === "ClassField" ? { isReadonly: true } : {},
1808
+ ...modifiers.has("override") ? { isOverride: true } : {}
1809
+ };
1741
1810
  }
1742
- parseClassMemberAfterAccessibility(start) {
1743
- const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1744
- if (isStatic) this.advance();
1811
+ /** Is the word here a member modifier, rather than a member's name? */
1812
+ startsClassModifier() {
1813
+ const t = this.current();
1814
+ if (t.type !== "Identifier" || !CLASS_MODIFIERS.has(t.value)) return false;
1815
+ const next = this.peek(1);
1816
+ return next.type === "Identifier" || next.type === "Keyword" && next.value === "function";
1817
+ }
1818
+ parseClassMemberAfterModifiers(start, modifiers) {
1819
+ const isStatic = modifiers.has("static");
1745
1820
  if (this.checkKeyword("function")) {
1746
1821
  this.advance();
1747
1822
  const memberName = this.parseIdentifier();
1823
+ if (modifiers.has("abstract")) {
1824
+ const head = this.parseFunctionHead();
1825
+ const func = {
1826
+ type: "FunctionBody",
1827
+ generics: head.generics,
1828
+ params: head.params,
1829
+ hasVarargs: head.hasVarargs,
1830
+ returnType: head.returnType,
1831
+ predicate: head.predicate,
1832
+ body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1833
+ ...spanFrom(head.start, this.previous())
1834
+ };
1835
+ if (!isStatic) bindThis(func, start);
1836
+ if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1837
+ if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1838
+ return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1839
+ }
1748
1840
  const signatures = [];
1749
1841
  let written = memberName;
1750
1842
  while (true) {
@@ -1862,12 +1954,17 @@ var Parser = class {
1862
1954
  parseReturnStatement(stopAtNewline = false) {
1863
1955
  const start = this.current();
1864
1956
  this.expectKeyword("return");
1865
- let args = [];
1957
+ let argument;
1866
1958
  const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
1867
1959
  if (!pastLine() && this.isExpressionStart()) {
1868
- args = this.expressionListOr(pastLine);
1960
+ const values = this.expressionListOr(pastLine);
1961
+ argument = values[0];
1962
+ if (values.length > 1) {
1963
+ this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
1964
+ argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
1965
+ }
1869
1966
  }
1870
- return { type: "ReturnStatement", arguments: args, ...spanFrom(start, this.previous()) };
1967
+ return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
1871
1968
  }
1872
1969
  parseTypeAliasStatement() {
1873
1970
  const start = this.current();
@@ -1926,7 +2023,7 @@ var Parser = class {
1926
2023
  ...spanFrom(start, this.previous())
1927
2024
  };
1928
2025
  }
1929
- if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
2026
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
1930
2027
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
1931
2028
  }
1932
2029
  let expression = first;
@@ -1979,6 +2076,15 @@ var Parser = class {
1979
2076
  }
1980
2077
  return false;
1981
2078
  }
2079
+ /** A string written straight before a method call — `"a":upper()`, or
2080
+ * `` `{n}`:rep(2) ``. Luau only takes `("a"):upper()`; the compiler adds
2081
+ * the parentheses. Anything else after a string (`"a".x`, `"a"(1)`) is
2082
+ * not a prefix expression here either. */
2083
+ startsStringMethodCall() {
2084
+ const t = this.current();
2085
+ const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
2086
+ return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
2087
+ }
1982
2088
  /** Does the next token start right where the current one ends? */
1983
2089
  touchesNext() {
1984
2090
  const current = this.current();
@@ -2165,6 +2271,7 @@ var Parser = class {
2165
2271
  }
2166
2272
  parseAtom() {
2167
2273
  const t = this.current();
2274
+ if (this.startsStringMethodCall()) return this.parsePrefixExpression();
2168
2275
  if (t.type === "Literal") {
2169
2276
  this.advance();
2170
2277
  const lit = t;
@@ -2184,8 +2291,9 @@ var Parser = class {
2184
2291
  return this.buildInterpolatedString(t);
2185
2292
  }
2186
2293
  if (t.type === "Operator" && t.value === "...") {
2294
+ this.problem(NO_DOTS);
2187
2295
  this.advance();
2188
- return { type: "VarargExpression", ...spanFrom(t, t) };
2296
+ return { type: "ErrorExpression", ...spanFrom(t, t) };
2189
2297
  }
2190
2298
  if (t.type === "Keyword" && t.value === "function") {
2191
2299
  this.advance();
@@ -2261,12 +2369,17 @@ var Parser = class {
2261
2369
  const start = this.current();
2262
2370
  let base;
2263
2371
  if (this.startsNew()) {
2264
- base = this.parseNewExpression();
2265
- } else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2372
+ this.problem("tilua has no 'new' operator: construct with 'Name.new(...)'");
2373
+ this.advance();
2374
+ }
2375
+ if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2266
2376
  this.advance();
2267
2377
  base = { type: "SuperExpression", ...spanFrom(start, start) };
2268
2378
  } else if (this.checkType("Identifier")) {
2269
2379
  base = this.parseIdentifier();
2380
+ } else if (this.startsStringMethodCall()) {
2381
+ const t = this.advance();
2382
+ base = t.type === "InterpolatedString" ? this.buildInterpolatedString(t) : { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2270
2383
  } else if (this.matchPunctuator("(")) {
2271
2384
  const inner = this.inBrackets(() => this.parseExpression());
2272
2385
  this.expectPunctuator(")");
@@ -2377,9 +2490,9 @@ var Parser = class {
2377
2490
  }
2378
2491
  return base;
2379
2492
  }
2380
- /** `new` is a soft keyword: it starts a construction only when a name
2381
- * follows it, so a function or field called `new` — `Instance.new(x)`,
2382
- * and `Vec.new(1)` itself — is untouched. */
2493
+ /** `new Name(...)`, the way other languages build an instance — only to
2494
+ * say so. A function or field called `new` — `Instance.new(x)`, and
2495
+ * `Vec.new(1)` itself — is untouched. */
2383
2496
  startsNew() {
2384
2497
  return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2385
2498
  }
@@ -2389,25 +2502,6 @@ var Parser = class {
2389
2502
  startsSuperUse() {
2390
2503
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2391
2504
  }
2392
- /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2393
- * name reached through a module — never an arbitrary expression, so the
2394
- * arguments are unambiguously the constructor's. */
2395
- parseNewExpression() {
2396
- const start = this.current();
2397
- this.advance();
2398
- let callee = this.parseIdentifier();
2399
- while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2400
- this.advance();
2401
- const property = this.parseIdentifier();
2402
- callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2403
- }
2404
- const typeArguments = this.tryCallTypeArguments();
2405
- if (!this.checkPunctuator("(")) {
2406
- this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2407
- }
2408
- const args = this.parseCallArguments();
2409
- return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2410
- }
2411
2505
  /** After `...`, is there something to spread? Nothing following it means
2412
2506
  * the vararg pack, which is what `f(...)` has always passed on. */
2413
2507
  startsSpread() {
@@ -2449,8 +2543,8 @@ var Parser = class {
2449
2543
  const errors = this.errors.length;
2450
2544
  try {
2451
2545
  this.advance();
2452
- const list = [this.parseTypeArgument()];
2453
- while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseTypeArgument());
2546
+ const list = [this.parseType()];
2547
+ while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
2454
2548
  this.expectOperator(">");
2455
2549
  if (!this.startsCallArguments()) throw new ParseRecover("not a call");
2456
2550
  return list;
@@ -2582,7 +2676,7 @@ var Parser = class {
2582
2676
  if (this.checkOperator("...")) {
2583
2677
  const dots = this.advance();
2584
2678
  if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2585
- elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2679
+ this.problemAt(dots, NO_DOTS);
2586
2680
  } else {
2587
2681
  const argument = this.expressionOr(stop);
2588
2682
  elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
@@ -2736,35 +2830,16 @@ var Parser = class {
2736
2830
  column: p.column
2737
2831
  };
2738
2832
  }
2739
- parseTypeOrTypePackReference() {
2833
+ /** `T...` written where a type goes: a pack, which tilua does not have. */
2834
+ rejectPackReference() {
2740
2835
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2741
- const start = this.current();
2742
- const base = this.expectIdentifier().value;
2836
+ this.problemAt(
2837
+ this.peek(1),
2838
+ "tilua has no type packs: several values are a tuple, '[A, B]', and a varying number an array, 'T[]'"
2839
+ );
2840
+ this.advance();
2743
2841
  this.advance();
2744
- const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
2745
- return {
2746
- type: "TypePackNode",
2747
- types: [],
2748
- hasVarargs: true,
2749
- // Wrap in `VariadicTypeNode`, matching the convention used by
2750
- // `parseFunctionTypeAfterParen`'s identifier-pack-reference
2751
- // branch, so the printer can tell `A...` (name-first, this
2752
- // case) apart from `...T` (dots-first) and append rather
2753
- // than prepend the `...`.
2754
- varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
2755
- ...spanFrom(start, this.previous())
2756
- };
2757
- }
2758
- return this.parseType();
2759
- }
2760
- parseTypeArgument() {
2761
- if (this.checkOperator("...")) {
2762
- return this.parseTypePack();
2763
- }
2764
- if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2765
- return this.parseTypeOrTypePackReference();
2766
2842
  }
2767
- return this.parseType();
2768
2843
  }
2769
2844
  /** The part shared by a real function body and an overload signature:
2770
2845
  * `<generics>(params): ReturnType`, up to (but not including) the block. */
@@ -2777,7 +2852,6 @@ var Parser = class {
2777
2852
  this.expectPunctuator("(");
2778
2853
  const params = [];
2779
2854
  let hasVarargs = false;
2780
- let varargTypeAnnotation;
2781
2855
  if (!this.checkPunctuator(")")) {
2782
2856
  while (true) {
2783
2857
  if (this.checkOperator("...")) {
@@ -2785,14 +2859,14 @@ var Parser = class {
2785
2859
  hasVarargs = true;
2786
2860
  if (this.checkType("Identifier")) {
2787
2861
  const nameTok = this.expectIdentifier();
2788
- let typeAnnotation2;
2862
+ let typeAnnotation3;
2789
2863
  if (this.matchPunctuator(":")) {
2790
- typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2864
+ typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
2791
2865
  }
2792
2866
  params.push({
2793
2867
  type: "FunctionParameter",
2794
2868
  name: nameTok.value,
2795
- typeAnnotation: typeAnnotation2,
2869
+ typeAnnotation: typeAnnotation3,
2796
2870
  rest: true,
2797
2871
  ...spanFrom(dots, this.previous())
2798
2872
  });
@@ -2801,9 +2875,20 @@ var Parser = class {
2801
2875
  }
2802
2876
  break;
2803
2877
  }
2878
+ this.problemAt(dots, NO_DOTS);
2879
+ let typeAnnotation2;
2804
2880
  if (this.matchPunctuator(":")) {
2805
- varargTypeAnnotation = this.parseTypeOrTypePackReference();
2881
+ this.rejectPackReference();
2882
+ const element = this.parseType();
2883
+ typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
2806
2884
  }
2885
+ params.push({
2886
+ type: "FunctionParameter",
2887
+ name: "args",
2888
+ typeAnnotation: typeAnnotation2,
2889
+ rest: true,
2890
+ ...spanFrom(dots, this.previous())
2891
+ });
2807
2892
  break;
2808
2893
  }
2809
2894
  const paramStart = this.current();
@@ -2845,10 +2930,16 @@ var Parser = class {
2845
2930
  if (this.matchPunctuator(":")) {
2846
2931
  predicate = this.tryParseTypePredicate();
2847
2932
  if (!predicate) {
2848
- returnType = this.attempt(() => this.parseTypeOrTypePackReference(), () => false, () => void 0);
2933
+ returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
2849
2934
  }
2850
2935
  }
2851
- return { start, generics, params, hasVarargs, varargTypeAnnotation, returnType, predicate };
2936
+ return { start, generics, params, hasVarargs, returnType, predicate };
2937
+ }
2938
+ /** What a function returns: one type. `T...` is reported as the pack it
2939
+ * is, and the type after it read. */
2940
+ parseReturnType() {
2941
+ this.rejectPackReference();
2942
+ return this.parseType();
2852
2943
  }
2853
2944
  /** TypeScript-style type-guard return annotations, in return position only:
2854
2945
  *
@@ -2918,7 +3009,6 @@ var Parser = class {
2918
3009
  generics: [],
2919
3010
  params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
2920
3011
  hasVarargs: false,
2921
- varargTypeAnnotation: void 0,
2922
3012
  returnType: void 0,
2923
3013
  predicate: void 0
2924
3014
  };
@@ -2930,7 +3020,6 @@ var Parser = class {
2930
3020
  generics: head.generics,
2931
3021
  params: head.params,
2932
3022
  hasVarargs: head.hasVarargs,
2933
- varargTypeAnnotation: head.varargTypeAnnotation,
2934
3023
  returnType: head.returnType,
2935
3024
  predicate: head.predicate,
2936
3025
  body,
@@ -2942,7 +3031,7 @@ var Parser = class {
2942
3031
  returnOf(expression) {
2943
3032
  const statement = {
2944
3033
  type: "ReturnStatement",
2945
- arguments: [expression],
3034
+ argument: expression,
2946
3035
  ...spanFrom(expression, expression)
2947
3036
  };
2948
3037
  return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
@@ -2955,7 +3044,6 @@ var Parser = class {
2955
3044
  generics: head.generics,
2956
3045
  params: head.params,
2957
3046
  hasVarargs: head.hasVarargs,
2958
- varargTypeAnnotation: head.varargTypeAnnotation,
2959
3047
  returnType: head.returnType,
2960
3048
  predicate: head.predicate,
2961
3049
  body,
@@ -2968,7 +3056,6 @@ var Parser = class {
2968
3056
  generics: head.generics,
2969
3057
  params: head.params,
2970
3058
  hasVarargs: head.hasVarargs,
2971
- varargTypeAnnotation: head.varargTypeAnnotation,
2972
3059
  returnType: head.returnType,
2973
3060
  predicate: head.predicate,
2974
3061
  ...spanFrom(head.start, this.previous())
@@ -2986,7 +3073,6 @@ var Parser = class {
2986
3073
  generics: head.generics,
2987
3074
  params: head.params,
2988
3075
  hasVarargs: head.hasVarargs,
2989
- varargTypeAnnotation: head.varargTypeAnnotation,
2990
3076
  returnType: head.returnType,
2991
3077
  predicate: head.predicate,
2992
3078
  body,
@@ -3127,9 +3213,10 @@ var Parser = class {
3127
3213
  return this.parseFunctionTypeAfterParen(t, []);
3128
3214
  }
3129
3215
  if (t.type === "Operator" && t.value === "...") {
3216
+ this.problem("'...' in a type goes before a rest parameter, '...args: T[]', or at the end of a tuple, '[A, ...B[]]'");
3130
3217
  this.advance();
3131
- const inner = this.parseType();
3132
- return { type: "VariadicTypeNode", typeAnnotation: inner, ...spanFrom(t, this.previous()) };
3218
+ const element = this.parseType();
3219
+ return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
3133
3220
  }
3134
3221
  if (t.type === "Punctuator" && t.value === "{") {
3135
3222
  return this.parseTableType();
@@ -3137,13 +3224,22 @@ var Parser = class {
3137
3224
  if (t.type === "Punctuator" && t.value === "[") {
3138
3225
  this.advance();
3139
3226
  const elements = [];
3227
+ let rest;
3140
3228
  while (!this.checkPunctuator("]")) {
3229
+ if (this.matchOperator("...")) {
3230
+ rest = this.parseType();
3231
+ if (rest.type !== "ArrayTypeNode" && !(rest.type === "TypeReference" && rest.base !== "nil")) {
3232
+ this.problem("The rest of a tuple is an array type: '...T[]'");
3233
+ }
3234
+ this.matchPunctuator(",");
3235
+ break;
3236
+ }
3141
3237
  elements.push(this.parseType());
3142
3238
  if (this.matchPunctuator(",")) continue;
3143
3239
  break;
3144
3240
  }
3145
3241
  this.expectPunctuator("]");
3146
- return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
3242
+ return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3147
3243
  }
3148
3244
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3149
3245
  this.advance();
@@ -3190,9 +3286,9 @@ var Parser = class {
3190
3286
  if (this.checkOperator("<")) {
3191
3287
  this.advance();
3192
3288
  if (!this.checkOperator(">")) {
3193
- typeArguments.push(this.parseTypeArgument());
3289
+ typeArguments.push(this.parseType());
3194
3290
  while (this.matchPunctuator(",") && !this.checkOperator(">")) {
3195
- typeArguments.push(this.parseTypeArgument());
3291
+ typeArguments.push(this.parseType());
3196
3292
  }
3197
3293
  }
3198
3294
  this.expectOperator(">");
@@ -3203,13 +3299,12 @@ var Parser = class {
3203
3299
  }
3204
3300
  parseFunctionTypeAfterParen(start, generics) {
3205
3301
  const params = [];
3206
- let hasVarargs = false;
3207
- let varargType;
3302
+ let rested = false;
3208
3303
  if (!this.checkPunctuator(")")) {
3209
3304
  while (true) {
3210
3305
  if (this.checkOperator("...")) {
3211
3306
  const dots = this.advance();
3212
- hasVarargs = true;
3307
+ rested = true;
3213
3308
  if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3214
3309
  const nameTok2 = this.expectIdentifier();
3215
3310
  this.advance();
@@ -3223,16 +3318,21 @@ var Parser = class {
3223
3318
  });
3224
3319
  break;
3225
3320
  }
3226
- varargType = this.parseType();
3321
+ this.problemAt(dots, "A function type takes the rest of its arguments as an array: '(...args: T[]) => R'");
3322
+ this.rejectPackReference();
3323
+ const element = this.parseType();
3324
+ params.push({
3325
+ type: "FunctionTypeParameter",
3326
+ name: "args",
3327
+ rest: true,
3328
+ typeAnnotation: { type: "ArrayTypeNode", element, ...spanFrom(element, element) },
3329
+ ...spanFrom(dots, this.previous())
3330
+ });
3227
3331
  break;
3228
3332
  }
3229
3333
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
3230
- const packStart = this.current();
3231
- const packRef = this.parseType();
3232
- this.advance();
3233
- hasVarargs = true;
3234
- varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
3235
- break;
3334
+ this.rejectPackReference();
3335
+ continue;
3236
3336
  }
3237
3337
  let name;
3238
3338
  let optional2 = false;
@@ -3262,19 +3362,17 @@ var Parser = class {
3262
3362
  this.expectPunctuator(")");
3263
3363
  if (this.matchPunctuator("=>") || this.mistypedArrow()) {
3264
3364
  const predicate = this.tryParseTypePredicate();
3265
- const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
3365
+ const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseReturnType();
3266
3366
  return {
3267
3367
  type: "FunctionTypeNode",
3268
3368
  generics,
3269
3369
  params,
3270
- hasVarargs,
3271
- varargType,
3272
3370
  returnType,
3273
3371
  predicate,
3274
3372
  ...spanFrom(start, this.previous())
3275
3373
  };
3276
3374
  }
3277
- if (params.length === 1 && !params[0].name && !hasVarargs) {
3375
+ if (params.length === 1 && !params[0].name && !rested) {
3278
3376
  return {
3279
3377
  type: "ParenthesizedTypeNode",
3280
3378
  typeAnnotation: params[0].typeAnnotation,
@@ -3284,11 +3382,14 @@ var Parser = class {
3284
3382
  if (params.some((p) => p.name !== void 0)) {
3285
3383
  this.error("Expected '=>' for function type");
3286
3384
  }
3385
+ if (!params.length) {
3386
+ this.problemAt(start, "'()' is not a type: a function that returns nothing returns 'nil'");
3387
+ return { type: "TypeReference", base: "nil", typeArguments: [], ...spanFrom(start, this.previous()) };
3388
+ }
3389
+ this.problemAt(start, "Several types in parentheses are a pack, which tilua does not have: write a tuple, '[A, B]'");
3287
3390
  return {
3288
- type: "TypePackNode",
3289
- types: params.map((p) => p.typeAnnotation),
3290
- hasVarargs,
3291
- varargType,
3391
+ type: "TupleTypeNode",
3392
+ elements: params.map((p) => p.typeAnnotation),
3292
3393
  ...spanFrom(start, this.previous())
3293
3394
  };
3294
3395
  }
@@ -3426,40 +3527,15 @@ var Parser = class {
3426
3527
  this.expectPunctuator("}");
3427
3528
  return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
3428
3529
  }
3429
- parseTypePack() {
3430
- const start = this.current();
3431
- if (this.matchOperator("...")) {
3432
- const varargType2 = this.parseType();
3433
- return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
3434
- }
3435
- this.expectPunctuator("(");
3436
- const types = [];
3437
- let hasVarargs = false;
3438
- let varargType;
3439
- if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
3440
- while (true) {
3441
- if (this.matchOperator("...")) {
3442
- hasVarargs = true;
3443
- varargType = this.parseType();
3444
- break;
3445
- }
3446
- types.push(this.parseType());
3447
- if (this.matchPunctuator(",")) continue;
3448
- break;
3449
- }
3450
- }
3451
- this.expectPunctuator(")");
3452
- return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
3453
- }
3454
3530
  parseGenericTypeParameterList() {
3455
3531
  const list = [];
3456
3532
  this.expectOperator("<");
3457
3533
  while (true) {
3458
3534
  const isConst = this.matchKeyword("const");
3459
3535
  const nameTok = this.expectIdentifier();
3460
- let isPack = false;
3461
- if (this.matchOperator("...")) {
3462
- isPack = true;
3536
+ const dots = this.checkOperator("...") ? this.advance() : void 0;
3537
+ if (dots) {
3538
+ this.problemAt(dots, "tilua has no pack parameters: write '<T extends unknown[]>', and take it as '...args: T'");
3463
3539
  }
3464
3540
  let constraint;
3465
3541
  if (this.checkIdentifierValue("extends")) {
@@ -3468,17 +3544,13 @@ var Parser = class {
3468
3544
  }
3469
3545
  let def;
3470
3546
  if (this.matchOperator("=")) {
3471
- if (isPack) {
3472
- def = this.parseTypePack();
3473
- } else {
3474
- def = this.parseType();
3475
- }
3547
+ this.rejectPackReference();
3548
+ def = this.parseType();
3476
3549
  }
3477
3550
  list.push({
3478
3551
  type: "GenericTypeParameter",
3479
3552
  name: nameTok.value,
3480
3553
  id: tokenIdentifier(nameTok),
3481
- isPack,
3482
3554
  isConst: isConst || void 0,
3483
3555
  constraint,
3484
3556
  default: def,
@@ -3569,7 +3641,7 @@ function childScope(parent) {
3569
3641
  var Analyzer = class {
3570
3642
  constructor(options) {
3571
3643
  this.options = options;
3572
- for (const name of options.builtinGlobals ?? []) {
3644
+ for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
3573
3645
  const id = this.getOrCreateGlobalBinding(name);
3574
3646
  this.bindings.get(id).isBuiltin = true;
3575
3647
  }
@@ -3971,14 +4043,14 @@ var Analyzer = class {
3971
4043
  return;
3972
4044
  }
3973
4045
  case "GenericForStatement": {
3974
- for (const it of stmt.iterators) this.visitExpression(it, scope);
4046
+ this.visitExpression(stmt.iterator, scope);
3975
4047
  const bodyScope = childScope(scope);
3976
- for (const v of stmt.variables) this.declarePattern(bodyScope, v, "for-generic", scope);
4048
+ this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
3977
4049
  this.visitBlock(stmt.body, bodyScope);
3978
4050
  return;
3979
4051
  }
3980
4052
  case "ReturnStatement":
3981
- for (const arg of stmt.arguments) this.visitExpression(arg, scope);
4053
+ if (stmt.argument) this.visitExpression(stmt.argument, scope);
3982
4054
  return;
3983
4055
  case "BreakStatement":
3984
4056
  case "ContinueStatement":
@@ -4046,7 +4118,6 @@ var Analyzer = class {
4046
4118
  this.declare(fnScope, param.name, kind, param);
4047
4119
  }
4048
4120
  });
4049
- this.visitType(func.varargTypeAnnotation, fnScope);
4050
4121
  this.visitType(func.returnType, fnScope);
4051
4122
  this.functionDepth++;
4052
4123
  try {
@@ -4074,6 +4145,7 @@ var Analyzer = class {
4074
4145
  this.visitGenerics(node.typeParams, scope);
4075
4146
  if (node.superclass) this.reference(outer, node.superclass);
4076
4147
  for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4148
+ for (const shape of node.implements ?? []) this.visitType(shape, scope);
4077
4149
  for (const member of node.members) {
4078
4150
  switch (member.type) {
4079
4151
  case "ClassField":
@@ -4133,7 +4205,6 @@ var Analyzer = class {
4133
4205
  case "BooleanLiteral":
4134
4206
  case "NumberLiteral":
4135
4207
  case "StringLiteral":
4136
- case "VarargExpression":
4137
4208
  case "ErrorExpression":
4138
4209
  return;
4139
4210
  case "InterpolatedStringExpression":
@@ -4173,11 +4244,6 @@ var Analyzer = class {
4173
4244
  this.visitExpression(expr.callee, scope);
4174
4245
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
4175
4246
  return;
4176
- case "NewExpression":
4177
- this.visitExpression(expr.callee, scope);
4178
- for (const argument of expr.arguments) this.visitExpression(argument, scope);
4179
- for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4180
- return;
4181
4247
  case "SuperExpression":
4182
4248
  return;
4183
4249
  case "SpreadElement":
@@ -4231,6 +4297,7 @@ var Analyzer = class {
4231
4297
  }
4232
4298
  }
4233
4299
  };
4300
+ var LANGUAGE_GLOBALS = ["scriptArgs"];
4234
4301
  function analyzeScopes(program, options = {}) {
4235
4302
  return new Analyzer(options).run(program);
4236
4303
  }
@@ -4249,8 +4316,8 @@ type Exclude<T, U> = T - U
4249
4316
  type Extract<T, U> = T extends U ? T : never
4250
4317
  type NonNullable<T> = T - nil
4251
4318
 
4252
- type ReturnType<T> = T extends (...unknown) => infer R ? R : never
4253
- type Parameters<T> = T extends (...infer P) => unknown ? P : never
4319
+ type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
4320
+ type Parameters<T> = T extends (...args: infer P) => unknown ? P : never
4254
4321
 
4255
4322
  type Partial<T> = { [K in keyof T]?: T[K] }
4256
4323
  type Required<T> = { [K in keyof T]-?: T[K] }
@@ -4335,8 +4402,11 @@ function literal(value) {
4335
4402
  function arrayOf(element) {
4336
4403
  return { kind: "array", element };
4337
4404
  }
4338
- function tuple(elements, isPack) {
4339
- return { kind: "tuple", elements, isPack };
4405
+ function tuple(elements, rest) {
4406
+ return rest ? { kind: "tuple", elements, rest } : { kind: "tuple", elements };
4407
+ }
4408
+ function tupleMembers(t) {
4409
+ return t.rest ? [...t.elements, t.rest] : t.elements;
4340
4410
  }
4341
4411
  function objectType(entries, indexer, frozen) {
4342
4412
  return { kind: "object", properties: new Map(entries), indexer, frozen };
@@ -4360,7 +4430,7 @@ function substitute(t, subst) {
4360
4430
  case "array":
4361
4431
  return arrayOf(substitute(t.element, subst));
4362
4432
  case "tuple":
4363
- return tuple(t.elements.map((e) => substitute(e, subst)), t.isPack);
4433
+ return tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst));
4364
4434
  case "object": {
4365
4435
  const entries = [];
4366
4436
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
@@ -4384,14 +4454,20 @@ function substitute(t, subst) {
4384
4454
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
4385
4455
  let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
4386
4456
  let varargs = t.varargs && substitute(t.varargs, inner);
4387
- if (varargs?.kind === "tuple" && varargs.isPack) {
4457
+ let restIsWhole = t.restIsWhole;
4458
+ if (restIsWhole && varargs?.kind === "tuple") {
4388
4459
  params = [...params, ...varargs.elements.map((type) => ({ type }))];
4389
- varargs = void 0;
4460
+ varargs = varargs.rest;
4461
+ restIsWhole = void 0;
4462
+ } else if (restIsWhole && varargs?.kind === "array") {
4463
+ varargs = varargs.element;
4464
+ restIsWhole = void 0;
4390
4465
  }
4391
4466
  return {
4392
4467
  kind: "function",
4393
4468
  params,
4394
4469
  varargs,
4470
+ ...restIsWhole ? { restIsWhole } : {},
4395
4471
  returns: substitute(t.returns, inner),
4396
4472
  typeParams: t.typeParams,
4397
4473
  typeParamDefaults: t.typeParamDefaults,
@@ -4474,11 +4550,19 @@ function unify(param, arg, vars, out) {
4474
4550
  switch (param.kind) {
4475
4551
  case "array":
4476
4552
  if (arg.kind === "array") unify(param.element, arg.element, vars, out);
4477
- else if (arg.kind === "tuple") for (const e of arg.elements) unify(param.element, e, vars, out);
4553
+ else if (arg.kind === "tuple") for (const e of tupleMembers(arg)) unify(param.element, e, vars, out);
4478
4554
  return;
4479
4555
  case "tuple":
4480
- if (arg.kind === "tuple") param.elements.forEach((p, i) => arg.elements[i] && unify(p, arg.elements[i], vars, out));
4481
- else if (arg.kind === "array") for (const p of param.elements) unify(p, arg.element, vars, out);
4556
+ if (arg.kind === "tuple") {
4557
+ param.elements.forEach((p, i) => {
4558
+ const a = arg.elements[i] ?? arg.rest;
4559
+ if (a) unify(p, a, vars, out);
4560
+ });
4561
+ if (param.rest) for (const a of arg.elements.slice(param.elements.length)) unify(param.rest, a, vars, out);
4562
+ if (param.rest && arg.rest) unify(param.rest, arg.rest, vars, out);
4563
+ } else if (arg.kind === "array") {
4564
+ for (const p of tupleMembers(param)) unify(p, arg.element, vars, out);
4565
+ }
4482
4566
  return;
4483
4567
  case "function":
4484
4568
  if (arg.kind === "function") {
@@ -4580,7 +4664,7 @@ function widen(t) {
4580
4664
  case "array":
4581
4665
  return arrayOf(widen(t.element));
4582
4666
  case "tuple":
4583
- return tuple(t.elements.map(widen), t.isPack);
4667
+ return tuple(t.elements.map(widen), t.rest && widen(t.rest));
4584
4668
  case "object": {
4585
4669
  if (t.frozen || t.class) return t;
4586
4670
  const entries = [];
@@ -4680,13 +4764,18 @@ function isAssignableInner(a, b) {
4680
4764
  if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
4681
4765
  if (a.kind === "array") {
4682
4766
  if (b.kind === "array") return isAssignable(a.element, b.element);
4767
+ if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
4683
4768
  return false;
4684
4769
  }
4685
4770
  if (a.kind === "tuple") {
4686
4771
  if (b.kind === "tuple") {
4687
- return a.elements.length === b.elements.length && a.elements.every((t, i) => isAssignable(t, b.elements[i]));
4772
+ if (a.elements.length < b.elements.length) return false;
4773
+ if (!b.elements.every((t, i) => isAssignable(a.elements[i], t))) return false;
4774
+ const extra = [...a.elements.slice(b.elements.length), ...a.rest ? [a.rest] : []];
4775
+ if (!b.rest) return extra.length === 0;
4776
+ return extra.every((t) => isAssignable(t, b.rest));
4688
4777
  }
4689
- if (b.kind === "array") return a.elements.every((t) => isAssignable(t, b.element));
4778
+ if (b.kind === "array") return tupleMembers(a).every((t) => isAssignable(t, b.element));
4690
4779
  return false;
4691
4780
  }
4692
4781
  if (a.kind === "object") {
@@ -4735,7 +4824,7 @@ function sameRefTarget(a, b) {
4735
4824
  return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
4736
4825
  }
4737
4826
  function isNoValue(t) {
4738
- return t.kind === "tuple" && t.elements.length === 0;
4827
+ return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
4739
4828
  }
4740
4829
  function equalTypes(a, b) {
4741
4830
  return formatType(a) === formatType(b);
@@ -4842,7 +4931,7 @@ function containsFreeTypeParam(t, seen, bound) {
4842
4931
  case "array":
4843
4932
  return containsTypeParam(t.element, seen, bound);
4844
4933
  case "tuple":
4845
- return t.elements.some((e) => containsTypeParam(e, seen, bound));
4934
+ return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
4846
4935
  case "union":
4847
4936
  case "intersection":
4848
4937
  return t.types.some((m) => containsTypeParam(m, seen, bound));
@@ -4887,15 +4976,19 @@ function matchInfer(arg, pattern, out) {
4887
4976
  switch (pattern.kind) {
4888
4977
  case "array":
4889
4978
  if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
4890
- if (arg.kind === "tuple") return matchInfer(union(arg.elements), pattern.element, out);
4979
+ if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
4891
4980
  return false;
4892
4981
  case "tuple":
4893
4982
  if (arg.kind !== "tuple" || arg.elements.length !== pattern.elements.length) return false;
4983
+ if (pattern.rest && !matchInfer(arg.rest ?? neverType, pattern.rest, out)) return false;
4894
4984
  return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
4895
4985
  case "function": {
4896
4986
  if (arg.kind !== "function") return false;
4897
4987
  if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
4898
- out.set(pattern.varargs.name, tuple(arg.params.map((p) => p.type)));
4988
+ out.set(pattern.varargs.name, tuple(
4989
+ arg.params.map((p) => p.type),
4990
+ arg.varargs && !arg.restIsWhole ? arg.varargs : void 0
4991
+ ));
4899
4992
  } else {
4900
4993
  for (let i = 0; i < pattern.params.length; i++) {
4901
4994
  if (!arg.params[i]) return false;
@@ -4969,7 +5062,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
4969
5062
  collectTypeParams(t.element, out, seen);
4970
5063
  return;
4971
5064
  case "tuple":
4972
- for (const e of t.elements) collectTypeParams(e, out, seen);
5065
+ for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
4973
5066
  return;
4974
5067
  case "union":
4975
5068
  case "intersection":
@@ -5016,9 +5109,8 @@ function formatTypeUncached(t) {
5016
5109
  case "array":
5017
5110
  return `${formatAtom(t.element)}[]`;
5018
5111
  case "tuple": {
5019
- if (!t.elements.length) return "()";
5020
- const inner = t.elements.map(formatType).join(", ");
5021
- return t.isPack ? `(${inner})` : `[${inner}]`;
5112
+ const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
5113
+ return `[${inner.join(", ")}]`;
5022
5114
  }
5023
5115
  case "object": {
5024
5116
  if (t.name) {
@@ -5042,7 +5134,7 @@ function formatTypeUncached(t) {
5042
5134
  return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
5043
5135
  }).join(", ")}>` : "";
5044
5136
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
5045
- if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
5137
+ if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
5046
5138
  return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
5047
5139
  }
5048
5140
  case "typeParam":
@@ -5246,6 +5338,11 @@ function moduleExports(program, scopes, types, resolveModule) {
5246
5338
  }
5247
5339
  return { values, types: exportedTypes, default: defaultType };
5248
5340
  }
5341
+ function isBelow(k, key) {
5342
+ if (!k.startsWith(key) || k.length === key.length) return false;
5343
+ const next = k[key.length];
5344
+ return next === "." || next === "#" || next === "[";
5345
+ }
5249
5346
  function bindKey(id) {
5250
5347
  return `$${id}`;
5251
5348
  }
@@ -5263,9 +5360,6 @@ function mergeEnv(a, b, base) {
5263
5360
  function mergeIndexer(a, b) {
5264
5361
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5265
5362
  }
5266
- function producesMultipleValues(e) {
5267
- return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
5268
- }
5269
5363
  function isFreshLiteralExpr(e) {
5270
5364
  if (!e) return false;
5271
5365
  switch (e.type) {
@@ -5302,11 +5396,11 @@ function collectInferNames(node) {
5302
5396
  walk(n.element);
5303
5397
  return;
5304
5398
  case "ParenthesizedTypeNode":
5305
- case "VariadicTypeNode":
5306
5399
  walk(n.typeAnnotation);
5307
5400
  return;
5308
5401
  case "TupleTypeNode":
5309
5402
  n.elements.forEach(walk);
5403
+ walk(n.rest);
5310
5404
  return;
5311
5405
  case "UnionTypeNode":
5312
5406
  case "IntersectionTypeNode":
@@ -5324,7 +5418,6 @@ function collectInferNames(node) {
5324
5418
  return;
5325
5419
  case "FunctionTypeNode":
5326
5420
  n.params.forEach((pp) => walk(pp.typeAnnotation));
5327
- walk(n.varargType);
5328
5421
  walk(n.returnType);
5329
5422
  return;
5330
5423
  case "TableTypeNode":
@@ -5369,6 +5462,75 @@ function callsSuper(block) {
5369
5462
  });
5370
5463
  return found;
5371
5464
  }
5465
+ var ALL_FIELDS = /* @__PURE__ */ new Set();
5466
+ function definitelyAssigned(block) {
5467
+ const names = /* @__PURE__ */ new Set();
5468
+ for (const statement of block.statements) {
5469
+ switch (statement.type) {
5470
+ case "AssignmentStatement":
5471
+ case "CompoundAssignmentStatement":
5472
+ for (const name of assignedFields({ ...block, statements: [statement] })) names.add(name);
5473
+ break;
5474
+ case "DoStatement": {
5475
+ const inner = definitelyAssigned(statement.body);
5476
+ if (!inner) return void 0;
5477
+ for (const name of inner) names.add(name);
5478
+ break;
5479
+ }
5480
+ case "IfStatement": {
5481
+ if (!statement.alternate) break;
5482
+ const arms = [...statement.clauses.map((c) => c.body), statement.alternate].map(definitelyAssigned);
5483
+ const finishing = arms.filter((arm) => arm !== void 0);
5484
+ if (!finishing.length) return void 0;
5485
+ for (const name of finishing[0]) {
5486
+ if (finishing.every((arm) => arm.has(name))) names.add(name);
5487
+ }
5488
+ break;
5489
+ }
5490
+ case "ReturnStatement":
5491
+ return void 0;
5492
+ case "CallStatement": {
5493
+ const call = statement.expression;
5494
+ if (call.type === "CallExpression" && call.callee.type === "Identifier" && call.callee.name === "error") {
5495
+ return void 0;
5496
+ }
5497
+ break;
5498
+ }
5499
+ }
5500
+ }
5501
+ return names;
5502
+ }
5503
+ function withoutReceiver(t) {
5504
+ if (t.kind === "intersection") return intersection(t.types.map(withoutReceiver));
5505
+ if (t.kind !== "function") return t;
5506
+ const first = t.params[0]?.name;
5507
+ if (first !== "this" && first !== "self") return t;
5508
+ return { ...t, params: t.params.slice(1) };
5509
+ }
5510
+ var METAMETHOD_OPERANDS = {
5511
+ __add: 1,
5512
+ __sub: 1,
5513
+ __mul: 1,
5514
+ __div: 1,
5515
+ __idiv: 1,
5516
+ __mod: 1,
5517
+ __pow: 1,
5518
+ __concat: 1,
5519
+ __eq: 1,
5520
+ __lt: 1,
5521
+ __le: 1,
5522
+ __unm: 0,
5523
+ __len: 0,
5524
+ __tostring: 0,
5525
+ __iter: 0,
5526
+ __call: void 0
5527
+ };
5528
+ var METAMETHOD_RETURNS = {
5529
+ __tostring: stringType,
5530
+ __eq: booleanType,
5531
+ __lt: booleanType,
5532
+ __le: booleanType
5533
+ };
5372
5534
  function assignedFields(block) {
5373
5535
  const names = /* @__PURE__ */ new Set();
5374
5536
  walkNodes(block, (node) => {
@@ -5394,7 +5556,7 @@ function walkNodes(root, visit) {
5394
5556
  if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5395
5557
  }
5396
5558
  }
5397
- var AliasMap = class extends Map {
5559
+ var LazyMap = class extends Map {
5398
5560
  pending = /* @__PURE__ */ new Map();
5399
5561
  defer(name, resolve5) {
5400
5562
  super.delete(name);
@@ -5509,14 +5671,14 @@ var TypeAnalyzer = class {
5509
5671
  scopes;
5510
5672
  options;
5511
5673
  typeOf = /* @__PURE__ */ new Map();
5512
- bindingType = /* @__PURE__ */ new Map();
5674
+ bindingType = new LazyMap();
5513
5675
  narrowedTypeOf = /* @__PURE__ */ new Map();
5514
5676
  typeOfTypeNode = /* @__PURE__ */ new Map();
5515
5677
  expectedTypeOf = /* @__PURE__ */ new Map();
5516
- iteratesValues = /* @__PURE__ */ new Set();
5678
+ loops = /* @__PURE__ */ new Map();
5517
5679
  /** Public: each alias resolved once (generic aliases keep their params as
5518
5680
  * `typeParam` nodes in the body). */
5519
- aliases = new AliasMap();
5681
+ aliases = new LazyMap();
5520
5682
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
5521
5683
  aliasDefs = /* @__PURE__ */ new Map();
5522
5684
  /** Put on every ref to one of `aliasDefs`, so a module that imports the
@@ -5530,8 +5692,12 @@ var TypeAnalyzer = class {
5530
5692
  /** Generic parameters currently in lexical scope (alias body / generic fn),
5531
5693
  * with their `extends` constraints resolved. */
5532
5694
  typeParamScope = [];
5533
- /** Global types contributed by `declare` statements (libs, then this program). */
5534
- libGlobalTypes = /* @__PURE__ */ new Map();
5695
+ /** Global types contributed by `declare` statements (libs, then this program).
5696
+ * A library's are resolved when the name is first used: an engine's
5697
+ * definitions declare hundreds of globals, and a script uses a few. */
5698
+ libGlobalTypes = new LazyMap();
5699
+ /** Each library `declare` of a name, in order, until it is resolved. */
5700
+ libDeclares = /* @__PURE__ */ new Map();
5535
5701
  /** Declaration node -> binding, built once so `bindingIdByName` is O(1)
5536
5702
  * instead of a scan of every binding per declaration site. */
5537
5703
  bindingByDecl = /* @__PURE__ */ new Map();
@@ -5576,8 +5742,11 @@ var TypeAnalyzer = class {
5576
5742
  this.indexDeclarations();
5577
5743
  for (const [name, id] of this.scopes.globalsByName) {
5578
5744
  if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
5579
- const t = this.options.globalTypes?.[name] ?? this.libGlobalTypes.get(name) ?? anyType;
5580
- this.bindingType.set(id, t);
5745
+ const given = this.options.globalTypes?.[name];
5746
+ if (given) this.bindingType.set(id, given);
5747
+ else if (this.libGlobalTypes.has(name)) this.bindingType.defer(id, () => this.libGlobalTypes.get(name) ?? anyType);
5748
+ else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(unknownType));
5749
+ else this.bindingType.set(id, anyType);
5581
5750
  }
5582
5751
  setAliasExpander((t) => this.expand(t));
5583
5752
  setDeferredBound((t) => this.deferredBound(t));
@@ -5597,7 +5766,7 @@ var TypeAnalyzer = class {
5597
5766
  typeOfTypeNode: this.typeOfTypeNode,
5598
5767
  expectedTypeOf: this.expectedTypeOf,
5599
5768
  aliases: this.resolveDeferredAliases(),
5600
- iteratesValues: this.iteratesValues,
5769
+ loops: this.loops,
5601
5770
  diagnostics: this.diagnostics
5602
5771
  };
5603
5772
  }
@@ -5949,28 +6118,55 @@ var TypeAnalyzer = class {
5949
6118
  type.name = `typeof ${this.className(stmt)}`;
5950
6119
  const shape = this.shapeOf(stmt);
5951
6120
  const local = this.superDecl(stmt);
5952
- const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
6121
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
5953
6122
  if (parent?.kind === "object") {
5954
6123
  for (const [key, property] of parent.properties) {
5955
6124
  if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
5956
6125
  }
5957
6126
  }
5958
6127
  for (const [key, property] of shape.statics) type.properties.set(key, property);
5959
- const constructor = this.constructorType(stmt);
6128
+ if (shape.filling) {
6129
+ (shape.filled ??= []).push(() => {
6130
+ for (const [key, property] of shape.statics) type.properties.set(key, property);
6131
+ });
6132
+ }
5960
6133
  const params = this.classTypeParams(stmt);
5961
- type.properties.set("new", {
5962
- type: fn(
5963
- constructor?.params.filter((p) => p.name !== "this") ?? [],
5964
- this.selfTypeOf(stmt),
5965
- constructor?.varargs,
5966
- params.map((p) => p.name)
5967
- ),
6134
+ let settled;
6135
+ const construct = {
5968
6136
  optional: false,
5969
- readonly: true
6137
+ readonly: true,
6138
+ // Kept, so a class extending it can read what it takes; reaching
6139
+ // it from outside is the error.
6140
+ ...stmt.isAbstract ? { abstract: true } : {}
6141
+ };
6142
+ Object.defineProperty(construct, "type", {
6143
+ enumerable: true,
6144
+ get: () => {
6145
+ if (settled) return settled;
6146
+ const constructor = this.constructorType(stmt);
6147
+ const made = fn(
6148
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
6149
+ this.selfTypeOf(stmt),
6150
+ constructor?.varargs,
6151
+ params.map((p) => p.name)
6152
+ );
6153
+ if (!this.shapeOf(stmt).filling) settled = made;
6154
+ return made;
6155
+ }
5970
6156
  });
6157
+ type.properties.set("new", construct);
5971
6158
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
5972
6159
  return type;
5973
6160
  }
6161
+ /** The value side of the class `superclass` names when it is not
6162
+ * declared in this file: what the name it refers to holds — an import
6163
+ * brings the class table with it — or else a class of that name. */
6164
+ classStaticsOf(superclass) {
6165
+ const id = this.bindingIdOf(superclass);
6166
+ const bound = id !== void 0 ? this.expand(this.bindingType.get(id) ?? unknownType) : void 0;
6167
+ if (bound?.kind === "object") return bound;
6168
+ return this.classStaticsByNameType(superclass.name);
6169
+ }
5974
6170
  /** The value side of a class named by a binding rather than by a
5975
6171
  * declaration in this file — an imported one. */
5976
6172
  classStaticsByNameType(name) {
@@ -6031,20 +6227,26 @@ var TypeAnalyzer = class {
6031
6227
  } finally {
6032
6228
  this.emitDiagnostics = wasEmitting;
6033
6229
  shape.filling = false;
6230
+ for (const then of shape.filled ?? []) then();
6231
+ shape.filled = void 0;
6034
6232
  }
6035
6233
  return shape;
6036
6234
  }
6037
6235
  fillShape(stmt, shape) {
6038
6236
  const className = this.className(stmt);
6039
6237
  const put = (isStatic, name, property) => {
6040
- const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && m.accessibility === "private");
6041
- const marked = member ? { ...property, private: { owner: stmt, className } } : property;
6238
+ const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && (m.accessibility === "private" || m.accessibility === "protected"));
6239
+ const marked = member ? { ...property, private: { owner: stmt, className, ...member.accessibility === "protected" ? { protected: true } : {} } } : property;
6042
6240
  (isStatic ? shape.statics : shape.instance).set(name, marked);
6043
6241
  };
6044
6242
  for (const member of stmt.members) {
6045
6243
  if (member.type !== "ClassField") continue;
6046
6244
  const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
6047
- put(member.isStatic, member.name.name, { type, optional: false });
6245
+ put(member.isStatic, member.name.name, {
6246
+ type,
6247
+ optional: false,
6248
+ ...member.isReadonly ? { readonly: true } : {}
6249
+ });
6048
6250
  }
6049
6251
  for (const member of stmt.members) {
6050
6252
  switch (member.type) {
@@ -6053,7 +6255,11 @@ var TypeAnalyzer = class {
6053
6255
  case "ClassMethod": {
6054
6256
  this.paramsFromSignatures(member.func, member.signatures);
6055
6257
  const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
6056
- put(member.isStatic, member.name.name, { type, optional: false });
6258
+ put(member.isStatic, member.name.name, {
6259
+ type,
6260
+ optional: false,
6261
+ ...member.isAbstract ? { abstract: true } : {}
6262
+ });
6057
6263
  break;
6058
6264
  }
6059
6265
  case "ClassAccessor": {
@@ -6109,7 +6315,14 @@ var TypeAnalyzer = class {
6109
6315
  for (const signature of member.signatures ?? []) {
6110
6316
  this.checkParamOrder(signature.params, member);
6111
6317
  }
6112
- this.visitFunctionBody(member.func, env);
6318
+ if (member.type === "ClassMethod" && member.isAbstract) continue;
6319
+ const constructing = this.constructing;
6320
+ if (member.type === "ClassConstructor") this.constructing = stmt;
6321
+ try {
6322
+ this.visitFunctionBody(member.func, env);
6323
+ } finally {
6324
+ this.constructing = constructing;
6325
+ }
6113
6326
  }
6114
6327
  });
6115
6328
  }));
@@ -6156,13 +6369,148 @@ var TypeAnalyzer = class {
6156
6369
  if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
6157
6370
  report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
6158
6371
  }
6159
- const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
6372
+ this.checkAbstractMembers(stmt);
6373
+ this.checkOverrides(stmt);
6374
+ this.checkImplements(stmt);
6375
+ this.checkMetamethods(stmt);
6376
+ const assigned = constructor ? definitelyAssigned(constructor.func.body) ?? ALL_FIELDS : /* @__PURE__ */ new Set();
6160
6377
  for (const member of stmt.members) {
6161
6378
  if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6162
- if (assigned.has(member.name.name)) continue;
6379
+ if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
6163
6380
  const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6164
6381
  if (isAssignable(nilType, type)) continue;
6165
- report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
6382
+ 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`);
6383
+ }
6384
+ }
6385
+ /** The class whose constructor is being checked: it, and only it, may
6386
+ * assign the `readonly` fields it declares. */
6387
+ constructing;
6388
+ /** A class that is not abstract has a `new`, so everything its instances
6389
+ * promise has to be there: no abstract member of its own, and every one
6390
+ * it inherits written. */
6391
+ checkAbstractMembers(stmt) {
6392
+ if (stmt.isAbstract) return;
6393
+ const name = this.className(stmt);
6394
+ for (const member of stmt.members) {
6395
+ if (member.type === "ClassMethod" && member.isAbstract) {
6396
+ this.diagnostics.push({
6397
+ node: member.name,
6398
+ message: `'${member.name.name}' is abstract, so '${name}' has to be an 'abstract class'`
6399
+ });
6400
+ }
6401
+ }
6402
+ const own = this.shapeOf(stmt).instance;
6403
+ const missing = [...this.instanceType(stmt).properties].filter(([key, property]) => property.abstract && !own.has(key)).map(([key]) => `'${key}'`);
6404
+ if (missing.length) {
6405
+ this.diagnostics.push({
6406
+ node: stmt.name ?? stmt,
6407
+ 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`
6408
+ });
6409
+ }
6410
+ }
6411
+ /** A member written again in a class that extends another has to fit
6412
+ * where the base's stood — anything reading an instance as the base
6413
+ * would otherwise be told wrong. `override` says a member is such a one,
6414
+ * and is wrong on a member that is not. */
6415
+ checkOverrides(stmt) {
6416
+ const base = this.baseInstance(stmt);
6417
+ const name = this.className(stmt);
6418
+ const own = this.shapeOf(stmt).instance;
6419
+ for (const member of stmt.members) {
6420
+ if (member.type === "ClassConstructor" || member.isStatic) continue;
6421
+ const key = member.name.name;
6422
+ const inherited = CLASS_LINKS.has(key) ? void 0 : base?.properties.get(key);
6423
+ if (member.isOverride && !inherited) {
6424
+ this.diagnostics.push({
6425
+ node: member.name,
6426
+ 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`
6427
+ });
6428
+ continue;
6429
+ }
6430
+ const mine = own.get(key);
6431
+ if (!inherited || !mine || inherited.private && !inherited.private.protected) continue;
6432
+ const written = withoutReceiver(mine.type);
6433
+ const expected = withoutReceiver(inherited.type);
6434
+ if (isAssignable(written, expected)) continue;
6435
+ this.diagnostics.push({
6436
+ node: member.name,
6437
+ message: `'${key}' in '${name}' does not fit the '${key}' of '${stmt.superclass.name}' it replaces: '${briefType(written)}' is not assignable to '${briefType(expected)}'`
6438
+ });
6439
+ }
6440
+ }
6441
+ /** `implements Shape`: every member the shape names, of a type that fits.
6442
+ * A method's own `this` is left out of the comparison on both sides. */
6443
+ checkImplements(stmt) {
6444
+ if (!stmt.implements?.length) return;
6445
+ const name = this.className(stmt);
6446
+ const instance = this.instanceType(stmt).properties;
6447
+ for (const node of stmt.implements) {
6448
+ const shown = node.type === "TypeReference" ? node.namespace ? `${node.namespace}.${node.base}` : node.base : void 0;
6449
+ const target = this.expand(this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(node)));
6450
+ if (target.kind === "any") continue;
6451
+ if (target.kind !== "object") {
6452
+ this.diagnostics.push({
6453
+ node,
6454
+ message: `A class can only implement an object type or a class, not '${briefType(target)}'`
6455
+ });
6456
+ continue;
6457
+ }
6458
+ const label = shown ?? briefType(target);
6459
+ const missing = [];
6460
+ for (const [key, wanted] of target.properties) {
6461
+ const have = instance.get(key);
6462
+ if (!have) {
6463
+ if (!wanted.optional) missing.push(`'${key}'`);
6464
+ continue;
6465
+ }
6466
+ const written = withoutReceiver(have.type);
6467
+ const expected = withoutReceiver(wanted.optional ? optional(wanted.type) : wanted.type);
6468
+ if (isAssignable(written, expected)) continue;
6469
+ this.diagnostics.push({
6470
+ node,
6471
+ message: `'${name}' does not implement '${label}': its '${key}' is '${briefType(written)}', not '${briefType(expected)}'`
6472
+ });
6473
+ }
6474
+ if (missing.length) {
6475
+ this.diagnostics.push({
6476
+ node,
6477
+ message: `'${name}' does not implement '${label}': ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing`
6478
+ });
6479
+ }
6480
+ }
6481
+ }
6482
+ /** A method named for a metamethod *is* one — the class table is its
6483
+ * instances' metatable — so it has to take what Luau hands it and give
6484
+ * back what Luau expects. */
6485
+ checkMetamethods(stmt) {
6486
+ const instance = this.shapeOf(stmt).instance;
6487
+ for (const member of stmt.members) {
6488
+ if (member.type !== "ClassMethod" || !(member.name.name in METAMETHOD_OPERANDS)) continue;
6489
+ const key = member.name.name;
6490
+ if (member.isStatic) {
6491
+ this.diagnostics.push({
6492
+ node: member.name,
6493
+ message: `'${key}' is a metamethod of the instances; it cannot be static`
6494
+ });
6495
+ continue;
6496
+ }
6497
+ const operands = METAMETHOD_OPERANDS[key];
6498
+ const written = member.func.params.filter((p) => p.name !== "this").length;
6499
+ if (operands !== void 0 && (written !== operands || member.func.hasVarargs)) {
6500
+ this.diagnostics.push({
6501
+ node: member.name,
6502
+ message: operands === 0 ? `'${key}' takes nothing besides 'this'` : `'${key}' takes one operand besides 'this'`
6503
+ });
6504
+ continue;
6505
+ }
6506
+ const required = METAMETHOD_RETURNS[key];
6507
+ const returns = this.overloadsOf(instance.get(key)?.type ?? anyType)[0]?.returns;
6508
+ if (required && returns && !isAssignable(returns, required)) {
6509
+ this.diagnostics.push({
6510
+ node: member.name,
6511
+ message: `'${key}' has to return ${briefType(required)}, not '${briefType(returns)}'`
6512
+ });
6513
+ }
6166
6514
  }
6167
6515
  }
6168
6516
  /** `extends` must name a class, and the chain must end. */
@@ -6220,19 +6568,42 @@ var TypeAnalyzer = class {
6220
6568
  harvestDeclares(block, own = false) {
6221
6569
  for (const stmt of block.statements) {
6222
6570
  if (stmt.type !== "DeclareStatement") continue;
6223
- if (own && (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name)))) {
6571
+ if (!own) {
6572
+ const declares = this.libDeclares.get(stmt.name);
6573
+ if (declares) declares.push(stmt);
6574
+ else {
6575
+ this.libDeclares.set(stmt.name, [stmt]);
6576
+ this.libGlobalTypes.defer(stmt.name, () => this.resolveLibDeclares(stmt.name));
6577
+ }
6578
+ continue;
6579
+ }
6580
+ if (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name))) {
6224
6581
  this.deferredDeclares.set(stmt.name, stmt);
6225
6582
  continue;
6226
6583
  }
6227
- const t = this.resolveType(stmt.valueType);
6228
- const prev = this.libGlobalTypes.get(stmt.name);
6229
- const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6230
- this.libGlobalTypes.set(
6231
- stmt.name,
6232
- overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
6233
- );
6584
+ this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
6234
6585
  }
6235
6586
  }
6587
+ /** Every library `declare` of `name`, in order. They are written at the
6588
+ * top level of their files, so they are read as if there — outside any
6589
+ * generic the use that asked for them happens to sit in. */
6590
+ resolveLibDeclares(name) {
6591
+ const scope = this.typeParamScope.splice(0);
6592
+ try {
6593
+ let type;
6594
+ for (const stmt of this.libDeclares.get(name) ?? []) type = this.declaredOver(type, stmt);
6595
+ return type ?? anyType;
6596
+ } finally {
6597
+ this.typeParamScope.push(...scope);
6598
+ }
6599
+ }
6600
+ /** A `declare` on top of what earlier ones said about the same name: a
6601
+ * function adds an overload, a table adds members. */
6602
+ declaredOver(prev, stmt) {
6603
+ const t = this.resolveType(stmt.valueType);
6604
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6605
+ return overload ? intersection([prev, t]) : this.mergeDeclared(prev, t);
6606
+ }
6236
6607
  resolveAllAliases() {
6237
6608
  for (const [name, def] of this.aliasDefs) {
6238
6609
  if (def.class && !this.program.body.statements.includes(def.class)) {
@@ -6278,11 +6649,10 @@ var TypeAnalyzer = class {
6278
6649
  }
6279
6650
  imported;
6280
6651
  /** What a `return` gives, against what the function declared. */
6281
- checkReturn(stmt, declared, types, sources, env) {
6652
+ checkReturn(stmt, declared, actual, env) {
6282
6653
  if (!declared || !this.emitDiagnostics) return;
6283
6654
  if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
6284
- const actual = stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true);
6285
- const source = stmt.arguments.length === 1 ? sources[0] : void 0;
6655
+ const source = stmt.argument;
6286
6656
  const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
6287
6657
  if (fits) return;
6288
6658
  this.diagnostics.push({
@@ -6430,21 +6800,12 @@ var TypeAnalyzer = class {
6430
6800
  this.instantiationDepth--;
6431
6801
  }
6432
6802
  }
6433
- /** Pair written type arguments with the parameters they instantiate. A
6434
- * pack parameter (`T...`) takes every argument from its position on, as
6435
- * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
6436
- * and `Signal<()>` to the empty pack. Left out, a parameter takes its
6437
- * default (`T... = ...any` is `any`), or `unknown`. */
6803
+ /** Pair written type arguments with the parameters they instantiate.
6804
+ * Left out, a parameter takes its default, or `unknown`. */
6438
6805
  bindTypeArguments(params, args) {
6439
6806
  const subst = /* @__PURE__ */ new Map();
6440
6807
  params.forEach((p, i) => {
6441
- let arg = args[i];
6442
- if (p.isPack && i < args.length) {
6443
- const rest = args.slice(i);
6444
- const single = rest.length === 1 ? rest[0] : void 0;
6445
- arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
6446
- }
6447
- subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
6808
+ subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
6448
6809
  });
6449
6810
  return subst;
6450
6811
  }
@@ -6536,8 +6897,13 @@ var TypeAnalyzer = class {
6536
6897
  return literal(node.value);
6537
6898
  case "ArrayTypeNode":
6538
6899
  return arrayOf(this.resolveType(node.element));
6539
- case "TupleTypeNode":
6540
- return tuple(node.elements.map((e) => this.resolveType(e)));
6900
+ case "TupleTypeNode": {
6901
+ const rest = node.rest && this.expand(this.resolveType(node.rest));
6902
+ return tuple(
6903
+ node.elements.map((e) => this.resolveType(e)),
6904
+ rest ? rest.kind === "array" ? rest.element : rest.kind === "any" ? anyType : unknownType : void 0
6905
+ );
6906
+ }
6541
6907
  case "UnionTypeNode":
6542
6908
  return union(node.types.map((t) => this.resolveType(t)));
6543
6909
  case "IntersectionTypeNode":
@@ -6573,15 +6939,13 @@ var TypeAnalyzer = class {
6573
6939
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
6574
6940
  optional: p.optional
6575
6941
  }));
6576
- const restParam = node.params.find((p) => p.rest);
6577
- const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
6578
- return this.withTypeParamDefaults(fn(
6942
+ return this.withTypeParamDefaults(this.withRest(fn(
6579
6943
  params,
6580
6944
  this.resolveType(node.returnType),
6581
- restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
6945
+ void 0,
6582
6946
  names,
6583
6947
  this.resolvePredicate(node.predicate, params)
6584
- ), node.generics);
6948
+ ), node.params), node.generics);
6585
6949
  });
6586
6950
  }
6587
6951
  case "TypeofTypeNode": {
@@ -6645,13 +7009,6 @@ var TypeAnalyzer = class {
6645
7009
  })
6646
7010
  );
6647
7011
  }
6648
- case "VariadicTypeNode":
6649
- return this.resolveType(node.typeAnnotation);
6650
- case "TypePackNode": {
6651
- if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
6652
- if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
6653
- return tuple(node.types.map((t) => this.resolveType(t)), true);
6654
- }
6655
7012
  }
6656
7013
  }
6657
7014
  // --------------------------------------------------------
@@ -6717,15 +7074,18 @@ var TypeAnalyzer = class {
6717
7074
  case "array":
6718
7075
  return arrayOf(this.reduceType(t.element));
6719
7076
  case "tuple":
6720
- return tuple(t.elements.map((e) => this.reduceType(e)), t.isPack);
7077
+ return tuple(t.elements.map((e) => this.reduceType(e)), t.rest && this.reduceType(t.rest));
6721
7078
  case "function":
6722
- return fn(
6723
- t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
6724
- this.reduceType(t.returns),
6725
- t.varargs && this.reduceType(t.varargs),
6726
- t.typeParams,
6727
- t.predicate
6728
- );
7079
+ return {
7080
+ ...fn(
7081
+ t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
7082
+ this.reduceType(t.returns),
7083
+ t.varargs && this.reduceType(t.varargs),
7084
+ t.typeParams,
7085
+ t.predicate
7086
+ ),
7087
+ ...t.restIsWhole ? { restIsWhole: true } : {}
7088
+ };
6729
7089
  case "object": {
6730
7090
  if (t.class) return t;
6731
7091
  const entries = [];
@@ -6940,7 +7300,8 @@ var TypeAnalyzer = class {
6940
7300
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
6941
7301
  }
6942
7302
  });
6943
- const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
7303
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7304
+ this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
6944
7305
  stmt.names.forEach((target, i) => {
6945
7306
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
6946
7307
  const source = sources[i];
@@ -7029,7 +7390,8 @@ var TypeAnalyzer = class {
7029
7390
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7030
7391
  }
7031
7392
  });
7032
- const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
7393
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7394
+ this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7033
7395
  stmt.targets.forEach((target, i) => {
7034
7396
  const vt = valueTypes[i] ?? unknownType;
7035
7397
  const source = sources[i];
@@ -7116,23 +7478,26 @@ var TypeAnalyzer = class {
7116
7478
  return;
7117
7479
  }
7118
7480
  case "GenericForStatement": {
7119
- const iterTypes = stmt.iterators.map((it) => this.infer(it, env));
7481
+ const sourceType = this.infer(stmt.iterator, env);
7120
7482
  const bodyEnv = forkEnv(env);
7121
- const rows = stmt.variables.length >= 2 ? this.iterationRows(stmt.iterators[0], iterTypes[0]) : void 0;
7483
+ const rows = this.iterationRows(stmt.iterator, sourceType);
7122
7484
  if (rows) {
7123
- const [key, value] = stmt.variables;
7124
- this.bindPattern(key, union(rows.map((r) => r[0])), bodyEnv, "keep");
7125
- this.bindPattern(value, union(rows.map((r) => r[1])), bodyEnv, "keep");
7126
- stmt.variables.slice(2).forEach((v) => this.bindPattern(v, unknownType, bodyEnv, "widen"));
7127
- const keyId = key.type === "IdentifierPattern" ? this.bindingIdByName(key.name, key) : void 0;
7128
- const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
7129
- if (keyId !== void 0 && valueId !== void 0) this.correlateBindings(bodyEnv, [keyId, valueId], rows);
7485
+ this.bindPattern(
7486
+ stmt.variable,
7487
+ union(rows.map((row) => tuple(row))),
7488
+ bodyEnv,
7489
+ stmt.kind === "const" ? "keep" : "widen"
7490
+ );
7491
+ const parts = stmt.variable.type === "ArrayPattern" && !stmt.variable.rest ? stmt.variable.elements.slice(0, 2).map((el) => el?.value) : [];
7492
+ const ids = parts.map((p) => p?.type === "IdentifierPattern" ? this.bindingIdByName(p.name, p) : void 0);
7493
+ if (ids.length === 2 && ids.every((id) => id !== void 0)) {
7494
+ this.correlateBindings(bodyEnv, ids, rows);
7495
+ }
7496
+ this.loops.set(stmt, { walks: "iteration", viaIter: false });
7130
7497
  } else {
7131
- const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
7132
- stmt.variables.forEach((v, i) => {
7133
- this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
7134
- });
7135
- if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
7498
+ const [item, form] = this.iterationOf(stmt.iterator, sourceType);
7499
+ this.bindPattern(stmt.variable, item, bodyEnv, "widen");
7500
+ this.loops.set(stmt, form);
7136
7501
  }
7137
7502
  this.visitBlock(stmt.body, bodyEnv);
7138
7503
  return;
@@ -7140,19 +7505,10 @@ var TypeAnalyzer = class {
7140
7505
  case "ReturnStatement": {
7141
7506
  const declared = this.declaredReturns[this.declaredReturns.length - 1];
7142
7507
  const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
7143
- if (wanted) {
7144
- if (stmt.arguments.length === 1) {
7145
- this.applyContext(stmt.arguments[0], wanted);
7146
- } else if (wanted.kind === "tuple" && wanted.isPack) {
7147
- stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
7148
- }
7149
- }
7150
- const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
7151
- const { types, sources } = this.valueList(stmt.arguments, env, want);
7152
- this.checkReturn(stmt, declared, types, sources, env);
7153
- if (this.returnTypes) {
7154
- this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
7155
- }
7508
+ if (wanted && stmt.argument) this.applyContext(stmt.argument, wanted);
7509
+ const actual = stmt.argument ? this.infer(stmt.argument, env) : nilType;
7510
+ this.checkReturn(stmt, declared, actual, env);
7511
+ if (this.returnTypes) this.returnTypes.push(actual);
7156
7512
  return;
7157
7513
  }
7158
7514
  case "ExportStatement":
@@ -7239,7 +7595,7 @@ var TypeAnalyzer = class {
7239
7595
  * `sources` maps each produced value back to the expression it came from
7240
7596
  * (undefined for the 2nd and later values of a multi-value call), so the
7241
7597
  * caller can still do contextual typing against the written expression. */
7242
- valueList(exprs, env, want = 0) {
7598
+ valueList(exprs, env) {
7243
7599
  const types = [];
7244
7600
  const sources = [];
7245
7601
  exprs.forEach((e, i) => {
@@ -7255,31 +7611,29 @@ var TypeAnalyzer = class {
7255
7611
  }
7256
7612
  return;
7257
7613
  }
7258
- do {
7259
- types.push(t);
7260
- sources.push(e);
7261
- } while (last && types.length < want);
7262
- return;
7263
- }
7264
- if (last && e.type === "VarargExpression") {
7265
- do {
7266
- types.push(t);
7267
- sources.push(types.length - 1 === i ? e : void 0);
7268
- } while (types.length < want);
7614
+ types.push(t);
7615
+ sources.push(e);
7269
7616
  return;
7270
7617
  }
7271
- if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
7272
- t.elements.forEach((el, j) => {
7273
- types.push(el);
7274
- sources.push(j === 0 ? e : void 0);
7275
- });
7276
- return;
7277
- }
7278
- types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
7618
+ void last;
7619
+ types.push(t);
7279
7620
  sources.push(e);
7280
7621
  });
7281
7622
  return { types, sources };
7282
7623
  }
7624
+ /** `const a, b = x, y` pairs names with values, one each. A call is one
7625
+ * value — several come back as an array — so `const a, b = f()` is a
7626
+ * name short of a value, and says how to take them apart. */
7627
+ checkValueCount(node, names, values, types) {
7628
+ if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7629
+ const last = values[values.length - 1];
7630
+ const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7631
+ const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7632
+ this.diagnostics.push({
7633
+ node,
7634
+ 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`
7635
+ });
7636
+ }
7283
7637
  /** Run `visit` with a fresh place to collect `break` states, and return
7284
7638
  * what it collected. */
7285
7639
  withBreakScope(visit) {
@@ -7428,18 +7782,29 @@ var TypeAnalyzer = class {
7428
7782
  * context. */
7429
7783
  contextualArrays = /* @__PURE__ */ new WeakMap();
7430
7784
  applyArrayContext(e, expected) {
7431
- const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
7785
+ const members = this.expectedMembers(expected);
7786
+ const tuples = members.filter((m) => m.kind === "tuple");
7787
+ const spread = e.elements.some((el) => el.type === "SpreadElement");
7788
+ const length = e.elements.length;
7789
+ const fitting = tuples.find((t) => !spread && !t.rest && t.elements.length === length) ?? tuples.find((t) => t.rest !== void 0 && (spread || length >= t.elements.length));
7790
+ const target = fitting ?? members.find((m) => m.kind === "array") ?? tuples[0];
7432
7791
  if (!target) return;
7792
+ if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
7433
7793
  if (!e.elements.length) {
7434
7794
  this.contextualArrays.set(e, target);
7435
7795
  return;
7436
7796
  }
7437
7797
  e.elements.forEach((element, i) => {
7438
7798
  if (element.type === "SpreadElement") return;
7439
- const elementType = target.kind === "array" ? target.element : target.elements[i];
7799
+ const tupleTarget = target;
7800
+ const elementType = target.kind === "array" ? target.element : tupleTarget.elements[i] ?? tupleTarget.rest;
7440
7801
  this.applyContext(element, elementType);
7441
7802
  });
7442
7803
  }
7804
+ /** Array literals written where a tuple is wanted — a `return` of a
7805
+ * function that returns one, an argument, an annotated `const` — and
7806
+ * the tuple each is to be. */
7807
+ tupleArrays = /* @__PURE__ */ new WeakMap();
7443
7808
  /** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
7444
7809
  * value gets its property's type as context. */
7445
7810
  applyTableContext(e, expected) {
@@ -7495,32 +7860,33 @@ var TypeAnalyzer = class {
7495
7860
  }
7496
7861
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
7497
7862
  }
7498
- /** What a vararg function's `...` holds, one value at a time.
7499
- *
7500
- * Written three ways, and they mean the same call: `...` says nothing,
7501
- * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7502
- * into an array the body reads by name. Only the last changes what the
7503
- * body sees — the signature is the same either way. */
7504
- varargElement(func) {
7505
- if (!func.hasVarargs) return void 0;
7506
- const rest = func.params.find((p) => p.rest);
7507
- if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7508
- const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7509
- return declared?.kind === "array" ? declared.element : unknownType;
7510
- }
7511
- /** The type of `...` in each function body being walked. */
7512
- varargs = [];
7863
+ /** A function type with the rest parameter among `params` written in:
7864
+ * `...args: T[]` collects `T`s, and `...args: A` — `A` a tuple, or a
7865
+ * type parameter standing for one — is exactly `A`'s elements. */
7866
+ withRest(f, params) {
7867
+ const rest = params.find((p) => p.rest);
7868
+ if (!rest) return f;
7869
+ if (!rest.typeAnnotation) return { ...f, varargs: unknownType };
7870
+ const declared = this.resolveType(rest.typeAnnotation);
7871
+ const t = this.expand(declared);
7872
+ if (t.kind === "array") return { ...f, varargs: t.element };
7873
+ if (t.kind === "any") return { ...f, varargs: anyType };
7874
+ if (declared.kind === "infer") return { ...f, varargs: declared, restIsWhole: true };
7875
+ if (t.kind === "tuple") {
7876
+ return { ...f, params: [...f.params, ...t.elements.map((type) => ({ type }))], varargs: t.rest };
7877
+ }
7878
+ if (declared.kind === "typeParam") return { ...f, varargs: declared, restIsWhole: true };
7879
+ return { ...f, varargs: unknownType };
7880
+ }
7513
7881
  /** What each function body being walked declared it returns. */
7514
7882
  declaredReturns = [];
7515
- /** Run `body` with `...` and `return` as `func` declares them. */
7883
+ /** Run `body` with `return` as `func` declares it. */
7516
7884
  withVarargs(func, body) {
7517
- this.varargs.push(this.varargElement(func));
7518
7885
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
7519
7886
  this.contextualReturns.push(this.contextualReturnOf.get(func));
7520
7887
  try {
7521
7888
  return body();
7522
7889
  } finally {
7523
- this.varargs.pop();
7524
7890
  this.declaredReturns.pop();
7525
7891
  this.contextualReturns.pop();
7526
7892
  }
@@ -7579,7 +7945,7 @@ var TypeAnalyzer = class {
7579
7945
  if (type.kind !== "function") return type;
7580
7946
  const defaults = {};
7581
7947
  for (const generic of generics) {
7582
- if (generic.default && !generic.isPack) defaults[generic.name] = this.resolveType(generic.default);
7948
+ if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
7583
7949
  }
7584
7950
  return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
7585
7951
  }
@@ -7598,7 +7964,10 @@ var TypeAnalyzer = class {
7598
7964
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
7599
7965
  unify(p.type, preserve ? arg : widen(arg), vars, subst);
7600
7966
  });
7601
- if (f.varargs) {
7967
+ if (f.varargs && f.restIsWhole) {
7968
+ const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
7969
+ unify(f.varargs, tuple(rest), vars, subst);
7970
+ } else if (f.varargs) {
7602
7971
  const keeps = keepsLiterals(f.varargs);
7603
7972
  for (let i = f.params.length; i < argTypes.length; i++) {
7604
7973
  const arg = argTypes[i];
@@ -7767,7 +8136,12 @@ var TypeAnalyzer = class {
7767
8136
  const arg = argTypes[i];
7768
8137
  const declared = declaredAt(i);
7769
8138
  if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
7770
- const expected = this.reduceType(substitute(declared, subst));
8139
+ let expected = this.reduceType(substitute(declared, subst));
8140
+ if (f.restIsWhole && i >= f.params.length) {
8141
+ const whole = this.expand(expected);
8142
+ expected = whole.kind === "tuple" ? whole.elements[i - f.params.length] ?? whole.rest : whole.kind === "array" ? whole.element : void 0;
8143
+ if (!expected) continue;
8144
+ }
7771
8145
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
7772
8146
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
7773
8147
  this.diagnostics.push({
@@ -7895,13 +8269,13 @@ var TypeAnalyzer = class {
7895
8269
  type: this.paramType(p, /* @__PURE__ */ new Map()),
7896
8270
  optional: p.optional || p.default !== void 0
7897
8271
  }));
7898
- return fn(
8272
+ return this.withRest(fn(
7899
8273
  params,
7900
8274
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
7901
- this.varargElement(sig),
8275
+ void 0,
7902
8276
  names,
7903
8277
  this.resolvePredicate(sig.predicate, params)
7904
- );
8278
+ ), sig.params);
7905
8279
  }));
7906
8280
  }
7907
8281
  /** `...rest: T[]` holds every argument from its position on, so its type
@@ -7909,7 +8283,7 @@ var TypeAnalyzer = class {
7909
8283
  checkRestType(p, node) {
7910
8284
  if (!this.emitDiagnostics || !p.typeAnnotation) return;
7911
8285
  const declared = this.resolveType(p.typeAnnotation);
7912
- if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
8286
+ if (declared.kind === "array" || declared.kind === "tuple" || declared.kind === "any" || declared.kind === "typeParam") return;
7913
8287
  this.diagnostics.push({
7914
8288
  node,
7915
8289
  message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
@@ -7965,13 +8339,13 @@ var TypeAnalyzer = class {
7965
8339
  return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
7966
8340
  }));
7967
8341
  }
7968
- return fn(
8342
+ return this.withRest(fn(
7969
8343
  params,
7970
8344
  returns,
7971
- this.varargElement(func),
8345
+ void 0,
7972
8346
  names,
7973
8347
  this.resolvePredicate(func.predicate, params)
7974
- );
8348
+ ), func.params);
7975
8349
  });
7976
8350
  }
7977
8351
  /** Where the return types of the function being walked are collected, so
@@ -8013,17 +8387,16 @@ var TypeAnalyzer = class {
8013
8387
  this.preVisitDepth--;
8014
8388
  }
8015
8389
  }
8016
- /** The `[key, value]` pairs iterating a record yields, one per property —
8017
- * for `pairs(t)`, `next, t` and `for k, v in t` over an object type with
8018
- * no indexer. `undefined` for anything else (an array, a dictionary, an
8019
- * iterator function), whose keys have no names to list. */
8390
+ /** The `[key, value]` items `pairs(record)` hands out, one per property.
8391
+ * `undefined` for anything else (an array, a dictionary, an iterator
8392
+ * function), whose keys have no names to list. */
8020
8393
  iterationRows(iterNode, iterType) {
8021
8394
  let source;
8022
8395
  if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8023
- if (iterNode.callee.name !== "pairs" && iterNode.callee.name !== "next") return void 0;
8396
+ if (iterNode.callee.name !== "pairs") return void 0;
8024
8397
  source = this.typeOf.get(iterNode.arguments[0]);
8025
8398
  } else {
8026
- source = iterType;
8399
+ return void 0;
8027
8400
  }
8028
8401
  const t = source && this.expand(source);
8029
8402
  if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
@@ -8164,54 +8537,69 @@ var TypeAnalyzer = class {
8164
8537
  if (ids.length < 2) return;
8165
8538
  this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
8166
8539
  }
8167
- /** `(keyType, valueType, iteratesValues)` yielded by a generic-for
8168
- * iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
8169
- * iteration (`for … in t`). `varCount` is how many loop variables were
8170
- * written. `iteratesValues` reports the one case where the first variable
8171
- * is bound to the value rather than the key, which is what lowering has
8172
- * to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
8173
- iterationTypes(iterNode, iterType, varCount) {
8174
- if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8175
- const name = iterNode.callee.name;
8176
- const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
8177
- if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
8178
- if (name === "pairs" || name === "next") {
8540
+ /** `[step, state, first]` — what a call such as `pairs(t)` answers — as
8541
+ * the `step` function, or `undefined` for an array that is just an
8542
+ * array. Three places exactly, the first a function. */
8543
+ iterationTriple(t) {
8544
+ if (t.kind !== "tuple" || t.rest || t.elements.length !== 3) return void 0;
8545
+ const step = this.expand(t.elements[0]);
8546
+ return step.kind === "function" ? step : void 0;
8547
+ }
8548
+ /** What one `for (const item in source)` hands over each time, and how it
8549
+ * walks the source. `pairs`/`ipairs` are read from the table they are
8550
+ * given, so a record's keys stay the literals they are. */
8551
+ iterationOf(source, sourceType) {
8552
+ if (source.type === "CallExpression" && source.callee.type === "Identifier" && source.arguments[0]) {
8553
+ const name = source.callee.name;
8554
+ const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
8555
+ const iteration = { walks: "iteration", viaIter: false };
8556
+ if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
8557
+ if (name === "pairs") {
8179
8558
  if (src.kind === "object") {
8180
- return [
8559
+ return [tuple([
8181
8560
  src.indexer?.key ?? stringType,
8182
- src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
8183
- false
8184
- ];
8561
+ src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
8562
+ ]), iteration];
8185
8563
  }
8186
- if (src.kind === "array") return [numberType, src.element, false];
8564
+ if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
8187
8565
  }
8188
8566
  }
8189
- const iterator = this.expand(iterType);
8190
- if (iterator.kind === "function") {
8191
- const returns = this.expand(iterator.returns);
8192
- const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
8193
- const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
8194
- return [at(0), at(1), false];
8195
- }
8196
- const t = this.expand(iterType);
8197
- if (t.kind === "array") {
8198
- return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
8199
- }
8567
+ const handsOut = (t2, viaIter) => {
8568
+ const x = this.expand(t2);
8569
+ if (x.kind === "function") return [withoutNil(this.expand(x.returns)), { walks: "function", viaIter }];
8570
+ const step = this.iterationTriple(x);
8571
+ if (step) return [withoutNil(this.expand(step.returns)), { walks: "iteration", viaIter }];
8572
+ return void 0;
8573
+ };
8574
+ const direct = handsOut(sourceType, false);
8575
+ if (direct) return direct;
8576
+ const values = { walks: "values", viaIter: false };
8577
+ const t = this.expand(sourceType);
8578
+ if (t.kind === "any") return [anyType, values];
8579
+ const iter = t.kind === "object" ? t.properties.get("__iter") : void 0;
8580
+ if (iter) {
8581
+ const made = this.overloadsOf(iter.type)[0]?.returns ?? unknownType;
8582
+ return handsOut(made, true) ?? [unknownType, { walks: "values", viaIter: true }];
8583
+ }
8584
+ if (t.kind === "array") return [t.element, values];
8585
+ if (t.kind === "tuple") return [union([...t.elements, ...t.rest ? [t.rest] : []]), values];
8200
8586
  if (t.kind === "object") {
8201
- const k = t.indexer?.key ?? stringType;
8202
- const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
8203
- return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
8587
+ return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
8204
8588
  }
8205
- return [unknownType, unknownType, false];
8589
+ if (t.kind !== "never" && this.emitDiagnostics) {
8590
+ this.diagnostics.push({
8591
+ node: source,
8592
+ message: `Cannot loop over '${formatType(sourceType)}': a loop walks an array, a table, an iterator function or an iteration such as 'pairs(t)'`
8593
+ });
8594
+ }
8595
+ return [unknownType, values];
8206
8596
  }
8207
8597
  inferReturnType(body, env) {
8208
8598
  const returns = [];
8209
8599
  const walk = (block) => {
8210
8600
  for (const s of block.statements) {
8211
8601
  if (s.type === "ReturnStatement") {
8212
- if (s.arguments.length === 0) returns.push(nilType);
8213
- else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
8214
- else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
8602
+ returns.push(s.argument ? this.infer(s.argument, env) : nilType);
8215
8603
  } else if (s.type === "IfStatement") {
8216
8604
  for (const c of s.clauses) walk(c.body);
8217
8605
  if (s.alternate) walk(s.alternate);
@@ -8233,8 +8621,9 @@ var TypeAnalyzer = class {
8233
8621
  * `infer` would have widened it to an array type). */
8234
8622
  fitsAnnotation(init, declared, inferred, env) {
8235
8623
  if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
8236
- if (init.elements.length !== declared.elements.length) return false;
8237
- return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i]));
8624
+ const fits = declared.rest ? init.elements.length >= declared.elements.length : init.elements.length === declared.elements.length;
8625
+ if (!fits) return false;
8626
+ return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i] ?? declared.rest));
8238
8627
  }
8239
8628
  if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
8240
8629
  if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
@@ -8291,7 +8680,7 @@ var TypeAnalyzer = class {
8291
8680
  target.elements.forEach((el, i) => {
8292
8681
  if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
8293
8682
  });
8294
- if (target.rest) this.reassignPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env);
8683
+ if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
8295
8684
  return;
8296
8685
  }
8297
8686
  }
@@ -8327,7 +8716,7 @@ var TypeAnalyzer = class {
8327
8716
  if (!el) return;
8328
8717
  this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
8329
8718
  });
8330
- if (target.rest) this.bindPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env, mode);
8719
+ if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
8331
8720
  return;
8332
8721
  }
8333
8722
  }
@@ -8484,7 +8873,7 @@ var TypeAnalyzer = class {
8484
8873
  * not one of them is a mistake worth reporting, rather than the nil Lua
8485
8874
  * would hand back. */
8486
8875
  checkStringMember(node, object, key) {
8487
- if (!this.emitDiagnostics) return;
8876
+ if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
8488
8877
  const parts = this.stringParts(object);
8489
8878
  if (!parts) return;
8490
8879
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
@@ -8496,6 +8885,120 @@ var TypeAnalyzer = class {
8496
8885
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8497
8886
  });
8498
8887
  }
8888
+ /** `game.Anything` on a `{ GetService: ... }`, or `a.a` on a number: a
8889
+ * value whose members are all known, and none of them is this one. Only
8890
+ * a type that is known through and through is reported — an indexer, an
8891
+ * `any`, or a part still waiting on a type parameter could each supply
8892
+ * the name. An empty `{}` is a table still being filled in, so it is left
8893
+ * alone; an array's and a string's members come from a library, so
8894
+ * without one they say nothing. A string on its own is `checkStringMember`'s. */
8895
+ checkMissingMember(node, object, name) {
8896
+ if (!this.emitDiagnostics || this.missingMemberReported.has(node)) return;
8897
+ if (!this.memberMissing(object, name)) return;
8898
+ this.missingMemberReported.add(node);
8899
+ this.diagnostics.push({
8900
+ node,
8901
+ message: `Property '${name}' does not exist on type '${briefType(withoutNil(this.expand(object)))}'`
8902
+ });
8903
+ }
8904
+ /** Is `name` certainly not a member of `object`? See `checkMissingMember`. */
8905
+ memberMissing(object, name) {
8906
+ if (this.stringParts(object)) return false;
8907
+ const raw = this.expand(object);
8908
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
8909
+ let missing = false;
8910
+ for (const part of parts) {
8911
+ switch (part.kind) {
8912
+ case "primitive":
8913
+ if (part.name === "nil") continue;
8914
+ if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
8915
+ if (part.name === "string" && this.builtInMethod(part, name)) continue;
8916
+ break;
8917
+ case "literal":
8918
+ case "templateLiteral":
8919
+ if (part.kind === "templateLiteral" || part.base === "string") {
8920
+ if (!this.aliasDefs.has("StringMethods")) return false;
8921
+ }
8922
+ if (this.builtInMethod(part, name)) continue;
8923
+ break;
8924
+ case "array":
8925
+ case "tuple":
8926
+ if (!this.aliasDefs.has("ArrayMethods")) return false;
8927
+ if (this.builtInMethod(part, name)) continue;
8928
+ break;
8929
+ case "object":
8930
+ if (part.properties.has(name)) continue;
8931
+ if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
8932
+ break;
8933
+ default:
8934
+ return false;
8935
+ }
8936
+ missing = true;
8937
+ }
8938
+ return missing;
8939
+ }
8940
+ /** `t[k]` read with a `k` that cannot be a key of `t`: an `unknown`, a
8941
+ * type none of `t`'s keys has (a number into a record, a string into an
8942
+ * array or a class), or a name `t` does not have — which is `t.name`'s
8943
+ * error. An `any` on either side says nothing, and neither does a type
8944
+ * parameter still to be decided. A plain `string` into a record is
8945
+ * allowed: it reads as one of the values or nil, and the nil has to be
8946
+ * dealt with. An empty `{}` is a table still being filled in, and is
8947
+ * left alone. Reports, and answers whether it did. */
8948
+ checkIndexKey(node, object, key) {
8949
+ if (!this.emitDiagnostics || this.badIndexReported.has(node)) return false;
8950
+ const index = this.expand(key);
8951
+ if (index.kind === "any" || index.kind === "never" || containsTypeParam(index)) return false;
8952
+ const keys = index.kind === "union" ? index.types.map((k) => this.expand(k)) : [index];
8953
+ const nameOf = (k) => k.kind === "literal" && typeof k.value === "string" ? k.value : void 0;
8954
+ const shown = briefType(withoutNil(this.expand(object)));
8955
+ const names = keys.map(nameOf);
8956
+ const others = keys.filter((_, i) => names[i] === void 0);
8957
+ let message;
8958
+ if (others.length && this.cannotIndex(object, others)) {
8959
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
8960
+ } else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
8961
+ message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
8962
+ }
8963
+ if (!message) return false;
8964
+ this.badIndexReported.add(node);
8965
+ this.diagnostics.push({ node, message });
8966
+ return true;
8967
+ }
8968
+ /** Can one of `keys` (string literals aside) not index some part of
8969
+ * `object`? `false` as soon as a part cannot be judged. */
8970
+ cannotIndex(object, keys) {
8971
+ const raw = this.expand(object);
8972
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
8973
+ const isNumber = (k) => k.kind === "primitive" && k.name === "number" || k.kind === "literal" && typeof k.value === "number";
8974
+ const isString = (k) => k.kind === "primitive" && k.name === "string" || k.kind === "templateLiteral";
8975
+ let bad = false;
8976
+ for (const part of parts) {
8977
+ switch (part.kind) {
8978
+ case "primitive":
8979
+ if (part.name === "nil") continue;
8980
+ return false;
8981
+ case "array":
8982
+ case "tuple":
8983
+ if (!keys.every(isNumber)) bad = true;
8984
+ continue;
8985
+ case "object": {
8986
+ if (!part.indexer && part.properties.size === 0 && !part.class) return false;
8987
+ 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));
8988
+ if (!keys.every(fits)) bad = true;
8989
+ continue;
8990
+ }
8991
+ default:
8992
+ return false;
8993
+ }
8994
+ }
8995
+ return bad;
8996
+ }
8997
+ /** Indexes already reported: a loop body is visited more than once. */
8998
+ badIndexReported = /* @__PURE__ */ new WeakSet();
8999
+ /** Members already reported as missing: a loop body, or an assignment
9000
+ * target, is visited more than once. */
9001
+ missingMemberReported = /* @__PURE__ */ new WeakSet();
8499
9002
  /** A `private` member read from outside the class that declared it —
8500
9003
  * a subclass counts as outside, as in TypeScript. */
8501
9004
  checkPrivateMember(node, object, name) {
@@ -8506,18 +9009,46 @@ var TypeAnalyzer = class {
8506
9009
  if (t.kind !== "object") continue;
8507
9010
  const access = t.properties.get(name)?.private;
8508
9011
  if (!access || this.insideClass(access.owner)) continue;
9012
+ if (access.protected && this.insideSubclassOf(access.owner)) continue;
8509
9013
  this.diagnostics.push({
8510
9014
  node,
8511
- message: `Property '${name}' is private and only accessible within class '${access.className}'`
9015
+ 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}'`
8512
9016
  });
8513
9017
  return;
8514
9018
  }
8515
9019
  }
9020
+ /** `Shape.new` on an abstract class, and `super.area` on an abstract
9021
+ * method: each reaches something with nothing behind it. */
9022
+ checkAbstractAccess(expr, object) {
9023
+ if (!this.emitDiagnostics) return;
9024
+ const t = this.expand(object);
9025
+ if (t.kind !== "object") return;
9026
+ const name = expr.property.name;
9027
+ const property = t.properties.get(name);
9028
+ if (!property?.abstract) return;
9029
+ if (name === "new" && expr.object.type !== "SuperExpression") {
9030
+ const label = expressionLabel(expr.object) ?? formatType(t);
9031
+ this.diagnostics.push({
9032
+ node: expr,
9033
+ message: `'${label}' is an abstract class: build one of the classes extending it instead`
9034
+ });
9035
+ } else if (expr.object.type === "SuperExpression") {
9036
+ this.diagnostics.push({
9037
+ node: expr.property,
9038
+ message: `'${name}' is abstract in the class this one extends: there is nothing to call`
9039
+ });
9040
+ }
9041
+ }
8516
9042
  /** Is the code being checked written inside `owner`'s body — its own
8517
9043
  * methods, or a class or function nested in them? */
8518
9044
  insideClass(owner) {
8519
9045
  return this.enclosingClasses.includes(owner);
8520
9046
  }
9047
+ /** Is the code being checked inside a class that extends `owner`? */
9048
+ insideSubclassOf(owner) {
9049
+ const identity = this.classIdentity(owner);
9050
+ return this.enclosingClasses.some((cls) => this.instanceType(cls).class?.ancestors.includes(identity));
9051
+ }
8521
9052
  propertyType(raw, name) {
8522
9053
  const t = this.deferredAccess(this.expand(raw));
8523
9054
  if (t.kind === "object") {
@@ -8572,8 +9103,12 @@ var TypeAnalyzer = class {
8572
9103
  }
8573
9104
  if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
8574
9105
  if (t.indexer) return t.indexer.value;
9106
+ const anyString = index.kind === "primitive" && index.name === "string" || index.kind === "templateLiteral";
9107
+ if (anyString && !t.class && t.properties.size > 0) {
9108
+ return union([...[...t.properties.values()].map((p) => p.type), nilType]);
9109
+ }
8575
9110
  }
8576
- return unknownType;
9111
+ return index.kind === "any" ? anyType : unknownType;
8577
9112
  }
8578
9113
  /** What a deferred `T[K]` can be: every property its index could name.
8579
9114
  * Reading a member of one, or calling it, sees that. */
@@ -8583,10 +9118,21 @@ var TypeAnalyzer = class {
8583
9118
  if (containsTypeParam(index)) return unknownType;
8584
9119
  return this.accessType(t.objectType, index);
8585
9120
  }
9121
+ /** `[a, ...rest]`: what `rest` holds — an array of whatever comes after
9122
+ * the first `from` places. */
9123
+ restAfter(raw, from) {
9124
+ const t = this.expand(raw);
9125
+ if (t.kind === "tuple") {
9126
+ const left = [...t.elements.slice(from), ...t.rest ? [t.rest] : []];
9127
+ return arrayOf(left.length ? union(left) : neverType);
9128
+ }
9129
+ if (t.kind === "union") return union(t.types.map((m) => this.restAfter(m, from)));
9130
+ return arrayOf(this.elementType(t, from));
9131
+ }
8586
9132
  elementType(raw, index) {
8587
9133
  const t = this.expand(raw);
8588
9134
  if (t.kind === "array") return t.element;
8589
- if (t.kind === "tuple") return t.elements[index] ?? unknownType;
9135
+ if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
8590
9136
  if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
8591
9137
  if (t.kind === "difference") return this.elementType(t.base, index);
8592
9138
  if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
@@ -8616,8 +9162,6 @@ var TypeAnalyzer = class {
8616
9162
  return stringType;
8617
9163
  }
8618
9164
  // `...` holds what the function declared it takes.
8619
- case "VarargExpression":
8620
- return this.varargs[this.varargs.length - 1] ?? anyType;
8621
9165
  // `f(a, ...rest)` — every value the array holds, one after
8622
9166
  // another. Each of them is an element, so that is what the
8623
9167
  // parameters it fills are checked against.
@@ -8650,10 +9194,8 @@ var TypeAnalyzer = class {
8650
9194
  this.checkParamOrder(expr.func.params, expr);
8651
9195
  this.visitFunctionBody(expr.func, env);
8652
9196
  return this.inferFunctionBody(expr.func, env);
8653
- case "ParenthesizedExpression": {
8654
- const inner = this.infer(expr.expression, env);
8655
- return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
8656
- }
9197
+ case "ParenthesizedExpression":
9198
+ return this.infer(expr.expression, env);
8657
9199
  case "TypeAssertionExpression": {
8658
9200
  this.infer(expr.expression, env);
8659
9201
  return this.resolveType(expr.typeAnnotation);
@@ -8686,7 +9228,7 @@ var TypeAnalyzer = class {
8686
9228
  case "not":
8687
9229
  return booleanType;
8688
9230
  case "-":
8689
- return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
9231
+ return this.operatorResult(expr, "-", arg, void 0) ?? this.arithmeticOn([arg]);
8690
9232
  case "#":
8691
9233
  return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
8692
9234
  }
@@ -8704,7 +9246,11 @@ var TypeAnalyzer = class {
8704
9246
  const left = this.infer(expr.left, env);
8705
9247
  const { whenFalse } = this.narrowFromCondition(expr.left, env);
8706
9248
  const right = this.infer(expr.right, whenFalse);
8707
- return union([narrowTruthy(left), right]);
9249
+ const fallback = unwrapParens(expr.right);
9250
+ const empty = fallback.type === "TableExpression" && fallback.fields.length === 0 || fallback.type === "ArrayExpression" && fallback.elements.length === 0;
9251
+ const truthy = narrowTruthy(left);
9252
+ if (empty && truthy.kind !== "never" && truthy.kind !== "any" && isAssignable(right, truthy)) return truthy;
9253
+ return union([truthy, right]);
8708
9254
  }
8709
9255
  const l = this.infer(expr.left, env);
8710
9256
  const r = this.infer(expr.right, env);
@@ -8715,12 +9261,14 @@ var TypeAnalyzer = class {
8715
9261
  switch (op) {
8716
9262
  case "..":
8717
9263
  return this.operatorResult(expr, op, l, r) ?? stringType;
8718
- case "==":
8719
- case "~=":
8720
9264
  case "<":
8721
9265
  case ">":
8722
9266
  case "<=":
8723
9267
  case ">=":
9268
+ this.checkComparison(expr, op, l, r);
9269
+ return booleanType;
9270
+ case "==":
9271
+ case "~=":
8724
9272
  return booleanType;
8725
9273
  case "+":
8726
9274
  case "-":
@@ -8729,16 +9277,18 @@ var TypeAnalyzer = class {
8729
9277
  case "//":
8730
9278
  case "%":
8731
9279
  case "^":
8732
- return this.operatorResult(expr, op, l, r) ?? numberType;
9280
+ return this.operatorResult(expr, op, l, r) ?? this.arithmeticOn([l, r]);
8733
9281
  }
8734
9282
  return union([l, r]);
8735
9283
  }
8736
9284
  case "MemberExpression": {
8737
9285
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
8738
9286
  this.checkPrivateMember(expr.property, obj, expr.property.name);
9287
+ this.checkAbstractAccess(expr, obj);
8739
9288
  const key = this.refKeyOf(expr);
8740
9289
  const narrowed = key === void 0 ? void 0 : env.get(key);
8741
9290
  this.checkStringMember(expr, obj, literal(expr.property.name));
9291
+ if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
8742
9292
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
8743
9293
  }
8744
9294
  case "IndexExpression": {
@@ -8747,6 +9297,7 @@ var TypeAnalyzer = class {
8747
9297
  const key = this.refKeyOf(expr);
8748
9298
  const narrowed = key === void 0 ? void 0 : env.get(key);
8749
9299
  this.checkStringMember(expr, obj, this.expand(idx));
9300
+ if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
8750
9301
  const member = this.stringMember(obj, this.expand(idx));
8751
9302
  if (member) return this.chainResult(expr, member, shortCircuits);
8752
9303
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
@@ -8756,8 +9307,6 @@ var TypeAnalyzer = class {
8756
9307
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
8757
9308
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
8758
9309
  }
8759
- case "NewExpression":
8760
- return this.inferNew(expr, env);
8761
9310
  case "ClassExpression":
8762
9311
  return this.visitClass(expr, env);
8763
9312
  case "SuperExpression": {
@@ -8794,18 +9343,14 @@ var TypeAnalyzer = class {
8794
9343
  /** `new Name(args)` is `Name.new(args)` — the same function, and the
8795
9344
  * same check. Saying so here rather than rewriting the tree keeps the
8796
9345
  * error messages pointing at what was written. */
8797
- inferNew(expr, env) {
8798
- const calleeType = this.infer(expr.callee, env);
8799
- const constructor = this.propertyType(calleeType, "new");
8800
- if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8801
- if (this.emitDiagnostics) {
8802
- const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8803
- this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8804
- }
8805
- for (const argument of expr.arguments) this.infer(argument, env);
8806
- return anyType;
8807
- }
8808
- return this.inferCall(expr, constructor, env);
9346
+ /** `__call` of a value with one, as what calling the value calls. */
9347
+ callMetamethod(raw) {
9348
+ const t = this.expand(raw);
9349
+ if (t.kind !== "object" || this.overloadsOf(t).length) return void 0;
9350
+ const method = t.properties.get("__call");
9351
+ if (!method) return void 0;
9352
+ const bound = this.overloadsOf(method.type).map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f);
9353
+ return bound.length ? intersection(bound) : void 0;
8809
9354
  }
8810
9355
  /** `super(...)` — the base constructor, run on the instance being built. */
8811
9356
  inferSuperCall(expr, env) {
@@ -8829,8 +9374,9 @@ var TypeAnalyzer = class {
8829
9374
  this.inferCall(expr, callable, env);
8830
9375
  return nilType;
8831
9376
  }
8832
- inferCall(expr, callee, env) {
9377
+ inferCall(expr, called, env) {
8833
9378
  this.checkAmbiguousCall(expr);
9379
+ const callee = this.callMetamethod(called) ?? called;
8834
9380
  const united = this.unionSignatures(callee);
8835
9381
  const fns = united ?? this.overloadsOf(callee);
8836
9382
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -8853,8 +9399,31 @@ var TypeAnalyzer = class {
8853
9399
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
8854
9400
  return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
8855
9401
  }
9402
+ this.checkCallable(expr.callee, callee);
8856
9403
  return callee.kind === "any" ? anyType : unknownType;
8857
9404
  }
9405
+ /** `a()` where `a` is `1`: a value whose type is known and has no call
9406
+ * signature. A union is reported when any member cannot be called, but
9407
+ * only when every member is decided — `unknown`, `any` and anything still
9408
+ * generic say too little. */
9409
+ checkCallable(node, callee) {
9410
+ if (!this.emitDiagnostics || this.notCallableReported.has(node)) return;
9411
+ const raw = this.expand(callee);
9412
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.expand(m));
9413
+ const notCallable = (m) => m.kind === "primitive" && m.name !== "nil" || m.kind === "literal" || m.kind === "templateLiteral" || m.kind === "array" || m.kind === "tuple" || m.kind === "object";
9414
+ const callable = (m) => this.overloadsOf(m).length > 0;
9415
+ const concrete = parts.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
9416
+ if (!concrete.some(notCallable) || !concrete.every((m) => notCallable(m) || callable(m))) return;
9417
+ this.notCallableReported.add(node);
9418
+ const label = node.type === "Identifier" ? node.name : expressionLabel(node);
9419
+ this.diagnostics.push({
9420
+ node,
9421
+ message: `This expression is not callable: ${label === void 0 ? "" : `'${label}' `}is of type '${briefType(withoutNil(raw))}'`
9422
+ });
9423
+ }
9424
+ /** Callees already reported as not callable: a loop body is visited more
9425
+ * than once. */
9426
+ notCallableReported = /* @__PURE__ */ new WeakSet();
8858
9427
  /** A `(` on a line of its own continues the statement above it:
8859
9428
  *
8860
9429
  * const value = map[key]
@@ -8872,6 +9441,8 @@ var TypeAnalyzer = class {
8872
9441
  }
8873
9442
  inferMethodCall(expr, objType, env) {
8874
9443
  this.checkPrivateMember(expr.method, objType, expr.method.name);
9444
+ this.checkStringMember(expr.method, objType, literal(expr.method.name));
9445
+ this.checkMissingMember(expr.method, objType, expr.method.name);
8875
9446
  const method = this.propertyType(objType, expr.method.name);
8876
9447
  const united = this.unionSignatures(method);
8877
9448
  const fns = united ?? this.overloadsOf(method);
@@ -8905,6 +9476,7 @@ var TypeAnalyzer = class {
8905
9476
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
8906
9477
  return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
8907
9478
  }
9479
+ this.checkCallable(expr.method, method);
8908
9480
  return objType.kind === "any" ? anyType : unknownType;
8909
9481
  }
8910
9482
  // --------------------------------------------------------
@@ -8965,6 +9537,8 @@ var TypeAnalyzer = class {
8965
9537
  if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
8966
9538
  }
8967
9539
  if (!this.isReadonlyProperty(this.expand(this.infer(object, env)), name)) return;
9540
+ const constructing = this.constructing;
9541
+ if (constructing && object.type === "Identifier" && object.name === "this" && constructing.members.some((m) => m.type === "ClassField" && !m.isStatic && m.isReadonly && m.name.name === name)) return;
8968
9542
  this.diagnostics.push({
8969
9543
  node: target,
8970
9544
  message: `Cannot assign to '${name}' because it is a read-only property`
@@ -9024,6 +9598,8 @@ var TypeAnalyzer = class {
9024
9598
  inferArray(expr, env, asConst) {
9025
9599
  const contextual = this.contextualArrays.get(expr);
9026
9600
  if (contextual && !asConst) return contextual;
9601
+ const wantedTuple = this.tupleArrays.get(expr);
9602
+ if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9027
9603
  const elems = [];
9028
9604
  let hadSpread = false;
9029
9605
  for (const el of expr.elements) {
@@ -9043,6 +9619,28 @@ var TypeAnalyzer = class {
9043
9619
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
9044
9620
  })) : unknownType);
9045
9621
  }
9622
+ /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
9623
+ * tuple's, and what comes after them — more elements, or a spread — is
9624
+ * its rest. */
9625
+ inferTupleLiteral(expr, env, wanted) {
9626
+ const fixed = [];
9627
+ const tail = [];
9628
+ let spread = false;
9629
+ expr.elements.forEach((element, i) => {
9630
+ if (element.type === "SpreadElement") {
9631
+ spread = true;
9632
+ const held = this.expand(this.infer(element.argument, env));
9633
+ if (held.kind === "array") tail.push(held.element);
9634
+ else if (held.kind === "tuple") tail.push(...tupleMembers(held));
9635
+ else tail.push(unknownType);
9636
+ return;
9637
+ }
9638
+ const t = this.widenUnlessAsked(this.infer(element, env), element);
9639
+ if (!spread && i < wanted.elements.length) fixed.push(t);
9640
+ else tail.push(t);
9641
+ });
9642
+ return tuple(fixed, tail.length ? union(tail) : void 0);
9643
+ }
9046
9644
  /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
9047
9645
  * `{ n: number }` — unless the surroundings said a literal belongs there.
9048
9646
  * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
@@ -9128,7 +9726,7 @@ var TypeAnalyzer = class {
9128
9726
  case "tuple": {
9129
9727
  const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
9130
9728
  if (tupleContext?.kind === "tuple") {
9131
- return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.isPack);
9729
+ return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.rest);
9132
9730
  }
9133
9731
  const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
9134
9732
  const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
@@ -9385,7 +9983,8 @@ var TypeAnalyzer = class {
9385
9983
  for (const f of candidates) {
9386
9984
  if (!f.predicate) continue;
9387
9985
  const arg = args[f.predicate.param];
9388
- if (!arg || this.refKeyOf(arg) === void 0) continue;
9986
+ const onCondition = f.predicate.asserts && !f.predicate.type;
9987
+ if (!arg || this.refKeyOf(arg) === void 0 && !onCondition) continue;
9389
9988
  if (f.typeParams?.length && f.predicate.type) {
9390
9989
  const subst = this.inferTypeArgs(f, argTypes);
9391
9990
  return {
@@ -9406,6 +10005,10 @@ var TypeAnalyzer = class {
9406
10005
  const found = this.predicateCallTarget(call, env);
9407
10006
  if (!found || !found.predicate.asserts) return;
9408
10007
  const filter = found.predicate.type;
10008
+ if (this.refKeyOf(found.arg) === void 0) {
10009
+ if (!filter) this.applyNarrowing(found.arg, env, env, forkEnv(env));
10010
+ return;
10011
+ }
9409
10012
  this.narrowRef(found.arg, env, env, forkEnv(env), (cur) => filter ? { yes: narrowTo(cur, filter), no: narrowExclude(cur, filter) } : { yes: narrowTruthy(cur), no: narrowFalsy(cur) });
9410
10013
  }
9411
10014
  // --------------------------------------------------------
@@ -9430,6 +10033,10 @@ var TypeAnalyzer = class {
9430
10033
  if (base === void 0) return void 0;
9431
10034
  if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
9432
10035
  if (expr.index.type === "NumberLiteral") return `${base}#${expr.index.value}`;
10036
+ if (expr.index.type === "Identifier") {
10037
+ const id = this.bindingIdOf(expr.index);
10038
+ return id === void 0 ? void 0 : `${base}[${bindKey(id)}]`;
10039
+ }
9433
10040
  return void 0;
9434
10041
  }
9435
10042
  default:
@@ -9525,26 +10132,45 @@ var TypeAnalyzer = class {
9525
10132
  * that copied it no longer holds that value: forget the alias. */
9526
10133
  unalias(key) {
9527
10134
  for (const k of [...this.refAliases.keys()]) {
9528
- if (k !== key && !k.startsWith(`${key}.`) && !k.startsWith(`${key}#`)) continue;
10135
+ if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
9529
10136
  for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
9530
10137
  this.refAliases.delete(k);
9531
10138
  }
9532
10139
  }
9533
- /** Drop every narrowing recorded for a path strictly under `key`. */
10140
+ /** Drop every narrowing recorded for a path strictly under `key` — and,
10141
+ * when `key` is a whole variable, for every `t[key]` indexed by it: that
10142
+ * now names a different slot. */
9534
10143
  invalidateBelow(env, key) {
10144
+ const asIndex = `[${key}]`;
9535
10145
  for (const k of [...env.keys()]) {
9536
- if (k.startsWith(`${key}.`) || k.startsWith(`${key}#`)) env.delete(k);
10146
+ if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
9537
10147
  }
9538
10148
  }
9539
10149
  /** An assignment through a reference invalidates it and everything under
9540
10150
  * it, then records the assigned type. */
9541
10151
  assignToRef(expr, value, env) {
10152
+ this.invalidateSiblings(expr, env);
9542
10153
  const key = this.refKeyOf(expr);
9543
10154
  if (key === void 0) return;
9544
10155
  this.invalidateBelow(env, key);
9545
10156
  this.unalias(key);
9546
10157
  env.set(key, value);
9547
10158
  }
10159
+ /** A write to one slot of a table can be a write to a slot narrowed under
10160
+ * another name: `t.x = nil` is `t[k] = nil` when `k` is `"x"`. So a
10161
+ * write by name forgets what is known through variable keys, and a write
10162
+ * through any computed key forgets everything known about the table's
10163
+ * slots. */
10164
+ invalidateSiblings(expr, env) {
10165
+ const inner = expr.type === "ParenthesizedExpression" ? expr.expression : expr;
10166
+ if (inner.type !== "MemberExpression" && inner.type !== "IndexExpression") return;
10167
+ const parent = this.refKeyOf(inner.object);
10168
+ if (parent === void 0) return;
10169
+ const computed = inner.type === "IndexExpression" && inner.index.type !== "StringLiteral" && inner.index.type !== "NumberLiteral";
10170
+ for (const k of [...env.keys()]) {
10171
+ if (computed ? isBelow(k, parent) : k.startsWith(`${parent}[`)) env.delete(k);
10172
+ }
10173
+ }
9548
10174
  // --------------------------------------------------------
9549
10175
  // Small helpers
9550
10176
  // --------------------------------------------------------
@@ -9559,6 +10185,35 @@ var TypeAnalyzer = class {
9559
10185
  this.selfType = saved;
9560
10186
  }
9561
10187
  }
10188
+ /** Arithmetic no declared metamethod answers: a number — unless an
10189
+ * operand is `any`, which may be a value with metamethods of its own.
10190
+ * `(a.Position - b.Position).Magnitude` on an `a` nothing typed is a
10191
+ * `Vector3` as far as anyone knows, not a number with no `Magnitude`. */
10192
+ arithmeticOn(operands) {
10193
+ return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
10194
+ }
10195
+ /** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
10196
+ * with the operands swapped for `>` and `>=`, and an instance without
10197
+ * one cannot be compared at all. */
10198
+ checkComparison(node, op, l, r) {
10199
+ if (!this.emitDiagnostics) return;
10200
+ const left = this.expand(l);
10201
+ const right = this.expand(r);
10202
+ const instance = (t) => t.kind === "object" && t.class !== void 0;
10203
+ if (!instance(left) && !instance(right)) return;
10204
+ const name = op === "<" || op === ">" ? "__lt" : "__le";
10205
+ const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
10206
+ for (const receiver of operands) {
10207
+ const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
10208
+ if (!method) continue;
10209
+ if (this.pickOverload(this.overloadsOf(method.type), operands)) return;
10210
+ break;
10211
+ }
10212
+ this.diagnostics.push({
10213
+ node,
10214
+ message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
10215
+ });
10216
+ }
9562
10217
  /** What an operator on a value with metamethods gives: `a + b` calls
9563
10218
  * `__add` on `a`, or failing that on `b` with the operands swapped — the
9564
10219
  * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
@@ -9883,7 +10538,7 @@ function briefType(t) {
9883
10538
  }
9884
10539
 
9885
10540
  // src/project/host.ts
9886
- import { readFileSync, statSync } from "fs";
10541
+ import { readdirSync, readFileSync, statSync } from "fs";
9887
10542
  var nodeHost = {
9888
10543
  readFile(path) {
9889
10544
  try {
@@ -9891,6 +10546,13 @@ var nodeHost = {
9891
10546
  } catch {
9892
10547
  return void 0;
9893
10548
  }
10549
+ },
10550
+ readDirectory(path) {
10551
+ try {
10552
+ return readdirSync(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => entry.name);
10553
+ } catch {
10554
+ return void 0;
10555
+ }
9894
10556
  }
9895
10557
  };
9896
10558
 
@@ -10082,13 +10744,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
10082
10744
  else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
10083
10745
  continue;
10084
10746
  }
10085
- const name = entry.startsWith("@tilua-types/") ? entry : `@tilua-types/${entry}`;
10086
- const found = findPackage(name, config.directory, host);
10747
+ if (entry.includes("*")) {
10748
+ const matched = matchPackages(entry, config.directory, host);
10749
+ if (matched === void 0) {
10750
+ problems.push({
10751
+ file: config.path,
10752
+ message: `'${entry}' needs the folders listed, and this tool cannot list them; name the libraries one by one`,
10753
+ ...entryPosition(config, entry)
10754
+ });
10755
+ } else if (!matched.length) {
10756
+ problems.push({
10757
+ file: config.path,
10758
+ message: `No installed type library matches '${entry}'`,
10759
+ ...entryPosition(config, entry)
10760
+ });
10761
+ }
10762
+ for (const found2 of matched ?? []) addPackage(found2.directory, found2.file, /* @__PURE__ */ new Set());
10763
+ continue;
10764
+ }
10765
+ const names = entry.startsWith("@") ? [entry] : [`@tilua-types/${entry}`, entry];
10766
+ const found = names.map((name) => findPackage(name, config.directory, host)).find(Boolean);
10087
10767
  if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
10088
10768
  else {
10089
10769
  problems.push({
10090
10770
  file: config.path,
10091
- message: `Cannot find type library '${name}'. Install it with: npm i -D ${name}`,
10771
+ 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]}`,
10092
10772
  ...entryPosition(config, entry)
10093
10773
  });
10094
10774
  }
@@ -10125,6 +10805,32 @@ function findPackage(name, from, host) {
10125
10805
  directory = parent;
10126
10806
  }
10127
10807
  }
10808
+ function matchPackages(pattern, from, host) {
10809
+ if (!host.readDirectory) return void 0;
10810
+ const slash = pattern.lastIndexOf("/");
10811
+ const scope = slash < 0 ? "" : pattern.slice(0, slash);
10812
+ const last = pattern.slice(slash + 1);
10813
+ const matches = new RegExp(`^${last.split("*").map(escapeRegExp2).join(".*")}$`);
10814
+ const found = /* @__PURE__ */ new Map();
10815
+ let directory = resolve2(from);
10816
+ for (; ; ) {
10817
+ const folder = join2(directory, "node_modules", ...scope ? scope.split("/") : []);
10818
+ for (const name of host.readDirectory(folder) ?? []) {
10819
+ const full = scope ? `${scope}/${name}` : name;
10820
+ if (!matches.test(name) || found.has(full)) continue;
10821
+ const candidate = join2(folder, name);
10822
+ const file = packageEntry(candidate, host);
10823
+ if (file) found.set(full, { directory: candidate, file });
10824
+ }
10825
+ const parent = dirname2(directory);
10826
+ if (parent === directory) break;
10827
+ directory = parent;
10828
+ }
10829
+ return [...found.keys()].sort().map((name) => found.get(name));
10830
+ }
10831
+ function escapeRegExp2(text) {
10832
+ return text.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
10833
+ }
10128
10834
  function dependencyNames(directory, host) {
10129
10835
  const manifest = readJson(join2(directory, "package.json"), host);
10130
10836
  const names = /* @__PURE__ */ new Set();
@@ -10223,7 +10929,8 @@ function sourceMapTypes(text, path, options) {
10223
10929
  for (const child of node.children ?? []) {
10224
10930
  if (!isNode(child)) continue;
10225
10931
  const childAlias = visit(child, [...segments, child.name], alias);
10226
- if (!IDENTIFIER.test(child.name) || taken.has(child.name) || named.has(child.name)) continue;
10932
+ const service = child.name === child.className && options.classes.has(child.className);
10933
+ if (!IDENTIFIER.test(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
10227
10934
  named.add(child.name);
10228
10935
  members.push(`${child.name}: ${childAlias}`);
10229
10936
  }
@@ -10283,6 +10990,7 @@ export {
10283
10990
  BinaryOperators,
10284
10991
  CONFIG_FILE_NAMES,
10285
10992
  Keywords,
10993
+ LANGUAGE_GLOBALS,
10286
10994
  LexError,
10287
10995
  Operators,
10288
10996
  PRELUDE_SOURCE,
@@ -10351,6 +11059,7 @@ export {
10351
11059
  tilua,
10352
11060
  tokenize,
10353
11061
  tuple,
11062
+ tupleMembers,
10354
11063
  typeParam,
10355
11064
  unify,
10356
11065
  union,