@tilua/parser 1.0.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.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() {
@@ -1727,11 +1776,67 @@ var Parser = class {
1727
1776
  }
1728
1777
  parseClassMember() {
1729
1778
  const start = this.current();
1730
- const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1731
- if (isStatic) this.advance();
1779
+ let accessibility;
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
+ }
1791
+ }
1792
+ const member = this.parseClassMemberAfterModifiers(start, modifiers);
1793
+ if (!member) return member;
1794
+ const misplaced = (message) => this.problemAt(start, message);
1795
+ if (member.type === "ClassConstructor") {
1796
+ const written = [...accessibility ? [accessibility] : [], ...modifiers];
1797
+ if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
1798
+ return member;
1799
+ }
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
+ };
1810
+ }
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");
1732
1820
  if (this.checkKeyword("function")) {
1733
1821
  this.advance();
1734
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
+ }
1735
1840
  const signatures = [];
1736
1841
  let written = memberName;
1737
1842
  while (true) {
@@ -1849,12 +1954,17 @@ var Parser = class {
1849
1954
  parseReturnStatement(stopAtNewline = false) {
1850
1955
  const start = this.current();
1851
1956
  this.expectKeyword("return");
1852
- let args = [];
1957
+ let argument;
1853
1958
  const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
1854
1959
  if (!pastLine() && this.isExpressionStart()) {
1855
- 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
+ }
1856
1966
  }
1857
- return { type: "ReturnStatement", arguments: args, ...spanFrom(start, this.previous()) };
1967
+ return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
1858
1968
  }
1859
1969
  parseTypeAliasStatement() {
1860
1970
  const start = this.current();
@@ -1913,7 +2023,7 @@ var Parser = class {
1913
2023
  ...spanFrom(start, this.previous())
1914
2024
  };
1915
2025
  }
1916
- if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
2026
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
1917
2027
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
1918
2028
  }
1919
2029
  let expression = first;
@@ -1966,6 +2076,15 @@ var Parser = class {
1966
2076
  }
1967
2077
  return false;
1968
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
+ }
1969
2088
  /** Does the next token start right where the current one ends? */
