luaut-parser 4.0.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -80,6 +80,7 @@ __export(index_exports, {
80
80
  resolveModulePath: () => resolveModulePath,
81
81
  resolveTypeLibraries: () => resolveTypeLibraries,
82
82
  setAliasExpander: () => setAliasExpander,
83
+ setDeferredBound: () => setDeferredBound,
83
84
  sourceMapTypes: () => sourceMapTypes,
84
85
  stringType: () => stringType,
85
86
  stripJsonComments: () => stripJsonComments,
@@ -168,6 +169,7 @@ var Punctuators = [
168
169
  ",",
169
170
  ".",
170
171
  "?",
172
+ "=>",
171
173
  "->",
172
174
  "&",
173
175
  "|",
@@ -692,6 +694,13 @@ function applyDirectives(directives, diagnostics, lineOf) {
692
694
  var UNUSED_EXPECT_ERROR = "Unused '@luaut-expect-error' directive";
693
695
 
694
696
  // src/ast/builders.ts
697
+ function bindThis(func, at) {
698
+ bindThisParam(func.params, at);
699
+ func.isMethod = true;
700
+ }
701
+ function bindThisParam(params, at) {
702
+ params.unshift({ type: "FunctionParameter", name: "this", ...spanFrom(at, at) });
703
+ }
695
704
  var ParseError = class extends Error {
696
705
  constructor(message, line, column) {
697
706
  super(`${message} (${line}:${column})`);
@@ -785,10 +794,13 @@ var Parser = class {
785
794
  indentation;
786
795
  /** Populated in recovery mode. */
787
796
  errors = [];
788
- /** Recovery found a block without its `end`. */
797
+ /** Recovery found a block without its `}`. */
789
798
  missingEnd = false;
790
799
  /** The column of the first token on each line, for `indentation`. */
791
800
  lineIndent;
801
+ /** Inside a class body, where `super` means the base class. Outside
802
+ * one it is an ordinary name, so existing code using it still reads. */
803
+ classDepth = 0;
792
804
  constructor(tokens, options = {}) {
793
805
  this.tokens = tokens;
794
806
  this.recover = options.recover ?? false;
@@ -927,8 +939,12 @@ var Parser = class {
927
939
  expressionOr(stop) {
928
940
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
929
941
  }
942
+ /** A comma-separated list of values: a `return`'s, a declaration's, an
943
+ * assignment's. `...xs` spreads an array into it, as in a call's
944
+ * arguments; bare `...` is the vararg pack, as it always was. */
930
945
  expressionListOr(stop) {
931
- const item = () => this.expressionOr(() => stop() || this.checkPunctuator(","));
946
+ const until = () => stop() || this.checkPunctuator(",");
947
+ const item = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
932
948
  const list = [item()];
933
949
  while (this.matchPunctuator(",")) list.push(item());
934
950
  return list;
@@ -1096,7 +1112,62 @@ var Parser = class {
1096
1112
  // Block / Statement
1097
1113
  // ============================================================
1098
1114
  isBlockEnd() {
1099
- return this.isAtEnd() || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
1115
+ return this.isAtEnd() || this.checkPunctuator("}") || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
1116
+ }
1117
+ // ============================================================
1118
+ // Bodies
1119
+ // ------------------------------------------------------------
1120
+ // luaut writes a block in braces — `if (ready) { ... }`, `function f() {
1121
+ // ... }`. The `end` spellings Lua uses are still read, so a file written
1122
+ // in them keeps working while it is being moved over.
1123
+ //
1124
+ // A condition is in parentheses because `f {}` is a call: without them
1125
+ // `if ready { ... }` would be a call of `ready` and then a block. With
1126
+ // them the form is decided by looking past the closing parenthesis, which
1127
+ // is why a parenthesized condition in the older spelling still reads.
1128
+ // ============================================================
1129
+ /** `{ ... }` — a block in braces. */
1130
+ parseBraceBlock() {
1131
+ const brace = this.advance();
1132
+ const body = this.parseBlock(brace);
1133
+ if (this.matchPunctuator("}")) return body;
1134
+ if (!this.recover) this.error(`Expected '}' to close the block opened on line ${brace.line.start}`);
1135
+ this.softError(`Expected '}' to close the block opened on line ${brace.line.start}`);
1136
+ this.missingEnd = true;
1137
+ return body;
1138
+ }
1139
+ /** The `{ ... }` a construct's body is written in. Half-written — the
1140
+ * `{` not typed yet — it is empty and says so, rather than throwing the
1141
+ * whole construct away: what has been written is what an editor answers
1142
+ * from. */
1143
+ parseBracedBody(what) {
1144
+ if (this.checkPunctuator("{")) return this.parseBraceBlock();
1145
+ const at = this.current();
1146
+ if (!this.recover) this.error(`Expected '{' to open the body of '${what}'`);
1147
+ this.softError(`Expected '{' to open the body of '${what}'`);
1148
+ return { type: "Block", statements: [], ...spanFrom(at, at) };
1149
+ }
1150
+ /** Does a `{` follow the parenthesized group starting here? That is what
1151
+ * tells `if (ready) { ... }` from `if (ready) then ... end`. */
1152
+ braceFollowsGroup() {
1153
+ if (!this.checkPunctuator("(")) return false;
1154
+ const closers = { "(": ")", "[": "]", "{": "}" };
1155
+ const stack = [];
1156
+ for (let i = 0; ; i++) {
1157
+ const token = this.peek(i);
1158
+ if (token.type === "EOF") return false;
1159
+ if (token.type === "Punctuator") {
1160
+ const value = String(token.value);
1161
+ if (closers[value]) stack.push(closers[value]);
1162
+ else if (value === stack[stack.length - 1]) {
1163
+ stack.pop();
1164
+ if (!stack.length) {
1165
+ const next = this.peek(i + 1);
1166
+ return next.type === "Punctuator" && next.value === "{";
1167
+ }
1168
+ }
1169
+ }
1170
+ }
1100
1171
  }
1101
1172
  /** `opener` is the token that began the block (`if`, `function`, ...), for
1102
1173
  * indentation recovery. */
@@ -1208,6 +1279,9 @@ var Parser = class {
1208
1279
  if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
1209
1280
  return this.parseTypeAliasStatement();
1210
1281
  }
1282
+ if (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
1283
+ return this.parseClassDeclaration();
1284
+ }
1211
1285
  if (t.type === "Identifier" && t.value === "declare") {
1212
1286
  const p1 = this.peek(1);
1213
1287
  if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
@@ -1233,6 +1307,7 @@ var Parser = class {
1233
1307
  name: p.name || void 0,
1234
1308
  id: p.name ? nameIdentifier(p.name, p) : void 0,
1235
1309
  optional: p.optional,
1310
+ rest: p.rest,
1236
1311
  typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
1237
1312
  line: p.line,
1238
1313
  column: p.column
@@ -1381,7 +1456,7 @@ var Parser = class {
1381
1456
  this.advance();
1382
1457
  if (this.checkIdentifierValue("default")) {
1383
1458
  this.advance();
1384
- const declaration = this.parseExpression(0);
1459
+ const declaration = this.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
1385
1460
  return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
1386
1461
  }
1387
1462
  if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
@@ -1392,6 +1467,10 @@ var Parser = class {
1392
1467
  const declaration = this.parseVariableDeclaration();
1393
1468
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1394
1469
  }
1470
+ if (this.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
1471
+ const declaration = this.parseClassDeclaration();
1472
+ return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1473
+ }
1395
1474
  if (this.checkKeyword("function")) {
1396
1475
  const declaration = this.parseFunctionStatement(true);
1397
1476
  if (declaration.type !== "FunctionDeclaration") this.error("An exported function needs a plain name: 'export function name()'");
@@ -1420,7 +1499,7 @@ var Parser = class {
1420
1499
  const source = this.parseModuleSource();
1421
1500
  return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
1422
1501
  }
1423
- this.error("Expected 'const', 'let', 'function', 'type', 'default', '{' or '*' after 'export'");
1502
+ this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
1424
1503
  }
1425
1504
  // `const x = ...` / `let x, y = ...`.
1426
1505
  // luaut has no `local` — `const` bindings are immutable, `let` mutable.
@@ -1448,63 +1527,63 @@ var Parser = class {
1448
1527
  parseIfStatement() {
1449
1528
  const start = this.current();
1450
1529
  this.expectKeyword("if");
1530
+ return this.parseBracedIf(start);
1531
+ }
1532
+ parseBracedIf(start) {
1451
1533
  const clauses = [];
1452
- const untilThen = () => this.checkKeyword("then");
1453
- const cond = this.expressionOr(untilThen);
1454
- this.expectKeywordSoft("then");
1455
- const body = this.parseBlock(start);
1456
- clauses.push({ type: "IfClause", condition: cond, body, ...spanFrom(cond, this.previous()) });
1457
- while (this.checkKeyword("elseif")) {
1534
+ const clause = () => {
1458
1535
  const clauseStart = this.current();
1459
- this.advance();
1460
- const c = this.expressionOr(untilThen);
1461
- this.expectKeywordSoft("then");
1462
- const b = this.parseBlock(start);
1463
- clauses.push({ type: "IfClause", condition: c, body: b, ...spanFrom(clauseStart, this.previous()) });
1464
- }
1536
+ this.expectPunctuator("(");
1537
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1538
+ this.expectCloser(")");
1539
+ const body = this.parseBracedBody("if");
1540
+ clauses.push({ type: "IfClause", condition, body, ...spanFrom(clauseStart, this.previous()) });
1541
+ };
1542
+ clause();
1465
1543
  let alternate;
1466
- if (this.matchKeyword("else")) {
1467
- alternate = this.parseBlock(start);
1544
+ while (this.checkKeyword("elseif") || this.checkKeyword("else")) {
1545
+ const isElse = this.checkKeyword("else");
1546
+ this.advance();
1547
+ if (!isElse) {
1548
+ clause();
1549
+ continue;
1550
+ }
1551
+ alternate = this.parseBracedBody("else");
1552
+ break;
1468
1553
  }
1469
- this.expectEnd(start);
1470
1554
  return { type: "IfStatement", clauses, alternate, ...spanFrom(start, this.previous()) };
1471
1555
  }
1472
1556
  parseWhileStatement() {
1473
1557
  const start = this.current();
1474
1558
  this.expectKeyword("while");
1475
- const condition = this.expressionOr(() => this.checkKeyword("do"));
1476
- this.expectKeywordSoft("do");
1477
- const body = this.parseBlock(start);
1478
- this.expectEnd(start);
1559
+ this.expectPunctuator("(");
1560
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1561
+ this.expectCloser(")");
1562
+ const body = this.parseBracedBody("while");
1479
1563
  return { type: "WhileStatement", condition, body, ...spanFrom(start, this.previous()) };
1480
1564
  }
1481
1565
  parseRepeatStatement() {
1482
1566
  const start = this.current();
1483
1567
  this.expectKeyword("repeat");
1484
- const body = this.parseBlock(start);
1485
- let condition;
1486
- if (this.checkKeyword("until") || !this.recover) {
1487
- this.expectKeyword("until");
1488
- condition = this.expressionOr(() => false);
1489
- } else {
1490
- this.softError(`Expected 'until' to close 'repeat' on line ${start.line.start}`);
1491
- this.missingEnd = true;
1492
- condition = this.errorExpression(this.current(), this.cursor);
1493
- }
1568
+ const body = this.parseBracedBody("repeat");
1569
+ this.expectKeyword("until");
1570
+ this.expectPunctuator("(");
1571
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1572
+ this.expectCloser(")");
1494
1573
  return { type: "RepeatStatement", body, condition, ...spanFrom(start, this.previous()) };
1495
1574
  }
1496
1575
  parseDoStatement() {
1497
1576
  const start = this.current();
1498
1577
  this.expectKeyword("do");
1499
- const body = this.parseBlock(start);
1500
- this.expectEnd(start);
1578
+ const body = this.parseBracedBody("do");
1501
1579
  return { type: "DoStatement", body, ...spanFrom(start, this.previous()) };
1502
1580
  }
1503
1581
  parseForStatement() {
1504
1582
  const start = this.current();
1505
1583
  this.expectKeyword("for");
1584
+ this.expectPunctuator("(");
1506
1585
  const first = this.parseBindingTarget(true);
1507
- const untilDo = () => this.checkKeyword("do");
1586
+ const untilDo = () => this.checkPunctuator(")");
1508
1587
  if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
1509
1588
  const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1510
1589
  this.expectPunctuator(",");
@@ -1513,9 +1592,7 @@ var Parser = class {
1513
1592
  if (this.matchPunctuator(",")) {
1514
1593
  step = this.expressionOr(untilDo);
1515
1594
  }
1516
- this.expectKeywordSoft("do");
1517
- const body2 = this.parseBlock(start);
1518
- this.expectEnd(start);
1595
+ const body2 = this.parseForBody();
1519
1596
  return {
1520
1597
  type: "NumericForStatement",
1521
1598
  variable: this.identifierPatternToTypedIdentifier(first),
@@ -1532,9 +1609,7 @@ var Parser = class {
1532
1609
  }
1533
1610
  this.expectKeyword("in");
1534
1611
  const iterators = this.expressionListOr(untilDo);
1535
- this.expectKeywordSoft("do");
1536
- const body = this.parseBlock(start);
1537
- this.expectEnd(start);
1612
+ const body = this.parseForBody();
1538
1613
  return {
1539
1614
  type: "GenericForStatement",
1540
1615
  variables,
@@ -1543,6 +1618,10 @@ var Parser = class {
1543
1618
  ...spanFrom(start, this.previous())
1544
1619
  };
1545
1620
  }
1621
+ parseForBody() {
1622
+ this.expectCloser(")");
1623
+ return this.parseBracedBody("for");
1624
+ }
1546
1625
  /** `function name() end` declares `name`; `function a.b() end` and
1547
1626
  * `function T:m() end` define a member. */
1548
1627
  /** `exported` — the `export` before this `function` has been consumed, so
@@ -1609,6 +1688,183 @@ var Parser = class {
1609
1688
  if (!this.recover) throw error;
1610
1689
  this.record(error);
1611
1690
  }
1691
+ /** `class Name extends Base <members> end`.
1692
+ *
1693
+ * The body is a block like every other in luaut, closed by `end` — not a
1694
+ * brace-delimited list. Members are written the way the same thing is
1695
+ * written outside a class: a field like a field (`x: number`), a method
1696
+ * like a function (`function m() ... end`). */
1697
+ parseClassDeclaration() {
1698
+ const start = this.current();
1699
+ this.advance();
1700
+ const name = this.parseIdentifier();
1701
+ const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1702
+ const { superclass, superArguments } = this.parseExtends();
1703
+ const members = this.parseClassBody(start);
1704
+ return {
1705
+ type: "ClassDeclaration",
1706
+ name,
1707
+ typeParams,
1708
+ superclass,
1709
+ superArguments,
1710
+ members,
1711
+ ...spanFrom(start, this.previous())
1712
+ };
1713
+ }
1714
+ /** `class ... end` as a value. It may be named — the name is for the class
1715
+ * itself, not for the scope around it — and takes no type parameters,
1716
+ * since nothing could write the arguments. */
1717
+ parseClassExpression() {
1718
+ const start = this.current();
1719
+ this.advance();
1720
+ const name = this.checkType("Identifier") && !this.checkIdentifierValue("extends") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(") ? this.parseIdentifier() : void 0;
1721
+ if (this.checkOperator("<")) {
1722
+ this.error("A class written as a value takes no type parameters: nothing could write the arguments");
1723
+ }
1724
+ const { superclass, superArguments } = this.parseExtends();
1725
+ const members = this.parseClassBody(start);
1726
+ return { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
1727
+ }
1728
+ /** `extends Base` / `extends Box<number>`. */
1729
+ parseExtends() {
1730
+ if (!this.checkIdentifierValue("extends")) return {};
1731
+ this.advance();
1732
+ const superclass = this.parseIdentifier();
1733
+ let superArguments;
1734
+ if (this.checkOperator("<")) {
1735
+ const written = this.tryTypeArguments();
1736
+ if (written) superArguments = written;
1737
+ }
1738
+ return { superclass, superArguments };
1739
+ }
1740
+ parseClassBody(start) {
1741
+ void start;
1742
+ if (!this.checkPunctuator("{")) this.error("Expected '{' to open the class body");
1743
+ this.advance();
1744
+ const members = [];
1745
+ this.classDepth++;
1746
+ try {
1747
+ while (!this.checkPunctuator("}") && !this.isAtEnd()) {
1748
+ if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
1749
+ const member = this.parseClassMember();
1750
+ if (member) members.push(member);
1751
+ }
1752
+ } finally {
1753
+ this.classDepth--;
1754
+ }
1755
+ this.expectCloser("}");
1756
+ return members;
1757
+ }
1758
+ /** `<A, B>` in a type position that is not a call: the arguments a class
1759
+ * extends its base with. */
1760
+ tryTypeArguments() {
1761
+ const saved = this.cursor;
1762
+ try {
1763
+ this.expectOperator("<");
1764
+ const args = [this.parseType()];
1765
+ while (this.matchPunctuator(",")) args.push(this.parseType());
1766
+ this.expectOperator(">");
1767
+ return args;
1768
+ } catch (error) {
1769
+ if (error instanceof ParseError || error instanceof ParseRecover) {
1770
+ this.cursor = saved;
1771
+ return void 0;
1772
+ }
1773
+ throw error;
1774
+ }
1775
+ }
1776
+ parseClassMember() {
1777
+ const start = this.current();
1778
+ const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1779
+ if (isStatic) this.advance();
1780
+ if (this.checkKeyword("function")) {
1781
+ this.advance();
1782
+ const memberName = this.parseIdentifier();
1783
+ const signatures = [];
1784
+ let written = memberName;
1785
+ while (true) {
1786
+ const head = this.parseFunctionHead();
1787
+ if (this.isClassOverloadContinuation(memberName.name, isStatic)) {
1788
+ if (!isStatic) bindThisParam(head.params, start);
1789
+ signatures.push({ ...this.headToSignature(head), name: written });
1790
+ if (isStatic) this.advance();
1791
+ this.expectKeyword("function");
1792
+ written = this.parseIdentifier();
1793
+ continue;
1794
+ }
1795
+ const func = this.headToBody(head, start);
1796
+ if (!isStatic) bindThis(func, start);
1797
+ return {
1798
+ type: "ClassMethod",
1799
+ name: memberName,
1800
+ isStatic,
1801
+ func,
1802
+ signatures: signatures.length ? signatures : void 0,
1803
+ ...spanFrom(start, this.previous())
1804
+ };
1805
+ }
1806
+ }
1807
+ if (!isStatic && this.checkIdentifierValue("constructor") && this.punctuatorAt(1, "(")) {
1808
+ this.advance();
1809
+ const head = this.parseFunctionHead();
1810
+ if (head.returnType) this.problem("A constructor has no return type; it always builds the instance");
1811
+ const func = this.headToBody(head, start);
1812
+ bindThis(func, start);
1813
+ return { type: "ClassConstructor", func, ...spanFrom(start, this.previous()) };
1814
+ }
1815
+ if ((this.checkIdentifierValue("get") || this.checkIdentifierValue("set")) && this.peek(1).type === "Identifier" && this.punctuatorAt(2, "(")) {
1816
+ const kind = this.advance().value;
1817
+ const memberName = this.parseIdentifier();
1818
+ const head = this.parseFunctionHead();
1819
+ const func = this.headToBody(head, start);
1820
+ if (!isStatic) bindThis(func, start);
1821
+ const written = func.params.length - (isStatic ? 0 : 1);
1822
+ if (kind === "get" && written > 0) {
1823
+ this.problem("A getter takes no parameters");
1824
+ }
1825
+ if (kind === "set" && written !== 1) {
1826
+ this.problem("A setter takes exactly one parameter: the value being assigned");
1827
+ }
1828
+ return { type: "ClassAccessor", kind, name: memberName, isStatic, func, ...spanFrom(start, this.previous()) };
1829
+ }
1830
+ if (this.checkType("Identifier")) {
1831
+ const memberName = this.parseIdentifier();
1832
+ let typeAnnotation;
1833
+ if (this.matchPunctuator(":")) {
1834
+ typeAnnotation = this.typeOr(() => this.checkOperator("="));
1835
+ }
1836
+ let init;
1837
+ if (this.matchOperator("=")) init = this.expressionOr(() => false);
1838
+ if (!typeAnnotation && !init) {
1839
+ this.error("A class field needs a type ('name: T') or a value ('name = v')");
1840
+ }
1841
+ return { type: "ClassField", name: memberName, isStatic, typeAnnotation, init, ...spanFrom(start, this.previous()) };
1842
+ }
1843
+ if (this.recover) {
1844
+ this.softError("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1845
+ this.advance();
1846
+ return void 0;
1847
+ }
1848
+ this.error("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1849
+ }
1850
+ /** Give a class method its `this`: a real first parameter, the way
1851
+ * `function T:m()` gets a real `self`. Every later pass — scopes, types,
1852
+ * arity, lowering — then sees an ordinary parameter and needs to know
1853
+ * nothing about classes. */
1854
+ /** After a bodyless head inside a class body, does another declaration of
1855
+ * the same member follow? Then the head was an overload signature. */
1856
+ isClassOverloadContinuation(name, isStatic) {
1857
+ const offset = isStatic ? 1 : 0;
1858
+ if (isStatic && !this.checkIdentifierValue("static")) return false;
1859
+ const keyword = this.peek(offset);
1860
+ if (!(keyword.type === "Keyword" && keyword.value === "function")) return false;
1861
+ const named = this.peek(offset + 1);
1862
+ return named.type === "Identifier" && named.value === name;
1863
+ }
1864
+ operatorAt(ahead, value) {
1865
+ const token = this.peek(ahead);
1866
+ return token.type === "Operator" && token.value === value;
1867
+ }
1612
1868
  parseFunctionName() {
1613
1869
  const start = this.current();
1614
1870
  const base = this.parseIdentifier();
@@ -1702,7 +1958,7 @@ var Parser = class {
1702
1958
  ...spanFrom(start, this.previous())
1703
1959
  };
1704
1960
  }
1705
- if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
1961
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
1706
1962
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
1707
1963
  }
1708
1964
  this.error("Unexpected expression statement (expected assignment or call)");
@@ -1890,12 +2146,20 @@ var Parser = class {
1890
2146
  if (t.type === "Keyword" && t.value === "if") {
1891
2147
  return this.parseIfElseExpression();
1892
2148
  }
2149
+ if (t.type === "Identifier" && t.value === "class") {
2150
+ return this.parseClassExpression();
2151
+ }
1893
2152
  if (t.type === "Punctuator" && t.value === "{") {
1894
2153
  return this.parseTableExpression();
1895
2154
  }
1896
2155
  if (t.type === "Punctuator" && t.value === "[") {
1897
2156
  return this.parseArrayExpression();
1898
2157
  }
2158
+ if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
2159
+ if (this.checkPunctuator("(") || this.checkOperator("<")) {
2160
+ const arrow = this.tryParse(() => this.parseArrow());
2161
+ if (arrow) return arrow;
2162
+ }
1899
2163
  if (t.type === "Identifier" || t.type === "Punctuator" && t.value === "(") {
1900
2164
  return this.parsePrefixExpression();
1901
2165
  }
@@ -1909,7 +2173,7 @@ var Parser = class {
1909
2173
  } else {
1910
2174
  let expression;
1911
2175
  try {
1912
- expression = shiftSpans(parseExpressionFromSource(p.raw), p.line, p.column);
2176
+ expression = shiftSpans(parseExpressionFromSource(p.raw, this.classDepth > 0), p.line, p.column);
1913
2177
  } catch (e) {
1914
2178
  if (!this.recover || !(e instanceof ParseError || e instanceof LexError)) throw e;
1915
2179
  const at = token;
@@ -1947,7 +2211,12 @@ var Parser = class {
1947
2211
  parsePrefixExpression() {
1948
2212
  const start = this.current();
1949
2213
  let base;
1950
- if (this.checkType("Identifier")) {
2214
+ if (this.startsNew()) {
2215
+ base = this.parseNewExpression();
2216
+ } else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2217
+ this.advance();
2218
+ base = { type: "SuperExpression", ...spanFrom(start, start) };
2219
+ } else if (this.checkType("Identifier")) {
1951
2220
  base = this.parseIdentifier();
1952
2221
  } else if (this.matchPunctuator("(")) {
1953
2222
  const inner = this.parseExpression();
@@ -2044,11 +2313,13 @@ var Parser = class {
2044
2313
  }
2045
2314
  }
2046
2315
  if (this.startsCallArguments()) {
2316
+ const onNewLine = this.current().line.start > base.line.end;
2047
2317
  const args = this.parseCallArguments();
2048
2318
  base = {
2049
2319
  type: "CallExpression",
2050
2320
  callee: base,
2051
2321
  arguments: args,
2322
+ argumentsOnNewLine: onNewLine || void 0,
2052
2323
  ...spanFrom(base, this.previous())
2053
2324
  };
2054
2325
  continue;
@@ -2057,6 +2328,52 @@ var Parser = class {
2057
2328
  }
2058
2329
  return base;
2059
2330
  }
2331
+ /** `new` is a soft keyword: it starts a construction only when a name
2332
+ * follows it, so a function or field called `new` — `Instance.new(x)`,
2333
+ * and `Vec.new(1)` itself — is untouched. */
2334
+ startsNew() {
2335
+ return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2336
+ }
2337
+ /** `super` on its own means the base class, and only `super(...)` and
2338
+ * `super.member` say anything; anything else is a name that happens to
2339
+ * be spelled that way. */
2340
+ startsSuperUse() {
2341
+ return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2342
+ }
2343
+ /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2344
+ * name reached through a module — never an arbitrary expression, so the
2345
+ * arguments are unambiguously the constructor's. */
2346
+ parseNewExpression() {
2347
+ const start = this.current();
2348
+ this.advance();
2349
+ let callee = this.parseIdentifier();
2350
+ while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2351
+ this.advance();
2352
+ const property = this.parseIdentifier();
2353
+ callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2354
+ }
2355
+ const typeArguments = this.tryCallTypeArguments();
2356
+ if (!this.checkPunctuator("(")) {
2357
+ this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2358
+ }
2359
+ const args = this.parseCallArguments();
2360
+ return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2361
+ }
2362
+ /** After `...`, is there something to spread? Nothing following it means
2363
+ * the vararg pack, which is what `f(...)` has always passed on. */
2364
+ startsSpread() {
2365
+ const next = this.peek(1);
2366
+ if (next.type === "Punctuator") {
2367
+ const value = next.value;
2368
+ return value === "(" || value === "{" || value === "[";
2369
+ }
2370
+ return next.type === "Identifier" || next.type === "Literal" || next.type === "InterpolatedString";
2371
+ }
2372
+ parseSpreadArgument(stop) {
2373
+ const dots = this.advance();
2374
+ const argument = this.expressionOr(stop);
2375
+ return { type: "SpreadElement", argument, ...spanFrom(dots, argument) };
2376
+ }
2060
2377
  /** Is the token `ahead` places on the punctuator `value`? */
2061
2378
  punctuatorAt(ahead, value) {
2062
2379
  const token = this.peek(ahead);
@@ -2103,7 +2420,7 @@ var Parser = class {
2103
2420
  while (true) {
2104
2421
  if (this.recover && this.onNewLine() && this.startsTableField() && !this.startsMethodCall(1)) break;
2105
2422
  const before = this.cursor;
2106
- const argument = this.expressionOr(stop);
2423
+ const argument = this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(stop) : this.expressionOr(stop);
2107
2424
  if (argument.type !== "ErrorExpression" || this.cursor > before || list.length) list.push(argument);
2108
2425
  if (this.matchPunctuator(",") && !this.checkPunctuator(")")) continue;
2109
2426
  if (!this.recover || this.checkPunctuator(")")) break;
@@ -2206,8 +2523,12 @@ var Parser = class {
2206
2523
  while (!this.checkPunctuator("]")) {
2207
2524
  if (this.checkOperator("...")) {
2208
2525
  const dots = this.advance();
2209
- const argument = this.expressionOr(stop);
2210
- elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
2526
+ if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2527
+ elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2528
+ } else {
2529
+ const argument = this.expressionOr(stop);
2530
+ elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
2531
+ }
2211
2532
  } else {
2212
2533
  elements.push(this.expressionOr(stop));
2213
2534
  }
@@ -2396,8 +2717,26 @@ var Parser = class {
2396
2717
  if (!this.checkPunctuator(")")) {
2397
2718
  while (true) {
2398
2719
  if (this.checkOperator("...")) {
2399
- this.advance();
2720
+ const dots = this.advance();
2400
2721
  hasVarargs = true;
2722
+ if (this.checkType("Identifier")) {
2723
+ const nameTok = this.expectIdentifier();
2724
+ let typeAnnotation2;
2725
+ if (this.matchPunctuator(":")) {
2726
+ typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2727
+ }
2728
+ params.push({
2729
+ type: "FunctionParameter",
2730
+ name: nameTok.value,
2731
+ typeAnnotation: typeAnnotation2,
2732
+ rest: true,
2733
+ ...spanFrom(dots, this.previous())
2734
+ });
2735
+ if (this.checkPunctuator(",")) {
2736
+ this.problem("A rest parameter is the last one: nothing can follow '...'");
2737
+ }
2738
+ break;
2739
+ }
2401
2740
  if (this.matchPunctuator(":")) {
2402
2741
  varargTypeAnnotation = this.parseTypeOrTypePackReference();
2403
2742
  }
@@ -2489,10 +2828,64 @@ var Parser = class {
2489
2828
  }
2490
2829
  return void 0;
2491
2830
  }
2831
+ /** Run `parse`, and put the parser back where it was if it fails. Used
2832
+ * where two forms start alike and only their end tells them apart: `(a,
2833
+ * b) => a + b` and `(a + b)` both open with a `(`. */
2834
+ tryParse(parse2) {
2835
+ const cursor = this.cursor;
2836
+ const errors = this.errors.length;
2837
+ try {
2838
+ return parse2();
2839
+ } catch (error) {
2840
+ if (!(error instanceof ParseError || error instanceof ParseRecover)) throw error;
2841
+ this.cursor = cursor;
2842
+ this.errors.length = errors;
2843
+ return void 0;
2844
+ }
2845
+ }
2846
+ /** `x => x * 2` — a function, written short. The body is an expression,
2847
+ * which is returned, or a block in braces, as in TypeScript. */
2848
+ parseArrow() {
2849
+ const start = this.current();
2850
+ const head = this.checkType("Identifier") ? (() => {
2851
+ const name = this.expectIdentifier();
2852
+ return {
2853
+ start,
2854
+ generics: [],
2855
+ params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
2856
+ hasVarargs: false,
2857
+ varargTypeAnnotation: void 0,
2858
+ returnType: void 0,
2859
+ predicate: void 0
2860
+ };
2861
+ })() : this.parseFunctionHead();
2862
+ this.expectPunctuator("=>");
2863
+ const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
2864
+ const func = {
2865
+ type: "FunctionBody",
2866
+ generics: head.generics,
2867
+ params: head.params,
2868
+ hasVarargs: head.hasVarargs,
2869
+ varargTypeAnnotation: head.varargTypeAnnotation,
2870
+ returnType: head.returnType,
2871
+ predicate: head.predicate,
2872
+ body,
2873
+ ...spanFrom(start, this.previous())
2874
+ };
2875
+ return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
2876
+ }
2877
+ /** A one-expression body: the value is what the function returns. */
2878
+ returnOf(expression) {
2879
+ const statement = {
2880
+ type: "ReturnStatement",
2881
+ arguments: [expression],
2882
+ ...spanFrom(expression, expression)
2883
+ };
2884
+ return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
2885
+ }
2492
2886
  parseFunctionBody(opener) {
2493
2887
  const head = this.parseFunctionHead();
2494
- const body = this.parseBlock(opener);
2495
- this.expectEnd(opener);
2888
+ const body = this.parseStatementBody(opener);
2496
2889
  return {
2497
2890
  type: "FunctionBody",
2498
2891
  generics: head.generics,
@@ -2517,9 +2910,13 @@ var Parser = class {
2517
2910
  ...spanFrom(head.start, this.previous())
2518
2911
  };
2519
2912
  }
2913
+ /** A function's statements. */
2914
+ parseStatementBody(opener) {
2915
+ void opener;
2916
+ return this.parseBracedBody("function");
2917
+ }
2520
2918
  headToBody(head, opener) {
2521
- const body = this.parseBlock(opener);
2522
- this.expectEnd(opener);
2919
+ const body = this.parseStatementBody(opener);
2523
2920
  return {
2524
2921
  type: "FunctionBody",
2525
2922
  generics: head.generics,
@@ -2743,8 +3140,21 @@ var Parser = class {
2743
3140
  if (!this.checkPunctuator(")")) {
2744
3141
  while (true) {
2745
3142
  if (this.checkOperator("...")) {
2746
- this.advance();
3143
+ const dots = this.advance();
2747
3144
  hasVarargs = true;
3145
+ if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3146
+ const nameTok2 = this.expectIdentifier();
3147
+ this.advance();
3148
+ params.push({
3149
+ type: "FunctionTypeParameter",
3150
+ name: nameTok2.value,
3151
+ id: tokenIdentifier(nameTok2),
3152
+ typeAnnotation: this.parseType(),
3153
+ rest: true,
3154
+ ...spanFrom(dots, this.previous())
3155
+ });
3156
+ break;
3157
+ }
2748
3158
  varargType = this.parseType();
2749
3159
  break;
2750
3160
  }
@@ -2782,7 +3192,7 @@ var Parser = class {
2782
3192
  }
2783
3193
  }
2784
3194
  this.expectPunctuator(")");
2785
- if (this.matchPunctuator("->")) {
3195
+ if (this.matchPunctuator("=>") || this.matchPunctuator("->")) {
2786
3196
  const predicate = this.tryParseTypePredicate();
2787
3197
  const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
2788
3198
  return {
@@ -2925,8 +3335,22 @@ var Parser = class {
2925
3335
  optional: optional2,
2926
3336
  ...spanFrom(propStart, this.previous())
2927
3337
  });
3338
+ } else if (this.checkType("Literal") && this.current().kind === "string" && this.peek(1).type === "Punctuator" && (this.peek(1).value === ":" || this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":")) {
3339
+ const keyTok = this.advance();
3340
+ const name = String(keyTok.value);
3341
+ const optional2 = this.matchPunctuator("?");
3342
+ this.expectPunctuator(":");
3343
+ const valueType = this.parseType();
3344
+ properties.push({
3345
+ type: "TableTypeProperty",
3346
+ name,
3347
+ key: tokenIdentifier(keyTok),
3348
+ valueType,
3349
+ optional: optional2,
3350
+ ...spanFrom(propStart, this.previous())
3351
+ });
2928
3352
  } else {
2929
- this.error("Expected object type property ('name: T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples");
3353
+ this.error(`Expected object type property ('name: T', '"name": T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples`);
2930
3354
  }
2931
3355
  if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
2932
3356
  break;
@@ -3012,9 +3436,10 @@ function parseTypeFromSource(raw) {
3012
3436
  const parser = new Parser(tokenize(raw));
3013
3437
  return parser.parseType();
3014
3438
  }
3015
- function parseExpressionFromSource(raw) {
3439
+ function parseExpressionFromSource(raw, inClass = false) {
3016
3440
  const tokens = tokenize(raw);
3017
3441
  const parser = new Parser(tokens);
3442
+ if (inClass) parser.classDepth = 1;
3018
3443
  const expr = parser.parseExpression();
3019
3444
  return expr;
3020
3445
  }
@@ -3123,6 +3548,11 @@ var Analyzer = class {
3123
3548
  hoistFunctions(block, scope) {
3124
3549
  for (const statement of block.statements) {
3125
3550
  const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
3551
+ if (declaration.type === "ClassDeclaration") {
3552
+ this.declare(scope, declaration.name.name, "local", declaration.name, true, "class");
3553
+ this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
3554
+ continue;
3555
+ }
3126
3556
  if (declaration.type !== "FunctionDeclaration") continue;
3127
3557
  this.declare(scope, declaration.name.name, "local", declaration.name, true, "function");
3128
3558
  this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
@@ -3135,7 +3565,7 @@ var Analyzer = class {
3135
3565
  * hoisted, and this does not apply. */
3136
3566
  checkUseBeforeDefine(identifier, id) {
3137
3567
  const binding = this.bindings.get(id);
3138
- if (binding.declaredBy !== "function" || this.typeQueryDepth > 0) return;
3568
+ if (binding.declaredBy !== "function" && binding.declaredBy !== "class" || this.typeQueryDepth > 0) return;
3139
3569
  const declaration = binding.declarationNode;
3140
3570
  const depth = declaration && this.hoisted.get(declaration);
3141
3571
  if (depth === void 0 || depth !== this.functionDepth) return;
@@ -3390,6 +3820,11 @@ var Analyzer = class {
3390
3820
  this.visitFunctionBody(stmt.func, scope);
3391
3821
  return;
3392
3822
  }
3823
+ case "ClassDeclaration": {
3824
+ if (!this.hoisted.has(stmt.name)) this.declare(scope, stmt.name.name, "local", stmt.name, true, "class");
3825
+ this.visitClassBody(stmt, scope);
3826
+ return;
3827
+ }
3393
3828
  case "FunctionDeclarationStatement": {
3394
3829
  if (stmt.target.path.length === 0 && !stmt.target.method) {
3395
3830
  this.referenceAsAssignmentTarget(scope, stmt.target.base);
@@ -3509,7 +3944,8 @@ var Analyzer = class {
3509
3944
  this.visitStatement(stmt.declaration, scope);
3510
3945
  return;
3511
3946
  case "ExportDefaultStatement":
3512
- this.visitExpression(stmt.declaration, scope);
3947
+ if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, scope);
3948
+ else this.visitExpression(stmt.declaration, scope);
3513
3949
  return;
3514
3950
  case "ExportNamedStatement":
3515
3951
  if (!stmt.source) {
@@ -3557,6 +3993,33 @@ var Analyzer = class {
3557
3993
  }
3558
3994
  /** `<K extends typeof config>` — a constraint is a type like any other,
3559
3995
  * and the `typeof` in it reads a value. */
3996
+ /** A class body. Its type parameters live in a scope of their own, and
3997
+ * every member is written inside it — so a method's annotations see `T`,
3998
+ * and everything else sees what the class declaration sees, itself
3999
+ * included. `this` is not declared here: the parser makes it a real first
4000
+ * parameter, so it arrives with the rest of them. */
4001
+ visitClassBody(node, outer) {
4002
+ const scope = node.typeParams?.length ? childScope(outer) : outer;
4003
+ this.visitGenerics(node.typeParams, scope);
4004
+ if (node.superclass) this.reference(outer, node.superclass);
4005
+ for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4006
+ for (const member of node.members) {
4007
+ switch (member.type) {
4008
+ case "ClassField":
4009
+ this.visitType(member.typeAnnotation, scope);
4010
+ if (member.init) this.visitExpression(member.init, scope);
4011
+ break;
4012
+ case "ClassMethod":
4013
+ for (const signature of member.signatures ?? []) this.visitSignature(signature, scope);
4014
+ this.visitFunctionBody(member.func, scope, member.func.isMethod);
4015
+ break;
4016
+ case "ClassAccessor":
4017
+ case "ClassConstructor":
4018
+ this.visitFunctionBody(member.func, scope, member.func.isMethod);
4019
+ break;
4020
+ }
4021
+ }
4022
+ }
3560
4023
  visitGenerics(generics, scope) {
3561
4024
  for (const generic of generics ?? []) {
3562
4025
  this.visitType(generic.constraint, scope);
@@ -3639,6 +4102,22 @@ var Analyzer = class {
3639
4102
  this.visitExpression(expr.callee, scope);
3640
4103
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
3641
4104
  return;
4105
+ case "NewExpression":
4106
+ this.visitExpression(expr.callee, scope);
4107
+ for (const argument of expr.arguments) this.visitExpression(argument, scope);
4108
+ for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4109
+ return;
4110
+ case "SuperExpression":
4111
+ return;
4112
+ case "SpreadElement":
4113
+ this.visitExpression(expr.argument, scope);
4114
+ return;
4115
+ case "ClassExpression": {
4116
+ const inner = childScope(scope);
4117
+ if (expr.name) this.declare(inner, expr.name.name, "local", expr.name, true, "class");
4118
+ this.visitClassBody(expr, inner);
4119
+ return;
4120
+ }
3642
4121
  case "MethodCallExpression":
3643
4122
  this.visitExpression(expr.object, scope);
3644
4123
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
@@ -3720,6 +4199,14 @@ function preludeProgram() {
3720
4199
  function isClassType(t) {
3721
4200
  return t.kind === "object" && t.class !== void 0;
3722
4201
  }
4202
+ function isClassAssignable(got, want) {
4203
+ if (!got || !got.ancestors.includes(want.name)) return false;
4204
+ const wanted = want.typeArguments?.get(want.name);
4205
+ if (!wanted?.length) return true;
4206
+ const given = got.typeArguments?.get(want.name);
4207
+ if (!given) return true;
4208
+ return wanted.every((w, i) => given[i] !== void 0 && isAssignable(given[i], w));
4209
+ }
3723
4210
  function typeParam(name, constraint, isConst) {
3724
4211
  return { kind: "typeParam", name, constraint, isConst };
3725
4212
  }
@@ -3771,11 +4258,21 @@ function substitute(t, subst) {
3771
4258
  case "object": {
3772
4259
  const entries = [];
3773
4260
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
3774
- return objectType(
4261
+ const out = objectType(
3775
4262
  entries,
3776
4263
  t.indexer && { key: substitute(t.indexer.key, subst), value: substitute(t.indexer.value, subst) },
3777
4264
  t.frozen
3778
4265
  );
4266
+ if (t.name) out.name = t.name;
4267
+ if (t.class) {
4268
+ out.class = {
4269
+ ...t.class,
4270
+ typeArguments: t.class.typeArguments && new Map(
4271
+ [...t.class.typeArguments].map(([name, args]) => [name, args.map((a) => substitute(a, subst))])
4272
+ )
4273
+ };
4274
+ }
4275
+ return out;
3779
4276
  }
3780
4277
  case "function": {
3781
4278
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
@@ -3842,6 +4339,11 @@ function substitute(t, subst) {
3842
4339
  return t;
3843
4340
  }
3844
4341
  }
4342
+ function classArguments(t, name) {
4343
+ if (t.kind === "genericRef") return t.name === name ? t.typeArguments : void 0;
4344
+ if (t.kind === "object") return t.class?.typeArguments?.get(name);
4345
+ return void 0;
4346
+ }
3845
4347
  function unify(param, arg, vars, out) {
3846
4348
  if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
3847
4349
  unify(param.constraint, arg, vars, out);
@@ -3869,7 +4371,12 @@ function unify(param, arg, vars, out) {
3869
4371
  }
3870
4372
  return;
3871
4373
  case "object":
3872
- if (param.class) return;
4374
+ if (param.class) {
4375
+ const wanted = param.class.typeArguments?.get(param.class.name);
4376
+ const given = classArguments(arg, param.class.name);
4377
+ if (wanted && given) wanted.forEach((w, i) => given[i] && unify(w, given[i], vars, out));
4378
+ return;
4379
+ }
3873
4380
  if (arg.kind === "object" && !arg.class) {
3874
4381
  for (const [k, pv] of param.properties) {
3875
4382
  const av = arg.properties.get(k);
@@ -3878,6 +4385,11 @@ function unify(param, arg, vars, out) {
3878
4385
  if (param.indexer && arg.indexer) unify(param.indexer.value, arg.indexer.value, vars, out);
3879
4386
  }
3880
4387
  return;
4388
+ case "genericRef": {
4389
+ const given = classArguments(arg, param.name);
4390
+ if (given) param.typeArguments.forEach((p, i) => given[i] && unify(p, given[i], vars, out));
4391
+ return;
4392
+ }
3881
4393
  case "union":
3882
4394
  for (const m of param.types) unify(m, arg, vars, out);
3883
4395
  return;
@@ -3972,6 +4484,10 @@ var expandAlias;
3972
4484
  function setAliasExpander(fn2) {
3973
4485
  expandAlias = fn2;
3974
4486
  }
4487
+ var deferredBound;
4488
+ function setDeferredBound(fn2) {
4489
+ deferredBound = fn2;
4490
+ }
3975
4491
  var comparing = [];
3976
4492
  function isAssignable(rawA, rawB) {
3977
4493
  let a = isNoValue(rawA) ? nilType : rawA;
@@ -4002,10 +4518,29 @@ function isAssignableInner(a, b) {
4002
4518
  if (b.kind === "unknown") return true;
4003
4519
  if (b.kind === "never") return false;
4004
4520
  if (a.kind === "unknown") return false;
4521
+ if (a.kind === "difference" && b.kind === "difference") {
4522
+ return isAssignable(a.base, b.base) && isAssignable(b.excluded, a.excluded);
4523
+ }
4005
4524
  if (b.kind === "difference") {
4006
4525
  return isAssignable(a, b.base) && !overlaps(a, b.excluded);
4007
4526
  }
4008
- if (a.kind === "difference") return isAssignable(a.base, b);
4527
+ if (a.kind === "difference") {
4528
+ if (b.kind === "union" && b.types.some((m) => isAssignable(a, m))) return true;
4529
+ return isAssignable(a.base, b);
4530
+ }
4531
+ if (a.kind === "conditional" || a.kind === "indexedAccess") {
4532
+ if ((b.kind === "conditional" || b.kind === "indexedAccess" || b.kind === "union") && equalTypes(a, b)) {
4533
+ return true;
4534
+ }
4535
+ if (b.kind === "union" && b.types.some((m) => equalTypes(a, m))) return true;
4536
+ const bound = deferredBound?.(a);
4537
+ if (bound && bound !== a) return isAssignable(bound, b);
4538
+ }
4539
+ if (a.kind === "typeParam") {
4540
+ if (b.kind === "typeParam" && a.name === b.name) return true;
4541
+ if (b.kind === "union" && b.types.some((m) => m.kind === "typeParam" && m.name === a.name)) return true;
4542
+ return a.constraint ? isAssignable(a.constraint, b) : false;
4543
+ }
4009
4544
  if (a.kind === "union") return a.types.every((t) => isAssignable(t, b));
4010
4545
  if (b.kind === "union") return b.types.some((t) => isAssignable(a, t));
4011
4546
  if (b.kind === "intersection") return b.types.every((t) => isAssignable(a, t));
@@ -4039,7 +4574,7 @@ function isAssignableInner(a, b) {
4039
4574
  }
4040
4575
  if (a.kind === "object") {
4041
4576
  if (b.kind !== "object") return false;
4042
- if (b.class) return a.class !== void 0 && a.class.ancestors.includes(b.class.name);
4577
+ if (b.class) return isClassAssignable(a.class, b.class);
4043
4578
  if (a.class && (b.indexer || b.properties.size === 0)) return false;
4044
4579
  for (const [name, bp] of b.properties) {
4045
4580
  const ap = a.properties.get(name);
@@ -4048,7 +4583,7 @@ function isAssignableInner(a, b) {
4048
4583
  if (a.indexer && isAssignable(a.indexer.value, bp.type)) continue;
4049
4584
  return false;
4050
4585
  }
4051
- if (!isAssignable(ap.type, bp.type)) return false;
4586
+ if (!isAssignable(ap.type, bp.optional ? optional(bp.type) : bp.type)) return false;
4052
4587
  }
4053
4588
  if (b.indexer) {
4054
4589
  for (const [name, ap] of a.properties) {
@@ -4069,10 +4604,6 @@ function isAssignableInner(a, b) {
4069
4604
  }
4070
4605
  return isAssignable(a.returns, b.returns);
4071
4606
  }
4072
- if (a.kind === "typeParam") {
4073
- if (b.kind === "typeParam" && a.name === b.name) return true;
4074
- return a.constraint ? isAssignable(a.constraint, b) : false;
4075
- }
4076
4607
  if (b.kind === "typeParam") return false;
4077
4608
  if (a.kind === "genericRef" || b.kind === "genericRef") {
4078
4609
  return a.kind === "genericRef" && b.kind === "genericRef" && a.name === b.name && a.typeArguments.length === b.typeArguments.length && a.typeArguments.every((x, i) => equalTypes(x, b.typeArguments[i]));
@@ -4192,7 +4723,9 @@ function containsFreeTypeParam(t, seen, bound) {
4192
4723
  case "intersection":
4193
4724
  return t.types.some((m) => containsTypeParam(m, seen, bound));
4194
4725
  case "object":
4195
- if (t.class) return false;
4726
+ if (t.class) {
4727
+ return [...t.class.typeArguments?.values() ?? []].some((args) => args.some((a) => containsTypeParam(a, seen, bound)));
4728
+ }
4196
4729
  return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
4197
4730
  case "function": {
4198
4731
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
@@ -4362,7 +4895,10 @@ function formatTypeUncached(t) {
4362
4895
  return t.isPack ? `(${inner})` : `[${inner}]`;
4363
4896
  }
4364
4897
  case "object": {
4365
- if (t.name) return t.name;
4898
+ if (t.name) {
4899
+ const args = t.class?.typeArguments?.get(t.class.name);
4900
+ return args?.length ? `${t.name}<${args.map(formatType).join(", ")}>` : t.name;
4901
+ }
4366
4902
  const props = [...t.properties.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${v.readonly ? "readonly " : ""}${formatKey(k)}${v.optional ? "?" : ""}: ${formatType(v.type)}`);
4367
4903
  if (t.indexer) props.push(`[${formatType(t.indexer.key)}]: ${formatType(t.indexer.value)}`);
4368
4904
  return props.length ? `{ ${props.join(", ")} }` : "{}";
@@ -4381,7 +4917,7 @@ function formatTypeUncached(t) {
4381
4917
  }).join(", ")}>` : "";
4382
4918
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
4383
4919
  if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
4384
- return `${gen}(${ps.join(", ")}) -> ${formatPredicate(t) ?? formatType(t.returns)}`;
4920
+ return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
4385
4921
  }
4386
4922
  case "typeParam":
4387
4923
  return t.name;
@@ -4425,7 +4961,7 @@ function formatPredicate(t) {
4425
4961
  }
4426
4962
  function formatAtom(t) {
4427
4963
  if (t.kind === "intersection" && t.name) return t.name;
4428
- if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference") {
4964
+ if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference" || t.kind === "conditional") {
4429
4965
  return `(${formatType(t)})`;
4430
4966
  }
4431
4967
  return formatType(t);
@@ -4529,13 +5065,33 @@ function moduleExports(program, scopes, types, resolveModule) {
4529
5065
  if (stmt.type === "ExportStatement") {
4530
5066
  const declaration = stmt.declaration;
4531
5067
  if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
4532
- else for (const target of declaration.names) exportPattern(target);
5068
+ else if (declaration.type === "ClassDeclaration") {
5069
+ exportName(declaration.name, declaration.name.name);
5070
+ const instance = types.aliases.get(declaration.name.name);
5071
+ if (instance) {
5072
+ exportedTypes.set(declaration.name.name, {
5073
+ type: instance,
5074
+ params: declaration.typeParams.map((g) => g.name)
5075
+ });
5076
+ }
5077
+ } else for (const target of declaration.names) exportPattern(target);
4533
5078
  } else if (stmt.type === "ExportTypeAliasStatement") {
4534
5079
  const name = stmt.alias.name.name;
4535
5080
  const type = types.aliases.get(name);
4536
5081
  if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
4537
5082
  } else if (stmt.type === "ExportDefaultStatement") {
4538
- defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
5083
+ if (stmt.declaration.type === "ClassDeclaration") {
5084
+ const id = byDeclaration.get(stmt.declaration.name);
5085
+ defaultType = (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType;
5086
+ const instance = types.aliases.get(stmt.declaration.name.name);
5087
+ if (instance) {
5088
+ const exported = { type: instance, params: stmt.declaration.typeParams.map((g) => g.name) };
5089
+ exportedTypes.set(stmt.declaration.name.name, exported);
5090
+ exportedTypes.set("default", exported);
5091
+ }
5092
+ } else {
5093
+ defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
5094
+ }
4539
5095
  } else if (stmt.type === "ExportNamedStatement") {
4540
5096
  if (stmt.source) {
4541
5097
  const from = resolveModule?.(stmt.source.value);
@@ -4663,6 +5219,51 @@ function keepsLiterals(paramType) {
4663
5219
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
4664
5220
  return members.some((m) => m.kind === "literal");
4665
5221
  }
5222
+ var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass"]);
5223
+ var CLASS_RESERVED = /* @__PURE__ */ new Set([
5224
+ "new",
5225
+ "ClassObject",
5226
+ "ParentClass",
5227
+ "__init",
5228
+ "__index",
5229
+ "__newindex",
5230
+ "__getters",
5231
+ "__setters",
5232
+ "__dynamic"
5233
+ ]);
5234
+ function callsSuper(block) {
5235
+ let found = false;
5236
+ walkNodes(block, (node) => {
5237
+ const record = node;
5238
+ if (record.type === "CallExpression" && record.callee?.type === "SuperExpression") found = true;
5239
+ });
5240
+ return found;
5241
+ }
5242
+ function assignedFields(block) {
5243
+ const names = /* @__PURE__ */ new Set();
5244
+ walkNodes(block, (node) => {
5245
+ const record = node;
5246
+ const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
5247
+ for (const target of targets ?? []) {
5248
+ const member = target;
5249
+ if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5250
+ names.add(member.property.name);
5251
+ }
5252
+ }
5253
+ });
5254
+ return names;
5255
+ }
5256
+ function walkNodes(root, visit) {
5257
+ if (!root || typeof root !== "object") return;
5258
+ if (Array.isArray(root)) {
5259
+ for (const item of root) walkNodes(item, visit);
5260
+ return;
5261
+ }
5262
+ visit(root);
5263
+ for (const [key, value] of Object.entries(root)) {
5264
+ if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5265
+ }
5266
+ }
4666
5267
  var AliasMap = class extends Map {
4667
5268
  pending = /* @__PURE__ */ new Map();
4668
5269
  defer(name, resolve5) {
@@ -4786,6 +5387,8 @@ var TypeAnalyzer = class {
4786
5387
  aliasDefs = /* @__PURE__ */ new Map();
4787
5388
  /** See `resolveClass`. */
4788
5389
  classTypes = /* @__PURE__ */ new WeakMap();
5390
+ /** See `instanceType` — one instance type per `class ... end`. */
5391
+ instanceTypes = /* @__PURE__ */ new WeakMap();
4789
5392
  classMembers = /* @__PURE__ */ new WeakMap();
4790
5393
  /** Generic parameters currently in lexical scope (alias body / generic fn),
4791
5394
  * with their `extends` constraints resolved. */
@@ -4827,6 +5430,7 @@ var TypeAnalyzer = class {
4827
5430
  this.registerAliasDefs(preludeProgram().body, true);
4828
5431
  for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
4829
5432
  this.registerAliasDefs(this.program.body);
5433
+ this.registerNestedClasses();
4830
5434
  for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
4831
5435
  this.registerImportedTypes();
4832
5436
  this.resolveAllAliases();
@@ -4838,6 +5442,7 @@ var TypeAnalyzer = class {
4838
5442
  this.bindingType.set(id, t);
4839
5443
  }
4840
5444
  setAliasExpander((t) => this.expand(t));
5445
+ setDeferredBound((t) => this.deferredBound(t));
4841
5446
  try {
4842
5447
  const env = /* @__PURE__ */ new Map();
4843
5448
  this.visitBlock(this.program.body, env);
@@ -4845,6 +5450,7 @@ var TypeAnalyzer = class {
4845
5450
  if (this.options.reportUnknownTypes) this.reportUnknownTypes();
4846
5451
  } finally {
4847
5452
  setAliasExpander(void 0);
5453
+ setDeferredBound(void 0);
4848
5454
  }
4849
5455
  return {
4850
5456
  typeOf: this.typeOf,
@@ -4894,6 +5500,11 @@ var TypeAnalyzer = class {
4894
5500
  this.aliases.set(qualified, exported.type);
4895
5501
  }
4896
5502
  }
5503
+ const asDefault = stmt.defaultImport && exports2.types.get("default");
5504
+ if (stmt.defaultImport && asDefault) {
5505
+ this.importedTypes.set(stmt.defaultImport.name, asDefault);
5506
+ this.aliases.set(stmt.defaultImport.name, asDefault.type);
5507
+ }
4897
5508
  for (const s of stmt.specifiers) {
4898
5509
  const exported = exports2.types.get(s.imported.name);
4899
5510
  if (exported) {
@@ -4928,10 +5539,40 @@ var TypeAnalyzer = class {
4928
5539
  if (stmt.type === "DeclareClassStatement") {
4929
5540
  this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
4930
5541
  }
5542
+ const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
5543
+ if (declaration.type === "ClassDeclaration") this.registerClass(declaration);
4931
5544
  }
4932
5545
  }
5546
+ /** A class written inside a function or a block names a type too — its
5547
+ * own instances', which its methods' `this` is annotated with. Type names
5548
+ * are one namespace here, so it is registered with the rest; only a
5549
+ * second class of the same name would notice. */
5550
+ registerNestedClasses() {
5551
+ walkNodes(this.program.body, (node) => {
5552
+ const record = node;
5553
+ if (record.type !== "ClassDeclaration") return;
5554
+ const declaration = node;
5555
+ if (this.aliasDefs.has(declaration.name.name)) return;
5556
+ this.registerClass(declaration);
5557
+ });
5558
+ }
5559
+ /** A class's name as a type. `node` is a placeholder: `resolveDef` and
5560
+ * `instantiateAlias` both go to the declaration itself. */
5561
+ registerClass(declaration) {
5562
+ this.aliasDefs.set(declaration.name.name, {
5563
+ params: declaration.typeParams,
5564
+ node: {
5565
+ type: "TableTypeNode",
5566
+ properties: [],
5567
+ line: declaration.line,
5568
+ column: declaration.column
5569
+ },
5570
+ runtimeClass: declaration
5571
+ });
5572
+ }
4933
5573
  /** A non-generic definition's type. */
4934
5574
  resolveDef(def) {
5575
+ if (def.runtimeClass) return this.instanceType(def.runtimeClass);
4935
5576
  return def.class ? this.classType(def.class) : this.resolveType(def.node);
4936
5577
  }
4937
5578
  /** One type per class declaration, so every mention of a class is the same
@@ -4989,6 +5630,381 @@ var TypeAnalyzer = class {
4989
5630
  if (this.program.body.statements.includes(stmt)) ownMembers();
4990
5631
  return type;
4991
5632
  }
5633
+ // ============================================================
5634
+ // `class ... end` — the runtime kind
5635
+ // ------------------------------------------------------------
5636
+ // A declaration says two things at once. Its *name as a type* is
5637
+ // the type of its instances, nominal the way `declare class` is:
5638
+ // only the class and the classes extending it produce one. Its
5639
+ // *name as a value* is the class table — the statics, the class
5640
+ // it extends (`ParentClass`), and the `new` that builds an
5641
+ // instance, which is an ordinary function and can be called as
5642
+ // one. An instance reaches its own class back through
5643
+ // `ClassObject`.
5644
+ //
5645
+ // Members are resolved into one shape, filled in two passes: the
5646
+ // fields first, then the functions. That order is what lets a
5647
+ // method body read `this.x` while the class it belongs to is
5648
+ // still being worked out.
5649
+ // ============================================================
5650
+ classShapes = /* @__PURE__ */ new WeakMap();
5651
+ classValues = /* @__PURE__ */ new WeakMap();
5652
+ /** Identity for a class written as a value, which has no name to be known
5653
+ * by: two of them are different types however alike they look. */
5654
+ classIdentities = /* @__PURE__ */ new WeakMap();
5655
+ classIdentityCount = 0;
5656
+ /** The class whose members are being read, so `super` knows its base. */
5657
+ currentClass;
5658
+ withClass(stmt, fn2) {
5659
+ const previous = this.currentClass;
5660
+ this.currentClass = stmt;
5661
+ try {
5662
+ return fn2();
5663
+ } finally {
5664
+ this.currentClass = previous;
5665
+ }
5666
+ }
5667
+ /** What the class is known by. A declaration is known by its name, the way
5668
+ * a `declare class` is — that is what makes it the same class across
5669
+ * modules. A class written as a value is known by where it is written. */
5670
+ classIdentity(stmt) {
5671
+ if (stmt.type === "ClassDeclaration") return stmt.name.name;
5672
+ let identity = this.classIdentities.get(stmt);
5673
+ if (!identity) {
5674
+ identity = `${stmt.name?.name ?? "class"}@${++this.classIdentityCount}`;
5675
+ this.classIdentities.set(stmt, identity);
5676
+ }
5677
+ return identity;
5678
+ }
5679
+ /** What it is *shown* as. A class written as a value has no name of its
5680
+ * own, so it borrows the one it is being bound to — `const Counter =
5681
+ * class ... end` reads as `Counter` everywhere. */
5682
+ className(stmt) {
5683
+ return stmt.name?.name ?? this.classDisplayNames.get(stmt) ?? "(class)";
5684
+ }
5685
+ classDisplayNames = /* @__PURE__ */ new WeakMap();
5686
+ /** `const Name = class ... end` — the name the class will be known by. */
5687
+ nameClassExpressions(stmt) {
5688
+ stmt.names.forEach((target, i) => {
5689
+ const value = stmt.init[i];
5690
+ if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
5691
+ this.classDisplayNames.set(value, target.name);
5692
+ }
5693
+ });
5694
+ }
5695
+ classTypeParams(stmt) {
5696
+ return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
5697
+ }
5698
+ /** The class a declaration extends, when it is one written in this file.
5699
+ * An imported class is reached through its type and its value instead. */
5700
+ superDecl(stmt) {
5701
+ if (!stmt.superclass) return void 0;
5702
+ const base = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
5703
+ return base && base !== stmt && !this.extendsThrough(base, stmt) ? base : void 0;
5704
+ }
5705
+ /** Does `from` reach `target` by `extends`? Guards against a cycle turning
5706
+ * resolution into a loop. */
5707
+ extendsThrough(from, target) {
5708
+ const seen = /* @__PURE__ */ new Set();
5709
+ for (let cls = from; cls && !seen.has(cls); ) {
5710
+ if (cls === target) return true;
5711
+ seen.add(cls);
5712
+ cls = cls.superclass ? this.aliasDefs.get(cls.superclass.name)?.runtimeClass : void 0;
5713
+ }
5714
+ return false;
5715
+ }
5716
+ /** The instance type of what `stmt` extends, with the arguments it was
5717
+ * extended with filled in — a class in this file, or any class type a
5718
+ * name in scope stands for (an imported one). */
5719
+ baseInstance(stmt) {
5720
+ if (!stmt.superclass) return void 0;
5721
+ const written = (stmt.superArguments ?? []).map((argument) => this.resolveType(argument));
5722
+ if (this.aliasDefs.get(stmt.superclass.name)?.runtimeClass) {
5723
+ const local = this.superDecl(stmt);
5724
+ if (!local) return void 0;
5725
+ const base = this.instanceType(local);
5726
+ const params = this.classTypeParams(local);
5727
+ if (!params.length) return base;
5728
+ const applied = substitute(base, this.bindTypeArguments(params, written));
5729
+ return applied.kind === "object" ? applied : void 0;
5730
+ }
5731
+ const imported = this.importedTypes.get(stmt.superclass.name);
5732
+ const named = imported ? this.importedType(imported, stmt.superArguments ?? []) : this.aliases.get(stmt.superclass.name);
5733
+ return named && isClassType(named) ? named : void 0;
5734
+ }
5735
+ /** One instance type per declaration, so every mention of the class is the
5736
+ * same object — the `this` of its own methods included. Members are read
5737
+ * lazily for the reason `declare class` reads them lazily: a class can
5738
+ * name itself, and two classes can name each other. */
5739
+ instanceType(stmt) {
5740
+ const cached = this.instanceTypes.get(stmt);
5741
+ if (cached) return cached;
5742
+ const name = this.className(stmt);
5743
+ const identity = this.classIdentity(stmt);
5744
+ const params = this.classTypeParams(stmt);
5745
+ const type = { kind: "object", name };
5746
+ this.instanceTypes.set(stmt, type);
5747
+ const own = params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0));
5748
+ const info = () => {
5749
+ const base = this.baseInstance(stmt)?.class;
5750
+ const typeArguments = new Map(base?.typeArguments ?? []);
5751
+ if (own.length) typeArguments.set(identity, own);
5752
+ return {
5753
+ name: identity,
5754
+ superclass: base?.name,
5755
+ ancestors: [identity, ...base?.ancestors ?? []],
5756
+ typeArguments: typeArguments.size ? typeArguments : void 0
5757
+ };
5758
+ };
5759
+ Object.defineProperties(type, {
5760
+ properties: {
5761
+ enumerable: true,
5762
+ get: () => {
5763
+ const shape = this.shapeOf(stmt);
5764
+ const base = this.baseInstance(stmt)?.properties;
5765
+ const members = base?.size ? new Map([...base, ...shape.instance]) : new Map(shape.instance);
5766
+ members.set("ClassObject", { type: this.classValueType(stmt), optional: false, readonly: true });
5767
+ return members;
5768
+ }
5769
+ },
5770
+ class: { enumerable: true, get: info }
5771
+ });
5772
+ return type;
5773
+ }
5774
+ /** `Box<T>` as its own methods see it — a reference, not the object, so
5775
+ * substituting the arguments in does not have to walk the class. */
5776
+ selfTypeOf(stmt) {
5777
+ const params = this.classTypeParams(stmt);
5778
+ if (!params.length || stmt.type !== "ClassDeclaration") return this.instanceType(stmt);
5779
+ return {
5780
+ kind: "genericRef",
5781
+ name: stmt.name.name,
5782
+ typeArguments: params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0))
5783
+ };
5784
+ }
5785
+ /** The class table: the statics, what it inherits from the class it
5786
+ * extends, `ParentClass`, `ClassObject`, and `new`. */
5787
+ classValueType(stmt) {
5788
+ const cached = this.classValues.get(stmt);
5789
+ if (cached) return cached;
5790
+ const type = objectType([]);
5791
+ this.classValues.set(stmt, type);
5792
+ type.name = `typeof ${this.className(stmt)}`;
5793
+ const shape = this.shapeOf(stmt);
5794
+ const local = this.superDecl(stmt);
5795
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
5796
+ if (parent?.kind === "object") {
5797
+ for (const [key, property] of parent.properties) {
5798
+ if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
5799
+ }
5800
+ }
5801
+ for (const [key, property] of shape.statics) type.properties.set(key, property);
5802
+ const constructor = this.constructorType(stmt);
5803
+ const params = this.classTypeParams(stmt);
5804
+ type.properties.set("new", {
5805
+ type: fn(
5806
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
5807
+ this.selfTypeOf(stmt),
5808
+ constructor?.varargs,
5809
+ params.map((p) => p.name)
5810
+ ),
5811
+ optional: false,
5812
+ readonly: true
5813
+ });
5814
+ type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
5815
+ return type;
5816
+ }
5817
+ /** The value side of a class named by a binding rather than by a
5818
+ * declaration in this file — an imported one. */
5819
+ classStaticsByNameType(name) {
5820
+ const id = this.classBindingByName(name);
5821
+ const declared = id !== void 0 ? this.bindingType.get(id) : void 0;
5822
+ return declared?.kind === "object" ? declared : void 0;
5823
+ }
5824
+ /** The binding a class name stands for, wherever it was declared. */
5825
+ classBindingByName(name) {
5826
+ for (const [id, binding] of this.scopes.bindings) {
5827
+ if (binding.name === name && binding.declaredBy === "class") return id;
5828
+ }
5829
+ return this.scopes.globalsByName.get(name);
5830
+ }
5831
+ /** What `super(...)` takes: the constructor of the class `stmt` extends,
5832
+ * its own skipped. */
5833
+ baseConstructorType(stmt) {
5834
+ return this.constructorType(stmt, /* @__PURE__ */ new Set([stmt]));
5835
+ }
5836
+ /** A class's constructor signature — its own, or the one it inherits. */
5837
+ constructorType(stmt, seen = /* @__PURE__ */ new Set()) {
5838
+ if (seen.has(stmt)) return void 0;
5839
+ seen.add(stmt);
5840
+ const own = this.shapeOf(stmt).ctor;
5841
+ if (own) return own;
5842
+ const local = this.superDecl(stmt);
5843
+ if (local) return this.constructorType(local, seen);
5844
+ const parent = stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
5845
+ const inherited = parent && this.overloadsOf(this.propertyType(parent, "new"))[0];
5846
+ return inherited;
5847
+ }
5848
+ /** `super` as a value: the base class's instance members with the `this`
5849
+ * slot already filled, because `super.m(a)` passes this instance. */
5850
+ superType(stmt) {
5851
+ const base = this.baseInstance(stmt);
5852
+ if (!base) return anyType;
5853
+ const entries = [];
5854
+ for (const [name, property] of base.properties) {
5855
+ const bound = this.overloadsOf(property.type);
5856
+ entries.push([name, bound.length ? {
5857
+ ...property,
5858
+ type: intersection(bound.map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f))
5859
+ } : property]);
5860
+ }
5861
+ return objectType(entries);
5862
+ }
5863
+ shapeOf(stmt) {
5864
+ const cached = this.classShapes.get(stmt);
5865
+ if (cached) return cached;
5866
+ const shape = { instance: /* @__PURE__ */ new Map(), statics: /* @__PURE__ */ new Map(), filling: true };
5867
+ this.classShapes.set(stmt, shape);
5868
+ const wasEmitting = this.emitDiagnostics;
5869
+ this.emitDiagnostics = false;
5870
+ try {
5871
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
5872
+ this.withSelfType(this.selfTypeOf(stmt), () => this.fillShape(stmt, shape));
5873
+ }));
5874
+ } finally {
5875
+ this.emitDiagnostics = wasEmitting;
5876
+ shape.filling = false;
5877
+ }
5878
+ return shape;
5879
+ }
5880
+ fillShape(stmt, shape) {
5881
+ const put = (isStatic, name, property) => {
5882
+ (isStatic ? shape.statics : shape.instance).set(name, property);
5883
+ };
5884
+ for (const member of stmt.members) {
5885
+ if (member.type !== "ClassField") continue;
5886
+ const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
5887
+ put(member.isStatic, member.name.name, { type, optional: false });
5888
+ }
5889
+ for (const member of stmt.members) {
5890
+ switch (member.type) {
5891
+ case "ClassField":
5892
+ break;
5893
+ case "ClassMethod": {
5894
+ this.paramsFromSignatures(member.func, member.signatures);
5895
+ const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5896
+ put(member.isStatic, member.name.name, { type, optional: false });
5897
+ break;
5898
+ }
5899
+ case "ClassAccessor": {
5900
+ const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5901
+ if (signature.kind !== "function") break;
5902
+ const target = member.isStatic ? shape.statics : shape.instance;
5903
+ const existing = target.get(member.name.name);
5904
+ if (member.kind === "get") {
5905
+ put(member.isStatic, member.name.name, {
5906
+ type: signature.returns,
5907
+ optional: false,
5908
+ readonly: existing === void 0 || existing.readonly !== false
5909
+ });
5910
+ } else {
5911
+ put(member.isStatic, member.name.name, {
5912
+ type: existing?.type ?? signature.params[signature.params.length - 1]?.type ?? anyType,
5913
+ optional: false,
5914
+ readonly: false
5915
+ });
5916
+ }
5917
+ break;
5918
+ }
5919
+ case "ClassConstructor": {
5920
+ const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5921
+ if (signature.kind === "function") shape.ctor = signature;
5922
+ break;
5923
+ }
5924
+ }
5925
+ }
5926
+ }
5927
+ /** Bind the class's value, and check what its members say. Shared by the
5928
+ * declaration and the expression forms. */
5929
+ visitClass(stmt, env) {
5930
+ this.checkClassDeclaration(stmt);
5931
+ const value = this.classValueType(stmt);
5932
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
5933
+ this.withSelfType(this.selfTypeOf(stmt), () => {
5934
+ for (const member of stmt.members) {
5935
+ if (member.type === "ClassField") {
5936
+ if (!member.init) continue;
5937
+ const declared = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : void 0;
5938
+ if (declared) this.applyContext(member.init, declared);
5939
+ const actual = this.infer(member.init, env);
5940
+ if (declared && this.emitDiagnostics && !isAssignable(actual, declared)) {
5941
+ this.diagnostics.push({
5942
+ node: member.init,
5943
+ message: `Type '${formatType(actual)}' is not assignable to type '${formatType(declared)}'`
5944
+ });
5945
+ }
5946
+ continue;
5947
+ }
5948
+ this.checkParamOrder(member.func.params, member);
5949
+ for (const signature of member.signatures ?? []) {
5950
+ this.checkParamOrder(signature.params, member);
5951
+ }
5952
+ this.visitFunctionBody(member.func, env);
5953
+ }
5954
+ });
5955
+ }));
5956
+ return value;
5957
+ }
5958
+ /** What a class gets wrong, reported where it is written. */
5959
+ checkClassDeclaration(stmt) {
5960
+ if (!this.emitDiagnostics) return;
5961
+ const report = (node, message) => {
5962
+ this.diagnostics.push({ node, message });
5963
+ };
5964
+ const name = this.className(stmt);
5965
+ if (stmt.superclass) {
5966
+ const local = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
5967
+ if (local && this.extendsThrough(local, stmt)) {
5968
+ report(stmt.superclass, `'${name}' cannot extend itself`);
5969
+ } else if (!this.baseInstance(stmt)) {
5970
+ const known = this.aliases.has(stmt.superclass.name) || this.importedTypes.has(stmt.superclass.name);
5971
+ report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
5972
+ }
5973
+ }
5974
+ for (const member of stmt.members) {
5975
+ if (member.type === "ClassConstructor") continue;
5976
+ if (CLASS_RESERVED.has(member.name.name)) {
5977
+ report(member.name, `'${member.name.name}' is what the compiler calls part of a class; a member cannot be named that`);
5978
+ }
5979
+ }
5980
+ const seen = /* @__PURE__ */ new Map();
5981
+ for (const member of stmt.members) {
5982
+ if (member.type === "ClassConstructor") {
5983
+ if (seen.has("constructor")) report(member, "A class has one constructor");
5984
+ seen.set("constructor", member.type);
5985
+ continue;
5986
+ }
5987
+ const key = `${member.isStatic ? "static " : ""}${member.name.name}`;
5988
+ const before = seen.get(key);
5989
+ const pair = member.type === "ClassAccessor" && before === "ClassAccessor";
5990
+ if (before !== void 0 && !pair) {
5991
+ report(member.name, `'${member.name.name}' is declared twice in class '${name}'`);
5992
+ }
5993
+ seen.set(key, member.type);
5994
+ }
5995
+ const constructor = stmt.members.find((m) => m.type === "ClassConstructor");
5996
+ if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
5997
+ report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
5998
+ }
5999
+ const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
6000
+ for (const member of stmt.members) {
6001
+ if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6002
+ if (assigned.has(member.name.name)) continue;
6003
+ const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6004
+ if (isAssignable(nilType, type)) continue;
6005
+ report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
6006
+ }
6007
+ }
4992
6008
  /** `extends` must name a class, and the chain must end. */
4993
6009
  checkClass(stmt) {
4994
6010
  if (!stmt.superclass || !this.emitDiagnostics) return;
@@ -5245,6 +6261,7 @@ var TypeAnalyzer = class {
5245
6261
  instantiateAlias(def, args) {
5246
6262
  if (this.instantiationDepth > 20) return unknownType;
5247
6263
  const subst = this.bindTypeArguments(def.params, args);
6264
+ if (def.runtimeClass) return substitute(this.instanceType(def.runtimeClass), subst);
5248
6265
  this.instantiationDepth++;
5249
6266
  try {
5250
6267
  const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
@@ -5389,15 +6406,17 @@ var TypeAnalyzer = class {
5389
6406
  case "FunctionTypeNode": {
5390
6407
  const names = node.generics.map((g) => g.name);
5391
6408
  return this.withTypeParams(node.generics, () => {
5392
- const params = node.params.map((p) => ({
6409
+ const params = node.params.filter((p) => !p.rest).map((p) => ({
5393
6410
  name: p.name,
5394
6411
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
5395
6412
  optional: p.optional
5396
6413
  }));
6414
+ const restParam = node.params.find((p) => p.rest);
6415
+ const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
5397
6416
  return this.withTypeParamDefaults(fn(
5398
6417
  params,
5399
6418
  this.resolveType(node.returnType),
5400
- node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
6419
+ restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
5401
6420
  names,
5402
6421
  this.resolvePredicate(node.predicate, params)
5403
6422
  ), node.generics);
@@ -5591,7 +6610,10 @@ var TypeAnalyzer = class {
5591
6610
  accessType(obj, index) {
5592
6611
  if (index.kind === "union") return union(index.types.map((m) => this.accessType(obj, m)));
5593
6612
  if (index.kind === "literal" && typeof index.value === "string") {
5594
- return this.propertyType(obj, index.value);
6613
+ const member = this.propertyType(obj, index.value);
6614
+ if (member.kind !== "unknown") return member;
6615
+ const t = this.expand(obj);
6616
+ return t.kind === "object" && !t.class && !t.indexer ? nilType : member;
5595
6617
  }
5596
6618
  return this.indexedType(obj, index);
5597
6619
  }
@@ -5744,12 +6766,13 @@ var TypeAnalyzer = class {
5744
6766
  visitStatement(stmt, env) {
5745
6767
  switch (stmt.type) {
5746
6768
  case "VariableDeclaration": {
6769
+ this.nameClassExpressions(stmt);
5747
6770
  stmt.names.forEach((target, i) => {
5748
6771
  if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
5749
6772
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
5750
6773
  }
5751
6774
  });
5752
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
6775
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
5753
6776
  stmt.names.forEach((target, i) => {
5754
6777
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
5755
6778
  const source = sources[i];
@@ -5774,6 +6797,15 @@ var TypeAnalyzer = class {
5774
6797
  });
5775
6798
  return;
5776
6799
  }
6800
+ case "ClassDeclaration": {
6801
+ const id = this.bindingIdByName(stmt.name.name, stmt.name);
6802
+ const value = this.visitClass(stmt, env);
6803
+ if (id !== void 0) {
6804
+ this.bindingType.set(id, value);
6805
+ this.setBinding(env, id, value);
6806
+ }
6807
+ return;
6808
+ }
5777
6809
  case "FunctionDeclaration": {
5778
6810
  this.checkParamOrder(stmt.func.params, stmt);
5779
6811
  for (const sig of stmt.signatures ?? []) this.checkParamOrder(sig.params, stmt);
@@ -5829,7 +6861,7 @@ var TypeAnalyzer = class {
5829
6861
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
5830
6862
  }
5831
6863
  });
5832
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
6864
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
5833
6865
  stmt.targets.forEach((target, i) => {
5834
6866
  const vt = valueTypes[i] ?? unknownType;
5835
6867
  const source = sources[i];
@@ -5937,7 +6969,8 @@ var TypeAnalyzer = class {
5937
6969
  stmt.arguments.forEach((a, i) => this.applyContext(a, declared.elements[i]));
5938
6970
  }
5939
6971
  }
5940
- const { types, sources } = this.valueList(stmt.arguments, env);
6972
+ const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
6973
+ const { types, sources } = this.valueList(stmt.arguments, env, want);
5941
6974
  this.checkReturn(stmt, declared, types, sources, env);
5942
6975
  if (this.returnTypes) {
5943
6976
  this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
@@ -5948,7 +6981,8 @@ var TypeAnalyzer = class {
5948
6981
  this.visitStatement(stmt.declaration, env);
5949
6982
  return;
5950
6983
  case "ExportDefaultStatement":
5951
- this.infer(stmt.declaration, env);
6984
+ if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, env);
6985
+ else this.infer(stmt.declaration, env);
5952
6986
  return;
5953
6987
  case "ExportNamedStatement": {
5954
6988
  if (stmt.source) {
@@ -6027,12 +7061,35 @@ var TypeAnalyzer = class {
6027
7061
  * `sources` maps each produced value back to the expression it came from
6028
7062
  * (undefined for the 2nd and later values of a multi-value call), so the
6029
7063
  * caller can still do contextual typing against the written expression. */
6030
- valueList(exprs, env) {
7064
+ valueList(exprs, env, want = 0) {
6031
7065
  const types = [];
6032
7066
  const sources = [];
6033
7067
  exprs.forEach((e, i) => {
6034
7068
  const t = this.infer(e, env);
6035
7069
  const last = i === exprs.length - 1;
7070
+ if (e.type === "SpreadElement") {
7071
+ const held = this.typeOf.get(e.argument);
7072
+ const expanded = held && this.expand(held);
7073
+ if (expanded?.kind === "tuple") {
7074
+ for (const element of expanded.elements) {
7075
+ types.push(element);
7076
+ sources.push(e);
7077
+ }
7078
+ return;
7079
+ }
7080
+ do {
7081
+ types.push(t);
7082
+ sources.push(e);
7083
+ } while (last && types.length < want);
7084
+ return;
7085
+ }
7086
+ if (last && e.type === "VarargExpression") {
7087
+ do {
7088
+ types.push(t);
7089
+ sources.push(types.length - 1 === i ? e : void 0);
7090
+ } while (types.length < want);
7091
+ return;
7092
+ }
6036
7093
  if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
6037
7094
  t.elements.forEach((el, j) => {
6038
7095
  types.push(el);
@@ -6121,7 +7178,8 @@ var TypeAnalyzer = class {
6121
7178
  /** A parameter's type: annotation, else a shape synthesized from a
6122
7179
  * destructuring pattern, else inferred from its default, else `any`. */
6123
7180
  paramType(p, env) {
6124
- if (!p.typeAnnotation && !p.pattern && !p.default && p.name === "self" && this.selfType) {
7181
+ const receiver = p.name === "self" || p.name === "this";
7182
+ if (!p.typeAnnotation && !p.pattern && !p.default && receiver && this.selfType) {
6125
7183
  return this.selfType;
6126
7184
  }
6127
7185
  if (p.typeAnnotation) {
@@ -6129,6 +7187,7 @@ var TypeAnalyzer = class {
6129
7187
  if (p.default) this.applyContext(p.default, t);
6130
7188
  return p.optional ? optional(t) : t;
6131
7189
  }
7190
+ if (p.rest) return arrayOf(unknownType);
6132
7191
  if (p.pattern) return this.patternToType(p.pattern, env);
6133
7192
  if (p.default) return widen(this.infer(p.default, env));
6134
7193
  return this.contextualParams.get(p) ?? anyType;
@@ -6149,6 +7208,11 @@ var TypeAnalyzer = class {
6149
7208
  this.expectedTypeOf.set(e, expected);
6150
7209
  if (e.type === "ArrayExpression") return this.applyArrayContext(e, expected);
6151
7210
  if (e.type === "TableExpression") return this.applyTableContext(e, expected);
7211
+ if (e.type === "BinaryExpression" && (e.operator === "or" || e.operator === "and")) {
7212
+ if (e.operator === "or") this.applyContext(e.left, expected);
7213
+ this.applyContext(e.right, expected);
7214
+ return;
7215
+ }
6152
7216
  if (e.type !== "FunctionExpression") return;
6153
7217
  const members = expected.kind === "union" ? expected.types : [expected];
6154
7218
  const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
@@ -6174,7 +7238,7 @@ var TypeAnalyzer = class {
6174
7238
  const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
6175
7239
  if (!target) return;
6176
7240
  if (!e.elements.length) {
6177
- if (!containsTypeParam(target)) this.contextualArrays.set(e, target);
7241
+ this.contextualArrays.set(e, target);
6178
7242
  return;
6179
7243
  }
6180
7244
  e.elements.forEach((element, i) => {
@@ -6234,13 +7298,26 @@ var TypeAnalyzer = class {
6234
7298
  }
6235
7299
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
6236
7300
  }
7301
+ /** What a vararg function's `...` holds, one value at a time.
7302
+ *
7303
+ * Written three ways, and they mean the same call: `...` says nothing,
7304
+ * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7305
+ * into an array the body reads by name. Only the last changes what the
7306
+ * body sees — the signature is the same either way. */
7307
+ varargElement(func) {
7308
+ if (!func.hasVarargs) return void 0;
7309
+ const rest = func.params.find((p) => p.rest);
7310
+ if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7311
+ const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7312
+ return declared?.kind === "array" ? declared.element : unknownType;
7313
+ }
6237
7314
  /** The type of `...` in each function body being walked. */
6238
7315
  varargs = [];
6239
7316
  /** What each function body being walked declared it returns. */
6240
7317
  declaredReturns = [];
6241
7318
  /** Run `body` with `...` and `return` as `func` declares them. */
6242
7319
  withVarargs(func, body) {
6243
- this.varargs.push(func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0);
7320
+ this.varargs.push(this.varargElement(func));
6244
7321
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
6245
7322
  try {
6246
7323
  return body();
@@ -6321,6 +7398,13 @@ var TypeAnalyzer = class {
6321
7398
  const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
6322
7399
  unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
6323
7400
  });
7401
+ if (f.varargs) {
7402
+ const keeps = keepsLiterals(f.varargs);
7403
+ for (let i = f.params.length; i < argTypes.length; i++) {
7404
+ const arg = argTypes[i];
7405
+ if (arg !== void 0) unify(f.varargs, keeps ? arg : widen(arg), vars, subst);
7406
+ }
7407
+ }
6324
7408
  for (const name of f.typeParams ?? []) {
6325
7409
  if (!subst.has(name)) subst.set(name, f.typeParamDefaults?.[name] ?? unknownType);
6326
7410
  }
@@ -6345,11 +7429,11 @@ var TypeAnalyzer = class {
6345
7429
  * after every concrete signature has been tried. That ordering is what
6346
7430
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
6347
7431
  * `<T>(v: T) -> string` and still pick the precise one. */
6348
- pickOverload(fns, argTypes, argsFor) {
7432
+ pickOverload(fns, argTypes, argsFor, spread) {
6349
7433
  for (const generic of [false, true]) {
6350
7434
  for (const f of fns) {
6351
7435
  if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
6352
- if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes)) return f;
7436
+ if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
6353
7437
  }
6354
7438
  }
6355
7439
  return void 0;
@@ -6384,12 +7468,27 @@ var TypeAnalyzer = class {
6384
7468
  * own type parameters stand for what the call would infer, so each is
6385
7469
  * checked only against its constraint — `<K extends keyof Services>`
6386
7470
  * accepts `"Players"` but not `""`. */
6387
- overloadAccepts(f, argTypes) {
6388
- if (!f.varargs && argTypes.length > f.params.length) return false;
7471
+ overloadAccepts(f, argTypes, spread) {
7472
+ const at = (i) => {
7473
+ if (!spread || i < spread.index) return argTypes[i];
7474
+ if (!spread.elements) return argTypes[spread.index];
7475
+ const held = spread.elements[i - spread.index];
7476
+ return held ?? argTypes[i - spread.index + 1 + spread.elements.length - 1];
7477
+ };
7478
+ const written = spread?.elements ? argTypes.length + spread.elements.length - 1 : argTypes.length;
7479
+ if (!f.varargs && (!spread || spread.elements) && written > f.params.length) return false;
7480
+ if (f.varargs && !f.typeParams?.length) {
7481
+ const last = spread && !spread.elements ? Math.max(f.params.length + 1, written) : written;
7482
+ for (let i = f.params.length; i < last; i++) {
7483
+ const arg = at(i);
7484
+ if (arg !== void 0 && !isAssignable(arg, f.varargs)) return false;
7485
+ }
7486
+ }
6389
7487
  const params = this.boundParams(f);
6390
7488
  return f.params.every((p, i) => {
6391
- if (argTypes[i] === void 0) return p.optional === true;
6392
- return isAssignable(argTypes[i], params[i]);
7489
+ const arg = at(i);
7490
+ if (arg === void 0) return p.optional === true;
7491
+ return isAssignable(arg, params[i]);
6393
7492
  });
6394
7493
  }
6395
7494
  /** A signature's parameter types as a call site sees them before inference:
@@ -6419,17 +7518,40 @@ var TypeAnalyzer = class {
6419
7518
  }
6420
7519
  /** Record what each written argument is expected to be — see
6421
7520
  * `TypeAnalysis.expectedTypeOf`. */
6422
- recordExpected(written, fns, selfOf) {
7521
+ recordExpected(written, fns, selfOf, argsOf = () => []) {
7522
+ const paramsOf = /* @__PURE__ */ new Map();
7523
+ for (const f of fns) paramsOf.set(f, this.paramsAsCalled(f, argsOf(f)));
6423
7524
  written.forEach((arg, j) => {
6424
7525
  const candidates = [];
6425
7526
  for (const f of fns) {
6426
7527
  const i = j + selfOf(f);
6427
- const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
7528
+ const params = paramsOf.get(f);
7529
+ const param = i < params.length ? params[i] : f.varargs;
6428
7530
  if (param) candidates.push(param);
6429
7531
  }
6430
7532
  if (candidates.length) this.expectedTypeOf.set(arg, union(candidates));
6431
7533
  });
6432
7534
  }
7535
+ /** The parameters as *this* call makes them read: a type argument the
7536
+ * arguments already written pin down is substituted in, and one nothing
7537
+ * has pinned down yet falls back to its constraint.
7538
+ *
7539
+ * It is what makes the second argument of
7540
+ * `get(page, skill: Extract<Rows, { Page: Page }>["Skills"][number])`
7541
+ * worth completing — with `page` written, `skill` is the skills of that
7542
+ * page, not of every page. */
7543
+ paramsAsCalled(f, argTypes) {
7544
+ const fallback = this.boundParams(f);
7545
+ if (!f.typeParams?.length || !argTypes.length) return fallback;
7546
+ return f.params.map((p, i) => {
7547
+ if (!containsTypeParam(p.type)) return p.type;
7548
+ const subst = this.inferTypeArgs(f, argTypes.map((t, k) => k === i ? void 0 : t));
7549
+ for (const [name, bound] of [...subst]) if (bound.kind === "unknown") subst.delete(name);
7550
+ if (!subst.size) return fallback[i];
7551
+ const applied = this.reduceType(substitute(p.type, subst));
7552
+ return containsTypeParam(applied) ? fallback[i] : applied;
7553
+ });
7554
+ }
6433
7555
  /** No signature accepts the call, and the argument count is not the
6434
7556
  * problem: say which argument is wrong, the way TypeScript does. */
6435
7557
  /** Check what was written against the parameters as this call's own type
@@ -6440,10 +7562,11 @@ var TypeAnalyzer = class {
6440
7562
  if (!this.emitDiagnostics || !f.typeParams?.length) return;
6441
7563
  const subst = this.inferTypeArgs(f, [...argTypes]);
6442
7564
  for (const bound of subst.values()) if (bound.kind === "unknown") return;
6443
- for (let i = 0; i < f.params.length; i++) {
7565
+ const declaredAt = (i) => i < f.params.length ? f.params[i].type : f.varargs;
7566
+ for (let i = 0; i < Math.max(f.params.length, argTypes.length); i++) {
6444
7567
  const arg = argTypes[i];
6445
- const declared = f.params[i].type;
6446
- if (arg === void 0 || !containsTypeParam(declared)) continue;
7568
+ const declared = declaredAt(i);
7569
+ if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
6447
7570
  const expected = this.reduceType(substitute(declared, subst));
6448
7571
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
6449
7572
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
@@ -6464,15 +7587,26 @@ var TypeAnalyzer = class {
6464
7587
  const args = argsFor(f);
6465
7588
  const params = this.boundParams(f);
6466
7589
  const self = selfOf(f);
7590
+ const spread = this.spreadOf(written, self);
6467
7591
  for (let i = 0; i < f.params.length; i++) {
6468
- const arg = args[i];
6469
- if (arg === void 0 || isAssignable(arg, params[i])) continue;
7592
+ const arg = spread && i >= spread.index ? this.spreadValue(spread, i) : args[i];
7593
+ if (spread && i > spread.index && !spread.elements) break;
7594
+ if (arg === void 0 || arg.kind === "never" || isAssignable(arg, params[i])) continue;
6470
7595
  this.diagnostics.push({
6471
- node: written[i - self] ?? call,
7596
+ node: (spread && i >= spread.index ? written[spread.index - self] : written[i - self]) ?? call,
6472
7597
  message: `Argument of type '${formatType(arg)}' is not assignable to parameter of type '${briefType(params[i])}'`
6473
7598
  });
6474
7599
  return;
6475
7600
  }
7601
+ if (!f.varargs || f.typeParams?.length) return;
7602
+ for (let i = f.params.length; i < args.length; i++) {
7603
+ if (isAssignable(args[i], f.varargs)) continue;
7604
+ this.diagnostics.push({
7605
+ node: written[i - self] ?? call,
7606
+ message: `Argument of type '${formatType(args[i])}' is not assignable to parameter of type '${briefType(f.varargs)}'`
7607
+ });
7608
+ return;
7609
+ }
6476
7610
  }
6477
7611
  /** A required parameter may not follow an optional one — otherwise the
6478
7612
  * optional one could never actually be omitted. Same rule as TypeScript,
@@ -6481,6 +7615,10 @@ var TypeAnalyzer = class {
6481
7615
  if (!this.emitDiagnostics) return;
6482
7616
  let seenOptional;
6483
7617
  for (const p of params) {
7618
+ if (p.rest === true) {
7619
+ this.checkRestType(p, node);
7620
+ continue;
7621
+ }
6484
7622
  const isOptional = p.optional === true || p.default !== void 0;
6485
7623
  if (isOptional) {
6486
7624
  if (seenOptional === void 0) seenOptional = p.name ?? "parameter";
@@ -6506,8 +7644,10 @@ var TypeAnalyzer = class {
6506
7644
  * when *no* overload accepts the count, so an overload set still reports
6507
7645
  * once, against its first signature. Returns whether the count fits, so
6508
7646
  * an argument's type is only complained about when its count is right. */
6509
- checkArity(node, fns, argCount, selfArgs) {
7647
+ checkArity(node, fns, argCount, selfArgs, spread) {
6510
7648
  if (!fns.length) return true;
7649
+ if (spread && !spread.elements) return true;
7650
+ if (spread?.elements) argCount += spread.elements.length - 1;
6511
7651
  const fits = fns.some((f) => {
6512
7652
  const { min: min2, max: max2 } = this.arityOf(f);
6513
7653
  const n = argCount + selfArgs;
@@ -6550,7 +7690,7 @@ var TypeAnalyzer = class {
6550
7690
  return type;
6551
7691
  };
6552
7692
  return record(this.withTypeParams(sig.generics, () => {
6553
- const params = sig.params.map((p) => ({
7693
+ const params = sig.params.filter((p) => !p.rest).map((p) => ({
6554
7694
  name: p.pattern ? void 0 : p.name,
6555
7695
  type: this.paramType(p, /* @__PURE__ */ new Map()),
6556
7696
  optional: p.optional || p.default !== void 0
@@ -6558,12 +7698,23 @@ var TypeAnalyzer = class {
6558
7698
  return fn(
6559
7699
  params,
6560
7700
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
6561
- sig.hasVarargs ? sig.varargTypeAnnotation ? this.resolveType(sig.varargTypeAnnotation) : anyType : void 0,
7701
+ this.varargElement(sig),
6562
7702
  names,
6563
7703
  this.resolvePredicate(sig.predicate, params)
6564
7704
  );
6565
7705
  }));
6566
7706
  }
7707
+ /** `...rest: T[]` holds every argument from its position on, so its type
7708
+ * is an array of what each one is. */
7709
+ checkRestType(p, node) {
7710
+ if (!this.emitDiagnostics || !p.typeAnnotation) return;
7711
+ const declared = this.resolveType(p.typeAnnotation);
7712
+ if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
7713
+ this.diagnostics.push({
7714
+ node,
7715
+ message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
7716
+ });
7717
+ }
6567
7718
  /** Turn a parsed `v is T` / `asserts v` annotation into a `TypePredicate`,
6568
7719
  * resolving the named parameter to its index. A guard naming a parameter
6569
7720
  * the function does not have is dropped rather than mis-narrowing an
@@ -6581,17 +7732,18 @@ var TypeAnalyzer = class {
6581
7732
  inferFunctionBody(func, env) {
6582
7733
  const names = func.generics.map((g) => g.name);
6583
7734
  return this.withTypeParams(func.generics, () => {
6584
- const params = func.params.map((p) => {
7735
+ const params = func.params.flatMap((p) => {
6585
7736
  const type = this.paramType(p, env);
6586
7737
  if (!p.pattern) {
6587
7738
  const id = this.bindingIdByName(p.name, p);
6588
7739
  if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
6589
7740
  }
6590
- return {
7741
+ if (p.rest) return [];
7742
+ return [{
6591
7743
  name: p.pattern ? void 0 : p.name,
6592
7744
  type,
6593
7745
  optional: p.optional || p.default !== void 0
6594
- };
7746
+ }];
6595
7747
  });
6596
7748
  const bodyEnv = forkEnv(env);
6597
7749
  for (const p of func.params) {
@@ -6616,7 +7768,7 @@ var TypeAnalyzer = class {
6616
7768
  return fn(
6617
7769
  params,
6618
7770
  returns,
6619
- func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0,
7771
+ this.varargElement(func),
6620
7772
  names,
6621
7773
  this.resolvePredicate(func.predicate, params)
6622
7774
  );
@@ -6830,6 +7982,13 @@ var TypeAnalyzer = class {
6830
7982
  if (src.kind === "array") return [numberType, src.element];
6831
7983
  }
6832
7984
  }
7985
+ const iterator = this.expand(iterType);
7986
+ if (iterator.kind === "function") {
7987
+ const returns = this.expand(iterator.returns);
7988
+ const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
7989
+ const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
7990
+ return [at(0), at(1)];
7991
+ }
6833
7992
  const t = this.expand(iterType);
6834
7993
  if (t.kind === "array") return varCount >= 2 ? [numberType, t.element] : [t.element, unknownType];
6835
7994
  if (t.kind === "object") {
@@ -6981,7 +8140,21 @@ var TypeAnalyzer = class {
6981
8140
  expand(t) {
6982
8141
  if (t.kind !== "genericRef") return t;
6983
8142
  const def = this.aliasDefs.get(t.name);
6984
- if (!def || this.resolvingAliases.has(t.name)) return t;
8143
+ if (!def) {
8144
+ const imported = this.importedTypes.get(t.name);
8145
+ if (!imported) return t;
8146
+ if (!imported.params.length) return imported.type;
8147
+ const subst = /* @__PURE__ */ new Map();
8148
+ imported.params.forEach((name, i) => subst.set(name, t.typeArguments[i] ?? unknownType));
8149
+ const key2 = `import ${formatType(t)}`;
8150
+ const cached2 = this.expandCache.get(key2);
8151
+ if (cached2) return cached2;
8152
+ this.expandCache.set(key2, t);
8153
+ const applied = substitute(imported.type, subst);
8154
+ this.expandCache.set(key2, applied);
8155
+ return applied;
8156
+ }
8157
+ if (this.resolvingAliases.has(t.name)) return t;
6985
8158
  const key = t.typeArguments.length ? formatType(t) : t.name;
6986
8159
  const cached = this.expandCache.get(key);
6987
8160
  if (cached) return cached;
@@ -7002,19 +8175,120 @@ var TypeAnalyzer = class {
7002
8175
  * those names again replaces the whole set, and nothing here is a special
7003
8176
  * case in the analyzer. The build lowers each call to a plain function. */
7004
8177
  builtInMethod(t, name) {
7005
- const element = t.kind === "array" ? t.element : t.kind === "tuple" ? union(t.elements) : void 0;
7006
- const methodTable = element !== void 0 ? "ArrayMethods" : t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" ? "StringMethods" : void 0;
8178
+ const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
8179
+ const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(m.elements) : void 0);
8180
+ const element = elements.every((e) => e !== void 0) ? union(elements) : void 0;
8181
+ const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
8182
+ const methodTable = element !== void 0 ? "ArrayMethods" : parts.every(isString) ? "StringMethods" : void 0;
7007
8183
  const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
7008
8184
  if (!def || def.class) return void 0;
7009
8185
  const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
7010
- const parts = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
7011
- for (let i = parts.length - 1; i >= 0; i--) {
7012
- const part = parts[i];
8186
+ const layers = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
8187
+ for (let i = layers.length - 1; i >= 0; i--) {
8188
+ const part = layers[i];
7013
8189
  const property = part.kind === "object" ? part.properties.get(name) : void 0;
7014
8190
  if (property) return property.type;
7015
8191
  }
7016
8192
  return void 0;
7017
8193
  }
8194
+ /** The most a deferred type could turn out to be. A conditional is one of
8195
+ * its branches, and the true branch stands for a member of what was
8196
+ * tested (`T` in `T extends U ? T : never`); an indexed access reads
8197
+ * through the bound of what it indexes. Anything else has no bound worth
8198
+ * giving — `undefined` leaves the comparison as it was. */
8199
+ deferredBound(t, depth = 0) {
8200
+ if (depth > 8) return void 0;
8201
+ switch (t.kind) {
8202
+ case "conditional": {
8203
+ const check = this.reduceType(t.checkType);
8204
+ if (containsTypeParam(check)) return void 0;
8205
+ const subst = /* @__PURE__ */ new Map();
8206
+ if (t.distributeParam) subst.set(t.distributeParam, check);
8207
+ for (const name of t.inferVars ?? []) subst.set(name, unknownType);
8208
+ const branches = [substitute(t.trueType, subst), t.falseType].map((branch) => this.reduceType(branch));
8209
+ if (branches.some((branch) => containsTypeParam(branch))) return void 0;
8210
+ return union(branches);
8211
+ }
8212
+ case "indexedAccess": {
8213
+ const object = this.deferredBound(t.objectType, depth + 1) ?? this.atConstraints(t.objectType);
8214
+ const index = this.atConstraints(this.reduceType(t.indexType));
8215
+ if (!object || !index) return void 0;
8216
+ return this.indexedType(object, index);
8217
+ }
8218
+ default:
8219
+ return void 0;
8220
+ }
8221
+ }
8222
+ /** `t` with every type parameter standing at its constraint: `Map[K]`
8223
+ * where `K extends "a" | "b"` is at most what those two keys hold. A
8224
+ * parameter with no constraint bounds nothing, and says so. */
8225
+ atConstraints(t) {
8226
+ if (!containsTypeParam(t)) return t;
8227
+ const bounds = /* @__PURE__ */ new Map();
8228
+ const seen = /* @__PURE__ */ new WeakSet();
8229
+ let open = false;
8230
+ const walk = (value) => {
8231
+ if (!value || typeof value !== "object" || seen.has(value)) return;
8232
+ seen.add(value);
8233
+ if (value instanceof Map) {
8234
+ value.forEach(walk);
8235
+ return;
8236
+ }
8237
+ const part = value;
8238
+ if (part.kind === "object" && part.class) return;
8239
+ if (part.kind === "typeParam" && typeof part.name === "string") {
8240
+ if (part.constraint && !containsTypeParam(part.constraint)) {
8241
+ bounds.set(part.name, this.reduceType(part.constraint));
8242
+ } else {
8243
+ open = true;
8244
+ }
8245
+ }
8246
+ for (const child of Object.values(value)) walk(child);
8247
+ };
8248
+ walk(t);
8249
+ if (open) return void 0;
8250
+ const applied = this.reduceType(substitute(t, bounds));
8251
+ return containsTypeParam(applied) ? void 0 : applied;
8252
+ }
8253
+ /** What `text["upper"]` reads: a string answers only to its methods, the
8254
+ * way Lua's string metatable does. */
8255
+ stringMember(object, index) {
8256
+ if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
8257
+ const parts = this.stringParts(object);
8258
+ if (!parts) return void 0;
8259
+ const found = parts.map((part) => this.builtInMethod(part, index.value));
8260
+ return found.every((t) => t !== void 0) ? union(found) : void 0;
8261
+ }
8262
+ /** The members of `t` when every one of them is a string — `"a" | "b"` is
8263
+ * as much a string as `string` is. */
8264
+ stringParts(t) {
8265
+ let expanded = this.expand(t);
8266
+ if (expanded.kind === "conditional" || expanded.kind === "indexedAccess") {
8267
+ const bound = this.deferredBound(expanded);
8268
+ if (!bound) return void 0;
8269
+ expanded = this.expand(bound);
8270
+ }
8271
+ const parts = (expanded.kind === "union" ? expanded.types : [expanded]).map((m) => this.expand(m));
8272
+ const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
8273
+ return parts.length && parts.every(isString) ? parts : void 0;
8274
+ }
8275
+ /** `text.Sans` or `text["Sans"]`: a string is not a table, and the only
8276
+ * members it has are the ones a type library gave it — so a name that is
8277
+ * not one of them is a mistake worth reporting, rather than the nil Lua
8278
+ * would hand back. */
8279
+ checkStringMember(node, object, key) {
8280
+ if (!this.emitDiagnostics) return;
8281
+ const parts = this.stringParts(object);
8282
+ if (!parts) return;
8283
+ const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
8284
+ if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
8285
+ const names = keys.map((m) => String(m.value));
8286
+ if (names.some((name) => parts.some((part) => this.builtInMethod(part, name)))) return;
8287
+ this.diagnostics.push({
8288
+ node,
8289
+ message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8290
+ });
8291
+ }
7018
8292
  propertyType(raw, name) {
7019
8293
  const t = this.deferredAccess(this.expand(raw));
7020
8294
  if (t.kind === "object") {
@@ -7024,7 +8298,11 @@ var TypeAnalyzer = class {
7024
8298
  }
7025
8299
  const built = this.builtInMethod(t, name);
7026
8300
  if (built) return built;
7027
- if (t.kind === "union") return union(t.types.map((m) => this.propertyType(m, name)));
8301
+ if (t.kind === "union") {
8302
+ const built2 = this.builtInMethod(t, name);
8303
+ if (built2) return built2;
8304
+ return union(t.types.map((m) => this.propertyType(m, name)));
8305
+ }
7028
8306
  if (t.kind === "intersection") {
7029
8307
  const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
7030
8308
  if (parts.length) return intersection(parts);
@@ -7032,6 +8310,10 @@ var TypeAnalyzer = class {
7032
8310
  if (t.kind === "typeParam" && t.constraint) return this.propertyType(t.constraint, name);
7033
8311
  if (t.kind === "difference") return this.propertyType(t.base, name);
7034
8312
  if (t.kind === "any") return anyType;
8313
+ if (t.kind === "conditional" || t.kind === "indexedAccess") {
8314
+ const bound = this.deferredBound(t);
8315
+ if (bound) return this.propertyType(bound, name);
8316
+ }
7035
8317
  return unknownType;
7036
8318
  }
7037
8319
  /** `t[k]`. A statically known string key resolves against the declared
@@ -7107,6 +8389,20 @@ var TypeAnalyzer = class {
7107
8389
  // `...` holds what the function declared it takes.
7108
8390
  case "VarargExpression":
7109
8391
  return this.varargs[this.varargs.length - 1] ?? anyType;
8392
+ // `f(a, ...rest)` — every value the array holds, one after
8393
+ // another. Each of them is an element, so that is what the
8394
+ // parameters it fills are checked against.
8395
+ case "SpreadElement": {
8396
+ const spread = this.infer(expr.argument, env);
8397
+ const element = this.spreadElement(spread);
8398
+ if (element === void 0 && this.emitDiagnostics) {
8399
+ this.diagnostics.push({
8400
+ node: expr,
8401
+ message: `Only an array can be spread, and '${formatType(spread)}' is not one`
8402
+ });
8403
+ }
8404
+ return element ?? anyType;
8405
+ }
7110
8406
  // Broken syntax is reported by the parser; nothing more to say.
7111
8407
  case "ErrorExpression":
7112
8408
  return anyType;
@@ -7212,6 +8508,7 @@ var TypeAnalyzer = class {
7212
8508
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
7213
8509
  const key = this.refKeyOf(expr);
7214
8510
  const narrowed = key === void 0 ? void 0 : env.get(key);
8511
+ this.checkStringMember(expr, obj, literal(expr.property.name));
7215
8512
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
7216
8513
  }
7217
8514
  case "IndexExpression": {
@@ -7219,12 +8516,33 @@ var TypeAnalyzer = class {
7219
8516
  const idx = this.infer(expr.index, env);
7220
8517
  const key = this.refKeyOf(expr);
7221
8518
  const narrowed = key === void 0 ? void 0 : env.get(key);
8519
+ this.checkStringMember(expr, obj, this.expand(idx));
8520
+ const member = this.stringMember(obj, this.expand(idx));
8521
+ if (member) return this.chainResult(expr, member, shortCircuits);
7222
8522
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
7223
8523
  }
7224
8524
  case "CallExpression": {
8525
+ if (expr.callee.type === "SuperExpression") return this.inferSuperCall(expr, env);
7225
8526
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
7226
8527
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
7227
8528
  }
8529
+ case "NewExpression":
8530
+ return this.inferNew(expr, env);
8531
+ case "ClassExpression":
8532
+ return this.visitClass(expr, env);
8533
+ case "SuperExpression": {
8534
+ const stmt = this.currentClass;
8535
+ if (!stmt?.superclass) {
8536
+ if (this.emitDiagnostics) {
8537
+ this.diagnostics.push({
8538
+ node: expr,
8539
+ message: "'super' is only available inside a class that extends another"
8540
+ });
8541
+ }
8542
+ return anyType;
8543
+ }
8544
+ return this.superType(stmt);
8545
+ }
7228
8546
  case "MethodCallExpression": {
7229
8547
  const { type: objType, shortCircuits } = this.chainObject(expr, expr.object, env);
7230
8548
  return this.chainResult(expr, this.inferMethodCall(expr, objType, env), shortCircuits);
@@ -7243,16 +8561,56 @@ var TypeAnalyzer = class {
7243
8561
  }
7244
8562
  }
7245
8563
  }
8564
+ /** `new Name(args)` is `Name.new(args)` — the same function, and the
8565
+ * same check. Saying so here rather than rewriting the tree keeps the
8566
+ * error messages pointing at what was written. */
8567
+ inferNew(expr, env) {
8568
+ const calleeType = this.infer(expr.callee, env);
8569
+ const constructor = this.propertyType(calleeType, "new");
8570
+ if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8571
+ if (this.emitDiagnostics) {
8572
+ const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8573
+ this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8574
+ }
8575
+ for (const argument of expr.arguments) this.infer(argument, env);
8576
+ return anyType;
8577
+ }
8578
+ return this.inferCall(expr, constructor, env);
8579
+ }
8580
+ /** `super(...)` — the base constructor, run on the instance being built. */
8581
+ inferSuperCall(expr, env) {
8582
+ const stmt = this.currentClass;
8583
+ const constructor = stmt ? this.baseConstructorType(stmt) : void 0;
8584
+ if (!stmt?.superclass) {
8585
+ if (this.emitDiagnostics) {
8586
+ this.diagnostics.push({
8587
+ node: expr,
8588
+ message: "'super(...)' is only available inside the constructor of a class that extends another"
8589
+ });
8590
+ }
8591
+ for (const argument of expr.arguments) this.infer(argument, env);
8592
+ return nilType;
8593
+ }
8594
+ if (!constructor) {
8595
+ for (const argument of expr.arguments) this.infer(argument, env);
8596
+ return nilType;
8597
+ }
8598
+ const callable = fn(constructor.params.filter((p) => p.name !== "this"), nilType, constructor.varargs);
8599
+ this.inferCall(expr, callable, env);
8600
+ return nilType;
8601
+ }
7246
8602
  inferCall(expr, callee, env) {
8603
+ this.checkAmbiguousCall(expr);
7247
8604
  const fns = this.overloadsOf(callee);
7248
8605
  const explicit = this.explicitTypeArguments(expr, fns);
7249
8606
  const expected = this.expectedArguments(expr.arguments, fns, () => 0);
7250
8607
  expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
7251
8608
  const argTypes = expr.arguments.map((a) => this.infer(a, env));
7252
8609
  if (fns.length) {
7253
- this.recordExpected(expr.arguments, fns, () => 0);
7254
- const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
7255
- const picked = this.pickOverload(fns, argTypes);
8610
+ this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
8611
+ const spread = this.spreadOf(expr.arguments);
8612
+ const arityFits = this.checkArity(expr, fns, argTypes.length, 0, spread);
8613
+ const picked = this.pickOverload(fns, argTypes, void 0, spread);
7256
8614
  const distributed = this.distributedReturn(fns, argTypes, picked, (_, args) => args);
7257
8615
  if (distributed) return distributed;
7258
8616
  if (picked) {
@@ -7264,6 +8622,21 @@ var TypeAnalyzer = class {
7264
8622
  }
7265
8623
  return callee.kind === "any" ? anyType : unknownType;
7266
8624
  }
8625
+ /** A `(` on a line of its own continues the statement above it:
8626
+ *
8627
+ * const value = map[key]
8628
+ * ("text"):upper()
8629
+ *
8630
+ * calls `map[key]`, in luaut as in Lua and in JavaScript. It is almost
8631
+ * never what was meant, and what it does instead is invisible — so say
8632
+ * so, and name the fix. */
8633
+ checkAmbiguousCall(expr) {
8634
+ if (!this.emitDiagnostics || !expr.argumentsOnNewLine) return;
8635
+ this.diagnostics.push({
8636
+ node: expr,
8637
+ message: "This calls the value the line above ends with \u2014 a line break does not end a statement. Write ';' before '(' if a new statement was meant."
8638
+ });
8639
+ }
7267
8640
  inferMethodCall(expr, objType, env) {
7268
8641
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
7269
8642
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -7273,9 +8646,11 @@ var TypeAnalyzer = class {
7273
8646
  if (fns.length) {
7274
8647
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
7275
8648
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
7276
- this.recordExpected(expr.arguments, fns, selfOf);
7277
- const arityFits = this.checkArity(expr, fns, argTypes.length, this.takesSelf(fns[0]) ? 1 : 0);
7278
- const picked = this.pickOverload(fns, argTypes, withSelf);
8649
+ this.recordExpected(expr.arguments, fns, selfOf, withSelf);
8650
+ const self0 = this.takesSelf(fns[0]) ? 1 : 0;
8651
+ const spread = this.spreadOf(expr.arguments, self0);
8652
+ const arityFits = this.checkArity(expr, fns, argTypes.length, self0, spread);
8653
+ const picked = this.pickOverload(fns, argTypes, withSelf, spread);
7279
8654
  const distributed = this.distributedReturn(
7280
8655
  fns,
7281
8656
  argTypes,
@@ -7924,7 +9299,39 @@ var TypeAnalyzer = class {
7924
9299
  * out. Every place that has to line arguments up with parameters goes
7925
9300
  * through here so the two sides cannot drift apart. */
7926
9301
  takesSelf(f) {
7927
- return f.params[0]?.name === "self";
9302
+ const first = f.params[0]?.name;
9303
+ return first === "self" || first === "this";
9304
+ }
9305
+ /** What one value of a spread array is, or `undefined` when the thing
9306
+ * spread is not a list of values at all. */
9307
+ spreadElement(t) {
9308
+ const spread = this.expand(t);
9309
+ if (spread.kind === "array") return spread.element;
9310
+ if (spread.kind === "tuple") return union(spread.elements);
9311
+ if (spread.kind === "any") return anyType;
9312
+ return void 0;
9313
+ }
9314
+ /** Where a call's arguments stop being one each, and what fills the rest.
9315
+ * From a spread on, every remaining parameter is filled by one of the
9316
+ * array's values — however many that turns out to be, so neither the
9317
+ * count nor the positions after it are known. A *tuple* is the exception:
9318
+ * it holds a known value at each position, and `elements` says which. */
9319
+ spreadOf(args, self = 0) {
9320
+ const index = args.findIndex((a) => a.type === "SpreadElement");
9321
+ if (index < 0) return void 0;
9322
+ const spread = args[index];
9323
+ const held = this.typeOf.get(spread.argument);
9324
+ const expanded = held && this.expand(held);
9325
+ return {
9326
+ index: index + self,
9327
+ element: this.typeOf.get(spread) ?? unknownType,
9328
+ elements: expanded?.kind === "tuple" ? expanded.elements : void 0
9329
+ };
9330
+ }
9331
+ /** One of `spread`'s values, at the position `i` of a call's arguments. */
9332
+ spreadValue(spread, i) {
9333
+ if (!spread.elements) return spread.element;
9334
+ return spread.elements[i - spread.index] ?? neverType;
7928
9335
  }
7929
9336
  /** A function type as a list of call signatures: a lone function is a
7930
9337
  * one-element list, an intersection is the overload set in source order. */
@@ -7979,6 +9386,8 @@ var TypeAnalyzer = class {
7979
9386
  type = this.resolveType(statement.valueType);
7980
9387
  } else if (statement.type === "FunctionDeclaration") {
7981
9388
  type = statement.signatures?.length ? intersection(statement.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(statement.func, /* @__PURE__ */ new Map());
9389
+ } else if (statement.type === "ClassDeclaration") {
9390
+ type = this.classValueType(statement);
7982
9391
  } else if (statement.type === "VariableDeclaration") {
7983
9392
  const target = statement.names[index];
7984
9393
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
@@ -8015,6 +9424,10 @@ var TypeAnalyzer = class {
8015
9424
  return;
8016
9425
  }
8017
9426
  const record = node;
9427
+ if (record.type === "ClassDeclaration" && record.name) {
9428
+ const id = this.bindingIdByName(record.name.name, record.name);
9429
+ if (id !== void 0) out.set(id, { statement: node, index: 0 });
9430
+ }
8018
9431
  if (record.type === "FunctionDeclaration" && record.name) {
8019
9432
  const id = this.bindingIdByName(record.name.name, record.name);
8020
9433
  if (id !== void 0) out.set(id, { statement: node, index: 0 });
@@ -8534,6 +9947,7 @@ var index_default = luautparser;
8534
9947
  resolveModulePath,
8535
9948
  resolveTypeLibraries,
8536
9949
  setAliasExpander,
9950
+ setDeferredBound,
8537
9951
  sourceMapTypes,
8538
9952
  stringType,
8539
9953
  stripJsonComments,