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.js CHANGED
@@ -70,6 +70,7 @@ var Punctuators = [
70
70
  ",",
71
71
  ".",
72
72
  "?",
73
+ "=>",
73
74
  "->",
74
75
  "&",
75
76
  "|",
@@ -594,6 +595,13 @@ function applyDirectives(directives, diagnostics, lineOf) {
594
595
  var UNUSED_EXPECT_ERROR = "Unused '@luaut-expect-error' directive";
595
596
 
596
597
  // src/ast/builders.ts
598
+ function bindThis(func, at) {
599
+ bindThisParam(func.params, at);
600
+ func.isMethod = true;
601
+ }
602
+ function bindThisParam(params, at) {
603
+ params.unshift({ type: "FunctionParameter", name: "this", ...spanFrom(at, at) });
604
+ }
597
605
  var ParseError = class extends Error {
598
606
  constructor(message, line, column) {
599
607
  super(`${message} (${line}:${column})`);
@@ -687,10 +695,13 @@ var Parser = class {
687
695
  indentation;
688
696
  /** Populated in recovery mode. */
689
697
  errors = [];
690
- /** Recovery found a block without its `end`. */
698
+ /** Recovery found a block without its `}`. */
691
699
  missingEnd = false;
692
700
  /** The column of the first token on each line, for `indentation`. */
693
701
  lineIndent;
702
+ /** Inside a class body, where `super` means the base class. Outside
703
+ * one it is an ordinary name, so existing code using it still reads. */
704
+ classDepth = 0;
694
705
  constructor(tokens, options = {}) {
695
706
  this.tokens = tokens;
696
707
  this.recover = options.recover ?? false;
@@ -829,8 +840,12 @@ var Parser = class {
829
840
  expressionOr(stop) {
830
841
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
831
842
  }
843
+ /** A comma-separated list of values: a `return`'s, a declaration's, an
844
+ * assignment's. `...xs` spreads an array into it, as in a call's
845
+ * arguments; bare `...` is the vararg pack, as it always was. */
832
846
  expressionListOr(stop) {
833
- const item = () => this.expressionOr(() => stop() || this.checkPunctuator(","));
847
+ const until = () => stop() || this.checkPunctuator(",");
848
+ const item = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
834
849
  const list = [item()];
835
850
  while (this.matchPunctuator(",")) list.push(item());
836
851
  return list;
@@ -998,7 +1013,62 @@ var Parser = class {
998
1013
  // Block / Statement
999
1014
  // ============================================================
1000
1015
  isBlockEnd() {
1001
- return this.isAtEnd() || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
1016
+ return this.isAtEnd() || this.checkPunctuator("}") || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
1017
+ }
1018
+ // ============================================================
1019
+ // Bodies
1020
+ // ------------------------------------------------------------
1021
+ // luaut writes a block in braces — `if (ready) { ... }`, `function f() {
1022
+ // ... }`. The `end` spellings Lua uses are still read, so a file written
1023
+ // in them keeps working while it is being moved over.
1024
+ //
1025
+ // A condition is in parentheses because `f {}` is a call: without them
1026
+ // `if ready { ... }` would be a call of `ready` and then a block. With
1027
+ // them the form is decided by looking past the closing parenthesis, which
1028
+ // is why a parenthesized condition in the older spelling still reads.
1029
+ // ============================================================
1030
+ /** `{ ... }` — a block in braces. */
1031
+ parseBraceBlock() {
1032
+ const brace = this.advance();
1033
+ const body = this.parseBlock(brace);
1034
+ if (this.matchPunctuator("}")) return body;
1035
+ if (!this.recover) this.error(`Expected '}' to close the block opened on line ${brace.line.start}`);
1036
+ this.softError(`Expected '}' to close the block opened on line ${brace.line.start}`);
1037
+ this.missingEnd = true;
1038
+ return body;
1039
+ }
1040
+ /** The `{ ... }` a construct's body is written in. Half-written — the
1041
+ * `{` not typed yet — it is empty and says so, rather than throwing the
1042
+ * whole construct away: what has been written is what an editor answers
1043
+ * from. */
1044
+ parseBracedBody(what) {
1045
+ if (this.checkPunctuator("{")) return this.parseBraceBlock();
1046
+ const at = this.current();
1047
+ if (!this.recover) this.error(`Expected '{' to open the body of '${what}'`);
1048
+ this.softError(`Expected '{' to open the body of '${what}'`);
1049
+ return { type: "Block", statements: [], ...spanFrom(at, at) };
1050
+ }
1051
+ /** Does a `{` follow the parenthesized group starting here? That is what
1052
+ * tells `if (ready) { ... }` from `if (ready) then ... end`. */
1053
+ braceFollowsGroup() {
1054
+ if (!this.checkPunctuator("(")) return false;
1055
+ const closers = { "(": ")", "[": "]", "{": "}" };
1056
+ const stack = [];
1057
+ for (let i = 0; ; i++) {
1058
+ const token = this.peek(i);
1059
+ if (token.type === "EOF") return false;
1060
+ if (token.type === "Punctuator") {
1061
+ const value = String(token.value);
1062
+ if (closers[value]) stack.push(closers[value]);
1063
+ else if (value === stack[stack.length - 1]) {
1064
+ stack.pop();
1065
+ if (!stack.length) {
1066
+ const next = this.peek(i + 1);
1067
+ return next.type === "Punctuator" && next.value === "{";
1068
+ }
1069
+ }
1070
+ }
1071
+ }
1002
1072
  }
1003
1073
  /** `opener` is the token that began the block (`if`, `function`, ...), for
1004
1074
  * indentation recovery. */
@@ -1110,6 +1180,9 @@ var Parser = class {
1110
1180
  if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
1111
1181
  return this.parseTypeAliasStatement();
1112
1182
  }
1183
+ if (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
1184
+ return this.parseClassDeclaration();
1185
+ }
1113
1186
  if (t.type === "Identifier" && t.value === "declare") {
1114
1187
  const p1 = this.peek(1);
1115
1188
  if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
@@ -1135,6 +1208,7 @@ var Parser = class {
1135
1208
  name: p.name || void 0,
1136
1209
  id: p.name ? nameIdentifier(p.name, p) : void 0,
1137
1210
  optional: p.optional,
1211
+ rest: p.rest,
1138
1212
  typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
1139
1213
  line: p.line,
1140
1214
  column: p.column
@@ -1283,7 +1357,7 @@ var Parser = class {
1283
1357
  this.advance();
1284
1358
  if (this.checkIdentifierValue("default")) {
1285
1359
  this.advance();
1286
- const declaration = this.parseExpression(0);
1360
+ const declaration = this.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
1287
1361
  return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
1288
1362
  }
1289
1363
  if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
@@ -1294,6 +1368,10 @@ var Parser = class {
1294
1368
  const declaration = this.parseVariableDeclaration();
1295
1369
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1296
1370
  }
1371
+ if (this.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
1372
+ const declaration = this.parseClassDeclaration();
1373
+ return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1374
+ }
1297
1375
  if (this.checkKeyword("function")) {
1298
1376
  const declaration = this.parseFunctionStatement(true);
1299
1377
  if (declaration.type !== "FunctionDeclaration") this.error("An exported function needs a plain name: 'export function name()'");
@@ -1322,7 +1400,7 @@ var Parser = class {
1322
1400
  const source = this.parseModuleSource();
1323
1401
  return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
1324
1402
  }
1325
- this.error("Expected 'const', 'let', 'function', 'type', 'default', '{' or '*' after 'export'");
1403
+ this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
1326
1404
  }
1327
1405
  // `const x = ...` / `let x, y = ...`.
1328
1406
  // luaut has no `local` — `const` bindings are immutable, `let` mutable.
@@ -1350,63 +1428,63 @@ var Parser = class {
1350
1428
  parseIfStatement() {
1351
1429
  const start = this.current();
1352
1430
  this.expectKeyword("if");
1431
+ return this.parseBracedIf(start);
1432
+ }
1433
+ parseBracedIf(start) {
1353
1434
  const clauses = [];
1354
- const untilThen = () => this.checkKeyword("then");
1355
- const cond = this.expressionOr(untilThen);
1356
- this.expectKeywordSoft("then");
1357
- const body = this.parseBlock(start);
1358
- clauses.push({ type: "IfClause", condition: cond, body, ...spanFrom(cond, this.previous()) });
1359
- while (this.checkKeyword("elseif")) {
1435
+ const clause = () => {
1360
1436
  const clauseStart = this.current();
1361
- this.advance();
1362
- const c = this.expressionOr(untilThen);
1363
- this.expectKeywordSoft("then");
1364
- const b = this.parseBlock(start);
1365
- clauses.push({ type: "IfClause", condition: c, body: b, ...spanFrom(clauseStart, this.previous()) });
1366
- }
1437
+ this.expectPunctuator("(");
1438
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1439
+ this.expectCloser(")");
1440
+ const body = this.parseBracedBody("if");
1441
+ clauses.push({ type: "IfClause", condition, body, ...spanFrom(clauseStart, this.previous()) });
1442
+ };
1443
+ clause();
1367
1444
  let alternate;
1368
- if (this.matchKeyword("else")) {
1369
- alternate = this.parseBlock(start);
1445
+ while (this.checkKeyword("elseif") || this.checkKeyword("else")) {
1446
+ const isElse = this.checkKeyword("else");
1447
+ this.advance();
1448
+ if (!isElse) {
1449
+ clause();
1450
+ continue;
1451
+ }
1452
+ alternate = this.parseBracedBody("else");
1453
+ break;
1370
1454
  }
1371
- this.expectEnd(start);
1372
1455
  return { type: "IfStatement", clauses, alternate, ...spanFrom(start, this.previous()) };
1373
1456
  }
1374
1457
  parseWhileStatement() {
1375
1458
  const start = this.current();
1376
1459
  this.expectKeyword("while");
1377
- const condition = this.expressionOr(() => this.checkKeyword("do"));
1378
- this.expectKeywordSoft("do");
1379
- const body = this.parseBlock(start);
1380
- this.expectEnd(start);
1460
+ this.expectPunctuator("(");
1461
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1462
+ this.expectCloser(")");
1463
+ const body = this.parseBracedBody("while");
1381
1464
  return { type: "WhileStatement", condition, body, ...spanFrom(start, this.previous()) };
1382
1465
  }
1383
1466
  parseRepeatStatement() {
1384
1467
  const start = this.current();
1385
1468
  this.expectKeyword("repeat");
1386
- const body = this.parseBlock(start);
1387
- let condition;
1388
- if (this.checkKeyword("until") || !this.recover) {
1389
- this.expectKeyword("until");
1390
- condition = this.expressionOr(() => false);
1391
- } else {
1392
- this.softError(`Expected 'until' to close 'repeat' on line ${start.line.start}`);
1393
- this.missingEnd = true;
1394
- condition = this.errorExpression(this.current(), this.cursor);
1395
- }
1469
+ const body = this.parseBracedBody("repeat");
1470
+ this.expectKeyword("until");
1471
+ this.expectPunctuator("(");
1472
+ const condition = this.expressionOr(() => this.checkPunctuator(")"));
1473
+ this.expectCloser(")");
1396
1474
  return { type: "RepeatStatement", body, condition, ...spanFrom(start, this.previous()) };
1397
1475
  }
1398
1476
  parseDoStatement() {
1399
1477
  const start = this.current();
1400
1478
  this.expectKeyword("do");
1401
- const body = this.parseBlock(start);
1402
- this.expectEnd(start);
1479
+ const body = this.parseBracedBody("do");
1403
1480
  return { type: "DoStatement", body, ...spanFrom(start, this.previous()) };
1404
1481
  }
1405
1482
  parseForStatement() {
1406
1483
  const start = this.current();
1407
1484
  this.expectKeyword("for");
1485
+ this.expectPunctuator("(");
1408
1486
  const first = this.parseBindingTarget(true);
1409
- const untilDo = () => this.checkKeyword("do");
1487
+ const untilDo = () => this.checkPunctuator(")");
1410
1488
  if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
1411
1489
  const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1412
1490
  this.expectPunctuator(",");
@@ -1415,9 +1493,7 @@ var Parser = class {
1415
1493
  if (this.matchPunctuator(",")) {
1416
1494
  step = this.expressionOr(untilDo);
1417
1495
  }
1418
- this.expectKeywordSoft("do");
1419
- const body2 = this.parseBlock(start);
1420
- this.expectEnd(start);
1496
+ const body2 = this.parseForBody();
1421
1497
  return {
1422
1498
  type: "NumericForStatement",
1423
1499
  variable: this.identifierPatternToTypedIdentifier(first),
@@ -1434,9 +1510,7 @@ var Parser = class {
1434
1510
  }
1435
1511
  this.expectKeyword("in");
1436
1512
  const iterators = this.expressionListOr(untilDo);
1437
- this.expectKeywordSoft("do");
1438
- const body = this.parseBlock(start);
1439
- this.expectEnd(start);
1513
+ const body = this.parseForBody();
1440
1514
  return {
1441
1515
  type: "GenericForStatement",
1442
1516
  variables,
@@ -1445,6 +1519,10 @@ var Parser = class {
1445
1519
  ...spanFrom(start, this.previous())
1446
1520
  };
1447
1521
  }
1522
+ parseForBody() {
1523
+ this.expectCloser(")");
1524
+ return this.parseBracedBody("for");
1525
+ }
1448
1526
  /** `function name() end` declares `name`; `function a.b() end` and
1449
1527
  * `function T:m() end` define a member. */
1450
1528
  /** `exported` — the `export` before this `function` has been consumed, so
@@ -1511,6 +1589,183 @@ var Parser = class {
1511
1589
  if (!this.recover) throw error;
1512
1590
  this.record(error);
1513
1591
  }
1592
+ /** `class Name extends Base <members> end`.
1593
+ *
1594
+ * The body is a block like every other in luaut, closed by `end` — not a
1595
+ * brace-delimited list. Members are written the way the same thing is
1596
+ * written outside a class: a field like a field (`x: number`), a method
1597
+ * like a function (`function m() ... end`). */
1598
+ parseClassDeclaration() {
1599
+ const start = this.current();
1600
+ this.advance();
1601
+ const name = this.parseIdentifier();
1602
+ const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1603
+ const { superclass, superArguments } = this.parseExtends();
1604
+ const members = this.parseClassBody(start);
1605
+ return {
1606
+ type: "ClassDeclaration",
1607
+ name,
1608
+ typeParams,
1609
+ superclass,
1610
+ superArguments,
1611
+ members,
1612
+ ...spanFrom(start, this.previous())
1613
+ };
1614
+ }
1615
+ /** `class ... end` as a value. It may be named — the name is for the class
1616
+ * itself, not for the scope around it — and takes no type parameters,
1617
+ * since nothing could write the arguments. */
1618
+ parseClassExpression() {
1619
+ const start = this.current();
1620
+ this.advance();
1621
+ const name = this.checkType("Identifier") && !this.checkIdentifierValue("extends") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(") ? this.parseIdentifier() : void 0;
1622
+ if (this.checkOperator("<")) {
1623
+ this.error("A class written as a value takes no type parameters: nothing could write the arguments");
1624
+ }
1625
+ const { superclass, superArguments } = this.parseExtends();
1626
+ const members = this.parseClassBody(start);
1627
+ return { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
1628
+ }
1629
+ /** `extends Base` / `extends Box<number>`. */
1630
+ parseExtends() {
1631
+ if (!this.checkIdentifierValue("extends")) return {};
1632
+ this.advance();
1633
+ const superclass = this.parseIdentifier();
1634
+ let superArguments;
1635
+ if (this.checkOperator("<")) {
1636
+ const written = this.tryTypeArguments();
1637
+ if (written) superArguments = written;
1638
+ }
1639
+ return { superclass, superArguments };
1640
+ }
1641
+ parseClassBody(start) {
1642
+ void start;
1643
+ if (!this.checkPunctuator("{")) this.error("Expected '{' to open the class body");
1644
+ this.advance();
1645
+ const members = [];
1646
+ this.classDepth++;
1647
+ try {
1648
+ while (!this.checkPunctuator("}") && !this.isAtEnd()) {
1649
+ if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
1650
+ const member = this.parseClassMember();
1651
+ if (member) members.push(member);
1652
+ }
1653
+ } finally {
1654
+ this.classDepth--;
1655
+ }
1656
+ this.expectCloser("}");
1657
+ return members;
1658
+ }
1659
+ /** `<A, B>` in a type position that is not a call: the arguments a class
1660
+ * extends its base with. */
1661
+ tryTypeArguments() {
1662
+ const saved = this.cursor;
1663
+ try {
1664
+ this.expectOperator("<");
1665
+ const args = [this.parseType()];
1666
+ while (this.matchPunctuator(",")) args.push(this.parseType());
1667
+ this.expectOperator(">");
1668
+ return args;
1669
+ } catch (error) {
1670
+ if (error instanceof ParseError || error instanceof ParseRecover) {
1671
+ this.cursor = saved;
1672
+ return void 0;
1673
+ }
1674
+ throw error;
1675
+ }
1676
+ }
1677
+ parseClassMember() {
1678
+ const start = this.current();
1679
+ const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1680
+ if (isStatic) this.advance();
1681
+ if (this.checkKeyword("function")) {
1682
+ this.advance();
1683
+ const memberName = this.parseIdentifier();
1684
+ const signatures = [];
1685
+ let written = memberName;
1686
+ while (true) {
1687
+ const head = this.parseFunctionHead();
1688
+ if (this.isClassOverloadContinuation(memberName.name, isStatic)) {
1689
+ if (!isStatic) bindThisParam(head.params, start);
1690
+ signatures.push({ ...this.headToSignature(head), name: written });
1691
+ if (isStatic) this.advance();
1692
+ this.expectKeyword("function");
1693
+ written = this.parseIdentifier();
1694
+ continue;
1695
+ }
1696
+ const func = this.headToBody(head, start);
1697
+ if (!isStatic) bindThis(func, start);
1698
+ return {
1699
+ type: "ClassMethod",
1700
+ name: memberName,
1701
+ isStatic,
1702
+ func,
1703
+ signatures: signatures.length ? signatures : void 0,
1704
+ ...spanFrom(start, this.previous())
1705
+ };
1706
+ }
1707
+ }
1708
+ if (!isStatic && this.checkIdentifierValue("constructor") && this.punctuatorAt(1, "(")) {
1709
+ this.advance();
1710
+ const head = this.parseFunctionHead();
1711
+ if (head.returnType) this.problem("A constructor has no return type; it always builds the instance");
1712
+ const func = this.headToBody(head, start);
1713
+ bindThis(func, start);
1714
+ return { type: "ClassConstructor", func, ...spanFrom(start, this.previous()) };
1715
+ }
1716
+ if ((this.checkIdentifierValue("get") || this.checkIdentifierValue("set")) && this.peek(1).type === "Identifier" && this.punctuatorAt(2, "(")) {
1717
+ const kind = this.advance().value;
1718
+ const memberName = this.parseIdentifier();
1719
+ const head = this.parseFunctionHead();
1720
+ const func = this.headToBody(head, start);
1721
+ if (!isStatic) bindThis(func, start);
1722
+ const written = func.params.length - (isStatic ? 0 : 1);
1723
+ if (kind === "get" && written > 0) {
1724
+ this.problem("A getter takes no parameters");
1725
+ }
1726
+ if (kind === "set" && written !== 1) {
1727
+ this.problem("A setter takes exactly one parameter: the value being assigned");
1728
+ }
1729
+ return { type: "ClassAccessor", kind, name: memberName, isStatic, func, ...spanFrom(start, this.previous()) };
1730
+ }
1731
+ if (this.checkType("Identifier")) {
1732
+ const memberName = this.parseIdentifier();
1733
+ let typeAnnotation;
1734
+ if (this.matchPunctuator(":")) {
1735
+ typeAnnotation = this.typeOr(() => this.checkOperator("="));
1736
+ }
1737
+ let init;
1738
+ if (this.matchOperator("=")) init = this.expressionOr(() => false);
1739
+ if (!typeAnnotation && !init) {
1740
+ this.error("A class field needs a type ('name: T') or a value ('name = v')");
1741
+ }
1742
+ return { type: "ClassField", name: memberName, isStatic, typeAnnotation, init, ...spanFrom(start, this.previous()) };
1743
+ }
1744
+ if (this.recover) {
1745
+ this.softError("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1746
+ this.advance();
1747
+ return void 0;
1748
+ }
1749
+ this.error("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
1750
+ }
1751
+ /** Give a class method its `this`: a real first parameter, the way
1752
+ * `function T:m()` gets a real `self`. Every later pass — scopes, types,
1753
+ * arity, lowering — then sees an ordinary parameter and needs to know
1754
+ * nothing about classes. */
1755
+ /** After a bodyless head inside a class body, does another declaration of
1756
+ * the same member follow? Then the head was an overload signature. */
1757
+ isClassOverloadContinuation(name, isStatic) {
1758
+ const offset = isStatic ? 1 : 0;
1759
+ if (isStatic && !this.checkIdentifierValue("static")) return false;
1760
+ const keyword = this.peek(offset);
1761
+ if (!(keyword.type === "Keyword" && keyword.value === "function")) return false;
1762
+ const named = this.peek(offset + 1);
1763
+ return named.type === "Identifier" && named.value === name;
1764
+ }
1765
+ operatorAt(ahead, value) {
1766
+ const token = this.peek(ahead);
1767
+ return token.type === "Operator" && token.value === value;
1768
+ }
1514
1769
  parseFunctionName() {
1515
1770
  const start = this.current();
1516
1771
  const base = this.parseIdentifier();
@@ -1604,7 +1859,7 @@ var Parser = class {
1604
1859
  ...spanFrom(start, this.previous())
1605
1860
  };
1606
1861
  }
1607
- if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
1862
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
1608
1863
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
1609
1864
  }
1610
1865
  this.error("Unexpected expression statement (expected assignment or call)");
@@ -1792,12 +2047,20 @@ var Parser = class {
1792
2047
  if (t.type === "Keyword" && t.value === "if") {
1793
2048
  return this.parseIfElseExpression();
1794
2049
  }
2050
+ if (t.type === "Identifier" && t.value === "class") {
2051
+ return this.parseClassExpression();
2052
+ }
1795
2053
  if (t.type === "Punctuator" && t.value === "{") {
1796
2054
  return this.parseTableExpression();
1797
2055
  }
1798
2056
  if (t.type === "Punctuator" && t.value === "[") {
1799
2057
  return this.parseArrayExpression();
1800
2058
  }
2059
+ if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
2060
+ if (this.checkPunctuator("(") || this.checkOperator("<")) {
2061
+ const arrow = this.tryParse(() => this.parseArrow());
2062
+ if (arrow) return arrow;
2063
+ }
1801
2064
  if (t.type === "Identifier" || t.type === "Punctuator" && t.value === "(") {
1802
2065
  return this.parsePrefixExpression();
1803
2066
  }
@@ -1811,7 +2074,7 @@ var Parser = class {
1811
2074
  } else {
1812
2075
  let expression;
1813
2076
  try {
1814
- expression = shiftSpans(parseExpressionFromSource(p.raw), p.line, p.column);
2077
+ expression = shiftSpans(parseExpressionFromSource(p.raw, this.classDepth > 0), p.line, p.column);
1815
2078
  } catch (e) {
1816
2079
  if (!this.recover || !(e instanceof ParseError || e instanceof LexError)) throw e;
1817
2080
  const at = token;
@@ -1849,7 +2112,12 @@ var Parser = class {
1849
2112
  parsePrefixExpression() {
1850
2113
  const start = this.current();
1851
2114
  let base;
1852
- if (this.checkType("Identifier")) {
2115
+ if (this.startsNew()) {
2116
+ base = this.parseNewExpression();
2117
+ } else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2118
+ this.advance();
2119
+ base = { type: "SuperExpression", ...spanFrom(start, start) };
2120
+ } else if (this.checkType("Identifier")) {
1853
2121
  base = this.parseIdentifier();
1854
2122
  } else if (this.matchPunctuator("(")) {
1855
2123
  const inner = this.parseExpression();
@@ -1946,11 +2214,13 @@ var Parser = class {
1946
2214
  }
1947
2215
  }
1948
2216
  if (this.startsCallArguments()) {
2217
+ const onNewLine = this.current().line.start > base.line.end;
1949
2218
  const args = this.parseCallArguments();
1950
2219
  base = {
1951
2220
  type: "CallExpression",
1952
2221
  callee: base,
1953
2222
  arguments: args,
2223
+ argumentsOnNewLine: onNewLine || void 0,
1954
2224
  ...spanFrom(base, this.previous())
1955
2225
  };
1956
2226
  continue;
@@ -1959,6 +2229,52 @@ var Parser = class {
1959
2229
  }
1960
2230
  return base;
1961
2231
  }
2232
+ /** `new` is a soft keyword: it starts a construction only when a name
2233
+ * follows it, so a function or field called `new` — `Instance.new(x)`,
2234
+ * and `Vec.new(1)` itself — is untouched. */
2235
+ startsNew() {
2236
+ return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2237
+ }
2238
+ /** `super` on its own means the base class, and only `super(...)` and
2239
+ * `super.member` say anything; anything else is a name that happens to
2240
+ * be spelled that way. */
2241
+ startsSuperUse() {
2242
+ return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2243
+ }
2244
+ /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2245
+ * name reached through a module — never an arbitrary expression, so the
2246
+ * arguments are unambiguously the constructor's. */
2247
+ parseNewExpression() {
2248
+ const start = this.current();
2249
+ this.advance();
2250
+ let callee = this.parseIdentifier();
2251
+ while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2252
+ this.advance();
2253
+ const property = this.parseIdentifier();
2254
+ callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2255
+ }
2256
+ const typeArguments = this.tryCallTypeArguments();
2257
+ if (!this.checkPunctuator("(")) {
2258
+ this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2259
+ }
2260
+ const args = this.parseCallArguments();
2261
+ return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2262
+ }
2263
+ /** After `...`, is there something to spread? Nothing following it means
2264
+ * the vararg pack, which is what `f(...)` has always passed on. */
2265
+ startsSpread() {
2266
+ const next = this.peek(1);
2267
+ if (next.type === "Punctuator") {
2268
+ const value = next.value;
2269
+ return value === "(" || value === "{" || value === "[";
2270
+ }
2271
+ return next.type === "Identifier" || next.type === "Literal" || next.type === "InterpolatedString";
2272
+ }
2273
+ parseSpreadArgument(stop) {
2274
+ const dots = this.advance();
2275
+ const argument = this.expressionOr(stop);
2276
+ return { type: "SpreadElement", argument, ...spanFrom(dots, argument) };
2277
+ }
1962
2278
  /** Is the token `ahead` places on the punctuator `value`? */
1963
2279
  punctuatorAt(ahead, value) {
1964
2280
  const token = this.peek(ahead);
@@ -2005,7 +2321,7 @@ var Parser = class {
2005
2321
  while (true) {
2006
2322
  if (this.recover && this.onNewLine() && this.startsTableField() && !this.startsMethodCall(1)) break;
2007
2323
  const before = this.cursor;
2008
- const argument = this.expressionOr(stop);
2324
+ const argument = this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(stop) : this.expressionOr(stop);
2009
2325
  if (argument.type !== "ErrorExpression" || this.cursor > before || list.length) list.push(argument);
2010
2326
  if (this.matchPunctuator(",") && !this.checkPunctuator(")")) continue;
2011
2327
  if (!this.recover || this.checkPunctuator(")")) break;
@@ -2108,8 +2424,12 @@ var Parser = class {
2108
2424
  while (!this.checkPunctuator("]")) {
2109
2425
  if (this.checkOperator("...")) {
2110
2426
  const dots = this.advance();
2111
- const argument = this.expressionOr(stop);
2112
- elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
2427
+ if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2428
+ elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2429
+ } else {
2430
+ const argument = this.expressionOr(stop);
2431
+ elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
2432
+ }
2113
2433
  } else {
2114
2434
  elements.push(this.expressionOr(stop));
2115
2435
  }
@@ -2298,8 +2618,26 @@ var Parser = class {
2298
2618
  if (!this.checkPunctuator(")")) {
2299
2619
  while (true) {
2300
2620
  if (this.checkOperator("...")) {
2301
- this.advance();
2621
+ const dots = this.advance();
2302
2622
  hasVarargs = true;
2623
+ if (this.checkType("Identifier")) {
2624
+ const nameTok = this.expectIdentifier();
2625
+ let typeAnnotation2;
2626
+ if (this.matchPunctuator(":")) {
2627
+ typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2628
+ }
2629
+ params.push({
2630
+ type: "FunctionParameter",
2631
+ name: nameTok.value,
2632
+ typeAnnotation: typeAnnotation2,
2633
+ rest: true,
2634
+ ...spanFrom(dots, this.previous())
2635
+ });
2636
+ if (this.checkPunctuator(",")) {
2637
+ this.problem("A rest parameter is the last one: nothing can follow '...'");
2638
+ }
2639
+ break;
2640
+ }
2303
2641
  if (this.matchPunctuator(":")) {
2304
2642
  varargTypeAnnotation = this.parseTypeOrTypePackReference();
2305
2643
  }
@@ -2391,10 +2729,64 @@ var Parser = class {
2391
2729
  }
2392
2730
  return void 0;
2393
2731
  }
2732
+ /** Run `parse`, and put the parser back where it was if it fails. Used
2733
+ * where two forms start alike and only their end tells them apart: `(a,
2734
+ * b) => a + b` and `(a + b)` both open with a `(`. */
2735
+ tryParse(parse2) {
2736
+ const cursor = this.cursor;
2737
+ const errors = this.errors.length;
2738
+ try {
2739
+ return parse2();
2740
+ } catch (error) {
2741
+ if (!(error instanceof ParseError || error instanceof ParseRecover)) throw error;
2742
+ this.cursor = cursor;
2743
+ this.errors.length = errors;
2744
+ return void 0;
2745
+ }
2746
+ }
2747
+ /** `x => x * 2` — a function, written short. The body is an expression,
2748
+ * which is returned, or a block in braces, as in TypeScript. */
2749
+ parseArrow() {
2750
+ const start = this.current();
2751
+ const head = this.checkType("Identifier") ? (() => {
2752
+ const name = this.expectIdentifier();
2753
+ return {
2754
+ start,
2755
+ generics: [],
2756
+ params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
2757
+ hasVarargs: false,
2758
+ varargTypeAnnotation: void 0,
2759
+ returnType: void 0,
2760
+ predicate: void 0
2761
+ };
2762
+ })() : this.parseFunctionHead();
2763
+ this.expectPunctuator("=>");
2764
+ const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
2765
+ const func = {
2766
+ type: "FunctionBody",
2767
+ generics: head.generics,
2768
+ params: head.params,
2769
+ hasVarargs: head.hasVarargs,
2770
+ varargTypeAnnotation: head.varargTypeAnnotation,
2771
+ returnType: head.returnType,
2772
+ predicate: head.predicate,
2773
+ body,
2774
+ ...spanFrom(start, this.previous())
2775
+ };
2776
+ return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
2777
+ }
2778
+ /** A one-expression body: the value is what the function returns. */
2779
+ returnOf(expression) {
2780
+ const statement = {
2781
+ type: "ReturnStatement",
2782
+ arguments: [expression],
2783
+ ...spanFrom(expression, expression)
2784
+ };
2785
+ return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
2786
+ }
2394
2787
  parseFunctionBody(opener) {
2395
2788
  const head = this.parseFunctionHead();
2396
- const body = this.parseBlock(opener);
2397
- this.expectEnd(opener);
2789
+ const body = this.parseStatementBody(opener);
2398
2790
  return {
2399
2791
  type: "FunctionBody",
2400
2792
  generics: head.generics,
@@ -2419,9 +2811,13 @@ var Parser = class {
2419
2811
  ...spanFrom(head.start, this.previous())
2420
2812
  };
2421
2813
  }
2814
+ /** A function's statements. */
2815
+ parseStatementBody(opener) {
2816
+ void opener;
2817
+ return this.parseBracedBody("function");
2818
+ }
2422
2819
  headToBody(head, opener) {
2423
- const body = this.parseBlock(opener);
2424
- this.expectEnd(opener);
2820
+ const body = this.parseStatementBody(opener);
2425
2821
  return {
2426
2822
  type: "FunctionBody",
2427
2823
  generics: head.generics,
@@ -2645,8 +3041,21 @@ var Parser = class {
2645
3041
  if (!this.checkPunctuator(")")) {
2646
3042
  while (true) {
2647
3043
  if (this.checkOperator("...")) {
2648
- this.advance();
3044
+ const dots = this.advance();
2649
3045
  hasVarargs = true;
3046
+ if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3047
+ const nameTok2 = this.expectIdentifier();
3048
+ this.advance();
3049
+ params.push({
3050
+ type: "FunctionTypeParameter",
3051
+ name: nameTok2.value,
3052
+ id: tokenIdentifier(nameTok2),
3053
+ typeAnnotation: this.parseType(),
3054
+ rest: true,
3055
+ ...spanFrom(dots, this.previous())
3056
+ });
3057
+ break;
3058
+ }
2650
3059
  varargType = this.parseType();
2651
3060
  break;
2652
3061
  }
@@ -2684,7 +3093,7 @@ var Parser = class {
2684
3093
  }
2685
3094
  }
2686
3095
  this.expectPunctuator(")");
2687
- if (this.matchPunctuator("->")) {
3096
+ if (this.matchPunctuator("=>") || this.matchPunctuator("->")) {
2688
3097
  const predicate = this.tryParseTypePredicate();
2689
3098
  const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
2690
3099
  return {
@@ -2827,8 +3236,22 @@ var Parser = class {
2827
3236
  optional: optional2,
2828
3237
  ...spanFrom(propStart, this.previous())
2829
3238
  });
3239
+ } 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 === ":")) {
3240
+ const keyTok = this.advance();
3241
+ const name = String(keyTok.value);
3242
+ const optional2 = this.matchPunctuator("?");
3243
+ this.expectPunctuator(":");
3244
+ const valueType = this.parseType();
3245
+ properties.push({
3246
+ type: "TableTypeProperty",
3247
+ name,
3248
+ key: tokenIdentifier(keyTok),
3249
+ valueType,
3250
+ optional: optional2,
3251
+ ...spanFrom(propStart, this.previous())
3252
+ });
2830
3253
  } else {
2831
- this.error("Expected object type property ('name: T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples");
3254
+ this.error(`Expected object type property ('name: T', '"name": T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples`);
2832
3255
  }
2833
3256
  if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
2834
3257
  break;
@@ -2914,9 +3337,10 @@ function parseTypeFromSource(raw) {
2914
3337
  const parser = new Parser(tokenize(raw));
2915
3338
  return parser.parseType();
2916
3339
  }
2917
- function parseExpressionFromSource(raw) {
3340
+ function parseExpressionFromSource(raw, inClass = false) {
2918
3341
  const tokens = tokenize(raw);
2919
3342
  const parser = new Parser(tokens);
3343
+ if (inClass) parser.classDepth = 1;
2920
3344
  const expr = parser.parseExpression();
2921
3345
  return expr;
2922
3346
  }
@@ -3025,6 +3449,11 @@ var Analyzer = class {
3025
3449
  hoistFunctions(block, scope) {
3026
3450
  for (const statement of block.statements) {
3027
3451
  const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
3452
+ if (declaration.type === "ClassDeclaration") {
3453
+ this.declare(scope, declaration.name.name, "local", declaration.name, true, "class");
3454
+ this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
3455
+ continue;
3456
+ }
3028
3457
  if (declaration.type !== "FunctionDeclaration") continue;
3029
3458
  this.declare(scope, declaration.name.name, "local", declaration.name, true, "function");
3030
3459
  this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
@@ -3037,7 +3466,7 @@ var Analyzer = class {
3037
3466
  * hoisted, and this does not apply. */
3038
3467
  checkUseBeforeDefine(identifier, id) {
3039
3468
  const binding = this.bindings.get(id);
3040
- if (binding.declaredBy !== "function" || this.typeQueryDepth > 0) return;
3469
+ if (binding.declaredBy !== "function" && binding.declaredBy !== "class" || this.typeQueryDepth > 0) return;
3041
3470
  const declaration = binding.declarationNode;
3042
3471
  const depth = declaration && this.hoisted.get(declaration);
3043
3472
  if (depth === void 0 || depth !== this.functionDepth) return;
@@ -3292,6 +3721,11 @@ var Analyzer = class {
3292
3721
  this.visitFunctionBody(stmt.func, scope);
3293
3722
  return;
3294
3723
  }
3724
+ case "ClassDeclaration": {
3725
+ if (!this.hoisted.has(stmt.name)) this.declare(scope, stmt.name.name, "local", stmt.name, true, "class");
3726
+ this.visitClassBody(stmt, scope);
3727
+ return;
3728
+ }
3295
3729
  case "FunctionDeclarationStatement": {
3296
3730
  if (stmt.target.path.length === 0 && !stmt.target.method) {
3297
3731
  this.referenceAsAssignmentTarget(scope, stmt.target.base);
@@ -3411,7 +3845,8 @@ var Analyzer = class {
3411
3845
  this.visitStatement(stmt.declaration, scope);
3412
3846
  return;
3413
3847
  case "ExportDefaultStatement":
3414
- this.visitExpression(stmt.declaration, scope);
3848
+ if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, scope);
3849
+ else this.visitExpression(stmt.declaration, scope);
3415
3850
  return;
3416
3851
  case "ExportNamedStatement":
3417
3852
  if (!stmt.source) {
@@ -3459,6 +3894,33 @@ var Analyzer = class {
3459
3894
  }
3460
3895
  /** `<K extends typeof config>` — a constraint is a type like any other,
3461
3896
  * and the `typeof` in it reads a value. */
3897
+ /** A class body. Its type parameters live in a scope of their own, and
3898
+ * every member is written inside it — so a method's annotations see `T`,
3899
+ * and everything else sees what the class declaration sees, itself
3900
+ * included. `this` is not declared here: the parser makes it a real first
3901
+ * parameter, so it arrives with the rest of them. */
3902
+ visitClassBody(node, outer) {
3903
+ const scope = node.typeParams?.length ? childScope(outer) : outer;
3904
+ this.visitGenerics(node.typeParams, scope);
3905
+ if (node.superclass) this.reference(outer, node.superclass);
3906
+ for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
3907
+ for (const member of node.members) {
3908
+ switch (member.type) {
3909
+ case "ClassField":
3910
+ this.visitType(member.typeAnnotation, scope);
3911
+ if (member.init) this.visitExpression(member.init, scope);
3912
+ break;
3913
+ case "ClassMethod":
3914
+ for (const signature of member.signatures ?? []) this.visitSignature(signature, scope);
3915
+ this.visitFunctionBody(member.func, scope, member.func.isMethod);
3916
+ break;
3917
+ case "ClassAccessor":
3918
+ case "ClassConstructor":
3919
+ this.visitFunctionBody(member.func, scope, member.func.isMethod);
3920
+ break;
3921
+ }
3922
+ }
3923
+ }
3462
3924
  visitGenerics(generics, scope) {
3463
3925
  for (const generic of generics ?? []) {
3464
3926
  this.visitType(generic.constraint, scope);
@@ -3541,6 +4003,22 @@ var Analyzer = class {
3541
4003
  this.visitExpression(expr.callee, scope);
3542
4004
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
3543
4005
  return;
4006
+ case "NewExpression":
4007
+ this.visitExpression(expr.callee, scope);
4008
+ for (const argument of expr.arguments) this.visitExpression(argument, scope);
4009
+ for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4010
+ return;
4011
+ case "SuperExpression":
4012
+ return;
4013
+ case "SpreadElement":
4014
+ this.visitExpression(expr.argument, scope);
4015
+ return;
4016
+ case "ClassExpression": {
4017
+ const inner = childScope(scope);
4018
+ if (expr.name) this.declare(inner, expr.name.name, "local", expr.name, true, "class");
4019
+ this.visitClassBody(expr, inner);
4020
+ return;
4021
+ }
3544
4022
  case "MethodCallExpression":
3545
4023
  this.visitExpression(expr.object, scope);
3546
4024
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
@@ -3622,6 +4100,14 @@ function preludeProgram() {
3622
4100
  function isClassType(t) {
3623
4101
  return t.kind === "object" && t.class !== void 0;
3624
4102
  }
4103
+ function isClassAssignable(got, want) {
4104
+ if (!got || !got.ancestors.includes(want.name)) return false;
4105
+ const wanted = want.typeArguments?.get(want.name);
4106
+ if (!wanted?.length) return true;
4107
+ const given = got.typeArguments?.get(want.name);
4108
+ if (!given) return true;
4109
+ return wanted.every((w, i) => given[i] !== void 0 && isAssignable(given[i], w));
4110
+ }
3625
4111
  function typeParam(name, constraint, isConst) {
3626
4112
  return { kind: "typeParam", name, constraint, isConst };
3627
4113
  }
@@ -3673,11 +4159,21 @@ function substitute(t, subst) {
3673
4159
  case "object": {
3674
4160
  const entries = [];
3675
4161
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
3676
- return objectType(
4162
+ const out = objectType(
3677
4163
  entries,
3678
4164
  t.indexer && { key: substitute(t.indexer.key, subst), value: substitute(t.indexer.value, subst) },
3679
4165
  t.frozen
3680
4166
  );
4167
+ if (t.name) out.name = t.name;
4168
+ if (t.class) {
4169
+ out.class = {
4170
+ ...t.class,
4171
+ typeArguments: t.class.typeArguments && new Map(
4172
+ [...t.class.typeArguments].map(([name, args]) => [name, args.map((a) => substitute(a, subst))])
4173
+ )
4174
+ };
4175
+ }
4176
+ return out;
3681
4177
  }
3682
4178
  case "function": {
3683
4179
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
@@ -3744,6 +4240,11 @@ function substitute(t, subst) {
3744
4240
  return t;
3745
4241
  }
3746
4242
  }
4243
+ function classArguments(t, name) {
4244
+ if (t.kind === "genericRef") return t.name === name ? t.typeArguments : void 0;
4245
+ if (t.kind === "object") return t.class?.typeArguments?.get(name);
4246
+ return void 0;
4247
+ }
3747
4248
  function unify(param, arg, vars, out) {
3748
4249
  if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
3749
4250
  unify(param.constraint, arg, vars, out);
@@ -3771,7 +4272,12 @@ function unify(param, arg, vars, out) {
3771
4272
  }
3772
4273
  return;
3773
4274
  case "object":
3774
- if (param.class) return;
4275
+ if (param.class) {
4276
+ const wanted = param.class.typeArguments?.get(param.class.name);
4277
+ const given = classArguments(arg, param.class.name);
4278
+ if (wanted && given) wanted.forEach((w, i) => given[i] && unify(w, given[i], vars, out));
4279
+ return;
4280
+ }
3775
4281
  if (arg.kind === "object" && !arg.class) {
3776
4282
  for (const [k, pv] of param.properties) {
3777
4283
  const av = arg.properties.get(k);
@@ -3780,6 +4286,11 @@ function unify(param, arg, vars, out) {
3780
4286
  if (param.indexer && arg.indexer) unify(param.indexer.value, arg.indexer.value, vars, out);
3781
4287
  }
3782
4288
  return;
4289
+ case "genericRef": {
4290
+ const given = classArguments(arg, param.name);
4291
+ if (given) param.typeArguments.forEach((p, i) => given[i] && unify(p, given[i], vars, out));
4292
+ return;
4293
+ }
3783
4294
  case "union":
3784
4295
  for (const m of param.types) unify(m, arg, vars, out);
3785
4296
  return;
@@ -3874,6 +4385,10 @@ var expandAlias;
3874
4385
  function setAliasExpander(fn2) {
3875
4386
  expandAlias = fn2;
3876
4387
  }
4388
+ var deferredBound;
4389
+ function setDeferredBound(fn2) {
4390
+ deferredBound = fn2;
4391
+ }
3877
4392
  var comparing = [];
3878
4393
  function isAssignable(rawA, rawB) {
3879
4394
  let a = isNoValue(rawA) ? nilType : rawA;
@@ -3904,10 +4419,29 @@ function isAssignableInner(a, b) {
3904
4419
  if (b.kind === "unknown") return true;
3905
4420
  if (b.kind === "never") return false;
3906
4421
  if (a.kind === "unknown") return false;
4422
+ if (a.kind === "difference" && b.kind === "difference") {
4423
+ return isAssignable(a.base, b.base) && isAssignable(b.excluded, a.excluded);
4424
+ }
3907
4425
  if (b.kind === "difference") {
3908
4426
  return isAssignable(a, b.base) && !overlaps(a, b.excluded);
3909
4427
  }
3910
- if (a.kind === "difference") return isAssignable(a.base, b);
4428
+ if (a.kind === "difference") {
4429
+ if (b.kind === "union" && b.types.some((m) => isAssignable(a, m))) return true;
4430
+ return isAssignable(a.base, b);
4431
+ }
4432
+ if (a.kind === "conditional" || a.kind === "indexedAccess") {
4433
+ if ((b.kind === "conditional" || b.kind === "indexedAccess" || b.kind === "union") && equalTypes(a, b)) {
4434
+ return true;
4435
+ }
4436
+ if (b.kind === "union" && b.types.some((m) => equalTypes(a, m))) return true;
4437
+ const bound = deferredBound?.(a);
4438
+ if (bound && bound !== a) return isAssignable(bound, b);
4439
+ }
4440
+ if (a.kind === "typeParam") {
4441
+ if (b.kind === "typeParam" && a.name === b.name) return true;
4442
+ if (b.kind === "union" && b.types.some((m) => m.kind === "typeParam" && m.name === a.name)) return true;
4443
+ return a.constraint ? isAssignable(a.constraint, b) : false;
4444
+ }
3911
4445
  if (a.kind === "union") return a.types.every((t) => isAssignable(t, b));
3912
4446
  if (b.kind === "union") return b.types.some((t) => isAssignable(a, t));
3913
4447
  if (b.kind === "intersection") return b.types.every((t) => isAssignable(a, t));
@@ -3941,7 +4475,7 @@ function isAssignableInner(a, b) {
3941
4475
  }
3942
4476
  if (a.kind === "object") {
3943
4477
  if (b.kind !== "object") return false;
3944
- if (b.class) return a.class !== void 0 && a.class.ancestors.includes(b.class.name);
4478
+ if (b.class) return isClassAssignable(a.class, b.class);
3945
4479
  if (a.class && (b.indexer || b.properties.size === 0)) return false;
3946
4480
  for (const [name, bp] of b.properties) {
3947
4481
  const ap = a.properties.get(name);
@@ -3950,7 +4484,7 @@ function isAssignableInner(a, b) {
3950
4484
  if (a.indexer && isAssignable(a.indexer.value, bp.type)) continue;
3951
4485
  return false;
3952
4486
  }
3953
- if (!isAssignable(ap.type, bp.type)) return false;
4487
+ if (!isAssignable(ap.type, bp.optional ? optional(bp.type) : bp.type)) return false;
3954
4488
  }
3955
4489
  if (b.indexer) {
3956
4490
  for (const [name, ap] of a.properties) {
@@ -3971,10 +4505,6 @@ function isAssignableInner(a, b) {
3971
4505
  }
3972
4506
  return isAssignable(a.returns, b.returns);
3973
4507
  }
3974
- if (a.kind === "typeParam") {
3975
- if (b.kind === "typeParam" && a.name === b.name) return true;
3976
- return a.constraint ? isAssignable(a.constraint, b) : false;
3977
- }
3978
4508
  if (b.kind === "typeParam") return false;
3979
4509
  if (a.kind === "genericRef" || b.kind === "genericRef") {
3980
4510
  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]));
@@ -4094,7 +4624,9 @@ function containsFreeTypeParam(t, seen, bound) {
4094
4624
  case "intersection":
4095
4625
  return t.types.some((m) => containsTypeParam(m, seen, bound));
4096
4626
  case "object":
4097
- if (t.class) return false;
4627
+ if (t.class) {
4628
+ return [...t.class.typeArguments?.values() ?? []].some((args) => args.some((a) => containsTypeParam(a, seen, bound)));
4629
+ }
4098
4630
  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));
4099
4631
  case "function": {
4100
4632
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
@@ -4264,7 +4796,10 @@ function formatTypeUncached(t) {
4264
4796
  return t.isPack ? `(${inner})` : `[${inner}]`;
4265
4797
  }
4266
4798
  case "object": {
4267
- if (t.name) return t.name;
4799
+ if (t.name) {
4800
+ const args = t.class?.typeArguments?.get(t.class.name);
4801
+ return args?.length ? `${t.name}<${args.map(formatType).join(", ")}>` : t.name;
4802
+ }
4268
4803
  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)}`);
4269
4804
  if (t.indexer) props.push(`[${formatType(t.indexer.key)}]: ${formatType(t.indexer.value)}`);
4270
4805
  return props.length ? `{ ${props.join(", ")} }` : "{}";
@@ -4283,7 +4818,7 @@ function formatTypeUncached(t) {
4283
4818
  }).join(", ")}>` : "";
4284
4819
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
4285
4820
  if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
4286
- return `${gen}(${ps.join(", ")}) -> ${formatPredicate(t) ?? formatType(t.returns)}`;
4821
+ return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
4287
4822
  }
4288
4823
  case "typeParam":
4289
4824
  return t.name;
@@ -4327,7 +4862,7 @@ function formatPredicate(t) {
4327
4862
  }
4328
4863
  function formatAtom(t) {
4329
4864
  if (t.kind === "intersection" && t.name) return t.name;
4330
- if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference") {
4865
+ if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference" || t.kind === "conditional") {
4331
4866
  return `(${formatType(t)})`;
4332
4867
  }
4333
4868
  return formatType(t);
@@ -4431,13 +4966,33 @@ function moduleExports(program, scopes, types, resolveModule) {
4431
4966
  if (stmt.type === "ExportStatement") {
4432
4967
  const declaration = stmt.declaration;
4433
4968
  if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
4434
- else for (const target of declaration.names) exportPattern(target);
4969
+ else if (declaration.type === "ClassDeclaration") {
4970
+ exportName(declaration.name, declaration.name.name);
4971
+ const instance = types.aliases.get(declaration.name.name);
4972
+ if (instance) {
4973
+ exportedTypes.set(declaration.name.name, {
4974
+ type: instance,
4975
+ params: declaration.typeParams.map((g) => g.name)
4976
+ });
4977
+ }
4978
+ } else for (const target of declaration.names) exportPattern(target);
4435
4979
  } else if (stmt.type === "ExportTypeAliasStatement") {
4436
4980
  const name = stmt.alias.name.name;
4437
4981
  const type = types.aliases.get(name);
4438
4982
  if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
4439
4983
  } else if (stmt.type === "ExportDefaultStatement") {
4440
- defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
4984
+ if (stmt.declaration.type === "ClassDeclaration") {
4985
+ const id = byDeclaration.get(stmt.declaration.name);
4986
+ defaultType = (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType;
4987
+ const instance = types.aliases.get(stmt.declaration.name.name);
4988
+ if (instance) {
4989
+ const exported = { type: instance, params: stmt.declaration.typeParams.map((g) => g.name) };
4990
+ exportedTypes.set(stmt.declaration.name.name, exported);
4991
+ exportedTypes.set("default", exported);
4992
+ }
4993
+ } else {
4994
+ defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
4995
+ }
4441
4996
  } else if (stmt.type === "ExportNamedStatement") {
4442
4997
  if (stmt.source) {
4443
4998
  const from = resolveModule?.(stmt.source.value);
@@ -4565,6 +5120,51 @@ function keepsLiterals(paramType) {
4565
5120
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
4566
5121
  return members.some((m) => m.kind === "literal");
4567
5122
  }
5123
+ var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass"]);
5124
+ var CLASS_RESERVED = /* @__PURE__ */ new Set([
5125
+ "new",
5126
+ "ClassObject",
5127
+ "ParentClass",
5128
+ "__init",
5129
+ "__index",
5130
+ "__newindex",
5131
+ "__getters",
5132
+ "__setters",
5133
+ "__dynamic"
5134
+ ]);
5135
+ function callsSuper(block) {
5136
+ let found = false;
5137
+ walkNodes(block, (node) => {
5138
+ const record = node;
5139
+ if (record.type === "CallExpression" && record.callee?.type === "SuperExpression") found = true;
5140
+ });
5141
+ return found;
5142
+ }
5143
+ function assignedFields(block) {
5144
+ const names = /* @__PURE__ */ new Set();
5145
+ walkNodes(block, (node) => {
5146
+ const record = node;
5147
+ const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
5148
+ for (const target of targets ?? []) {
5149
+ const member = target;
5150
+ if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5151
+ names.add(member.property.name);
5152
+ }
5153
+ }
5154
+ });
5155
+ return names;
5156
+ }
5157
+ function walkNodes(root, visit) {
5158
+ if (!root || typeof root !== "object") return;
5159
+ if (Array.isArray(root)) {
5160
+ for (const item of root) walkNodes(item, visit);
5161
+ return;
5162
+ }
5163
+ visit(root);
5164
+ for (const [key, value] of Object.entries(root)) {
5165
+ if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5166
+ }
5167
+ }
4568
5168
  var AliasMap = class extends Map {
4569
5169
  pending = /* @__PURE__ */ new Map();
4570
5170
  defer(name, resolve5) {
@@ -4688,6 +5288,8 @@ var TypeAnalyzer = class {
4688
5288
  aliasDefs = /* @__PURE__ */ new Map();
4689
5289
  /** See `resolveClass`. */
4690
5290
  classTypes = /* @__PURE__ */ new WeakMap();
5291
+ /** See `instanceType` — one instance type per `class ... end`. */
5292
+ instanceTypes = /* @__PURE__ */ new WeakMap();
4691
5293
  classMembers = /* @__PURE__ */ new WeakMap();
4692
5294
  /** Generic parameters currently in lexical scope (alias body / generic fn),
4693
5295
  * with their `extends` constraints resolved. */
@@ -4729,6 +5331,7 @@ var TypeAnalyzer = class {
4729
5331
  this.registerAliasDefs(preludeProgram().body, true);
4730
5332
  for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
4731
5333
  this.registerAliasDefs(this.program.body);
5334
+ this.registerNestedClasses();
4732
5335
  for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
4733
5336
  this.registerImportedTypes();
4734
5337
  this.resolveAllAliases();
@@ -4740,6 +5343,7 @@ var TypeAnalyzer = class {
4740
5343
  this.bindingType.set(id, t);
4741
5344
  }
4742
5345
  setAliasExpander((t) => this.expand(t));
5346
+ setDeferredBound((t) => this.deferredBound(t));
4743
5347
  try {
4744
5348
  const env = /* @__PURE__ */ new Map();
4745
5349
  this.visitBlock(this.program.body, env);
@@ -4747,6 +5351,7 @@ var TypeAnalyzer = class {
4747
5351
  if (this.options.reportUnknownTypes) this.reportUnknownTypes();
4748
5352
  } finally {
4749
5353
  setAliasExpander(void 0);
5354
+ setDeferredBound(void 0);
4750
5355
  }
4751
5356
  return {
4752
5357
  typeOf: this.typeOf,
@@ -4796,6 +5401,11 @@ var TypeAnalyzer = class {
4796
5401
  this.aliases.set(qualified, exported.type);
4797
5402
  }
4798
5403
  }
5404
+ const asDefault = stmt.defaultImport && exports.types.get("default");
5405
+ if (stmt.defaultImport && asDefault) {
5406
+ this.importedTypes.set(stmt.defaultImport.name, asDefault);
5407
+ this.aliases.set(stmt.defaultImport.name, asDefault.type);
5408
+ }
4799
5409
  for (const s of stmt.specifiers) {
4800
5410
  const exported = exports.types.get(s.imported.name);
4801
5411
  if (exported) {
@@ -4830,10 +5440,40 @@ var TypeAnalyzer = class {
4830
5440
  if (stmt.type === "DeclareClassStatement") {
4831
5441
  this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
4832
5442
  }
5443
+ const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
5444
+ if (declaration.type === "ClassDeclaration") this.registerClass(declaration);
4833
5445
  }
4834
5446
  }
5447
+ /** A class written inside a function or a block names a type too — its
5448
+ * own instances', which its methods' `this` is annotated with. Type names
5449
+ * are one namespace here, so it is registered with the rest; only a
5450
+ * second class of the same name would notice. */
5451
+ registerNestedClasses() {
5452
+ walkNodes(this.program.body, (node) => {
5453
+ const record = node;
5454
+ if (record.type !== "ClassDeclaration") return;
5455
+ const declaration = node;
5456
+ if (this.aliasDefs.has(declaration.name.name)) return;
5457
+ this.registerClass(declaration);
5458
+ });
5459
+ }
5460
+ /** A class's name as a type. `node` is a placeholder: `resolveDef` and
5461
+ * `instantiateAlias` both go to the declaration itself. */
5462
+ registerClass(declaration) {
5463
+ this.aliasDefs.set(declaration.name.name, {
5464
+ params: declaration.typeParams,
5465
+ node: {
5466
+ type: "TableTypeNode",
5467
+ properties: [],
5468
+ line: declaration.line,
5469
+ column: declaration.column
5470
+ },
5471
+ runtimeClass: declaration
5472
+ });
5473
+ }
4835
5474
  /** A non-generic definition's type. */
4836
5475
  resolveDef(def) {
5476
+ if (def.runtimeClass) return this.instanceType(def.runtimeClass);
4837
5477
  return def.class ? this.classType(def.class) : this.resolveType(def.node);
4838
5478
  }
4839
5479
  /** One type per class declaration, so every mention of a class is the same
@@ -4891,6 +5531,381 @@ var TypeAnalyzer = class {
4891
5531
  if (this.program.body.statements.includes(stmt)) ownMembers();
4892
5532
  return type;
4893
5533
  }
5534
+ // ============================================================
5535
+ // `class ... end` — the runtime kind
5536
+ // ------------------------------------------------------------
5537
+ // A declaration says two things at once. Its *name as a type* is
5538
+ // the type of its instances, nominal the way `declare class` is:
5539
+ // only the class and the classes extending it produce one. Its
5540
+ // *name as a value* is the class table — the statics, the class
5541
+ // it extends (`ParentClass`), and the `new` that builds an
5542
+ // instance, which is an ordinary function and can be called as
5543
+ // one. An instance reaches its own class back through
5544
+ // `ClassObject`.
5545
+ //
5546
+ // Members are resolved into one shape, filled in two passes: the
5547
+ // fields first, then the functions. That order is what lets a
5548
+ // method body read `this.x` while the class it belongs to is
5549
+ // still being worked out.
5550
+ // ============================================================
5551
+ classShapes = /* @__PURE__ */ new WeakMap();
5552
+ classValues = /* @__PURE__ */ new WeakMap();
5553
+ /** Identity for a class written as a value, which has no name to be known
5554
+ * by: two of them are different types however alike they look. */
5555
+ classIdentities = /* @__PURE__ */ new WeakMap();
5556
+ classIdentityCount = 0;
5557
+ /** The class whose members are being read, so `super` knows its base. */
5558
+ currentClass;
5559
+ withClass(stmt, fn2) {
5560
+ const previous = this.currentClass;
5561
+ this.currentClass = stmt;
5562
+ try {
5563
+ return fn2();
5564
+ } finally {
5565
+ this.currentClass = previous;
5566
+ }
5567
+ }
5568
+ /** What the class is known by. A declaration is known by its name, the way
5569
+ * a `declare class` is — that is what makes it the same class across
5570
+ * modules. A class written as a value is known by where it is written. */
5571
+ classIdentity(stmt) {
5572
+ if (stmt.type === "ClassDeclaration") return stmt.name.name;
5573
+ let identity = this.classIdentities.get(stmt);
5574
+ if (!identity) {
5575
+ identity = `${stmt.name?.name ?? "class"}@${++this.classIdentityCount}`;
5576
+ this.classIdentities.set(stmt, identity);
5577
+ }
5578
+ return identity;
5579
+ }
5580
+ /** What it is *shown* as. A class written as a value has no name of its
5581
+ * own, so it borrows the one it is being bound to — `const Counter =
5582
+ * class ... end` reads as `Counter` everywhere. */
5583
+ className(stmt) {
5584
+ return stmt.name?.name ?? this.classDisplayNames.get(stmt) ?? "(class)";
5585
+ }
5586
+ classDisplayNames = /* @__PURE__ */ new WeakMap();
5587
+ /** `const Name = class ... end` — the name the class will be known by. */
5588
+ nameClassExpressions(stmt) {
5589
+ stmt.names.forEach((target, i) => {
5590
+ const value = stmt.init[i];
5591
+ if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
5592
+ this.classDisplayNames.set(value, target.name);
5593
+ }
5594
+ });
5595
+ }
5596
+ classTypeParams(stmt) {
5597
+ return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
5598
+ }
5599
+ /** The class a declaration extends, when it is one written in this file.
5600
+ * An imported class is reached through its type and its value instead. */
5601
+ superDecl(stmt) {
5602
+ if (!stmt.superclass) return void 0;
5603
+ const base = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
5604
+ return base && base !== stmt && !this.extendsThrough(base, stmt) ? base : void 0;
5605
+ }
5606
+ /** Does `from` reach `target` by `extends`? Guards against a cycle turning
5607
+ * resolution into a loop. */
5608
+ extendsThrough(from, target) {
5609
+ const seen = /* @__PURE__ */ new Set();
5610
+ for (let cls = from; cls && !seen.has(cls); ) {
5611
+ if (cls === target) return true;
5612
+ seen.add(cls);
5613
+ cls = cls.superclass ? this.aliasDefs.get(cls.superclass.name)?.runtimeClass : void 0;
5614
+ }
5615
+ return false;
5616
+ }
5617
+ /** The instance type of what `stmt` extends, with the arguments it was
5618
+ * extended with filled in — a class in this file, or any class type a
5619
+ * name in scope stands for (an imported one). */
5620
+ baseInstance(stmt) {
5621
+ if (!stmt.superclass) return void 0;
5622
+ const written = (stmt.superArguments ?? []).map((argument) => this.resolveType(argument));
5623
+ if (this.aliasDefs.get(stmt.superclass.name)?.runtimeClass) {
5624
+ const local = this.superDecl(stmt);
5625
+ if (!local) return void 0;
5626
+ const base = this.instanceType(local);
5627
+ const params = this.classTypeParams(local);
5628
+ if (!params.length) return base;
5629
+ const applied = substitute(base, this.bindTypeArguments(params, written));
5630
+ return applied.kind === "object" ? applied : void 0;
5631
+ }
5632
+ const imported = this.importedTypes.get(stmt.superclass.name);
5633
+ const named = imported ? this.importedType(imported, stmt.superArguments ?? []) : this.aliases.get(stmt.superclass.name);
5634
+ return named && isClassType(named) ? named : void 0;
5635
+ }
5636
+ /** One instance type per declaration, so every mention of the class is the
5637
+ * same object — the `this` of its own methods included. Members are read
5638
+ * lazily for the reason `declare class` reads them lazily: a class can
5639
+ * name itself, and two classes can name each other. */
5640
+ instanceType(stmt) {
5641
+ const cached = this.instanceTypes.get(stmt);
5642
+ if (cached) return cached;
5643
+ const name = this.className(stmt);
5644
+ const identity = this.classIdentity(stmt);
5645
+ const params = this.classTypeParams(stmt);
5646
+ const type = { kind: "object", name };
5647
+ this.instanceTypes.set(stmt, type);
5648
+ const own = params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0));
5649
+ const info = () => {
5650
+ const base = this.baseInstance(stmt)?.class;
5651
+ const typeArguments = new Map(base?.typeArguments ?? []);
5652
+ if (own.length) typeArguments.set(identity, own);
5653
+ return {
5654
+ name: identity,
5655
+ superclass: base?.name,
5656
+ ancestors: [identity, ...base?.ancestors ?? []],
5657
+ typeArguments: typeArguments.size ? typeArguments : void 0
5658
+ };
5659
+ };
5660
+ Object.defineProperties(type, {
5661
+ properties: {
5662
+ enumerable: true,
5663
+ get: () => {
5664
+ const shape = this.shapeOf(stmt);
5665
+ const base = this.baseInstance(stmt)?.properties;
5666
+ const members = base?.size ? new Map([...base, ...shape.instance]) : new Map(shape.instance);
5667
+ members.set("ClassObject", { type: this.classValueType(stmt), optional: false, readonly: true });
5668
+ return members;
5669
+ }
5670
+ },
5671
+ class: { enumerable: true, get: info }
5672
+ });
5673
+ return type;
5674
+ }
5675
+ /** `Box<T>` as its own methods see it — a reference, not the object, so
5676
+ * substituting the arguments in does not have to walk the class. */
5677
+ selfTypeOf(stmt) {
5678
+ const params = this.classTypeParams(stmt);
5679
+ if (!params.length || stmt.type !== "ClassDeclaration") return this.instanceType(stmt);
5680
+ return {
5681
+ kind: "genericRef",
5682
+ name: stmt.name.name,
5683
+ typeArguments: params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0))
5684
+ };
5685
+ }
5686
+ /** The class table: the statics, what it inherits from the class it
5687
+ * extends, `ParentClass`, `ClassObject`, and `new`. */
5688
+ classValueType(stmt) {
5689
+ const cached = this.classValues.get(stmt);
5690
+ if (cached) return cached;
5691
+ const type = objectType([]);
5692
+ this.classValues.set(stmt, type);
5693
+ type.name = `typeof ${this.className(stmt)}`;
5694
+ const shape = this.shapeOf(stmt);
5695
+ const local = this.superDecl(stmt);
5696
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
5697
+ if (parent?.kind === "object") {
5698
+ for (const [key, property] of parent.properties) {
5699
+ if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
5700
+ }
5701
+ }
5702
+ for (const [key, property] of shape.statics) type.properties.set(key, property);
5703
+ const constructor = this.constructorType(stmt);
5704
+ const params = this.classTypeParams(stmt);
5705
+ type.properties.set("new", {
5706
+ type: fn(
5707
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
5708
+ this.selfTypeOf(stmt),
5709
+ constructor?.varargs,
5710
+ params.map((p) => p.name)
5711
+ ),
5712
+ optional: false,
5713
+ readonly: true
5714
+ });
5715
+ type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
5716
+ return type;
5717
+ }
5718
+ /** The value side of a class named by a binding rather than by a
5719
+ * declaration in this file — an imported one. */
5720
+ classStaticsByNameType(name) {
5721
+ const id = this.classBindingByName(name);
5722
+ const declared = id !== void 0 ? this.bindingType.get(id) : void 0;
5723
+ return declared?.kind === "object" ? declared : void 0;
5724
+ }
5725
+ /** The binding a class name stands for, wherever it was declared. */
5726
+ classBindingByName(name) {
5727
+ for (const [id, binding] of this.scopes.bindings) {
5728
+ if (binding.name === name && binding.declaredBy === "class") return id;
5729
+ }
5730
+ return this.scopes.globalsByName.get(name);
5731
+ }
5732
+ /** What `super(...)` takes: the constructor of the class `stmt` extends,
5733
+ * its own skipped. */
5734
+ baseConstructorType(stmt) {
5735
+ return this.constructorType(stmt, /* @__PURE__ */ new Set([stmt]));
5736
+ }
5737
+ /** A class's constructor signature — its own, or the one it inherits. */
5738
+ constructorType(stmt, seen = /* @__PURE__ */ new Set()) {
5739
+ if (seen.has(stmt)) return void 0;
5740
+ seen.add(stmt);
5741
+ const own = this.shapeOf(stmt).ctor;
5742
+ if (own) return own;
5743
+ const local = this.superDecl(stmt);
5744
+ if (local) return this.constructorType(local, seen);
5745
+ const parent = stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
5746
+ const inherited = parent && this.overloadsOf(this.propertyType(parent, "new"))[0];
5747
+ return inherited;
5748
+ }
5749
+ /** `super` as a value: the base class's instance members with the `this`
5750
+ * slot already filled, because `super.m(a)` passes this instance. */
5751
+ superType(stmt) {
5752
+ const base = this.baseInstance(stmt);
5753
+ if (!base) return anyType;
5754
+ const entries = [];
5755
+ for (const [name, property] of base.properties) {
5756
+ const bound = this.overloadsOf(property.type);
5757
+ entries.push([name, bound.length ? {
5758
+ ...property,
5759
+ type: intersection(bound.map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f))
5760
+ } : property]);
5761
+ }
5762
+ return objectType(entries);
5763
+ }
5764
+ shapeOf(stmt) {
5765
+ const cached = this.classShapes.get(stmt);
5766
+ if (cached) return cached;
5767
+ const shape = { instance: /* @__PURE__ */ new Map(), statics: /* @__PURE__ */ new Map(), filling: true };
5768
+ this.classShapes.set(stmt, shape);
5769
+ const wasEmitting = this.emitDiagnostics;
5770
+ this.emitDiagnostics = false;
5771
+ try {
5772
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
5773
+ this.withSelfType(this.selfTypeOf(stmt), () => this.fillShape(stmt, shape));
5774
+ }));
5775
+ } finally {
5776
+ this.emitDiagnostics = wasEmitting;
5777
+ shape.filling = false;
5778
+ }
5779
+ return shape;
5780
+ }
5781
+ fillShape(stmt, shape) {
5782
+ const put = (isStatic, name, property) => {
5783
+ (isStatic ? shape.statics : shape.instance).set(name, property);
5784
+ };
5785
+ for (const member of stmt.members) {
5786
+ if (member.type !== "ClassField") continue;
5787
+ const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
5788
+ put(member.isStatic, member.name.name, { type, optional: false });
5789
+ }
5790
+ for (const member of stmt.members) {
5791
+ switch (member.type) {
5792
+ case "ClassField":
5793
+ break;
5794
+ case "ClassMethod": {
5795
+ this.paramsFromSignatures(member.func, member.signatures);
5796
+ const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5797
+ put(member.isStatic, member.name.name, { type, optional: false });
5798
+ break;
5799
+ }
5800
+ case "ClassAccessor": {
5801
+ const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5802
+ if (signature.kind !== "function") break;
5803
+ const target = member.isStatic ? shape.statics : shape.instance;
5804
+ const existing = target.get(member.name.name);
5805
+ if (member.kind === "get") {
5806
+ put(member.isStatic, member.name.name, {
5807
+ type: signature.returns,
5808
+ optional: false,
5809
+ readonly: existing === void 0 || existing.readonly !== false
5810
+ });
5811
+ } else {
5812
+ put(member.isStatic, member.name.name, {
5813
+ type: existing?.type ?? signature.params[signature.params.length - 1]?.type ?? anyType,
5814
+ optional: false,
5815
+ readonly: false
5816
+ });
5817
+ }
5818
+ break;
5819
+ }
5820
+ case "ClassConstructor": {
5821
+ const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
5822
+ if (signature.kind === "function") shape.ctor = signature;
5823
+ break;
5824
+ }
5825
+ }
5826
+ }
5827
+ }
5828
+ /** Bind the class's value, and check what its members say. Shared by the
5829
+ * declaration and the expression forms. */
5830
+ visitClass(stmt, env) {
5831
+ this.checkClassDeclaration(stmt);
5832
+ const value = this.classValueType(stmt);
5833
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
5834
+ this.withSelfType(this.selfTypeOf(stmt), () => {
5835
+ for (const member of stmt.members) {
5836
+ if (member.type === "ClassField") {
5837
+ if (!member.init) continue;
5838
+ const declared = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : void 0;
5839
+ if (declared) this.applyContext(member.init, declared);
5840
+ const actual = this.infer(member.init, env);
5841
+ if (declared && this.emitDiagnostics && !isAssignable(actual, declared)) {
5842
+ this.diagnostics.push({
5843
+ node: member.init,
5844
+ message: `Type '${formatType(actual)}' is not assignable to type '${formatType(declared)}'`
5845
+ });
5846
+ }
5847
+ continue;
5848
+ }
5849
+ this.checkParamOrder(member.func.params, member);
5850
+ for (const signature of member.signatures ?? []) {
5851
+ this.checkParamOrder(signature.params, member);
5852
+ }
5853
+ this.visitFunctionBody(member.func, env);
5854
+ }
5855
+ });
5856
+ }));
5857
+ return value;
5858
+ }
5859
+ /** What a class gets wrong, reported where it is written. */
5860
+ checkClassDeclaration(stmt) {
5861
+ if (!this.emitDiagnostics) return;
5862
+ const report = (node, message) => {
5863
+ this.diagnostics.push({ node, message });
5864
+ };
5865
+ const name = this.className(stmt);
5866
+ if (stmt.superclass) {
5867
+ const local = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
5868
+ if (local && this.extendsThrough(local, stmt)) {
5869
+ report(stmt.superclass, `'${name}' cannot extend itself`);
5870
+ } else if (!this.baseInstance(stmt)) {
5871
+ const known = this.aliases.has(stmt.superclass.name) || this.importedTypes.has(stmt.superclass.name);
5872
+ report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
5873
+ }
5874
+ }
5875
+ for (const member of stmt.members) {
5876
+ if (member.type === "ClassConstructor") continue;
5877
+ if (CLASS_RESERVED.has(member.name.name)) {
5878
+ report(member.name, `'${member.name.name}' is what the compiler calls part of a class; a member cannot be named that`);
5879
+ }
5880
+ }
5881
+ const seen = /* @__PURE__ */ new Map();
5882
+ for (const member of stmt.members) {
5883
+ if (member.type === "ClassConstructor") {
5884
+ if (seen.has("constructor")) report(member, "A class has one constructor");
5885
+ seen.set("constructor", member.type);
5886
+ continue;
5887
+ }
5888
+ const key = `${member.isStatic ? "static " : ""}${member.name.name}`;
5889
+ const before = seen.get(key);
5890
+ const pair = member.type === "ClassAccessor" && before === "ClassAccessor";
5891
+ if (before !== void 0 && !pair) {
5892
+ report(member.name, `'${member.name.name}' is declared twice in class '${name}'`);
5893
+ }
5894
+ seen.set(key, member.type);
5895
+ }
5896
+ const constructor = stmt.members.find((m) => m.type === "ClassConstructor");
5897
+ if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
5898
+ report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
5899
+ }
5900
+ const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
5901
+ for (const member of stmt.members) {
5902
+ if (member.type !== "ClassField" || member.isStatic || member.init) continue;
5903
+ if (assigned.has(member.name.name)) continue;
5904
+ const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
5905
+ if (isAssignable(nilType, type)) continue;
5906
+ report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
5907
+ }
5908
+ }
4894
5909
  /** `extends` must name a class, and the chain must end. */
4895
5910
  checkClass(stmt) {
4896
5911
  if (!stmt.superclass || !this.emitDiagnostics) return;
@@ -5147,6 +6162,7 @@ var TypeAnalyzer = class {
5147
6162
  instantiateAlias(def, args) {
5148
6163
  if (this.instantiationDepth > 20) return unknownType;
5149
6164
  const subst = this.bindTypeArguments(def.params, args);
6165
+ if (def.runtimeClass) return substitute(this.instanceType(def.runtimeClass), subst);
5150
6166
  this.instantiationDepth++;
5151
6167
  try {
5152
6168
  const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
@@ -5291,15 +6307,17 @@ var TypeAnalyzer = class {
5291
6307
  case "FunctionTypeNode": {
5292
6308
  const names = node.generics.map((g) => g.name);
5293
6309
  return this.withTypeParams(node.generics, () => {
5294
- const params = node.params.map((p) => ({
6310
+ const params = node.params.filter((p) => !p.rest).map((p) => ({
5295
6311
  name: p.name,
5296
6312
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
5297
6313
  optional: p.optional
5298
6314
  }));
6315
+ const restParam = node.params.find((p) => p.rest);
6316
+ const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
5299
6317
  return this.withTypeParamDefaults(fn(
5300
6318
  params,
5301
6319
  this.resolveType(node.returnType),
5302
- node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
6320
+ restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
5303
6321
  names,
5304
6322
  this.resolvePredicate(node.predicate, params)
5305
6323
  ), node.generics);
@@ -5493,7 +6511,10 @@ var TypeAnalyzer = class {
5493
6511
  accessType(obj, index) {
5494
6512
  if (index.kind === "union") return union(index.types.map((m) => this.accessType(obj, m)));
5495
6513
  if (index.kind === "literal" && typeof index.value === "string") {
5496
- return this.propertyType(obj, index.value);
6514
+ const member = this.propertyType(obj, index.value);
6515
+ if (member.kind !== "unknown") return member;
6516
+ const t = this.expand(obj);
6517
+ return t.kind === "object" && !t.class && !t.indexer ? nilType : member;
5497
6518
  }
5498
6519
  return this.indexedType(obj, index);
5499
6520
  }
@@ -5646,12 +6667,13 @@ var TypeAnalyzer = class {
5646
6667
  visitStatement(stmt, env) {
5647
6668
  switch (stmt.type) {
5648
6669
  case "VariableDeclaration": {
6670
+ this.nameClassExpressions(stmt);
5649
6671
  stmt.names.forEach((target, i) => {
5650
6672
  if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
5651
6673
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
5652
6674
  }
5653
6675
  });
5654
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
6676
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
5655
6677
  stmt.names.forEach((target, i) => {
5656
6678
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
5657
6679
  const source = sources[i];
@@ -5676,6 +6698,15 @@ var TypeAnalyzer = class {
5676
6698
  });
5677
6699
  return;
5678
6700
  }
6701
+ case "ClassDeclaration": {
6702
+ const id = this.bindingIdByName(stmt.name.name, stmt.name);
6703
+ const value = this.visitClass(stmt, env);
6704
+ if (id !== void 0) {
6705
+ this.bindingType.set(id, value);
6706
+ this.setBinding(env, id, value);
6707
+ }
6708
+ return;
6709
+ }
5679
6710
  case "FunctionDeclaration": {
5680
6711
  this.checkParamOrder(stmt.func.params, stmt);
5681
6712
  for (const sig of stmt.signatures ?? []) this.checkParamOrder(sig.params, stmt);
@@ -5731,7 +6762,7 @@ var TypeAnalyzer = class {
5731
6762
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
5732
6763
  }
5733
6764
  });
5734
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
6765
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
5735
6766
  stmt.targets.forEach((target, i) => {
5736
6767
  const vt = valueTypes[i] ?? unknownType;
5737
6768
  const source = sources[i];
@@ -5839,7 +6870,8 @@ var TypeAnalyzer = class {
5839
6870
  stmt.arguments.forEach((a, i) => this.applyContext(a, declared.elements[i]));
5840
6871
  }
5841
6872
  }
5842
- const { types, sources } = this.valueList(stmt.arguments, env);
6873
+ const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
6874
+ const { types, sources } = this.valueList(stmt.arguments, env, want);
5843
6875
  this.checkReturn(stmt, declared, types, sources, env);
5844
6876
  if (this.returnTypes) {
5845
6877
  this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
@@ -5850,7 +6882,8 @@ var TypeAnalyzer = class {
5850
6882
  this.visitStatement(stmt.declaration, env);
5851
6883
  return;
5852
6884
  case "ExportDefaultStatement":
5853
- this.infer(stmt.declaration, env);
6885
+ if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, env);
6886
+ else this.infer(stmt.declaration, env);
5854
6887
  return;
5855
6888
  case "ExportNamedStatement": {
5856
6889
  if (stmt.source) {
@@ -5929,12 +6962,35 @@ var TypeAnalyzer = class {
5929
6962
  * `sources` maps each produced value back to the expression it came from
5930
6963
  * (undefined for the 2nd and later values of a multi-value call), so the
5931
6964
  * caller can still do contextual typing against the written expression. */
5932
- valueList(exprs, env) {
6965
+ valueList(exprs, env, want = 0) {
5933
6966
  const types = [];
5934
6967
  const sources = [];
5935
6968
  exprs.forEach((e, i) => {
5936
6969
  const t = this.infer(e, env);
5937
6970
  const last = i === exprs.length - 1;
6971
+ if (e.type === "SpreadElement") {
6972
+ const held = this.typeOf.get(e.argument);
6973
+ const expanded = held && this.expand(held);
6974
+ if (expanded?.kind === "tuple") {
6975
+ for (const element of expanded.elements) {
6976
+ types.push(element);
6977
+ sources.push(e);
6978
+ }
6979
+ return;
6980
+ }
6981
+ do {
6982
+ types.push(t);
6983
+ sources.push(e);
6984
+ } while (last && types.length < want);
6985
+ return;
6986
+ }
6987
+ if (last && e.type === "VarargExpression") {
6988
+ do {
6989
+ types.push(t);
6990
+ sources.push(types.length - 1 === i ? e : void 0);
6991
+ } while (types.length < want);
6992
+ return;
6993
+ }
5938
6994
  if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
5939
6995
  t.elements.forEach((el, j) => {
5940
6996
  types.push(el);
@@ -6023,7 +7079,8 @@ var TypeAnalyzer = class {
6023
7079
  /** A parameter's type: annotation, else a shape synthesized from a
6024
7080
  * destructuring pattern, else inferred from its default, else `any`. */
6025
7081
  paramType(p, env) {
6026
- if (!p.typeAnnotation && !p.pattern && !p.default && p.name === "self" && this.selfType) {
7082
+ const receiver = p.name === "self" || p.name === "this";
7083
+ if (!p.typeAnnotation && !p.pattern && !p.default && receiver && this.selfType) {
6027
7084
  return this.selfType;
6028
7085
  }
6029
7086
  if (p.typeAnnotation) {
@@ -6031,6 +7088,7 @@ var TypeAnalyzer = class {
6031
7088
  if (p.default) this.applyContext(p.default, t);
6032
7089
  return p.optional ? optional(t) : t;
6033
7090
  }
7091
+ if (p.rest) return arrayOf(unknownType);
6034
7092
  if (p.pattern) return this.patternToType(p.pattern, env);
6035
7093
  if (p.default) return widen(this.infer(p.default, env));
6036
7094
  return this.contextualParams.get(p) ?? anyType;
@@ -6051,6 +7109,11 @@ var TypeAnalyzer = class {
6051
7109
  this.expectedTypeOf.set(e, expected);
6052
7110
  if (e.type === "ArrayExpression") return this.applyArrayContext(e, expected);
6053
7111
  if (e.type === "TableExpression") return this.applyTableContext(e, expected);
7112
+ if (e.type === "BinaryExpression" && (e.operator === "or" || e.operator === "and")) {
7113
+ if (e.operator === "or") this.applyContext(e.left, expected);
7114
+ this.applyContext(e.right, expected);
7115
+ return;
7116
+ }
6054
7117
  if (e.type !== "FunctionExpression") return;
6055
7118
  const members = expected.kind === "union" ? expected.types : [expected];
6056
7119
  const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
@@ -6076,7 +7139,7 @@ var TypeAnalyzer = class {
6076
7139
  const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
6077
7140
  if (!target) return;
6078
7141
  if (!e.elements.length) {
6079
- if (!containsTypeParam(target)) this.contextualArrays.set(e, target);
7142
+ this.contextualArrays.set(e, target);
6080
7143
  return;
6081
7144
  }
6082
7145
  e.elements.forEach((element, i) => {
@@ -6136,13 +7199,26 @@ var TypeAnalyzer = class {
6136
7199
  }
6137
7200
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
6138
7201
  }
7202
+ /** What a vararg function's `...` holds, one value at a time.
7203
+ *
7204
+ * Written three ways, and they mean the same call: `...` says nothing,
7205
+ * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7206
+ * into an array the body reads by name. Only the last changes what the
7207
+ * body sees — the signature is the same either way. */
7208
+ varargElement(func) {
7209
+ if (!func.hasVarargs) return void 0;
7210
+ const rest = func.params.find((p) => p.rest);
7211
+ if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7212
+ const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7213
+ return declared?.kind === "array" ? declared.element : unknownType;
7214
+ }
6139
7215
  /** The type of `...` in each function body being walked. */
6140
7216
  varargs = [];
6141
7217
  /** What each function body being walked declared it returns. */
6142
7218
  declaredReturns = [];
6143
7219
  /** Run `body` with `...` and `return` as `func` declares them. */
6144
7220
  withVarargs(func, body) {
6145
- this.varargs.push(func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0);
7221
+ this.varargs.push(this.varargElement(func));
6146
7222
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
6147
7223
  try {
6148
7224
  return body();
@@ -6223,6 +7299,13 @@ var TypeAnalyzer = class {
6223
7299
  const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
6224
7300
  unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
6225
7301
  });
7302
+ if (f.varargs) {
7303
+ const keeps = keepsLiterals(f.varargs);
7304
+ for (let i = f.params.length; i < argTypes.length; i++) {
7305
+ const arg = argTypes[i];
7306
+ if (arg !== void 0) unify(f.varargs, keeps ? arg : widen(arg), vars, subst);
7307
+ }
7308
+ }
6226
7309
  for (const name of f.typeParams ?? []) {
6227
7310
  if (!subst.has(name)) subst.set(name, f.typeParamDefaults?.[name] ?? unknownType);
6228
7311
  }
@@ -6247,11 +7330,11 @@ var TypeAnalyzer = class {
6247
7330
  * after every concrete signature has been tried. That ordering is what
6248
7331
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
6249
7332
  * `<T>(v: T) -> string` and still pick the precise one. */
6250
- pickOverload(fns, argTypes, argsFor) {
7333
+ pickOverload(fns, argTypes, argsFor, spread) {
6251
7334
  for (const generic of [false, true]) {
6252
7335
  for (const f of fns) {
6253
7336
  if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
6254
- if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes)) return f;
7337
+ if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
6255
7338
  }
6256
7339
  }
6257
7340
  return void 0;
@@ -6286,12 +7369,27 @@ var TypeAnalyzer = class {
6286
7369
  * own type parameters stand for what the call would infer, so each is
6287
7370
  * checked only against its constraint — `<K extends keyof Services>`
6288
7371
  * accepts `"Players"` but not `""`. */
6289
- overloadAccepts(f, argTypes) {
6290
- if (!f.varargs && argTypes.length > f.params.length) return false;
7372
+ overloadAccepts(f, argTypes, spread) {
7373
+ const at = (i) => {
7374
+ if (!spread || i < spread.index) return argTypes[i];
7375
+ if (!spread.elements) return argTypes[spread.index];
7376
+ const held = spread.elements[i - spread.index];
7377
+ return held ?? argTypes[i - spread.index + 1 + spread.elements.length - 1];
7378
+ };
7379
+ const written = spread?.elements ? argTypes.length + spread.elements.length - 1 : argTypes.length;
7380
+ if (!f.varargs && (!spread || spread.elements) && written > f.params.length) return false;
7381
+ if (f.varargs && !f.typeParams?.length) {
7382
+ const last = spread && !spread.elements ? Math.max(f.params.length + 1, written) : written;
7383
+ for (let i = f.params.length; i < last; i++) {
7384
+ const arg = at(i);
7385
+ if (arg !== void 0 && !isAssignable(arg, f.varargs)) return false;
7386
+ }
7387
+ }
6291
7388
  const params = this.boundParams(f);
6292
7389
  return f.params.every((p, i) => {
6293
- if (argTypes[i] === void 0) return p.optional === true;
6294
- return isAssignable(argTypes[i], params[i]);
7390
+ const arg = at(i);
7391
+ if (arg === void 0) return p.optional === true;
7392
+ return isAssignable(arg, params[i]);
6295
7393
  });
6296
7394
  }
6297
7395
  /** A signature's parameter types as a call site sees them before inference:
@@ -6321,17 +7419,40 @@ var TypeAnalyzer = class {
6321
7419
  }
6322
7420
  /** Record what each written argument is expected to be — see
6323
7421
  * `TypeAnalysis.expectedTypeOf`. */
6324
- recordExpected(written, fns, selfOf) {
7422
+ recordExpected(written, fns, selfOf, argsOf = () => []) {
7423
+ const paramsOf = /* @__PURE__ */ new Map();
7424
+ for (const f of fns) paramsOf.set(f, this.paramsAsCalled(f, argsOf(f)));
6325
7425
  written.forEach((arg, j) => {
6326
7426
  const candidates = [];
6327
7427
  for (const f of fns) {
6328
7428
  const i = j + selfOf(f);
6329
- const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
7429
+ const params = paramsOf.get(f);
7430
+ const param = i < params.length ? params[i] : f.varargs;
6330
7431
  if (param) candidates.push(param);
6331
7432
  }
6332
7433
  if (candidates.length) this.expectedTypeOf.set(arg, union(candidates));
6333
7434
  });
6334
7435
  }
7436
+ /** The parameters as *this* call makes them read: a type argument the
7437
+ * arguments already written pin down is substituted in, and one nothing
7438
+ * has pinned down yet falls back to its constraint.
7439
+ *
7440
+ * It is what makes the second argument of
7441
+ * `get(page, skill: Extract<Rows, { Page: Page }>["Skills"][number])`
7442
+ * worth completing — with `page` written, `skill` is the skills of that
7443
+ * page, not of every page. */
7444
+ paramsAsCalled(f, argTypes) {
7445
+ const fallback = this.boundParams(f);
7446
+ if (!f.typeParams?.length || !argTypes.length) return fallback;
7447
+ return f.params.map((p, i) => {
7448
+ if (!containsTypeParam(p.type)) return p.type;
7449
+ const subst = this.inferTypeArgs(f, argTypes.map((t, k) => k === i ? void 0 : t));
7450
+ for (const [name, bound] of [...subst]) if (bound.kind === "unknown") subst.delete(name);
7451
+ if (!subst.size) return fallback[i];
7452
+ const applied = this.reduceType(substitute(p.type, subst));
7453
+ return containsTypeParam(applied) ? fallback[i] : applied;
7454
+ });
7455
+ }
6335
7456
  /** No signature accepts the call, and the argument count is not the
6336
7457
  * problem: say which argument is wrong, the way TypeScript does. */
6337
7458
  /** Check what was written against the parameters as this call's own type
@@ -6342,10 +7463,11 @@ var TypeAnalyzer = class {
6342
7463
  if (!this.emitDiagnostics || !f.typeParams?.length) return;
6343
7464
  const subst = this.inferTypeArgs(f, [...argTypes]);
6344
7465
  for (const bound of subst.values()) if (bound.kind === "unknown") return;
6345
- for (let i = 0; i < f.params.length; i++) {
7466
+ const declaredAt = (i) => i < f.params.length ? f.params[i].type : f.varargs;
7467
+ for (let i = 0; i < Math.max(f.params.length, argTypes.length); i++) {
6346
7468
  const arg = argTypes[i];
6347
- const declared = f.params[i].type;
6348
- if (arg === void 0 || !containsTypeParam(declared)) continue;
7469
+ const declared = declaredAt(i);
7470
+ if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
6349
7471
  const expected = this.reduceType(substitute(declared, subst));
6350
7472
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
6351
7473
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
@@ -6366,15 +7488,26 @@ var TypeAnalyzer = class {
6366
7488
  const args = argsFor(f);
6367
7489
  const params = this.boundParams(f);
6368
7490
  const self = selfOf(f);
7491
+ const spread = this.spreadOf(written, self);
6369
7492
  for (let i = 0; i < f.params.length; i++) {
6370
- const arg = args[i];
6371
- if (arg === void 0 || isAssignable(arg, params[i])) continue;
7493
+ const arg = spread && i >= spread.index ? this.spreadValue(spread, i) : args[i];
7494
+ if (spread && i > spread.index && !spread.elements) break;
7495
+ if (arg === void 0 || arg.kind === "never" || isAssignable(arg, params[i])) continue;
6372
7496
  this.diagnostics.push({
6373
- node: written[i - self] ?? call,
7497
+ node: (spread && i >= spread.index ? written[spread.index - self] : written[i - self]) ?? call,
6374
7498
  message: `Argument of type '${formatType(arg)}' is not assignable to parameter of type '${briefType(params[i])}'`
6375
7499
  });
6376
7500
  return;
6377
7501
  }
7502
+ if (!f.varargs || f.typeParams?.length) return;
7503
+ for (let i = f.params.length; i < args.length; i++) {
7504
+ if (isAssignable(args[i], f.varargs)) continue;
7505
+ this.diagnostics.push({
7506
+ node: written[i - self] ?? call,
7507
+ message: `Argument of type '${formatType(args[i])}' is not assignable to parameter of type '${briefType(f.varargs)}'`
7508
+ });
7509
+ return;
7510
+ }
6378
7511
  }
6379
7512
  /** A required parameter may not follow an optional one — otherwise the
6380
7513
  * optional one could never actually be omitted. Same rule as TypeScript,
@@ -6383,6 +7516,10 @@ var TypeAnalyzer = class {
6383
7516
  if (!this.emitDiagnostics) return;
6384
7517
  let seenOptional;
6385
7518
  for (const p of params) {
7519
+ if (p.rest === true) {
7520
+ this.checkRestType(p, node);
7521
+ continue;
7522
+ }
6386
7523
  const isOptional = p.optional === true || p.default !== void 0;
6387
7524
  if (isOptional) {
6388
7525
  if (seenOptional === void 0) seenOptional = p.name ?? "parameter";
@@ -6408,8 +7545,10 @@ var TypeAnalyzer = class {
6408
7545
  * when *no* overload accepts the count, so an overload set still reports
6409
7546
  * once, against its first signature. Returns whether the count fits, so
6410
7547
  * an argument's type is only complained about when its count is right. */
6411
- checkArity(node, fns, argCount, selfArgs) {
7548
+ checkArity(node, fns, argCount, selfArgs, spread) {
6412
7549
  if (!fns.length) return true;
7550
+ if (spread && !spread.elements) return true;
7551
+ if (spread?.elements) argCount += spread.elements.length - 1;
6413
7552
  const fits = fns.some((f) => {
6414
7553
  const { min: min2, max: max2 } = this.arityOf(f);
6415
7554
  const n = argCount + selfArgs;
@@ -6452,7 +7591,7 @@ var TypeAnalyzer = class {
6452
7591
  return type;
6453
7592
  };
6454
7593
  return record(this.withTypeParams(sig.generics, () => {
6455
- const params = sig.params.map((p) => ({
7594
+ const params = sig.params.filter((p) => !p.rest).map((p) => ({
6456
7595
  name: p.pattern ? void 0 : p.name,
6457
7596
  type: this.paramType(p, /* @__PURE__ */ new Map()),
6458
7597
  optional: p.optional || p.default !== void 0
@@ -6460,12 +7599,23 @@ var TypeAnalyzer = class {
6460
7599
  return fn(
6461
7600
  params,
6462
7601
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
6463
- sig.hasVarargs ? sig.varargTypeAnnotation ? this.resolveType(sig.varargTypeAnnotation) : anyType : void 0,
7602
+ this.varargElement(sig),
6464
7603
  names,
6465
7604
  this.resolvePredicate(sig.predicate, params)
6466
7605
  );
6467
7606
  }));
6468
7607
  }
7608
+ /** `...rest: T[]` holds every argument from its position on, so its type
7609
+ * is an array of what each one is. */
7610
+ checkRestType(p, node) {
7611
+ if (!this.emitDiagnostics || !p.typeAnnotation) return;
7612
+ const declared = this.resolveType(p.typeAnnotation);
7613
+ if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
7614
+ this.diagnostics.push({
7615
+ node,
7616
+ message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
7617
+ });
7618
+ }
6469
7619
  /** Turn a parsed `v is T` / `asserts v` annotation into a `TypePredicate`,
6470
7620
  * resolving the named parameter to its index. A guard naming a parameter
6471
7621
  * the function does not have is dropped rather than mis-narrowing an
@@ -6483,17 +7633,18 @@ var TypeAnalyzer = class {
6483
7633
  inferFunctionBody(func, env) {
6484
7634
  const names = func.generics.map((g) => g.name);
6485
7635
  return this.withTypeParams(func.generics, () => {
6486
- const params = func.params.map((p) => {
7636
+ const params = func.params.flatMap((p) => {
6487
7637
  const type = this.paramType(p, env);
6488
7638
  if (!p.pattern) {
6489
7639
  const id = this.bindingIdByName(p.name, p);
6490
7640
  if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
6491
7641
  }
6492
- return {
7642
+ if (p.rest) return [];
7643
+ return [{
6493
7644
  name: p.pattern ? void 0 : p.name,
6494
7645
  type,
6495
7646
  optional: p.optional || p.default !== void 0
6496
- };
7647
+ }];
6497
7648
  });
6498
7649
  const bodyEnv = forkEnv(env);
6499
7650
  for (const p of func.params) {
@@ -6518,7 +7669,7 @@ var TypeAnalyzer = class {
6518
7669
  return fn(
6519
7670
  params,
6520
7671
  returns,
6521
- func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0,
7672
+ this.varargElement(func),
6522
7673
  names,
6523
7674
  this.resolvePredicate(func.predicate, params)
6524
7675
  );
@@ -6732,6 +7883,13 @@ var TypeAnalyzer = class {
6732
7883
  if (src.kind === "array") return [numberType, src.element];
6733
7884
  }
6734
7885
  }
7886
+ const iterator = this.expand(iterType);
7887
+ if (iterator.kind === "function") {
7888
+ const returns = this.expand(iterator.returns);
7889
+ const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
7890
+ const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
7891
+ return [at(0), at(1)];
7892
+ }
6735
7893
  const t = this.expand(iterType);
6736
7894
  if (t.kind === "array") return varCount >= 2 ? [numberType, t.element] : [t.element, unknownType];
6737
7895
  if (t.kind === "object") {
@@ -6883,7 +8041,21 @@ var TypeAnalyzer = class {
6883
8041
  expand(t) {
6884
8042
  if (t.kind !== "genericRef") return t;
6885
8043
  const def = this.aliasDefs.get(t.name);
6886
- if (!def || this.resolvingAliases.has(t.name)) return t;
8044
+ if (!def) {
8045
+ const imported = this.importedTypes.get(t.name);
8046
+ if (!imported) return t;
8047
+ if (!imported.params.length) return imported.type;
8048
+ const subst = /* @__PURE__ */ new Map();
8049
+ imported.params.forEach((name, i) => subst.set(name, t.typeArguments[i] ?? unknownType));
8050
+ const key2 = `import ${formatType(t)}`;
8051
+ const cached2 = this.expandCache.get(key2);
8052
+ if (cached2) return cached2;
8053
+ this.expandCache.set(key2, t);
8054
+ const applied = substitute(imported.type, subst);
8055
+ this.expandCache.set(key2, applied);
8056
+ return applied;
8057
+ }
8058
+ if (this.resolvingAliases.has(t.name)) return t;
6887
8059
  const key = t.typeArguments.length ? formatType(t) : t.name;
6888
8060
  const cached = this.expandCache.get(key);
6889
8061
  if (cached) return cached;
@@ -6904,19 +8076,120 @@ var TypeAnalyzer = class {
6904
8076
  * those names again replaces the whole set, and nothing here is a special
6905
8077
  * case in the analyzer. The build lowers each call to a plain function. */
6906
8078
  builtInMethod(t, name) {
6907
- const element = t.kind === "array" ? t.element : t.kind === "tuple" ? union(t.elements) : void 0;
6908
- const methodTable = element !== void 0 ? "ArrayMethods" : t.kind === "primitive" && t.name === "string" || t.kind === "literal" && t.base === "string" ? "StringMethods" : void 0;
8079
+ const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
8080
+ const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(m.elements) : void 0);
8081
+ const element = elements.every((e) => e !== void 0) ? union(elements) : void 0;
8082
+ const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
8083
+ const methodTable = element !== void 0 ? "ArrayMethods" : parts.every(isString) ? "StringMethods" : void 0;
6909
8084
  const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
6910
8085
  if (!def || def.class) return void 0;
6911
8086
  const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
6912
- const parts = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
6913
- for (let i = parts.length - 1; i >= 0; i--) {
6914
- const part = parts[i];
8087
+ const layers = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
8088
+ for (let i = layers.length - 1; i >= 0; i--) {
8089
+ const part = layers[i];
6915
8090
  const property = part.kind === "object" ? part.properties.get(name) : void 0;
6916
8091
  if (property) return property.type;
6917
8092
  }
6918
8093
  return void 0;
6919
8094
  }
8095
+ /** The most a deferred type could turn out to be. A conditional is one of
8096
+ * its branches, and the true branch stands for a member of what was
8097
+ * tested (`T` in `T extends U ? T : never`); an indexed access reads
8098
+ * through the bound of what it indexes. Anything else has no bound worth
8099
+ * giving — `undefined` leaves the comparison as it was. */
8100
+ deferredBound(t, depth = 0) {
8101
+ if (depth > 8) return void 0;
8102
+ switch (t.kind) {
8103
+ case "conditional": {
8104
+ const check = this.reduceType(t.checkType);
8105
+ if (containsTypeParam(check)) return void 0;
8106
+ const subst = /* @__PURE__ */ new Map();
8107
+ if (t.distributeParam) subst.set(t.distributeParam, check);
8108
+ for (const name of t.inferVars ?? []) subst.set(name, unknownType);
8109
+ const branches = [substitute(t.trueType, subst), t.falseType].map((branch) => this.reduceType(branch));
8110
+ if (branches.some((branch) => containsTypeParam(branch))) return void 0;
8111
+ return union(branches);
8112
+ }
8113
+ case "indexedAccess": {
8114
+ const object = this.deferredBound(t.objectType, depth + 1) ?? this.atConstraints(t.objectType);
8115
+ const index = this.atConstraints(this.reduceType(t.indexType));
8116
+ if (!object || !index) return void 0;
8117
+ return this.indexedType(object, index);
8118
+ }
8119
+ default:
8120
+ return void 0;
8121
+ }
8122
+ }
8123
+ /** `t` with every type parameter standing at its constraint: `Map[K]`
8124
+ * where `K extends "a" | "b"` is at most what those two keys hold. A
8125
+ * parameter with no constraint bounds nothing, and says so. */
8126
+ atConstraints(t) {
8127
+ if (!containsTypeParam(t)) return t;
8128
+ const bounds = /* @__PURE__ */ new Map();
8129
+ const seen = /* @__PURE__ */ new WeakSet();
8130
+ let open = false;
8131
+ const walk = (value) => {
8132
+ if (!value || typeof value !== "object" || seen.has(value)) return;
8133
+ seen.add(value);
8134
+ if (value instanceof Map) {
8135
+ value.forEach(walk);
8136
+ return;
8137
+ }
8138
+ const part = value;
8139
+ if (part.kind === "object" && part.class) return;
8140
+ if (part.kind === "typeParam" && typeof part.name === "string") {
8141
+ if (part.constraint && !containsTypeParam(part.constraint)) {
8142
+ bounds.set(part.name, this.reduceType(part.constraint));
8143
+ } else {
8144
+ open = true;
8145
+ }
8146
+ }
8147
+ for (const child of Object.values(value)) walk(child);
8148
+ };
8149
+ walk(t);
8150
+ if (open) return void 0;
8151
+ const applied = this.reduceType(substitute(t, bounds));
8152
+ return containsTypeParam(applied) ? void 0 : applied;
8153
+ }
8154
+ /** What `text["upper"]` reads: a string answers only to its methods, the
8155
+ * way Lua's string metatable does. */
8156
+ stringMember(object, index) {
8157
+ if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
8158
+ const parts = this.stringParts(object);
8159
+ if (!parts) return void 0;
8160
+ const found = parts.map((part) => this.builtInMethod(part, index.value));
8161
+ return found.every((t) => t !== void 0) ? union(found) : void 0;
8162
+ }
8163
+ /** The members of `t` when every one of them is a string — `"a" | "b"` is
8164
+ * as much a string as `string` is. */
8165
+ stringParts(t) {
8166
+ let expanded = this.expand(t);
8167
+ if (expanded.kind === "conditional" || expanded.kind === "indexedAccess") {
8168
+ const bound = this.deferredBound(expanded);
8169
+ if (!bound) return void 0;
8170
+ expanded = this.expand(bound);
8171
+ }
8172
+ const parts = (expanded.kind === "union" ? expanded.types : [expanded]).map((m) => this.expand(m));
8173
+ const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
8174
+ return parts.length && parts.every(isString) ? parts : void 0;
8175
+ }
8176
+ /** `text.Sans` or `text["Sans"]`: a string is not a table, and the only
8177
+ * members it has are the ones a type library gave it — so a name that is
8178
+ * not one of them is a mistake worth reporting, rather than the nil Lua
8179
+ * would hand back. */
8180
+ checkStringMember(node, object, key) {
8181
+ if (!this.emitDiagnostics) return;
8182
+ const parts = this.stringParts(object);
8183
+ if (!parts) return;
8184
+ const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
8185
+ if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
8186
+ const names = keys.map((m) => String(m.value));
8187
+ if (names.some((name) => parts.some((part) => this.builtInMethod(part, name)))) return;
8188
+ this.diagnostics.push({
8189
+ node,
8190
+ message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8191
+ });
8192
+ }
6920
8193
  propertyType(raw, name) {
6921
8194
  const t = this.deferredAccess(this.expand(raw));
6922
8195
  if (t.kind === "object") {
@@ -6926,7 +8199,11 @@ var TypeAnalyzer = class {
6926
8199
  }
6927
8200
  const built = this.builtInMethod(t, name);
6928
8201
  if (built) return built;
6929
- if (t.kind === "union") return union(t.types.map((m) => this.propertyType(m, name)));
8202
+ if (t.kind === "union") {
8203
+ const built2 = this.builtInMethod(t, name);
8204
+ if (built2) return built2;
8205
+ return union(t.types.map((m) => this.propertyType(m, name)));
8206
+ }
6930
8207
  if (t.kind === "intersection") {
6931
8208
  const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
6932
8209
  if (parts.length) return intersection(parts);
@@ -6934,6 +8211,10 @@ var TypeAnalyzer = class {
6934
8211
  if (t.kind === "typeParam" && t.constraint) return this.propertyType(t.constraint, name);
6935
8212
  if (t.kind === "difference") return this.propertyType(t.base, name);
6936
8213
  if (t.kind === "any") return anyType;
8214
+ if (t.kind === "conditional" || t.kind === "indexedAccess") {
8215
+ const bound = this.deferredBound(t);
8216
+ if (bound) return this.propertyType(bound, name);
8217
+ }
6937
8218
  return unknownType;
6938
8219
  }
6939
8220
  /** `t[k]`. A statically known string key resolves against the declared
@@ -7009,6 +8290,20 @@ var TypeAnalyzer = class {
7009
8290
  // `...` holds what the function declared it takes.
7010
8291
  case "VarargExpression":
7011
8292
  return this.varargs[this.varargs.length - 1] ?? anyType;
8293
+ // `f(a, ...rest)` — every value the array holds, one after
8294
+ // another. Each of them is an element, so that is what the
8295
+ // parameters it fills are checked against.
8296
+ case "SpreadElement": {
8297
+ const spread = this.infer(expr.argument, env);
8298
+ const element = this.spreadElement(spread);
8299
+ if (element === void 0 && this.emitDiagnostics) {
8300
+ this.diagnostics.push({
8301
+ node: expr,
8302
+ message: `Only an array can be spread, and '${formatType(spread)}' is not one`
8303
+ });
8304
+ }
8305
+ return element ?? anyType;
8306
+ }
7012
8307
  // Broken syntax is reported by the parser; nothing more to say.
7013
8308
  case "ErrorExpression":
7014
8309
  return anyType;
@@ -7114,6 +8409,7 @@ var TypeAnalyzer = class {
7114
8409
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
7115
8410
  const key = this.refKeyOf(expr);
7116
8411
  const narrowed = key === void 0 ? void 0 : env.get(key);
8412
+ this.checkStringMember(expr, obj, literal(expr.property.name));
7117
8413
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
7118
8414
  }
7119
8415
  case "IndexExpression": {
@@ -7121,12 +8417,33 @@ var TypeAnalyzer = class {
7121
8417
  const idx = this.infer(expr.index, env);
7122
8418
  const key = this.refKeyOf(expr);
7123
8419
  const narrowed = key === void 0 ? void 0 : env.get(key);
8420
+ this.checkStringMember(expr, obj, this.expand(idx));
8421
+ const member = this.stringMember(obj, this.expand(idx));
8422
+ if (member) return this.chainResult(expr, member, shortCircuits);
7124
8423
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
7125
8424
  }
7126
8425
  case "CallExpression": {
8426
+ if (expr.callee.type === "SuperExpression") return this.inferSuperCall(expr, env);
7127
8427
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
7128
8428
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
7129
8429
  }
8430
+ case "NewExpression":
8431
+ return this.inferNew(expr, env);
8432
+ case "ClassExpression":
8433
+ return this.visitClass(expr, env);
8434
+ case "SuperExpression": {
8435
+ const stmt = this.currentClass;
8436
+ if (!stmt?.superclass) {
8437
+ if (this.emitDiagnostics) {
8438
+ this.diagnostics.push({
8439
+ node: expr,
8440
+ message: "'super' is only available inside a class that extends another"
8441
+ });
8442
+ }
8443
+ return anyType;
8444
+ }
8445
+ return this.superType(stmt);
8446
+ }
7130
8447
  case "MethodCallExpression": {
7131
8448
  const { type: objType, shortCircuits } = this.chainObject(expr, expr.object, env);
7132
8449
  return this.chainResult(expr, this.inferMethodCall(expr, objType, env), shortCircuits);
@@ -7145,16 +8462,56 @@ var TypeAnalyzer = class {
7145
8462
  }
7146
8463
  }
7147
8464
  }
8465
+ /** `new Name(args)` is `Name.new(args)` — the same function, and the
8466
+ * same check. Saying so here rather than rewriting the tree keeps the
8467
+ * error messages pointing at what was written. */
8468
+ inferNew(expr, env) {
8469
+ const calleeType = this.infer(expr.callee, env);
8470
+ const constructor = this.propertyType(calleeType, "new");
8471
+ if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8472
+ if (this.emitDiagnostics) {
8473
+ const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8474
+ this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8475
+ }
8476
+ for (const argument of expr.arguments) this.infer(argument, env);
8477
+ return anyType;
8478
+ }
8479
+ return this.inferCall(expr, constructor, env);
8480
+ }
8481
+ /** `super(...)` — the base constructor, run on the instance being built. */
8482
+ inferSuperCall(expr, env) {
8483
+ const stmt = this.currentClass;
8484
+ const constructor = stmt ? this.baseConstructorType(stmt) : void 0;
8485
+ if (!stmt?.superclass) {
8486
+ if (this.emitDiagnostics) {
8487
+ this.diagnostics.push({
8488
+ node: expr,
8489
+ message: "'super(...)' is only available inside the constructor of a class that extends another"
8490
+ });
8491
+ }
8492
+ for (const argument of expr.arguments) this.infer(argument, env);
8493
+ return nilType;
8494
+ }
8495
+ if (!constructor) {
8496
+ for (const argument of expr.arguments) this.infer(argument, env);
8497
+ return nilType;
8498
+ }
8499
+ const callable = fn(constructor.params.filter((p) => p.name !== "this"), nilType, constructor.varargs);
8500
+ this.inferCall(expr, callable, env);
8501
+ return nilType;
8502
+ }
7148
8503
  inferCall(expr, callee, env) {
8504
+ this.checkAmbiguousCall(expr);
7149
8505
  const fns = this.overloadsOf(callee);
7150
8506
  const explicit = this.explicitTypeArguments(expr, fns);
7151
8507
  const expected = this.expectedArguments(expr.arguments, fns, () => 0);
7152
8508
  expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
7153
8509
  const argTypes = expr.arguments.map((a) => this.infer(a, env));
7154
8510
  if (fns.length) {
7155
- this.recordExpected(expr.arguments, fns, () => 0);
7156
- const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
7157
- const picked = this.pickOverload(fns, argTypes);
8511
+ this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
8512
+ const spread = this.spreadOf(expr.arguments);
8513
+ const arityFits = this.checkArity(expr, fns, argTypes.length, 0, spread);
8514
+ const picked = this.pickOverload(fns, argTypes, void 0, spread);
7158
8515
  const distributed = this.distributedReturn(fns, argTypes, picked, (_, args) => args);
7159
8516
  if (distributed) return distributed;
7160
8517
  if (picked) {
@@ -7166,6 +8523,21 @@ var TypeAnalyzer = class {
7166
8523
  }
7167
8524
  return callee.kind === "any" ? anyType : unknownType;
7168
8525
  }
8526
+ /** A `(` on a line of its own continues the statement above it:
8527
+ *
8528
+ * const value = map[key]
8529
+ * ("text"):upper()
8530
+ *
8531
+ * calls `map[key]`, in luaut as in Lua and in JavaScript. It is almost
8532
+ * never what was meant, and what it does instead is invisible — so say
8533
+ * so, and name the fix. */
8534
+ checkAmbiguousCall(expr) {
8535
+ if (!this.emitDiagnostics || !expr.argumentsOnNewLine) return;
8536
+ this.diagnostics.push({
8537
+ node: expr,
8538
+ 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."
8539
+ });
8540
+ }
7169
8541
  inferMethodCall(expr, objType, env) {
7170
8542
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
7171
8543
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -7175,9 +8547,11 @@ var TypeAnalyzer = class {
7175
8547
  if (fns.length) {
7176
8548
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
7177
8549
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
7178
- this.recordExpected(expr.arguments, fns, selfOf);
7179
- const arityFits = this.checkArity(expr, fns, argTypes.length, this.takesSelf(fns[0]) ? 1 : 0);
7180
- const picked = this.pickOverload(fns, argTypes, withSelf);
8550
+ this.recordExpected(expr.arguments, fns, selfOf, withSelf);
8551
+ const self0 = this.takesSelf(fns[0]) ? 1 : 0;
8552
+ const spread = this.spreadOf(expr.arguments, self0);
8553
+ const arityFits = this.checkArity(expr, fns, argTypes.length, self0, spread);
8554
+ const picked = this.pickOverload(fns, argTypes, withSelf, spread);
7181
8555
  const distributed = this.distributedReturn(
7182
8556
  fns,
7183
8557
  argTypes,
@@ -7826,7 +9200,39 @@ var TypeAnalyzer = class {
7826
9200
  * out. Every place that has to line arguments up with parameters goes
7827
9201
  * through here so the two sides cannot drift apart. */
7828
9202
  takesSelf(f) {
7829
- return f.params[0]?.name === "self";
9203
+ const first = f.params[0]?.name;
9204
+ return first === "self" || first === "this";
9205
+ }
9206
+ /** What one value of a spread array is, or `undefined` when the thing
9207
+ * spread is not a list of values at all. */
9208
+ spreadElement(t) {
9209
+ const spread = this.expand(t);
9210
+ if (spread.kind === "array") return spread.element;
9211
+ if (spread.kind === "tuple") return union(spread.elements);
9212
+ if (spread.kind === "any") return anyType;
9213
+ return void 0;
9214
+ }
9215
+ /** Where a call's arguments stop being one each, and what fills the rest.
9216
+ * From a spread on, every remaining parameter is filled by one of the
9217
+ * array's values — however many that turns out to be, so neither the
9218
+ * count nor the positions after it are known. A *tuple* is the exception:
9219
+ * it holds a known value at each position, and `elements` says which. */
9220
+ spreadOf(args, self = 0) {
9221
+ const index = args.findIndex((a) => a.type === "SpreadElement");
9222
+ if (index < 0) return void 0;
9223
+ const spread = args[index];
9224
+ const held = this.typeOf.get(spread.argument);
9225
+ const expanded = held && this.expand(held);
9226
+ return {
9227
+ index: index + self,
9228
+ element: this.typeOf.get(spread) ?? unknownType,
9229
+ elements: expanded?.kind === "tuple" ? expanded.elements : void 0
9230
+ };
9231
+ }
9232
+ /** One of `spread`'s values, at the position `i` of a call's arguments. */
9233
+ spreadValue(spread, i) {
9234
+ if (!spread.elements) return spread.element;
9235
+ return spread.elements[i - spread.index] ?? neverType;
7830
9236
  }
7831
9237
  /** A function type as a list of call signatures: a lone function is a
7832
9238
  * one-element list, an intersection is the overload set in source order. */
@@ -7881,6 +9287,8 @@ var TypeAnalyzer = class {
7881
9287
  type = this.resolveType(statement.valueType);
7882
9288
  } else if (statement.type === "FunctionDeclaration") {
7883
9289
  type = statement.signatures?.length ? intersection(statement.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(statement.func, /* @__PURE__ */ new Map());
9290
+ } else if (statement.type === "ClassDeclaration") {
9291
+ type = this.classValueType(statement);
7884
9292
  } else if (statement.type === "VariableDeclaration") {
7885
9293
  const target = statement.names[index];
7886
9294
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
@@ -7917,6 +9325,10 @@ var TypeAnalyzer = class {
7917
9325
  return;
7918
9326
  }
7919
9327
  const record = node;
9328
+ if (record.type === "ClassDeclaration" && record.name) {
9329
+ const id = this.bindingIdByName(record.name.name, record.name);
9330
+ if (id !== void 0) out.set(id, { statement: node, index: 0 });
9331
+ }
7920
9332
  if (record.type === "FunctionDeclaration" && record.name) {
7921
9333
  const id = this.bindingIdByName(record.name.name, record.name);
7922
9334
  if (id !== void 0) out.set(id, { statement: node, index: 0 });
@@ -8436,6 +9848,7 @@ export {
8436
9848
  resolveModulePath,
8437
9849
  resolveTypeLibraries,
8438
9850
  setAliasExpander,
9851
+ setDeferredBound,
8439
9852
  sourceMapTypes,
8440
9853
  stringType,
8441
9854
  stripJsonComments,