1970
2089
  touchesNext() {
1971
2090
  const current = this.current();
@@ -2152,6 +2271,7 @@ var Parser = class {
2152
2271
  }
2153
2272
  parseAtom() {
2154
2273
  const t = this.current();
2274
+ if (this.startsStringMethodCall()) return this.parsePrefixExpression();
2155
2275
  if (t.type === "Literal") {
2156
2276
  this.advance();
2157
2277
  const lit = t;
@@ -2171,8 +2291,9 @@ var Parser = class {
2171
2291
  return this.buildInterpolatedString(t);
2172
2292
  }
2173
2293
  if (t.type === "Operator" && t.value === "...") {
2294
+ this.problem(NO_DOTS);
2174
2295
  this.advance();
2175
- return { type: "VarargExpression", ...spanFrom(t, t) };
2296
+ return { type: "ErrorExpression", ...spanFrom(t, t) };
2176
2297
  }
2177
2298
  if (t.type === "Keyword" && t.value === "function") {
2178
2299
  this.advance();
@@ -2248,12 +2369,17 @@ var Parser = class {
2248
2369
  const start = this.current();
2249
2370
  let base;
2250
2371
  if (this.startsNew()) {
2251
- base = this.parseNewExpression();
2252
- } 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()) {
2253
2376
  this.advance();
2254
2377
  base = { type: "SuperExpression", ...spanFrom(start, start) };
2255
2378
  } else if (this.checkType("Identifier")) {
2256
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) };
2257
2383
  } else if (this.matchPunctuator("(")) {
2258
2384
  const inner = this.inBrackets(() => this.parseExpression());
2259
2385
  this.expectPunctuator(")");
@@ -2364,9 +2490,9 @@ var Parser = class {
2364
2490
  }
2365
2491
  return base;
2366
2492
  }
2367
- /** `new` is a soft keyword: it starts a construction only when a name
2368
- * follows it, so a function or field called `new` — `Instance.new(x)`,
2369
- * 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. */
2370
2496
  startsNew() {
2371
2497
  return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2372
2498
  }
@@ -2376,25 +2502,6 @@ var Parser = class {
2376
2502
  startsSuperUse() {
2377
2503
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2378
2504
  }
2379
- /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2380
- * name reached through a module — never an arbitrary expression, so the
2381
- * arguments are unambiguously the constructor's. */
2382
- parseNewExpression() {
2383
- const start = this.current();
2384
- this.advance();
2385
- let callee = this.parseIdentifier();
2386
- while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2387
- this.advance();
2388
- const property = this.parseIdentifier();
2389
- callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2390
- }
2391
- const typeArguments = this.tryCallTypeArguments();
2392
- if (!this.checkPunctuator("(")) {
2393
- this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2394
- }
2395
- const args = this.parseCallArguments();
2396
- return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2397
- }
2398
2505
  /** After `...`, is there something to spread? Nothing following it means
2399
2506
  * the vararg pack, which is what `f(...)` has always passed on. */
2400
2507
  startsSpread() {
@@ -2436,8 +2543,8 @@ var Parser = class {
2436
2543
  const errors = this.errors.length;
2437
2544
  try {
2438
2545
  this.advance();
2439
- const list = [this.parseTypeArgument()];
2440
- while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseTypeArgument());
2546
+ const list = [this.parseType()];
2547
+ while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
2441
2548
  this.expectOperator(">");
2442
2549
  if (!this.startsCallArguments()) throw new ParseRecover("not a call");
2443
2550
  return list;
@@ -2569,7 +2676,7 @@ var Parser = class {
2569
2676
  if (this.checkOperator("...")) {
2570
2677
  const dots = this.advance();
2571
2678
  if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2572
- elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2679
+ this.problemAt(dots, NO_DOTS);
2573
2680
  } else {
2574
2681
  const argument = this.expressionOr(stop);
2575
2682
  elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
@@ -2723,35 +2830,16 @@ var Parser = class {
2723
2830
  column: p.column
2724
2831
  };
2725
2832
  }
2726
- parseTypeOrTypePackReference() {
2833
+ /** `T...` written where a type goes: a pack, which tilua does not have. */
2834
+ rejectPackReference() {
2727
2835
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2728
- const start = this.current();
2729
- 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();
2730
2841
  this.advance();
2731
- const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
2732
- return {
2733
- type: "TypePackNode",
2734
- types: [],
2735
- hasVarargs: true,
2736
- // Wrap in `VariadicTypeNode`, matching the convention used by
2737
- // `parseFunctionTypeAfterParen`'s identifier-pack-reference
2738
- // branch, so the printer can tell `A...` (name-first, this
2739
- // case) apart from `...T` (dots-first) and append rather
2740
- // than prepend the `...`.
2741
- varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
2742
- ...spanFrom(start, this.previous())
2743
- };
2744
- }
2745
- return this.parseType();
2746
- }
2747
- parseTypeArgument() {
2748
- if (this.checkOperator("...")) {
2749
- return this.parseTypePack();
2750
- }
2751
- if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2752
- return this.parseTypeOrTypePackReference();
2753
2842
  }
2754
- return this.parseType();
2755
2843
  }
2756
2844
  /** The part shared by a real function body and an overload signature:
2757
2845
  * `<generics>(params): ReturnType`, up to (but not including) the block. */
@@ -2764,7 +2852,6 @@ var Parser = class {
2764
2852
  this.expectPunctuator("(");
2765
2853
  const params = [];
2766
2854
  let hasVarargs = false;
2767
- let varargTypeAnnotation;
2768
2855
  if (!this.checkPunctuator(")")) {
2769
2856
  while (true) {
2770
2857
  if (this.checkOperator("...")) {
@@ -2772,14 +2859,14 @@ var Parser = class {
2772
2859
  hasVarargs = true;
2773
2860
  if (this.checkType("Identifier")) {
2774
2861
  const nameTok = this.expectIdentifier();
2775
- let typeAnnotation2;
2862
+ let typeAnnotation3;
2776
2863
  if (this.matchPunctuator(":")) {
2777
- typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2864
+ typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
2778
2865
  }
2779
2866
  params.push({
2780
2867
  type: "FunctionParameter",
2781
2868
  name: nameTok.value,
2782
- typeAnnotation: typeAnnotation2,
2869
+ typeAnnotation: typeAnnotation3,
2783
2870
  rest: true,
2784
2871
  ...spanFrom(dots, this.previous())
2785
2872
  });
@@ -2788,9 +2875,20 @@ var Parser = class {
2788
2875
  }
2789
2876
  break;
2790
2877
  }
2878
+ this.problemAt(dots, NO_DOTS);
2879
+ let typeAnnotation2;
2791
2880
  if (this.matchPunctuator(":")) {
2792
- varargTypeAnnotation = this.parseTypeOrTypePackReference();
2881
+ this.rejectPackReference();
2882
+ const element = this.parseType();
2883
+ typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
2793
2884
  }
2885
+ params.push({
2886
+ type: "FunctionParameter",
2887
+ name: "args",
2888
+ typeAnnotation: typeAnnotation2,
2889
+ rest: true,
2890
+ ...spanFrom(dots, this.previous())
2891
+ });
2794
2892
  break;
2795
2893
  }
2796
2894
  const paramStart = this.current();
@@ -2832,10 +2930,16 @@ var Parser = class {
2832
2930
  if (this.matchPunctuator(":")) {
2833
2931
  predicate = this.tryParseTypePredicate();
2834
2932
  if (!predicate) {
2835
- returnType = this.attempt(() => this.parseTypeOrTypePackReference(), () => false, () => void 0);
2933
+ returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
2836
2934
  }
2837
2935
  }
2838
- 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();
2839
2943
  }
2840
2944
  /** TypeScript-style type-guard return annotations, in return position only:
2841
2945
  *
@@ -2905,7 +3009,6 @@ var Parser = class {
2905
3009
  generics: [],
2906
3010
  params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
2907
3011
  hasVarargs: false,
2908
- varargTypeAnnotation: void 0,
2909
3012
  returnType: void 0,
2910
3013
  predicate: void 0
2911
3014
  };
@@ -2917,7 +3020,6 @@ var Parser = class {
2917
3020
  generics: head.generics,
2918
3021
  params: head.params,
2919
3022
  hasVarargs: head.hasVarargs,
2920
- varargTypeAnnotation: head.varargTypeAnnotation,
2921
3023
  returnType: head.returnType,
2922
3024
  predicate: head.predicate,
2923
3025
  body,
@@ -2929,7 +3031,7 @@ var Parser = class {
2929
3031
  returnOf(expression) {
2930
3032
  const statement = {
2931
3033
  type: "ReturnStatement",
2932
- arguments: [expression],
3034
+ argument: expression,
2933
3035
  ...spanFrom(expression, expression)
2934
3036
  };
2935
3037
  return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
@@ -2942,7 +3044,6 @@ var Parser = class {
2942
3044
  generics: head.generics,
2943
3045
  params: head.params,
2944
3046
  hasVarargs: head.hasVarargs,
2945
- varargTypeAnnotation: head.varargTypeAnnotation,
2946
3047
  returnType: head.returnType,
2947
3048
  predicate: head.predicate,
2948
3049
  body,
@@ -2955,7 +3056,6 @@ var Parser = class {
2955
3056
  generics: head.generics,
2956
3057
  params: head.params,
2957
3058
  hasVarargs: head.hasVarargs,
2958
- varargTypeAnnotation: head.varargTypeAnnotation,
2959
3059
  returnType: head.returnType,
2960
3060
  predicate: head.predicate,
2961
3061
  ...spanFrom(head.start, this.previous())
@@ -2973,7 +3073,6 @@ var Parser = class {
2973
3073
  generics: head.generics,
2974
3074
  params: head.params,
2975
3075
  hasVarargs: head.hasVarargs,
2976
- varargTypeAnnotation: head.varargTypeAnnotation,
2977
3076
  returnType: head.returnType,
2978
3077
  predicate: head.predicate,
2979
3078
  body,
@@ -3114,9 +3213,10 @@ var Parser = class {
3114
3213
  return this.parseFunctionTypeAfterParen(t, []);
3115
3214
  }
3116
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[]]'");
3117
3217
  this.advance();
3118
- const inner = this.parseType();
3119
- return { type: "VariadicTypeNode", typeAnnotation: inner, ...spanFrom(t, this.previous()) };
3218
+ const element = this.parseType();
3219
+ return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
3120
3220
  }
3121
3221
  if (t.type === "Punctuator" && t.value === "{") {
3122
3222
  return this.parseTableType();
@@ -3124,13 +3224,22 @@ var Parser = class {
3124
3224
  if (t.type === "Punctuator" && t.value === "[") {
3125
3225
  this.advance();
3126
3226
  const elements = [];
3227
+ let rest;
3127
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
+ }
3128
3237
  elements.push(this.parseType());
3129
3238
  if (this.matchPunctuator(",")) continue;
3130
3239
  break;
3131
3240
  }
3132
3241
  this.expectPunctuator("]");
3133
- return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
3242
+ return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3134
3243
  }
3135
3244
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3136
3245
  this.advance();
@@ -3177,9 +3286,9 @@ var Parser = class {
3177
3286
  if (this.checkOperator("<")) {
3178
3287
  this.advance();
3179
3288
  if (!this.checkOperator(">")) {
3180
- typeArguments.push(this.parseTypeArgument());
3289
+ typeArguments.push(this.parseType());
3181
3290
  while (this.matchPunctuator(",") && !this.checkOperator(">")) {
3182
- typeArguments.push(this.parseTypeArgument());
3291
+ typeArguments.push(this.parseType());
3183
3292
  }
3184
3293
  }
3185
3294
  this.expectOperator(">");
@@ -3190,13 +3299,12 @@ var Parser = class {
3190
3299
  }
3191
3300
  parseFunctionTypeAfterParen(start, generics) {
3192
3301
  const params = [];
3193
- let hasVarargs = false;
3194
- let varargType;
3302
+ let rested = false;
3195
3303
  if (!this.checkPunctuator(")")) {
3196
3304
  while (true) {
3197
3305
  if (this.checkOperator("...")) {
3198
3306
  const dots = this.advance();
3199
- hasVarargs = true;
3307
+ rested = true;
3200
3308
  if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3201
3309
  const nameTok2 = this.expectIdentifier();
3202
3310
  this.advance();
@@ -3210,16 +3318,21 @@ var Parser = class {
3210
3318
  });
3211
3319
  break;
3212
3320
  }
3213
- 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
+ });
3214
3331
  break;
3215
3332
  }
3216
3333
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
3217
- const packStart = this.current();
3218
- const packRef = this.parseType();
3219
- this.advance();
3220
- hasVarargs = true;
3221
- varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
3222
- break;
3334
+ this.rejectPackReference();
3335
+ continue;
3223
3336
  }
3224
3337
  let name;
3225
3338
  let optional2 = false;
@@ -3249,19 +3362,17 @@ var Parser = class {
3249
3362
  this.expectPunctuator(")");
3250
3363
  if (this.matchPunctuator("=>") || this.mistypedArrow()) {
3251
3364
  const predicate = this.tryParseTypePredicate();
3252
- 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();
3253
3366
  return {
3254
3367
  type: "FunctionTypeNode",
3255
3368
  generics,
3256
3369
  params,
3257
- hasVarargs,
3258
- varargType,
3259
3370
  returnType,
3260
3371
  predicate,
3261
3372
  ...spanFrom(start, this.previous())
3262
3373
  };
3263
3374
  }
3264
- if (params.length === 1 && !params[0].name && !hasVarargs) {
3375
+ if (params.length === 1 && !params[0].name && !rested) {
3265
3376
  return {
3266
3377
  type: "ParenthesizedTypeNode",
3267
3378
  typeAnnotation: params[0].typeAnnotation,
@@ -3271,11 +3382,14 @@ var Parser = class {
3271
3382
  if (params.some((p) => p.name !== void 0)) {
3272
3383
  this.error("Expected '=>' for function type");
3273
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]'");
3274
3390
  return {
3275
- type: "TypePackNode",
3276
- types: params.map((p) => p.typeAnnotation),
3277
- hasVarargs,
3278
- varargType,
3391
+ type: "TupleTypeNode",
3392
+ elements: params.map((p) => p.typeAnnotation),
3279
3393
  ...spanFrom(start, this.previous())
3280
3394
  };
3281
3395
  }
@@ -3413,40 +3527,15 @@ var Parser = class {
3413
3527
  this.expectPunctuator("}");
3414
3528
  return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
3415
3529
  }
3416
- parseTypePack() {
3417
- const start = this.current();
3418
- if (this.matchOperator("...")) {
3419
- const varargType2 = this.parseType();
3420
- return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
3421
- }
3422
- this.expectPunctuator("(");
3423
- const types = [];
3424
- let hasVarargs = false;
3425
- let varargType;
3426
- if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
3427
- while (true) {
3428
- if (this.matchOperator("...")) {
3429
- hasVarargs = true;
3430
- varargType = this.parseType();
3431
- break;
3432
- }
3433
- types.push(this.parseType());
3434
- if (this.matchPunctuator(",")) continue;
3435
- break;
3436
- }
3437
- }
3438
- this.expectPunctuator(")");
3439
- return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
3440
- }
3441
3530
  parseGenericTypeParameterList() {
3442
3531
  const list = [];
3443
3532
  this.expectOperator("<");
3444
3533
  while (true) {
3445
3534
  const isConst = this.matchKeyword("const");
3446
3535
  const nameTok = this.expectIdentifier();
3447
- let isPack = false;
3448
- if (this.matchOperator("...")) {
3449
- 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'");
3450
3539
  }
3451
3540
  let constraint;
3452
3541
  if (this.checkIdentifierValue("extends")) {
@@ -3455,17 +3544,13 @@ var Parser = class {
3455
3544
  }
3456
3545
  let def;
3457
3546
  if (this.matchOperator("=")) {
3458
- if (isPack) {
3459
- def = this.parseTypePack();
3460
- } else {
3461
- def = this.parseType();
3462
- }
3547
+ this.rejectPackReference();
3548
+ def = this.parseType();
3463
3549
  }
3464
3550
  list.push({
3465
3551
  type: "GenericTypeParameter",
3466
3552
  name: nameTok.value,
3467
3553
  id: tokenIdentifier(nameTok),
3468
- isPack,
3469
3554
  isConst: isConst || void 0,
3470
3555
  constraint,
3471
3556
  default: def,
@@ -3556,7 +3641,7 @@ function childScope(parent) {
3556
3641
  var Analyzer = class {
3557
3642
  constructor(options) {
3558
3643
  this.options = options;
3559
- for (const name of options.builtinGlobals ?? []) {
3644
+ for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
3560
3645
  const id = this.getOrCreateGlobalBinding(name);
3561
3646
  this.bindings.get(id).isBuiltin = true;
3562
3647
  }
@@ -3958,14 +4043,14 @@ var Analyzer = class {
3958
4043
  return;
3959
4044
  }
3960
4045
  case "GenericForStatement": {
3961
- for (const it of stmt.iterators) this.visitExpression(it, scope);
4046
+ this.visitExpression(stmt.iterator, scope);
3962
4047
  const bodyScope = childScope(scope);
3963
- 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");
3964
4049
  this.visitBlock(stmt.body, bodyScope);
3965
4050
  return;
3966
4051
  }
3967
4052
  case "ReturnStatement":
3968
- for (const arg of stmt.arguments) this.visitExpression(arg, scope);
4053
+ if (stmt.argument) this.visitExpression(stmt.argument, scope);
3969
4054
  return;
3970
4055
  case "BreakStatement":
3971
4056
  case "ContinueStatement":
@@ -4033,7 +4118,6 @@ var Analyzer = class {
4033
4118
  this.declare(fnScope, param.name, kind, param);
4034
4119
  }
4035
4120
  });
4036
- this.visitType(func.varargTypeAnnotation, fnScope);
4037
4121
  this.visitType(func.returnType, fnScope);
4038
4122
  this.functionDepth++;
4039
4123
  try {
@@ -4061,6 +4145,7 @@ var Analyzer = class {
4061
4145
  this.visitGenerics(node.typeParams, scope);
4062
4146
  if (node.superclass) this.reference(outer, node.superclass);
4063
4147
  for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4148
+ for (const shape of node.implements ?? []) this.visitType(shape, scope);
4064
4149
  for (const member of node.members) {
4065
4150
  switch (member.type) {
4066
4151
  case "ClassField":
@@ -4120,7 +4205,6 @@ var Analyzer = class {
4120
4205
  case "BooleanLiteral":
4121
4206
  case "NumberLiteral":
4122
4207
  case "StringLiteral":
4123
- case "VarargExpression":
4124
4208
  case "ErrorExpression":
4125
4209
  return;
4126
4210
  case "InterpolatedStringExpression":
@@ -4160,11 +4244,6 @@ var Analyzer = class {
4160
4244
  this.visitExpression(expr.callee, scope);
4161
4245
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
4162
4246
  return;
4163
- case "NewExpression":
4164
- this.visitExpression(expr.callee, scope);
4165
- for (const argument of expr.arguments) this.visitExpression(argument, scope);
4166
- for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4167
- return;
4168
4247
  case "SuperExpression":
4169
4248
  return;
4170
4249
  case "SpreadElement":
@@ -4218,6 +4297,7 @@ var Analyzer = class {
4218
4297
  }
4219
4298
  }
4220
4299
  };
4300
+ var LANGUAGE_GLOBALS = ["scriptArgs"];
4221
4301
  function analyzeScopes(program, options = {}) {
4222
4302
  return new Analyzer(options).run(program);
4223
4303
  }
@@ -4236,8 +4316,8 @@ type Exclude<T, U> = T - U
4236
4316
  type Extract<T, U> = T extends U ? T : never
4237
4317
  type NonNullable<T> = T - nil
4238
4318
 
4239
- type ReturnType<T> = T extends (...unknown) => infer R ? R : never
4240
- 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
4241
4321
 
4242
4322
  type Partial<T> = { [K in keyof T]?: T[K] }
4243
4323
  type Required<T> = { [K in keyof T]-?: T[K] }
@@ -4322,8 +4402,11 @@ function literal(value) {
4322
4402
  function arrayOf(element) {
4323
4403
  return { kind: "array", element };
4324
4404
  }
4325
- function tuple(elements, isPack) {
4326
- 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;
4327
4410
  }
4328
4411
  function objectType(entries, indexer, frozen) {
4329
4412
  return { kind: "object", properties: new Map(entries), indexer, frozen };
@@ -4347,7 +4430,7 @@ function substitute(t, subst) {
4347
4430
  case "array":
4348
4431
  return arrayOf(substitute(t.element, subst));
4349
4432
  case "tuple":
4350
- 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));
4351
4434
  case "object": {
4352
4435
  const entries = [];
4353
4436
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
@@ -4371,14 +4454,20 @@ function substitute(t, subst) {
4371
4454
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
4372
4455
  let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
4373
4456
  let varargs = t.varargs && substitute(t.varargs, inner);
4374
- if (varargs?.kind === "tuple" && varargs.isPack) {
4457
+ let restIsWhole = t.restIsWhole;
4458
+ if (restIsWhole && varargs?.kind === "tuple") {
4375
4459
  params = [...params, ...varargs.elements.map((type) => ({ type }))];
4376
- 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;
4377
4465
  }
4378
4466
  return {
4379
4467
  kind: "function",
4380
4468
  params,
4381
4469
  varargs,
4470
+ ...restIsWhole ? { restIsWhole } : {},
4382
4471
  returns: substitute(t.returns, inner),
4383
4472
  typeParams: t.typeParams,
4384
4473
  typeParamDefaults: t.typeParamDefaults,
@@ -4461,11 +4550,19 @@ function unify(param, arg, vars, out) {
4461
4550
  switch (param.kind) {
4462
4551
  case "array":
4463
4552
  if (arg.kind === "array") unify(param.element, arg.element, vars, out);
4464
- 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);
4465
4554
  return;
4466
4555
  case "tuple":
4467
- if (arg.kind === "tuple") param.elements.forEach((p, i) => arg.elements[i] && unify(p, arg.elements[i], vars, out));
4468
- 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
+ }
4469
4566
  return;
4470
4567
  case "function":
4471
4568
  if (arg.kind === "function") {
@@ -4567,7 +4664,7 @@ function widen(t) {
4567
4664
  case "array":
4568
4665
  return arrayOf(widen(t.element));
4569
4666
  case "tuple":
4570
- return tuple(t.elements.map(widen), t.isPack);
4667
+ return tuple(t.elements.map(widen), t.rest && widen(t.rest));
4571
4668
  case "object": {
4572
4669
  if (t.frozen || t.class) return t;
4573
4670
  const entries = [];
@@ -4667,13 +4764,18 @@ function isAssignableInner(a, b) {
4667
4764
  if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
4668
4765
  if (a.kind === "array") {
4669
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);
4670
4768
  return false;
4671
4769
  }
4672
4770
  if (a.kind === "tuple") {
4673
4771
  if (b.kind === "tuple") {
4674
- 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));
4675
4777
  }
4676
- 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));
4677
4779
  return false;
4678
4780
  }
4679
4781
  if (a.kind === "object") {
@@ -4722,7 +4824,7 @@ function sameRefTarget(a, b) {
4722
4824
  return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
4723
4825
  }
4724
4826
  function isNoValue(t) {
4725
- return t.kind === "tuple" && t.elements.length === 0;
4827
+ return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
4726
4828
  }
4727
4829
  function equalTypes(a, b) {
4728
4830
  return formatType(a) === formatType(b);
@@ -4829,7 +4931,7 @@ function containsFreeTypeParam(t, seen, bound) {
4829
4931
  case "array":
4830
4932
  return containsTypeParam(t.element, seen, bound);
4831
4933
  case "tuple":
4832
- return t.elements.some((e) => containsTypeParam(e, seen, bound));
4934
+ return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
4833
4935
  case "union":
4834
4936
  case "intersection":
4835
4937
  return t.types.some((m) => containsTypeParam(m, seen, bound));
@@ -4874,15 +4976,19 @@ function matchInfer(arg, pattern, out) {
4874
4976
  switch (pattern.kind) {
4875
4977
  case "array":
4876
4978
  if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
4877
- 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);
4878
4980
  return false;
4879
4981
  case "tuple":
4880
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;
4881
4984
  return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
4882
4985
  case "function": {
4883
4986
  if (arg.kind !== "function") return false;
4884
4987
  if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
4885
- 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
+ ));
4886
4992
  } else {
4887
4993
  for (let i = 0; i < pattern.params.length; i++) {
4888
4994
  if (!arg.params[i]) return false;
@@ -4956,7 +5062,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
4956
5062
  collectTypeParams(t.element, out, seen);
4957
5063
  return;
4958
5064
  case "tuple":
4959
- for (const e of t.elements) collectTypeParams(e, out, seen);
5065
+ for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
4960
5066
  return;
4961
5067
  case "union":
4962
5068
  case "intersection":
@@ -5003,9 +5109,8 @@ function formatTypeUncached(t) {
5003
5109
  case "array":
5004
5110
  return `${formatAtom(t.element)}[]`;
5005
5111
  case "tuple": {
5006
- if (!t.elements.length) return "()";
5007
- const inner = t.elements.map(formatType).join(", ");
5008
- return t.isPack ? `(${inner})` : `[${inner}]`;
5112
+ const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
5113
+ return `[${inner.join(", ")}]`;
5009
5114
  }
5010
5115
  case "object": {
5011
5116
  if (t.name) {
@@ -5029,7 +5134,7 @@ function formatTypeUncached(t) {
5029
5134
  return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
5030
5135
  }).join(", ")}>` : "";
5031
5136
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
5032
- if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
5137
+ if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
5033
5138
  return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
5034
5139
  }
5035
5140
  case "typeParam":
@@ -5233,6 +5338,11 @@ function moduleExports(program, scopes, types, resolveModule) {
5233
5338
  }
5234
5339
  return { values, types: exportedTypes, default: defaultType };
5235
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
+ }
5236
5346
  function bindKey(id) {
5237
5347
  return `$${id}`;
5238
5348
  }
@@ -5250,9 +5360,6 @@ function mergeEnv(a, b, base) {
5250
5360
  function mergeIndexer(a, b) {
5251
5361
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5252
5362
  }
5253
- function producesMultipleValues(e) {
5254
- return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
5255
- }
5256
5363
  function isFreshLiteralExpr(e) {
5257
5364
  if (!e) return false;
5258
5365
  switch (e.type) {
@@ -5289,11 +5396,11 @@ function collectInferNames(node) {
5289
5396
  walk(n.element);
5290
5397
  return;
5291
5398
  case "ParenthesizedTypeNode":
5292
- case "VariadicTypeNode":
5293
5399
  walk(n.typeAnnotation);
5294
5400
  return;
5295
5401
  case "TupleTypeNode":
5296
5402
  n.elements.forEach(walk);
5403
+ walk(n.rest);
5297
5404
  return;
5298
5405
  case "UnionTypeNode":
5299
5406
  case "IntersectionTypeNode":
@@ -5311,7 +5418,6 @@ function collectInferNames(node) {
5311
5418
  return;
5312
5419
  case "FunctionTypeNode":
5313
5420
  n.params.forEach((pp) => walk(pp.typeAnnotation));
5314
- walk(n.varargType);
5315
5421
  walk(n.returnType);
5316
5422
  return;
5317
5423
  case "TableTypeNode":
@@ -5356,6 +5462,75 @@ function callsSuper(block) {
5356
5462
  });
5357
5463
  return found;
5358
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
+ };
5359
5534
  function assignedFields(block) {
5360
5535
  const names = /* @__PURE__ */ new Set();
5361
5536
  walkNodes(block, (node) => {
@@ -5381,7 +5556,7 @@ function walkNodes(root, visit) {
5381
5556
  if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5382
5557
  }
5383
5558
  }
5384
- var AliasMap = class extends Map {
5559
+ var LazyMap = class extends Map {
5385
5560
  pending = /* @__PURE__ */ new Map();
5386
5561
  defer(name, resolve5) {
5387
5562
  super.delete(name);
@@ -5496,14 +5671,14 @@ var TypeAnalyzer = class {
5496
5671
  scopes;
5497
5672
  options;
5498
5673
  typeOf = /* @__PURE__ */ new Map();
5499
- bindingType = /* @__PURE__ */ new Map();
5674
+ bindingType = new LazyMap();
5500
5675
  narrowedTypeOf = /* @__PURE__ */ new Map();
5501
5676
  typeOfTypeNode = /* @__PURE__ */ new Map();
5502
5677
  expectedTypeOf = /* @__PURE__ */ new Map();
5503
- iteratesValues = /* @__PURE__ */ new Set();
5678
+ loops = /* @__PURE__ */ new Map();
5504
5679
  /** Public: each alias resolved once (generic aliases keep their params as
5505
5680
  * `typeParam` nodes in the body). */
5506
- aliases = new AliasMap();
5681
+ aliases = new LazyMap();
5507
5682
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
5508
5683
  aliasDefs = /* @__PURE__ */ new Map();
5509
5684
  /** Put on every ref to one of `aliasDefs`, so a module that imports the
@@ -5517,8 +5692,12 @@ var TypeAnalyzer = class {
5517
5692
  /** Generic parameters currently in lexical scope (alias body / generic fn),
5518
5693
  * with their `extends` constraints resolved. */
5519
5694
  typeParamScope = [];
5520
- /** Global types contributed by `declare` statements (libs, then this program). */
5521
- 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();
5522
5701
  /** Declaration node -> binding, built once so `bindingIdByName` is O(1)
5523
5702
  * instead of a scan of every binding per declaration site. */
5524
5703
  bindingByDecl = /* @__PURE__ */ new Map();
@@ -5563,8 +5742,11 @@ var TypeAnalyzer = class {
5563
5742
  this.indexDeclarations();
5564
5743
  for (const [name, id] of this.scopes.globalsByName) {
5565
5744
  if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
5566
- const t = this.options.globalTypes?.[name] ?? this.libGlobalTypes.get(name) ?? anyType;
5567
- 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);
5568
5750
  }
5569
5751
  setAliasExpander((t) => this.expand(t));
5570
5752
  setDeferredBound((t) => this.deferredBound(t));
@@ -5584,7 +5766,7 @@ var TypeAnalyzer = class {
5584
5766
  typeOfTypeNode: this.typeOfTypeNode,
5585
5767
  expectedTypeOf: this.expectedTypeOf,
5586
5768
  aliases: this.resolveDeferredAliases(),
5587
- iteratesValues: this.iteratesValues,
5769
+ loops: this.loops,
5588
5770
  diagnostics: this.diagnostics
5589
5771
  };
5590
5772
  }
@@ -5793,13 +5975,18 @@ var TypeAnalyzer = class {
5793
5975
  classIdentityCount = 0;
5794
5976
  /** The class whose members are being read, so `super` knows its base. */
5795
5977
  currentClass;
5978
+ /** Every class whose body the checker is inside, innermost last — what
5979
+ * decides whether a `private` member may be reached. */
5980
+ enclosingClasses = [];
5796
5981
  withClass(stmt, fn2) {
5797
5982
  const previous = this.currentClass;
5798
5983
  this.currentClass = stmt;
5984
+ this.enclosingClasses.push(stmt);
5799
5985
  try {
5800
5986
  return fn2();
5801
5987
  } finally {
5802
5988
  this.currentClass = previous;
5989
+ this.enclosingClasses.pop();
5803
5990
  }
5804
5991
  }
5805
5992
  /** What the class is known by. A declaration is known by its name, the way
@@ -5931,28 +6118,55 @@ var TypeAnalyzer = class {
5931
6118
  type.name = `typeof ${this.className(stmt)}`;
5932
6119
  const shape = this.shapeOf(stmt);
5933
6120
  const local = this.superDecl(stmt);
5934
- 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;
5935
6122
  if (parent?.kind === "object") {
5936
6123
  for (const [key, property] of parent.properties) {
5937
6124
  if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
5938
6125
  }
5939
6126
  }
5940
6127
  for (const [key, property] of shape.statics) type.properties.set(key, property);
5941
- 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
+ }
5942
6133
  const params = this.classTypeParams(stmt);
5943
- type.properties.set("new", {
5944
- type: fn(
5945
- constructor?.params.filter((p) => p.name !== "this") ?? [],
5946
- this.selfTypeOf(stmt),
5947
- constructor?.varargs,
5948
- params.map((p) => p.name)
5949
- ),
6134
+ let settled;
6135
+ const construct = {
5950
6136
  optional: false,
5951
- 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
+ }
5952
6156
  });
6157
+ type.properties.set("new", construct);
5953
6158
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
5954
6159
  return type;
5955
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
+ }
5956
6170
  /** The value side of a class named by a binding rather than by a
5957
6171
  * declaration in this file — an imported one. */
5958
6172
  classStaticsByNameType(name) {
@@ -6013,17 +6227,26 @@ var TypeAnalyzer = class {
6013
6227
  } finally {
6014
6228
  this.emitDiagnostics = wasEmitting;
6015
6229
  shape.filling = false;
6230
+ for (const then of shape.filled ?? []) then();
6231
+ shape.filled = void 0;
6016
6232
  }
6017
6233
  return shape;
6018
6234
  }
6019
6235
  fillShape(stmt, shape) {
6236
+ const className = this.className(stmt);
6020
6237
  const put = (isStatic, name, property) => {
6021
- (isStatic ? shape.statics : shape.instance).set(name, 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;
6240
+ (isStatic ? shape.statics : shape.instance).set(name, marked);
6022
6241
  };
6023
6242
  for (const member of stmt.members) {
6024
6243
  if (member.type !== "ClassField") continue;
6025
6244
  const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
6026
- 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
+ });
6027
6250
  }
6028
6251
  for (const member of stmt.members) {
6029
6252
  switch (member.type) {
@@ -6032,7 +6255,11 @@ var TypeAnalyzer = class {
6032
6255
  case "ClassMethod": {
6033
6256
  this.paramsFromSignatures(member.func, member.signatures);
6034
6257
  const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
6035
- 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
+ });
6036
6263
  break;
6037
6264
  }
6038
6265
  case "ClassAccessor": {
@@ -6088,7 +6315,14 @@ var TypeAnalyzer = class {
6088
6315
  for (const signature of member.signatures ?? []) {
6089
6316
  this.checkParamOrder(signature.params, member);
6090
6317
  }
6091
- 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
+ }
6092
6326
  }
6093
6327
  });
6094
6328
  }));
@@ -6135,13 +6369,148 @@ var TypeAnalyzer = class {
6135
6369
  if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
6136
6370
  report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
6137
6371
  }
6138
- 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();
6139
6377
  for (const member of stmt.members) {
6140
6378
  if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6141
- if (assigned.has(member.name.name)) continue;
6379
+ if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
6142
6380
  const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6143
6381
  if (isAssignable(nilType, type)) continue;
6144
- 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
+ }
6145
6514
  }
6146
6515
  }
6147
6516
  /** `extends` must name a class, and the chain must end. */
@@ -6199,19 +6568,42 @@ var TypeAnalyzer = class {
6199
6568
  harvestDeclares(block, own = false) {
6200
6569
  for (const stmt of block.statements) {
6201
6570
  if (stmt.type !== "DeclareStatement") continue;
6202
- 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))) {
6203
6581
  this.deferredDeclares.set(stmt.name, stmt);
6204
6582
  continue;
6205
6583
  }
6206
- const t = this.resolveType(stmt.valueType);
6207
- const prev = this.libGlobalTypes.get(stmt.name);
6208
- const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6209
- this.libGlobalTypes.set(
6210
- stmt.name,
6211
- overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
6212
- );
6584
+ this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
6585
+ }
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);
6213
6598
  }
6214
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
+ }
6215
6607
  resolveAllAliases() {
6216
6608
  for (const [name, def] of this.aliasDefs) {
6217
6609
  if (def.class && !this.program.body.statements.includes(def.class)) {
@@ -6257,11 +6649,10 @@ var TypeAnalyzer = class {
6257
6649
  }
6258
6650
  imported;
6259
6651
  /** What a `return` gives, against what the function declared. */
6260
- checkReturn(stmt, declared, types, sources, env) {
6652
+ checkReturn(stmt, declared, actual, env) {
6261
6653
  if (!declared || !this.emitDiagnostics) return;
6262
6654
  if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
6263
- const actual = stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true);
6264
- const source = stmt.arguments.length === 1 ? sources[0] : void 0;
6655
+ const source = stmt.argument;
6265
6656
  const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
6266
6657
  if (fits) return;
6267
6658
  this.diagnostics.push({
@@ -6409,21 +6800,12 @@ var TypeAnalyzer = class {
6409
6800
  this.instantiationDepth--;
6410
6801
  }
6411
6802
  }
6412
- /** Pair written type arguments with the parameters they instantiate. A
6413
- * pack parameter (`T...`) takes every argument from its position on, as
6414
- * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
6415
- * and `Signal<()>` to the empty pack. Left out, a parameter takes its
6416
- * 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`. */
6417
6805
  bindTypeArguments(params, args) {
6418
6806
  const subst = /* @__PURE__ */ new Map();
6419
6807
  params.forEach((p, i) => {
6420
- let arg = args[i];
6421
- if (p.isPack && i < args.length) {
6422
- const rest = args.slice(i);
6423
- const single = rest.length === 1 ? rest[0] : void 0;
6424
- arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
6425
- }
6426
- 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));
6427
6809
  });
6428
6810
  return subst;
6429
6811
  }
@@ -6515,8 +6897,13 @@ var TypeAnalyzer = class {
6515
6897
  return literal(node.value);
6516
6898
  case "ArrayTypeNode":
6517
6899
  return arrayOf(this.resolveType(node.element));
6518
- case "TupleTypeNode":
6519
- 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
+ }
6520
6907
  case "UnionTypeNode":
6521
6908
  return union(node.types.map((t) => this.resolveType(t)));
6522
6909
  case "IntersectionTypeNode":
@@ -6552,15 +6939,13 @@ var TypeAnalyzer = class {
6552
6939
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
6553
6940
  optional: p.optional
6554
6941
  }));
6555
- const restParam = node.params.find((p) => p.rest);
6556
- const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
6557
- return this.withTypeParamDefaults(fn(
6942
+ return this.withTypeParamDefaults(this.withRest(fn(
6558
6943
  params,
6559
6944
  this.resolveType(node.returnType),
6560
- restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
6945
+ void 0,
6561
6946
  names,
6562
6947
  this.resolvePredicate(node.predicate, params)
6563
- ), node.generics);
6948
+ ), node.params), node.generics);
6564
6949
  });
6565
6950
  }
6566
6951
  case "TypeofTypeNode": {
@@ -6624,13 +7009,6 @@ var TypeAnalyzer = class {
6624
7009
  })
6625
7010
  );
