luaut-parser 3.1.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. */
@@ -4726,9 +5328,10 @@ var TypeAnalyzer = class {
4726
5328
  /** Recursion guard for `preVisitBody`. */
4727
5329
  preVisitDepth = 0;
4728
5330
  run() {
4729
- this.registerAliasDefs(preludeProgram().body);
4730
- for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body);
5331
+ this.registerAliasDefs(preludeProgram().body, true);
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) {
@@ -4805,17 +5415,65 @@ var TypeAnalyzer = class {
4805
5415
  }
4806
5416
  }
4807
5417
  }
4808
- registerAliasDefs(block) {
5418
+ /** `layering` is on for the prelude and for definitions files: a second
5419
+ * library that declares an alias already declared *adds* to it, the way a
5420
+ * second `declare` of a table's name does, so `@luaut/roblox` can give
5421
+ * `StringMethods` Luau's `split` without restating Lua's. The file being
5422
+ * analysed is not a layer: its own alias replaces what the libraries
5423
+ * gave, which is how a project opts out of a set. */
5424
+ registerAliasDefs(block, layering = false) {
4809
5425
  for (const stmt of block.statements) {
4810
5426
  const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
4811
- if (alias) this.aliasDefs.set(alias.name.name, { params: alias.generics, node: alias.definition });
5427
+ if (alias) {
5428
+ const previous = layering ? this.aliasDefs.get(alias.name.name) : void 0;
5429
+ const node = previous && !previous.class ? {
5430
+ type: "IntersectionTypeNode",
5431
+ types: [previous.node, alias.definition],
5432
+ line: alias.definition.line,
5433
+ column: alias.definition.column
5434
+ } : alias.definition;
5435
+ this.aliasDefs.set(alias.name.name, {
5436
+ params: previous && !previous.class && previous.params.length ? previous.params : alias.generics,
5437
+ node
5438
+ });
5439
+ }
4812
5440
  if (stmt.type === "DeclareClassStatement") {
4813
5441
  this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
4814
5442
  }
5443
+ const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
5444
+ if (declaration.type === "ClassDeclaration") this.registerClass(declaration);
4815
5445
  }
4816
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
+ }
4817
5474
  /** A non-generic definition's type. */
4818
5475
  resolveDef(def) {
5476
+ if (def.runtimeClass) return this.instanceType(def.runtimeClass);
4819
5477
  return def.class ? this.classType(def.class) : this.resolveType(def.node);
4820
5478
  }
4821
5479
  /** One type per class declaration, so every mention of a class is the same
@@ -4873,6 +5531,381 @@ var TypeAnalyzer = class {
4873
5531
  if (this.program.body.statements.includes(stmt)) ownMembers();
4874
5532
  return type;
4875
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
+ }
4876
5909
  /** `extends` must name a class, and the chain must end. */
4877
5910
  checkClass(stmt) {
4878
5911
  if (!stmt.superclass || !this.emitDiagnostics) return;
@@ -5129,6 +6162,7 @@ var TypeAnalyzer = class {
5129
6162
  instantiateAlias(def, args) {
5130
6163
  if (this.instantiationDepth > 20) return unknownType;
5131
6164
  const subst = this.bindTypeArguments(def.params, args);
6165
+ if (def.runtimeClass) return substitute(this.instanceType(def.runtimeClass), subst);
5132
6166
  this.instantiationDepth++;
5133
6167
  try {
5134
6168
  const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
@@ -5273,15 +6307,17 @@ var TypeAnalyzer = class {
5273
6307
  case "FunctionTypeNode": {
5274
6308
  const names = node.generics.map((g) => g.name);
5275
6309
  return this.withTypeParams(node.generics, () => {
5276
- const params = node.params.map((p) => ({
6310
+ const params = node.params.filter((p) => !p.rest).map((p) => ({
5277
6311
  name: p.name,
5278
6312
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
5279
6313
  optional: p.optional
5280
6314
  }));
6315
+ const restParam = node.params.find((p) => p.rest);
6316
+ const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
5281
6317
  return this.withTypeParamDefaults(fn(
5282
6318
  params,
5283
6319
  this.resolveType(node.returnType),
5284
- 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,
5285
6321
  names,
5286
6322
  this.resolvePredicate(node.predicate, params)
5287
6323
  ), node.generics);
@@ -5475,7 +6511,10 @@ var TypeAnalyzer = class {
5475
6511
  accessType(obj, index) {
5476
6512
  if (index.kind === "union") return union(index.types.map((m) => this.accessType(obj, m)));
5477
6513
  if (index.kind === "literal" && typeof index.value === "string") {
5478
- 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;
5479
6518
  }
5480
6519
  return this.indexedType(obj, index);
5481
6520
  }
@@ -5628,12 +6667,13 @@ var TypeAnalyzer = class {
5628
6667
  visitStatement(stmt, env) {
5629
6668
  switch (stmt.type) {
5630
6669
  case "VariableDeclaration": {
6670
+ this.nameClassExpressions(stmt);
5631
6671
  stmt.names.forEach((target, i) => {
5632
6672
  if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
5633
6673
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
5634
6674
  }
5635
6675
  });
5636
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
6676
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
5637
6677
  stmt.names.forEach((target, i) => {
5638
6678
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
5639
6679
  const source = sources[i];
@@ -5658,6 +6698,15 @@ var TypeAnalyzer = class {
5658
6698
  });
5659
6699
  return;
5660
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
+ }
5661
6710
  case "FunctionDeclaration": {
5662
6711
  this.checkParamOrder(stmt.func.params, stmt);
5663
6712
  for (const sig of stmt.signatures ?? []) this.checkParamOrder(sig.params, stmt);
@@ -5713,7 +6762,7 @@ var TypeAnalyzer = class {
5713
6762
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
5714
6763
  }
5715
6764
  });
5716
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
6765
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
5717
6766
  stmt.targets.forEach((target, i) => {
5718
6767
  const vt = valueTypes[i] ?? unknownType;
5719
6768
  const source = sources[i];
@@ -5821,7 +6870,8 @@ var TypeAnalyzer = class {
5821
6870
  stmt.arguments.forEach((a, i) => this.applyContext(a, declared.elements[i]));
5822
6871
  }
5823
6872
  }
5824
- 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);
5825
6875
  this.checkReturn(stmt, declared, types, sources, env);
5826
6876
  if (this.returnTypes) {
5827
6877
  this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
@@ -5832,7 +6882,8 @@ var TypeAnalyzer = class {
5832
6882
  this.visitStatement(stmt.declaration, env);
5833
6883
  return;
5834
6884
  case "ExportDefaultStatement":
5835
- this.infer(stmt.declaration, env);
6885
+ if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, env);
6886
+ else this.infer(stmt.declaration, env);
5836
6887
  return;
5837
6888
  case "ExportNamedStatement": {
5838
6889
  if (stmt.source) {
@@ -5911,12 +6962,35 @@ var TypeAnalyzer = class {
5911
6962
  * `sources` maps each produced value back to the expression it came from
5912
6963
  * (undefined for the 2nd and later values of a multi-value call), so the
5913
6964
  * caller can still do contextual typing against the written expression. */
5914
- valueList(exprs, env) {
6965
+ valueList(exprs, env, want = 0) {
5915
6966
  const types = [];
5916
6967
  const sources = [];
5917
6968
  exprs.forEach((e, i) => {
5918
6969
  const t = this.infer(e, env);
5919
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
+ }
5920
6994
  if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
5921
6995
  t.elements.forEach((el, j) => {
5922
6996
  types.push(el);
@@ -6005,7 +7079,8 @@ var TypeAnalyzer = class {
6005
7079
  /** A parameter's type: annotation, else a shape synthesized from a
6006
7080
  * destructuring pattern, else inferred from its default, else `any`. */
6007
7081
  paramType(p, env) {
6008
- 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) {
6009
7084
  return this.selfType;
6010
7085
  }
6011
7086
  if (p.typeAnnotation) {
@@ -6013,6 +7088,7 @@ var TypeAnalyzer = class {
6013
7088
  if (p.default) this.applyContext(p.default, t);
6014
7089
  return p.optional ? optional(t) : t;
6015
7090
  }
7091
+ if (p.rest) return arrayOf(unknownType);
6016
7092
  if (p.pattern) return this.patternToType(p.pattern, env);
6017
7093
  if (p.default) return widen(this.infer(p.default, env));
6018
7094
  return this.contextualParams.get(p) ?? anyType;
@@ -6033,6 +7109,11 @@ var TypeAnalyzer = class {
6033
7109
  this.expectedTypeOf.set(e, expected);
6034
7110
  if (e.type === "ArrayExpression") return this.applyArrayContext(e, expected);
6035
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
+ }
6036
7117
  if (e.type !== "FunctionExpression") return;
6037
7118
  const members = expected.kind === "union" ? expected.types : [expected];
6038
7119
  const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
@@ -6058,7 +7139,7 @@ var TypeAnalyzer = class {
6058
7139
  const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
6059
7140
  if (!target) return;
6060
7141
  if (!e.elements.length) {
6061
- if (!containsTypeParam(target)) this.contextualArrays.set(e, target);
7142
+ this.contextualArrays.set(e, target);
6062
7143
  return;
6063
7144
  }
6064
7145
  e.elements.forEach((element, i) => {
@@ -6073,13 +7154,15 @@ var TypeAnalyzer = class {
6073
7154
  const objects = this.expectedMembers(expected).filter((m) => m.kind === "object");
6074
7155
  if (!objects.length) return;
6075
7156
  for (const field of e.fields) {
6076
- if (field.type !== "TableFieldNamed") continue;
6077
- const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
7157
+ if (field.type !== "TableFieldNamed" && field.type !== "TableFieldShorthand") continue;
7158
+ const key = field.type === "TableFieldShorthand" ? field.name.name : field.key.type === "Identifier" ? field.key.name : field.key.value;
6078
7159
  const types = objects.flatMap((o) => {
6079
7160
  const property = o.properties.get(key);
6080
7161
  return property ? [property.type] : o.indexer ? [o.indexer.value] : [];
6081
7162
  });
6082
- if (types.length) this.applyContext(field.value, union(types));
7163
+ if (types.length) {
7164
+ this.applyContext(field.type === "TableFieldShorthand" ? field.name : field.value, union(types));
7165
+ }
6083
7166
  }
6084
7167
  }
6085
7168
  /** The members of an expected type worth matching a literal against:
@@ -6116,13 +7199,26 @@ var TypeAnalyzer = class {
6116
7199
  }
6117
7200
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
6118
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
+ }
6119
7215
  /** The type of `...` in each function body being walked. */
6120
7216
  varargs = [];
6121
7217
  /** What each function body being walked declared it returns. */
6122
7218
  declaredReturns = [];
6123
7219
  /** Run `body` with `...` and `return` as `func` declares them. */
6124
7220
  withVarargs(func, body) {
6125
- this.varargs.push(func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0);
7221
+ this.varargs.push(this.varargElement(func));
6126
7222
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
6127
7223
  try {
6128
7224
  return body();
@@ -6203,6 +7299,13 @@ var TypeAnalyzer = class {
6203
7299
  const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
6204
7300
  unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
6205
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
+ }
6206
7309
  for (const name of f.typeParams ?? []) {
6207
7310
  if (!subst.has(name)) subst.set(name, f.typeParamDefaults?.[name] ?? unknownType);
6208
7311
  }
@@ -6227,11 +7330,11 @@ var TypeAnalyzer = class {
6227
7330
  * after every concrete signature has been tried. That ordering is what
6228
7331
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
6229
7332
  * `<T>(v: T) -> string` and still pick the precise one. */
6230
- pickOverload(fns, argTypes, argsFor) {
7333
+ pickOverload(fns, argTypes, argsFor, spread) {
6231
7334
  for (const generic of [false, true]) {
6232
7335
  for (const f of fns) {
6233
7336
  if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
6234
- if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes)) return f;
7337
+ if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
6235
7338
  }
6236
7339
  }
6237
7340
  return void 0;
@@ -6266,12 +7369,27 @@ var TypeAnalyzer = class {
6266
7369
  * own type parameters stand for what the call would infer, so each is
6267
7370
  * checked only against its constraint — `<K extends keyof Services>`
6268
7371
  * accepts `"Players"` but not `""`. */
6269
- overloadAccepts(f, argTypes) {
6270
- 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
+ }
6271
7388
  const params = this.boundParams(f);
6272
7389
  return f.params.every((p, i) => {
6273
- if (argTypes[i] === void 0) return p.optional === true;
6274
- 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]);
6275
7393
  });
6276
7394
  }
6277
7395
  /** A signature's parameter types as a call site sees them before inference:
@@ -6301,17 +7419,40 @@ var TypeAnalyzer = class {
6301
7419
  }
6302
7420
  /** Record what each written argument is expected to be — see
6303
7421
  * `TypeAnalysis.expectedTypeOf`. */
6304
- 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)));
6305
7425
  written.forEach((arg, j) => {
6306
7426
  const candidates = [];
6307
7427
  for (const f of fns) {
6308
7428
  const i = j + selfOf(f);
6309
- 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;
6310
7431
  if (param) candidates.push(param);
6311
7432
  }
6312
7433
  if (candidates.length) this.expectedTypeOf.set(arg, union(candidates));
6313
7434
  });
6314
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
+ }
6315
7456
  /** No signature accepts the call, and the argument count is not the
6316
7457
  * problem: say which argument is wrong, the way TypeScript does. */
6317
7458
  /** Check what was written against the parameters as this call's own type
@@ -6322,10 +7463,11 @@ var TypeAnalyzer = class {
6322
7463
  if (!this.emitDiagnostics || !f.typeParams?.length) return;
6323
7464
  const subst = this.inferTypeArgs(f, [...argTypes]);
6324
7465
  for (const bound of subst.values()) if (bound.kind === "unknown") return;
6325
- 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++) {
6326
7468
  const arg = argTypes[i];
6327
- const declared = f.params[i].type;
6328
- if (arg === void 0 || !containsTypeParam(declared)) continue;
7469
+ const declared = declaredAt(i);
7470
+ if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
6329
7471
  const expected = this.reduceType(substitute(declared, subst));
6330
7472
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
6331
7473
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
@@ -6346,15 +7488,26 @@ var TypeAnalyzer = class {
6346
7488
  const args = argsFor(f);
6347
7489
  const params = this.boundParams(f);
6348
7490
  const self = selfOf(f);
7491
+ const spread = this.spreadOf(written, self);
6349
7492
  for (let i = 0; i < f.params.length; i++) {
6350
- const arg = args[i];
6351
- 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;
6352
7496
  this.diagnostics.push({
6353
- node: written[i - self] ?? call,
7497
+ node: (spread && i >= spread.index ? written[spread.index - self] : written[i - self]) ?? call,
6354
7498
  message: `Argument of type '${formatType(arg)}' is not assignable to parameter of type '${briefType(params[i])}'`
6355
7499
  });
6356
7500
  return;
6357
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
+ }
6358
7511
  }
6359
7512
  /** A required parameter may not follow an optional one — otherwise the
6360
7513
  * optional one could never actually be omitted. Same rule as TypeScript,
@@ -6363,6 +7516,10 @@ var TypeAnalyzer = class {
6363
7516
  if (!this.emitDiagnostics) return;
6364
7517
  let seenOptional;
6365
7518
  for (const p of params) {
7519
+ if (p.rest === true) {
7520
+ this.checkRestType(p, node);
7521
+ continue;
7522
+ }
6366
7523
  const isOptional = p.optional === true || p.default !== void 0;
6367
7524
  if (isOptional) {
6368
7525
  if (seenOptional === void 0) seenOptional = p.name ?? "parameter";
@@ -6388,8 +7545,10 @@ var TypeAnalyzer = class {
6388
7545
  * when *no* overload accepts the count, so an overload set still reports
6389
7546
  * once, against its first signature. Returns whether the count fits, so
6390
7547
  * an argument's type is only complained about when its count is right. */
6391
- checkArity(node, fns, argCount, selfArgs) {
7548
+ checkArity(node, fns, argCount, selfArgs, spread) {
6392
7549
  if (!fns.length) return true;
7550
+ if (spread && !spread.elements) return true;
7551
+ if (spread?.elements) argCount += spread.elements.length - 1;
6393
7552
  const fits = fns.some((f) => {
6394
7553
  const { min: min2, max: max2 } = this.arityOf(f);
6395
7554
  const n = argCount + selfArgs;
@@ -6432,7 +7591,7 @@ var TypeAnalyzer = class {
6432
7591
  return type;
6433
7592
  };
6434
7593
  return record(this.withTypeParams(sig.generics, () => {
6435
- const params = sig.params.map((p) => ({
7594
+ const params = sig.params.filter((p) => !p.rest).map((p) => ({
6436
7595
  name: p.pattern ? void 0 : p.name,
6437
7596
  type: this.paramType(p, /* @__PURE__ */ new Map()),
6438
7597
  optional: p.optional || p.default !== void 0
@@ -6440,12 +7599,23 @@ var TypeAnalyzer = class {
6440
7599
  return fn(
6441
7600
  params,
6442
7601
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
6443
- sig.hasVarargs ? sig.varargTypeAnnotation ? this.resolveType(sig.varargTypeAnnotation) : anyType : void 0,
7602
+ this.varargElement(sig),
6444
7603
  names,
6445
7604
  this.resolvePredicate(sig.predicate, params)
6446
7605
  );
6447
7606
  }));
6448
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
+ }
6449
7619
  /** Turn a parsed `v is T` / `asserts v` annotation into a `TypePredicate`,
6450
7620
  * resolving the named parameter to its index. A guard naming a parameter
6451
7621
  * the function does not have is dropped rather than mis-narrowing an
@@ -6463,17 +7633,18 @@ var TypeAnalyzer = class {
6463
7633
  inferFunctionBody(func, env) {
6464
7634
  const names = func.generics.map((g) => g.name);
6465
7635
  return this.withTypeParams(func.generics, () => {
6466
- const params = func.params.map((p) => {
7636
+ const params = func.params.flatMap((p) => {
6467
7637
  const type = this.paramType(p, env);
6468
7638
  if (!p.pattern) {
6469
7639
  const id = this.bindingIdByName(p.name, p);
6470
7640
  if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
6471
7641
  }
6472
- return {
7642
+ if (p.rest) return [];
7643
+ return [{
6473
7644
  name: p.pattern ? void 0 : p.name,
6474
7645
  type,
6475
7646
  optional: p.optional || p.default !== void 0
6476
- };
7647
+ }];
6477
7648
  });
6478
7649
  const bodyEnv = forkEnv(env);
6479
7650
  for (const p of func.params) {
@@ -6498,7 +7669,7 @@ var TypeAnalyzer = class {
6498
7669
  return fn(
6499
7670
  params,
6500
7671
  returns,
6501
- func.hasVarargs ? func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType : void 0,
7672
+ this.varargElement(func),
6502
7673
  names,
6503
7674
  this.resolvePredicate(func.predicate, params)
6504
7675
  );
@@ -6712,6 +7883,13 @@ var TypeAnalyzer = class {
6712
7883
  if (src.kind === "array") return [numberType, src.element];
6713
7884
  }
6714
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
+ }
6715
7893
  const t = this.expand(iterType);
6716
7894
  if (t.kind === "array") return varCount >= 2 ? [numberType, t.element] : [t.element, unknownType];
6717
7895
  if (t.kind === "object") {
@@ -6863,7 +8041,21 @@ var TypeAnalyzer = class {
6863
8041
  expand(t) {
6864
8042
  if (t.kind !== "genericRef") return t;
6865
8043
  const def = this.aliasDefs.get(t.name);
6866
- 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;
6867
8059
  const key = t.typeArguments.length ? formatType(t) : t.name;
6868
8060
  const cached = this.expandCache.get(key);
6869
8061
  if (cached) return cached;
@@ -6878,6 +8070,126 @@ var TypeAnalyzer = class {
6878
8070
  this.resolvingAliases.delete(t.name);
6879
8071
  }
6880
8072
  }
8073
+ /** `names:filter(f)`, `text:trim()` — the methods arrays and strings have.
8074
+ * They are written in the prelude as `ArrayMethods<T>` and
8075
+ * `StringMethods`, so a file (or a type library) that declares one of
8076
+ * those names again replaces the whole set, and nothing here is a special
8077
+ * case in the analyzer. The build lowers each call to a plain function. */
8078
+ builtInMethod(t, name) {
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;
8084
+ const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
8085
+ if (!def || def.class) return void 0;
8086
+ const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
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];
8090
+ const property = part.kind === "object" ? part.properties.get(name) : void 0;
8091
+ if (property) return property.type;
8092
+ }
8093
+ return void 0;
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
+ }
6881
8193
  propertyType(raw, name) {
6882
8194
  const t = this.deferredAccess(this.expand(raw));
6883
8195
  if (t.kind === "object") {
@@ -6885,7 +8197,13 @@ var TypeAnalyzer = class {
6885
8197
  if (p) return p.optional ? optional(p.type) : p.type;
6886
8198
  if (t.indexer) return t.indexer.value;
6887
8199
  }
6888
- if (t.kind === "union") return union(t.types.map((m) => this.propertyType(m, name)));
8200
+ const built = this.builtInMethod(t, name);
8201
+ if (built) return built;
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
+ }
6889
8207
  if (t.kind === "intersection") {
6890
8208
  const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
6891
8209
  if (parts.length) return intersection(parts);
@@ -6893,6 +8211,10 @@ var TypeAnalyzer = class {
6893
8211
  if (t.kind === "typeParam" && t.constraint) return this.propertyType(t.constraint, name);
6894
8212
  if (t.kind === "difference") return this.propertyType(t.base, name);
6895
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
+ }
6896
8218
  return unknownType;
6897
8219
  }
6898
8220
  /** `t[k]`. A statically known string key resolves against the declared
@@ -6968,6 +8290,20 @@ var TypeAnalyzer = class {
6968
8290
  // `...` holds what the function declared it takes.
6969
8291
  case "VarargExpression":
6970
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
+ }
6971
8307
  // Broken syntax is reported by the parser; nothing more to say.
6972
8308
  case "ErrorExpression":
6973
8309
  return anyType;
@@ -7073,6 +8409,7 @@ var TypeAnalyzer = class {
7073
8409
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
7074
8410
  const key = this.refKeyOf(expr);
7075
8411
  const narrowed = key === void 0 ? void 0 : env.get(key);
8412
+ this.checkStringMember(expr, obj, literal(expr.property.name));
7076
8413
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
7077
8414
  }
7078
8415
  case "IndexExpression": {
@@ -7080,12 +8417,33 @@ var TypeAnalyzer = class {
7080
8417
  const idx = this.infer(expr.index, env);
7081
8418
  const key = this.refKeyOf(expr);
7082
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);
7083
8423
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
7084
8424
  }
7085
8425
  case "CallExpression": {
8426
+ if (expr.callee.type === "SuperExpression") return this.inferSuperCall(expr, env);
7086
8427
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
7087
8428
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
7088
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
+ }
7089
8447
  case "MethodCallExpression": {
7090
8448
  const { type: objType, shortCircuits } = this.chainObject(expr, expr.object, env);
7091
8449
  return this.chainResult(expr, this.inferMethodCall(expr, objType, env), shortCircuits);
@@ -7104,16 +8462,56 @@ var TypeAnalyzer = class {
7104
8462
  }
7105
8463
  }
7106
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
+ }
7107
8503
  inferCall(expr, callee, env) {
8504
+ this.checkAmbiguousCall(expr);
7108
8505
  const fns = this.overloadsOf(callee);
7109
8506
  const explicit = this.explicitTypeArguments(expr, fns);
7110
8507
  const expected = this.expectedArguments(expr.arguments, fns, () => 0);
7111
8508
  expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
7112
8509
  const argTypes = expr.arguments.map((a) => this.infer(a, env));
7113
8510
  if (fns.length) {
7114
- this.recordExpected(expr.arguments, fns, () => 0);
7115
- const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
7116
- 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);
7117
8515
  const distributed = this.distributedReturn(fns, argTypes, picked, (_, args) => args);
7118
8516
  if (distributed) return distributed;
7119
8517
  if (picked) {
@@ -7125,6 +8523,21 @@ var TypeAnalyzer = class {
7125
8523
  }
7126
8524
  return callee.kind === "any" ? anyType : unknownType;
7127
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
+ }
7128
8541
  inferMethodCall(expr, objType, env) {
7129
8542
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
7130
8543
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -7134,9 +8547,11 @@ var TypeAnalyzer = class {
7134
8547
  if (fns.length) {
7135
8548
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
7136
8549
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
7137
- this.recordExpected(expr.arguments, fns, selfOf);
7138
- const arityFits = this.checkArity(expr, fns, argTypes.length, this.takesSelf(fns[0]) ? 1 : 0);
7139
- 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);
7140
8555
  const distributed = this.distributedReturn(
7141
8556
  fns,
7142
8557
  argTypes,
@@ -7238,7 +8653,21 @@ var TypeAnalyzer = class {
7238
8653
  }
7239
8654
  }
7240
8655
  if (asConst && !hadSpread) return tuple(elems);
7241
- return arrayOf(elems.length ? union(elems.map((t) => asConst ? t : widen(t))) : unknownType);
8656
+ return arrayOf(elems.length ? union(elems.map((t, i) => {
8657
+ const element = expr.elements[i];
8658
+ return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
8659
+ })) : unknownType);
8660
+ }
8661
+ /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
8662
+ * `{ n: number }` — unless the surroundings said a literal belongs there.
8663
+ * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
8664
+ * string literals, exactly as TypeScript's contextual typing does, and
8665
+ * goes on widening to `string` when the parameter only says `string`.
8666
+ * The context was recorded by `applyContext` before the value was
8667
+ * inferred, so this is a lookup rather than a second pass. */
8668
+ widenUnlessAsked(value, at) {
8669
+ const wanted = this.expectedTypeOf.get(at);
8670
+ return wanted === void 0 ? widen(value) : this.keepContextualLiterals(value, wanted);
7242
8671
  }
7243
8672
  inferObject(expr, env, asConst) {
7244
8673
  const entries = [];
@@ -7246,16 +8675,24 @@ var TypeAnalyzer = class {
7246
8675
  for (const field of expr.fields) {
7247
8676
  if (field.type === "TableFieldNamed") {
7248
8677
  const key = field.key.type === "Identifier" ? field.key.name : field.key.value;
7249
- const v = asConst ? this.inferAsConst(field.value, env) : widen(this.infer(field.value, env));
8678
+ const v = asConst ? this.inferAsConst(field.value, env) : this.widenUnlessAsked(this.infer(field.value, env), field.value);
7250
8679
  entries.push([key, { type: v, optional: false, readonly: asConst }]);
7251
8680
  } else if (field.type === "TableFieldShorthand") {
7252
8681
  const v = this.infer(field.name, env);
7253
- entries.push([field.name.name, { type: asConst ? v : widen(v), optional: false, readonly: asConst }]);
8682
+ entries.push([field.name.name, {
8683
+ type: asConst ? v : this.widenUnlessAsked(v, field.name),
8684
+ optional: false,
8685
+ readonly: asConst
8686
+ }]);
7254
8687
  } else if (field.type === "TableFieldComputed") {
7255
8688
  const k = this.infer(field.key, env);
7256
8689
  const v = this.infer(field.value, env);
7257
8690
  if (k.kind === "literal" && typeof k.value === "string") {
7258
- entries.push([k.value, { type: asConst ? v : widen(v), optional: false, readonly: asConst }]);
8691
+ entries.push([k.value, {
8692
+ type: asConst ? v : this.widenUnlessAsked(v, field.value),
8693
+ optional: false,
8694
+ readonly: asConst
8695
+ }]);
7259
8696
  } else {
7260
8697
  indexer = mergeIndexer(indexer, { key: widen(k), value: asConst ? v : widen(v) });
7261
8698
  }
@@ -7763,7 +9200,39 @@ var TypeAnalyzer = class {
7763
9200
  * out. Every place that has to line arguments up with parameters goes
7764
9201
  * through here so the two sides cannot drift apart. */
7765
9202
  takesSelf(f) {
7766
- 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;
7767
9236
  }
7768
9237
  /** A function type as a list of call signatures: a lone function is a
7769
9238
  * one-element list, an intersection is the overload set in source order. */
@@ -7818,6 +9287,8 @@ var TypeAnalyzer = class {
7818
9287
  type = this.resolveType(statement.valueType);
7819
9288
  } else if (statement.type === "FunctionDeclaration") {
7820
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);
7821
9292
  } else if (statement.type === "VariableDeclaration") {
7822
9293
  const target = statement.names[index];
7823
9294
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
@@ -7854,6 +9325,10 @@ var TypeAnalyzer = class {
7854
9325
  return;
7855
9326
  }
7856
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
+ }
7857
9332
  if (record.type === "FunctionDeclaration" && record.name) {
7858
9333
  const id = this.bindingIdByName(record.name.name, record.name);
7859
9334
  if (id !== void 0) out.set(id, { statement: node, index: 0 });
@@ -8080,6 +9555,7 @@ function offsetPosition(source, offset) {
8080
9555
  import { dirname as dirname2, join as join2, resolve as resolve2 } from "path";
8081
9556
  function resolveTypeLibraries(config, host = nodeHost) {
8082
9557
  const files = [];
9558
+ const lowerings = [];
8083
9559
  const problems = [];
8084
9560
  const loaded = /* @__PURE__ */ new Set();
8085
9561
  const addFile = (file) => {
@@ -8097,6 +9573,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
8097
9573
  if (found) addPackage(found.directory, found.file, visiting);
8098
9574
  }
8099
9575
  addFile(entryFile);
9576
+ const lowering = loweringModule(directory, host, problems, config);
9577
+ if (lowering) lowerings.push(lowering);
8100
9578
  };
8101
9579
  for (const entry of config.types) {
8102
9580
  const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);
@@ -8123,7 +9601,19 @@ function resolveTypeLibraries(config, host = nodeHost) {
8123
9601
  });
8124
9602
  }
8125
9603
  }
8126
- return { files, problems };
9604
+ return { files, lowerings, problems };
9605
+ }
9606
+ function loweringModule(directory, host, problems, config) {
9607
+ const manifest = readJson(join2(directory, "package.json"), host);
9608
+ const declared = manifest?.luaut?.lowering;
9609
+ if (typeof declared !== "string") return void 0;
9610
+ const from = typeof manifest?.name === "string" ? manifest.name : directory;
9611
+ const file = resolve2(directory, declared);
9612
+ if (host.readFile(file) === void 0) {
9613
+ problems.push({ file: config.path, message: `'${from}' names a lowering module '${declared}', which is not there` });
9614
+ return void 0;
9615
+ }
9616
+ return { file, from };
8127
9617
  }
8128
9618
  var ENTRY_FILE = "index.d.luaut";
8129
9619
  function packageEntry(directory, host) {
@@ -8358,6 +9848,7 @@ export {
8358
9848
  resolveModulePath,
8359
9849
  resolveTypeLibraries,
8360
9850
  setAliasExpander,
9851
+ setDeferredBound,
8361
9852
  sourceMapTypes,
8362
9853
  stringType,
8363
9854
  stripJsonComments,