6626
7011
  }
6627
- case "VariadicTypeNode":
6628
- return this.resolveType(node.typeAnnotation);
6629
- case "TypePackNode": {
6630
- if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
6631
- if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
6632
- return tuple(node.types.map((t) => this.resolveType(t)), true);
6633
- }
6634
7012
  }
6635
7013
  }
6636
7014
  // --------------------------------------------------------
@@ -6696,15 +7074,18 @@ var TypeAnalyzer = class {
6696
7074
  case "array":
6697
7075
  return arrayOf(this.reduceType(t.element));
6698
7076
  case "tuple":
6699
- 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));
6700
7078
  case "function":
6701
- return fn(
6702
- t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
6703
- this.reduceType(t.returns),
6704
- t.varargs && this.reduceType(t.varargs),
6705
- t.typeParams,
6706
- t.predicate
6707
- );
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
+ };
6708
7089
  case "object": {
6709
7090
  if (t.class) return t;
6710
7091
  const entries = [];
@@ -6919,7 +7300,8 @@ var TypeAnalyzer = class {
6919
7300
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
6920
7301
  }
6921
7302
  });
6922
- 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);
6923
7305
  stmt.names.forEach((target, i) => {
6924
7306
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
6925
7307
  const source = sources[i];
@@ -7008,7 +7390,8 @@ var TypeAnalyzer = class {
7008
7390
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7009
7391
  }
7010
7392
  });
7011
- 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);
7012
7395
  stmt.targets.forEach((target, i) => {
7013
7396
  const vt = valueTypes[i] ?? unknownType;
7014
7397
  const source = sources[i];
@@ -7095,23 +7478,26 @@ var TypeAnalyzer = class {
7095
7478
  return;
7096
7479
  }
7097
7480
  case "GenericForStatement": {
7098
- const iterTypes = stmt.iterators.map((it) => this.infer(it, env));
7481
+ const sourceType = this.infer(stmt.iterator, env);
7099
7482
  const bodyEnv = forkEnv(env);
7100
- const rows = stmt.variables.length >= 2 ? this.iterationRows(stmt.iterators[0], iterTypes[0]) : void 0;
7483
+ const rows = this.iterationRows(stmt.iterator, sourceType);
7101
7484
  if (rows) {
7102
- const [key, value] = stmt.variables;
7103
- this.bindPattern(key, union(rows.map((r) => r[0])), bodyEnv, "keep");
7104
- this.bindPattern(value, union(rows.map((r) => r[1])), bodyEnv, "keep");
7105
- stmt.variables.slice(2).forEach((v) => this.bindPattern(v, unknownType, bodyEnv, "widen"));
7106
- const keyId = key.type === "IdentifierPattern" ? this.bindingIdByName(key.name, key) : void 0;
7107
- const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
7108
- 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 });
7109
7497
  } else {
7110
- const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
7111
- stmt.variables.forEach((v, i) => {
7112
- this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
7113
- });
7114
- 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);
7115
7501
  }
7116
7502
  this.visitBlock(stmt.body, bodyEnv);
7117
7503
  return;
@@ -7119,19 +7505,10 @@ var TypeAnalyzer = class {
7119
7505
  case "ReturnStatement": {
7120
7506
  const declared = this.declaredReturns[this.declaredReturns.length - 1];
7121
7507
  const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
7122
- if (wanted) {
7123
- if (stmt.arguments.length === 1) {
7124
- this.applyContext(stmt.arguments[0], wanted);
7125
- } else if (wanted.kind === "tuple" && wanted.isPack) {
7126
- stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
7127
- }
7128
- }
7129
- const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
7130
- const { types, sources } = this.valueList(stmt.arguments, env, want);
7131
- this.checkReturn(stmt, declared, types, sources, env);
7132
- if (this.returnTypes) {
7133
- this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
7134
- }
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);
7135
7512
  return;
7136
7513
  }
7137
7514
  case "ExportStatement":
@@ -7218,7 +7595,7 @@ var TypeAnalyzer = class {
7218
7595
  * `sources` maps each produced value back to the expression it came from
7219
7596
  * (undefined for the 2nd and later values of a multi-value call), so the
7220
7597
  * caller can still do contextual typing against the written expression. */
7221
- valueList(exprs, env, want = 0) {
7598
+ valueList(exprs, env) {
7222
7599
  const types = [];
7223
7600
  const sources = [];
7224
7601
  exprs.forEach((e, i) => {
@@ -7234,31 +7611,29 @@ var TypeAnalyzer = class {
7234
7611
  }
7235
7612
  return;
7236
7613
  }
7237
- do {
7238
- types.push(t);
7239
- sources.push(e);
7240
- } while (last && types.length < want);
7241
- return;
7242
- }
7243
- if (last && e.type === "VarargExpression") {
7244
- do {
7245
- types.push(t);
7246
- sources.push(types.length - 1 === i ? e : void 0);
7247
- } while (types.length < want);
7614
+ types.push(t);
7615
+ sources.push(e);
7248
7616
  return;
7249
7617
  }
7250
- if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
7251
- t.elements.forEach((el, j) => {
7252
- types.push(el);
7253
- sources.push(j === 0 ? e : void 0);
7254
- });
7255
- return;
7256
- }
7257
- types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
7618
+ void last;
7619
+ types.push(t);
7258
7620
  sources.push(e);
7259
7621
  });
7260
7622
  return { types, sources };
7261
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
+ }
7262
7637
  /** Run `visit` with a fresh place to collect `break` states, and return
7263
7638
  * what it collected. */
7264
7639
  withBreakScope(visit) {
@@ -7407,18 +7782,29 @@ var TypeAnalyzer = class {
7407
7782
  * context. */
7408
7783
  contextualArrays = /* @__PURE__ */ new WeakMap();
7409
7784
  applyArrayContext(e, expected) {
7410
- 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];
7411
7791
  if (!target) return;
7792
+ if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
7412
7793
  if (!e.elements.length) {
7413
7794
  this.contextualArrays.set(e, target);
7414
7795
  return;
7415
7796
  }
7416
7797
  e.elements.forEach((element, i) => {
7417
7798
  if (element.type === "SpreadElement") return;
7418
- 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;
7419
7801
  this.applyContext(element, elementType);
7420
7802
  });
7421
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();
7422
7808
  /** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
7423
7809
  * value gets its property's type as context. */
7424
7810
  applyTableContext(e, expected) {
@@ -7474,32 +7860,33 @@ var TypeAnalyzer = class {
7474
7860
  }
7475
7861
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
7476
7862
  }
7477
- /** What a vararg function's `...` holds, one value at a time.
7478
- *
7479
- * Written three ways, and they mean the same call: `...` says nothing,
7480
- * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7481
- * into an array the body reads by name. Only the last changes what the
7482
- * body sees — the signature is the same either way. */
7483
- varargElement(func) {
7484
- if (!func.hasVarargs) return void 0;
7485
- const rest = func.params.find((p) => p.rest);
7486
- if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7487
- const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7488
- return declared?.kind === "array" ? declared.element : unknownType;
7489
- }
7490
- /** The type of `...` in each function body being walked. */
7491
- 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
+ }
7492
7881
  /** What each function body being walked declared it returns. */
7493
7882
  declaredReturns = [];
7494
- /** Run `body` with `...` and `return` as `func` declares them. */
7883
+ /** Run `body` with `return` as `func` declares it. */
7495
7884
  withVarargs(func, body) {
7496
- this.varargs.push(this.varargElement(func));
7497
7885
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
7498
7886
  this.contextualReturns.push(this.contextualReturnOf.get(func));
7499
7887
  try {
7500
7888
  return body();
7501
7889
  } finally {
7502
- this.varargs.pop();
7503
7890
  this.declaredReturns.pop();
7504
7891
  this.contextualReturns.pop();
7505
7892
  }
@@ -7558,7 +7945,7 @@ var TypeAnalyzer = class {
7558
7945
  if (type.kind !== "function") return type;
7559
7946
  const defaults = {};
7560
7947
  for (const generic of generics) {
7561
- if (generic.default && !generic.isPack) defaults[generic.name] = this.resolveType(generic.default);
7948
+ if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
7562
7949
  }
7563
7950
  return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
7564
7951
  }
@@ -7577,7 +7964,10 @@ var TypeAnalyzer = class {
7577
7964
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
7578
7965
  unify(p.type, preserve ? arg : widen(arg), vars, subst);
7579
7966
  });
7580
- 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) {
7581
7971
  const keeps = keepsLiterals(f.varargs);
7582
7972
  for (let i = f.params.length; i < argTypes.length; i++) {
7583
7973
  const arg = argTypes[i];
@@ -7746,7 +8136,12 @@ var TypeAnalyzer = class {
7746
8136
  const arg = argTypes[i];
7747
8137
  const declared = declaredAt(i);
7748
8138
  if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
7749
- 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
+ }
7750
8145
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
7751
8146
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
7752
8147
  this.diagnostics.push({
@@ -7874,13 +8269,13 @@ var TypeAnalyzer = class {
7874
8269
  type: this.paramType(p, /* @__PURE__ */ new Map()),
7875
8270
  optional: p.optional || p.default !== void 0
7876
8271
  }));
7877
- return fn(
8272
+ return this.withRest(fn(
7878
8273
  params,
7879
8274
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
7880
- this.varargElement(sig),
8275
+ void 0,
7881
8276
  names,
7882
8277
  this.resolvePredicate(sig.predicate, params)
7883
- );
8278
+ ), sig.params);
7884
8279
  }));
7885
8280
  }
7886
8281
  /** `...rest: T[]` holds every argument from its position on, so its type
@@ -7888,7 +8283,7 @@ var TypeAnalyzer = class {
7888
8283
  checkRestType(p, node) {
7889
8284
  if (!this.emitDiagnostics || !p.typeAnnotation) return;
7890
8285
  const declared = this.resolveType(p.typeAnnotation);
7891
- 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;
7892
8287
  this.diagnostics.push({
7893
8288
  node,
7894
8289
  message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
@@ -7944,13 +8339,13 @@ var TypeAnalyzer = class {
7944
8339
  return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
7945
8340
  }));
7946
8341
  }
7947
- return fn(
8342
+ return this.withRest(fn(
7948
8343
  params,
7949
8344
  returns,
7950
- this.varargElement(func),
8345
+ void 0,
7951
8346
  names,
7952
8347
  this.resolvePredicate(func.predicate, params)
7953
- );
8348
+ ), func.params);
7954
8349
  });
7955
8350
  }
7956
8351
  /** Where the return types of the function being walked are collected, so
@@ -7992,17 +8387,16 @@ var TypeAnalyzer = class {
7992
8387
  this.preVisitDepth--;
7993
8388
  }
7994
8389
  }
7995
- /** The `[key, value]` pairs iterating a record yields, one per property —
7996
- * for `pairs(t)`, `next, t` and `for k, v in t` over an object type with
7997
- * no indexer. `undefined` for anything else (an array, a dictionary, an
7998
- * 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. */
7999
8393
  iterationRows(iterNode, iterType) {
8000
8394
  let source;
8001
8395
  if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8002
- if (iterNode.callee.name !== "pairs" && iterNode.callee.name !== "next") return void 0;
8396
+ if (iterNode.callee.name !== "pairs") return void 0;
8003
8397
  source = this.typeOf.get(iterNode.arguments[0]);
8004
8398
  } else {
8005
- source = iterType;
8399
+ return void 0;
8006
8400
  }
8007
8401
  const t = source && this.expand(source);
8008
8402
  if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
@@ -8143,54 +8537,69 @@ var TypeAnalyzer = class {
8143
8537
  if (ids.length < 2) return;
8144
8538
  this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
8145
8539
  }
8146
- /** `(keyType, valueType, iteratesValues)` yielded by a generic-for
8147
- * iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
8148
- * iteration (`for … in t`). `varCount` is how many loop variables were
8149
- * written. `iteratesValues` reports the one case where the first variable
8150
- * is bound to the value rather than the key, which is what lowering has
8151
- * to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
8152
- iterationTypes(iterNode, iterType, varCount) {
8153
- if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8154
- const name = iterNode.callee.name;
8155
- const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
8156
- if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
8157
- 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") {
8158
8558
  if (src.kind === "object") {
8159
- return [
8559
+ return [tuple([
8160
8560
  src.indexer?.key ?? stringType,
8161
- src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
8162
- false
8163
- ];
8561
+ src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
8562
+ ]), iteration];
8164
8563
  }
8165
- if (src.kind === "array") return [numberType, src.element, false];
8564
+ if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
8166
8565
  }
8167
8566
  }
8168
- const iterator = this.expand(iterType);
8169
- if (iterator.kind === "function") {
8170
- const returns = this.expand(iterator.returns);
8171
- const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
8172
- const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
8173
- return [at(0), at(1), false];
8174
- }
8175
- const t = this.expand(iterType);
8176
- if (t.kind === "array") {
8177
- return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
8178
- }
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];
8179
8586
  if (t.kind === "object") {
8180
- const k = t.indexer?.key ?? stringType;
8181
- const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
8182
- return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
8587
+ return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
8183
8588
  }
8184
- 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];
8185
8596
  }
8186
8597
  inferReturnType(body, env) {
8187
8598
  const returns = [];
8188
8599
  const walk = (block) => {
8189
8600
  for (const s of block.statements) {
8190
8601
  if (s.type === "ReturnStatement") {
8191
- if (s.arguments.length === 0) returns.push(nilType);
8192
- else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
8193
- else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
8602
+ returns.push(s.argument ? this.infer(s.argument, env) : nilType);
8194
8603
  } else if (s.type === "IfStatement") {
8195
8604
  for (const c of s.clauses) walk(c.body);
8196
8605
  if (s.alternate) walk(s.alternate);
@@ -8212,8 +8621,9 @@ var TypeAnalyzer = class {
8212
8621
  * `infer` would have widened it to an array type). */
8213
8622
  fitsAnnotation(init, declared, inferred, env) {
8214
8623
  if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
8215
- if (init.elements.length !== declared.elements.length) return false;
8216
- 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));
8217
8627
  }
8218
8628
  if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
8219
8629
  if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
@@ -8270,7 +8680,7 @@ var TypeAnalyzer = class {
8270
8680
  target.elements.forEach((el, i) => {
8271
8681
  if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
8272
8682
  });
8273
- 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);
8274
8684
  return;
8275
8685
  }
8276
8686
  }
@@ -8306,7 +8716,7 @@ var TypeAnalyzer = class {
8306
8716
  if (!el) return;
8307
8717
  this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
8308
8718
  });
8309
- 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);
8310
8720
  return;
8311
8721
  }
8312
8722
  }
@@ -8463,7 +8873,7 @@ var TypeAnalyzer = class {
8463
8873
  * not one of them is a mistake worth reporting, rather than the nil Lua
8464
8874
  * would hand back. */
8465
8875
  checkStringMember(node, object, key) {
8466
- if (!this.emitDiagnostics) return;
8876
+ if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
8467
8877
  const parts = this.stringParts(object);
8468
8878
  if (!parts) return;
8469
8879
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
@@ -8475,6 +8885,170 @@ var TypeAnalyzer = class {
8475
8885
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8476
8886
  });
8477
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();
9002
+ /** A `private` member read from outside the class that declared it —
9003
+ * a subclass counts as outside, as in TypeScript. */
9004
+ checkPrivateMember(node, object, name) {
9005
+ if (!this.emitDiagnostics) return;
9006
+ const raw = this.expand(object);
9007
+ for (const part of raw.kind === "union" ? raw.types : [raw]) {
9008
+ const t = this.deferredAccess(this.expand(part));
9009
+ if (t.kind !== "object") continue;
9010
+ const access = t.properties.get(name)?.private;
9011
+ if (!access || this.insideClass(access.owner)) continue;
9012
+ if (access.protected && this.insideSubclassOf(access.owner)) continue;
9013
+ this.diagnostics.push({
9014
+ node,
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}'`
9016
+ });
9017
+ return;
9018
+ }
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
+ }
9042
+ /** Is the code being checked written inside `owner`'s body — its own
9043
+ * methods, or a class or function nested in them? */
9044
+ insideClass(owner) {
9045
+ return this.enclosingClasses.includes(owner);
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
+ }
8478
9052
  propertyType(raw, name) {
8479
9053
  const t = this.deferredAccess(this.expand(raw));
8480
9054
  if (t.kind === "object") {
@@ -8529,8 +9103,12 @@ var TypeAnalyzer = class {
8529
9103
  }
8530
9104
  if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
8531
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
+ }
8532
9110
  }
8533
- return unknownType;
9111
+ return index.kind === "any" ? anyType : unknownType;
8534
9112
  }
8535
9113
  /** What a deferred `T[K]` can be: every property its index could name.
8536
9114
  * Reading a member of one, or calling it, sees that. */
@@ -8540,10 +9118,21 @@ var TypeAnalyzer = class {
8540
9118
  if (containsTypeParam(index)) return unknownType;
8541
9119
  return this.accessType(t.objectType, index);
8542
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
+ }
8543
9132
  elementType(raw, index) {
8544
9133
  const t = this.expand(raw);
8545
9134
  if (t.kind === "array") return t.element;
8546
- if (t.kind === "tuple") return t.elements[index] ?? unknownType;
9135
+ if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
8547
9136
  if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
8548
9137
  if (t.kind === "difference") return this.elementType(t.base, index);
8549
9138
  if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
@@ -8573,8 +9162,6 @@ var TypeAnalyzer = class {
8573
9162
  return stringType;
8574
9163
  }
8575
9164
  // `...` holds what the function declared it takes.
8576
- case "VarargExpression":
8577
- return this.varargs[this.varargs.length - 1] ?? anyType;
8578
9165
  // `f(a, ...rest)` — every value the array holds, one after
8579
9166
  // another. Each of them is an element, so that is what the
8580
9167
  // parameters it fills are checked against.
@@ -8607,10 +9194,8 @@ var TypeAnalyzer = class {
8607
9194
  this.checkParamOrder(expr.func.params, expr);
8608
9195
  this.visitFunctionBody(expr.func, env);
8609
9196
  return this.inferFunctionBody(expr.func, env);
8610
- case "ParenthesizedExpression": {
8611
- const inner = this.infer(expr.expression, env);
8612
- return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
8613
- }
9197
+ case "ParenthesizedExpression":
9198
+ return this.infer(expr.expression, env);
8614
9199
  case "TypeAssertionExpression": {
8615
9200
  this.infer(expr.expression, env);
8616
9201
  return this.resolveType(expr.typeAnnotation);
@@ -8643,7 +9228,7 @@ var TypeAnalyzer = class {
8643
9228
  case "not":
8644
9229
  return booleanType;
8645
9230
  case "-":
8646
- return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
9231
+ return this.operatorResult(expr, "-", arg, void 0) ?? this.arithmeticOn([arg]);
8647
9232
  case "#":
8648
9233
  return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
8649
9234
  }
@@ -8661,7 +9246,11 @@ var TypeAnalyzer = class {
8661
9246
  const left = this.infer(expr.left, env);
8662
9247
  const { whenFalse } = this.narrowFromCondition(expr.left, env);
8663
9248
  const right = this.infer(expr.right, whenFalse);
8664
- 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]);
8665
9254
  }
8666
9255
  const l = this.infer(expr.left, env);
8667
9256
  const r = this.infer(expr.right, env);
@@ -8672,12 +9261,14 @@ var TypeAnalyzer = class {
8672
9261
  switch (op) {
8673
9262
  case "..":
8674
9263
  return this.operatorResult(expr, op, l, r) ?? stringType;
8675
- case "==":
8676
- case "~=":
8677
9264
  case "<":
8678
9265
  case ">":
8679
9266
  case "<=":
8680
9267
  case ">=":
9268
+ this.checkComparison(expr, op, l, r);
9269
+ return booleanType;
9270
+ case "==":
9271
+ case "~=":
8681
9272
  return booleanType;
8682
9273
  case "+":
8683
9274
  case "-":
@@ -8686,15 +9277,18 @@ var TypeAnalyzer = class {
8686
9277
  case "//":
8687
9278
  case "%":
8688
9279
  case "^":
8689
- return this.operatorResult(expr, op, l, r) ?? numberType;
9280
+ return this.operatorResult(expr, op, l, r) ?? this.arithmeticOn([l, r]);
8690
9281
  }
8691
9282
  return union([l, r]);
8692
9283
  }
8693
9284
  case "MemberExpression": {
8694
9285
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
9286
+ this.checkPrivateMember(expr.property, obj, expr.property.name);
9287
+ this.checkAbstractAccess(expr, obj);
8695
9288
  const key = this.refKeyOf(expr);
8696
9289
  const narrowed = key === void 0 ? void 0 : env.get(key);
8697
9290
  this.checkStringMember(expr, obj, literal(expr.property.name));
9291
+ if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
8698
9292
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
8699
9293
  }
8700
9294
  case "IndexExpression": {
@@ -8703,6 +9297,7 @@ var TypeAnalyzer = class {
8703
9297
  const key = this.refKeyOf(expr);
8704
9298
  const narrowed = key === void 0 ? void 0 : env.get(key);
8705
9299
  this.checkStringMember(expr, obj, this.expand(idx));
9300
+ if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
8706
9301
  const member = this.stringMember(obj, this.expand(idx));
8707
9302
  if (member) return this.chainResult(expr, member, shortCircuits);
8708
9303
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
@@ -8712,8 +9307,6 @@ var TypeAnalyzer = class {
8712
9307
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
8713
9308
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
8714
9309
  }
8715
- case "NewExpression":
8716
- return this.inferNew(expr, env);
8717
9310
  case "ClassExpression":
8718
9311
  return this.visitClass(expr, env);
8719
9312
  case "SuperExpression": {
@@ -8750,18 +9343,14 @@ var TypeAnalyzer = class {
8750
9343
  /** `new Name(args)` is `Name.new(args)` — the same function, and the
8751
9344
  * same check. Saying so here rather than rewriting the tree keeps the
8752
9345
  * error messages pointing at what was written. */
8753
- inferNew(expr, env) {
8754
- const calleeType = this.infer(expr.callee, env);
8755
- const constructor = this.propertyType(calleeType, "new");
8756
- if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8757
- if (this.emitDiagnostics) {
8758
- const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8759
- this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8760
- }
8761
- for (const argument of expr.arguments) this.infer(argument, env);
8762
- return anyType;
8763
- }
8764
- 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;
8765
9354
  }
8766
9355
  /** `super(...)` — the base constructor, run on the instance being built. */
8767
9356
  inferSuperCall(expr, env) {
@@ -8785,8 +9374,9 @@ var TypeAnalyzer = class {
8785
9374
  this.inferCall(expr, callable, env);
8786
9375
  return nilType;
8787
9376
  }
8788
- inferCall(expr, callee, env) {
9377
+ inferCall(expr, called, env) {
8789
9378
  this.checkAmbiguousCall(expr);
9379
+ const callee = this.callMetamethod(called) ?? called;
8790
9380
  const united = this.unionSignatures(callee);
8791
9381
  const fns = united ?? this.overloadsOf(callee);
8792
9382
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -8809,8 +9399,31 @@ var TypeAnalyzer = class {
8809
9399
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
8810
9400
  return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
8811
9401
  }
9402
+ this.checkCallable(expr.callee, callee);
8812
9403
  return callee.kind === "any" ? anyType : unknownType;
8813
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();
8814
9427
  /** A `(` on a line of its own continues the statement above it:
8815
9428
  *
8816
9429
  * const value = map[key]
@@ -8827,6 +9440,9 @@ var TypeAnalyzer = class {
8827
9440
  });
8828
9441
  }
8829
9442
  inferMethodCall(expr, objType, env) {
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);
8830
9446
  const method = this.propertyType(objType, expr.method.name);
8831
9447
  const united = this.unionSignatures(method);
8832
9448
  const fns = united ?? this.overloadsOf(method);
@@ -8860,6 +9476,7 @@ var TypeAnalyzer = class {
8860
9476
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
8861
9477
  return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
8862
9478
  }
9479
+ this.checkCallable(expr.method, method);
8863
9480
  return objType.kind === "any" ? anyType : unknownType;
8864
9481
  }
8865
9482
  // --------------------------------------------------------
@@ -8920,6 +9537,8 @@ var TypeAnalyzer = class {
8920
9537
  if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
8921
9538
  }
8922
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;
8923
9542
  this.diagnostics.push({
8924
9543
  node: target,
8925
9544
  message: `Cannot assign to '${name}' because it is a read-only property`
@@ -8979,6 +9598,8 @@ var TypeAnalyzer = class {
8979
9598
  inferArray(expr, env, asConst) {
8980
9599
  const contextual = this.contextualArrays.get(expr);
8981
9600
  if (contextual && !asConst) return contextual;
9601
+ const wantedTuple = this.tupleArrays.get(expr);
9602
+ if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
8982
9603
  const elems = [];
8983
9604
  let hadSpread = false;
8984
9605
  for (const el of expr.elements) {
@@ -8998,6 +9619,28 @@ var TypeAnalyzer = class {
8998
9619
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
8999
9620
  })) : unknownType);
9000
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
+ }
9001
9644
  /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
9002
9645
  * `{ n: number }` — unless the surroundings said a literal belongs there.
9003
9646
  * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
@@ -9083,7 +9726,7 @@ var TypeAnalyzer = class {
9083
9726
  case "tuple": {
9084
9727
  const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
9085
9728
  if (tupleContext?.kind === "tuple") {
9086
- 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);
9087
9730
  }
9088
9731
  const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
9089
9732
  const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
@@ -9340,7 +9983,8 @@ var TypeAnalyzer = class {
9340
9983
  for (const f of candidates) {
9341
9984
  if (!f.predicate) continue;
9342
9985
  const arg = args[f.predicate.param];
9343
- 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;
9344
9988
  if (f.typeParams?.length && f.predicate.type) {
9345
9989
  const subst = this.inferTypeArgs(f, argTypes);
9346
9990
  return {
@@ -9361,6 +10005,10 @@ var TypeAnalyzer = class {
9361
10005
  const found = this.predicateCallTarget(call, env);
9362
10006
  if (!found || !found.predicate.asserts) return;
9363
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
+ }
9364
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) });
9365
10013
  }
9366
10014
  // --------------------------------------------------------
@@ -9385,6 +10033,10 @@ var TypeAnalyzer = class {
9385
10033
  if (base === void 0) return void 0;
9386
10034
  if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
9387
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
+ }
9388
10040
  return void 0;
9389
10041
  }
9390
10042
  default:
@@ -9480,26 +10132,45 @@ var TypeAnalyzer = class {
9480
10132
  * that copied it no longer holds that value: forget the alias. */
9481
10133
  unalias(key) {
9482
10134
  for (const k of [...this.refAliases.keys()]) {
9483
- if (k !== key && !k.startsWith(`${key}.`) && !k.startsWith(`${key}#`)) continue;
10135
+ if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
9484
10136
  for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
9485
10137
  this.refAliases.delete(k);
9486
10138
  }
9487
10139
  }
9488
- /** 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. */
9489
10143
  invalidateBelow(env, key) {
10144
+ const asIndex = `[${key}]`;
9490
10145
  for (const k of [...env.keys()]) {
9491
- if (k.startsWith(`${key}.`) || k.startsWith(`${key}#`)) env.delete(k);
10146
+ if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
9492
10147
  }
9493
10148
  }
9494
10149
  /** An assignment through a reference invalidates it and everything under
9495
10150
  * it, then records the assigned type. */
9496
10151
  assignToRef(expr, value, env) {
10152
+ this.invalidateSiblings(expr, env);
9497
10153
  const key = this.refKeyOf(expr);
9498
10154
  if (key === void 0) return;
9499
10155
  this.invalidateBelow(env, key);
9500
10156
  this.unalias(key);
9501
10157
  env.set(key, value);
9502
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
+ }
9503
10174
  // --------------------------------------------------------
9504
10175
  // Small helpers
9505
10176
  // --------------------------------------------------------
@@ -9514,6 +10185,35 @@ var TypeAnalyzer = class {
9514
10185
  this.selfType = saved;
9515
10186
  }
9516
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
+ }
9517
10217
  /** What an operator on a value with metamethods gives: `a + b` calls
9518
10218
  * `__add` on `a`, or failing that on `b` with the operands swapped — the
9519
10219
  * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
@@ -9838,7 +10538,7 @@ function briefType(t) {
9838
10538
  }
9839
10539
 
9840
10540
  // src/project/host.ts
9841
- import { readFileSync, statSync } from "fs";
10541
+ import { readdirSync, readFileSync, statSync } from "fs";
9842
10542
  var nodeHost = {
9843
10543
  readFile(path) {
9844
10544
  try {
@@ -9846,6 +10546,13 @@ var nodeHost = {
9846
10546
  } catch {
9847
10547
  return void 0;
9848
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
+ }
9849
10556
  }
9850
10557
  };
9851
10558
 
@@ -10037,13 +10744,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
10037
10744
  else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
10038
10745
  continue;
10039
10746
  }
10040
- const name = entry.startsWith("@tilua-types/") ? entry : `@tilua-types/${entry}`;
10041
- 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);
10042
10767
  if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
10043
10768
  else {
10044
10769
  problems.push({
10045
10770
  file: config.path,
10046
- 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]}`,
10047
10772
  ...entryPosition(config, entry)
10048
10773
  });
10049
10774
  }
@@ -10080,6 +10805,32 @@ function findPackage(name, from, host) {
10080
10805
  directory = parent;
10081
10806
  }
10082
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
+ }
10083
10834
  function dependencyNames(directory, host) {
10084
10835
  const manifest = readJson(join2(directory, "package.json"), host);
10085
10836
  const names = /* @__PURE__ */ new Set();
@@ -10178,7 +10929,8 @@ function sourceMapTypes(text, path, options) {
10178
10929
  for (const child of node.children ?? []) {
10179
10930
  if (!isNode(child)) continue;
10180
10931
  const childAlias = visit(child, [...segments, child.name], alias);
10181
- 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;
10182
10934
  named.add(child.name);
10183
10935
  members.push(`${child.name}: ${childAlias}`);
10184
10936
  }
@@ -10238,6 +10990,7 @@ export {
10238
10990
  BinaryOperators,
10239
10991
  CONFIG_FILE_NAMES,
10240
10992
  Keywords,
10993
+ LANGUAGE_GLOBALS,
10241
10994
  LexError,
10242
10995
  Operators,
10243
10996
  PRELUDE_SOURCE,
@@ -10306,6 +11059,7 @@ export {
10306
11059
  tilua,
10307
11060
  tokenize,
10308
11061
  tuple,
11062
+ tupleMembers,
10309
11063
  typeParam,
10310
11064
  unify,
10311
11065
  union,