@tilua/parser 1.1.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -595,6 +595,9 @@ function applyDirectives(directives, diagnostics, lineOf) {
595
595
  var UNUSED_EXPECT_ERROR = "Unused '@tilua-expect-error' directive";
596
596
 
597
597
  // src/ast/builders.ts
598
+ var NO_DOTS = "tilua has no bare '...': take a rest parameter, '...args: T[]', or 'scriptArgs' for the script's own";
599
+ var ACCESSIBILITIES = /* @__PURE__ */ new Set(["public", "private", "protected"]);
600
+ var CLASS_MODIFIERS = /* @__PURE__ */ new Set([...ACCESSIBILITIES, "static", "abstract", "override", "readonly"]);
598
601
  function bindThis(func, at) {
599
602
  bindThisParam(func.params, at);
600
603
  func.isMethod = true;
@@ -862,11 +865,16 @@ var Parser = class {
862
865
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
863
866
  }
864
867
  /** A comma-separated list of values: a `return`'s, a declaration's, an
865
- * assignment's. `...xs` spreads an array into it, as in a call's
866
- * arguments; bare `...` is the vararg pack, as it always was. */
868
+ * assignment's. Each is one value; a spread belongs in a call's
869
+ * arguments or an array, and is reported here — then read as one, so
870
+ * the rest still means something. */
867
871
  expressionListOr(stop) {
868
872
  const until = () => stop() || this.checkPunctuator(",");
869
- const item = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
873
+ const item = () => {
874
+ if (!(this.checkOperator("...") && this.startsSpread())) return this.expressionOr(until);
875
+ this.problem("A spread goes in a call's arguments or an array: take values out of an array with a destructuring, 'const [a, b] = xs'");
876
+ return this.parseSpreadArgument(until);
877
+ };
870
878
  const list = [item()];
871
879
  while (this.matchPunctuator(",")) list.push(item());
872
880
  return list;
@@ -1231,7 +1239,7 @@ var Parser = class {
1231
1239
  if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
1232
1240
  return this.parseTypeAliasStatement();
1233
1241
  }
1234
- if (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
1242
+ if (this.startsClassDeclaration()) {
1235
1243
  return this.parseClassDeclaration();
1236
1244
  }
1237
1245
  if (t.type === "Identifier" && t.value === "declare") {
@@ -1268,8 +1276,6 @@ var Parser = class {
1268
1276
  type: "FunctionTypeNode",
1269
1277
  generics: head.generics,
1270
1278
  params,
1271
- hasVarargs: head.hasVarargs,
1272
- varargType: head.varargTypeAnnotation,
1273
1279
  returnType: head.returnType ?? { type: "TypeReference", base: head.predicate ? "boolean" : "unknown", typeArguments: [], ...spanFrom(start, start) },
1274
1280
  predicate: head.predicate,
1275
1281
  ...spanFrom(start, this.previous())
@@ -1408,7 +1414,7 @@ var Parser = class {
1408
1414
  this.advance();
1409
1415
  if (this.checkIdentifierValue("default")) {
1410
1416
  this.advance();
1411
- const declaration = this.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
1417
+ const declaration = this.startsClassDeclaration() ? this.parseClassDeclaration() : this.parseExpression(0);
1412
1418
  return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
1413
1419
  }
1414
1420
  if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
@@ -1419,7 +1425,7 @@ var Parser = class {
1419
1425
  const declaration = this.parseVariableDeclaration();
1420
1426
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1421
1427
  }
1422
- if (this.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
1428
+ if (this.startsClassDeclaration()) {
1423
1429
  const declaration = this.parseClassDeclaration();
1424
1430
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
1425
1431
  }
@@ -1534,9 +1540,11 @@ var Parser = class {
1534
1540
  const start = this.current();
1535
1541
  this.expectKeyword("for");
1536
1542
  this.expectPunctuator("(");
1543
+ const declared = this.checkKeyword("const") || this.checkKeyword("let") ? this.advance().value : void 0;
1537
1544
  const first = this.parseBindingTarget(true);
1538
1545
  const untilDo = () => this.checkPunctuator(")");
1539
1546
  if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
1547
+ if (declared) this.problemAt(start, `A counting loop names its variable without '${declared}': 'for (i = 1, 10)'`);
1540
1548
  const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1541
1549
  this.expectPunctuator(",");
1542
1550
  const to = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
@@ -1555,17 +1563,24 @@ var Parser = class {
1555
1563
  ...spanFrom(start, this.previous())
1556
1564
  };
1557
1565
  }
1558
- const variables = [first];
1559
- while (this.matchPunctuator(",")) {
1560
- variables.push(this.parseBindingTarget(true));
1566
+ if (this.checkPunctuator(",")) {
1567
+ this.problemAt(this.current(), "A loop takes one value each time: take its parts with a destructuring, 'for (const [k, v] in pairs(t))'");
1568
+ while (this.matchPunctuator(",")) this.parseBindingTarget(true);
1569
+ } else if (!declared) {
1570
+ this.problemAt(start, "A loop over a source names its value with 'const' or 'let': 'for (const item in source)'");
1561
1571
  }
1562
1572
  this.expectKeyword("in");
1563
- const iterators = this.expressionListOr(untilDo);
1573
+ const iterator = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1574
+ if (this.checkPunctuator(",")) {
1575
+ this.problemAt(this.current(), "A loop has one source: an array, a table, an iterator function or an iteration '[step, state, first]'");
1576
+ while (this.matchPunctuator(",")) this.expressionOr(() => untilDo() || this.checkPunctuator(","));
1577
+ }
1564
1578
  const body = this.parseForBody();
1565
1579
  return {
1566
1580
  type: "GenericForStatement",
1567
- variables,
1568
- iterators,
1581
+ kind: declared ?? "const",
1582
+ variable: first,
1583
+ iterator,
1569
1584
  body,
1570
1585
  ...spanFrom(start, this.previous())
1571
1586
  };
@@ -1635,7 +1650,10 @@ var Parser = class {
1635
1650
  * the compiler must not accept it, and recorded inside. The message says
1636
1651
  * what is wrong on its own — no token is appended. */
1637
1652
  problem(message) {
1638
- const t = this.current();
1653
+ this.problemAt(this.current(), message);
1654
+ }
1655
+ /** `problem`, pointed at `t` rather than at where parsing has got to. */
1656
+ problemAt(t, message) {
1639
1657
  const error = new ParseError(message, t.line.start, t.column.start);
1640
1658
  if (!this.recover) throw error;
1641
1659
  this.record(error);
@@ -1648,10 +1666,13 @@ var Parser = class {
1648
1666
  * like a function (`function m() ... end`). */
1649
1667
  parseClassDeclaration() {
1650
1668
  const start = this.current();
1669
+ const isAbstract = this.checkIdentifierValue("abstract");
1670
+ if (isAbstract) this.advance();
1651
1671
  this.advance();
1652
1672
  const name = this.parseIdentifier();
1653
1673
  const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1654
1674
  const { superclass, superArguments } = this.parseExtends();
1675
+ const shapes = this.parseImplements();
1655
1676
  const members = this.parseClassBody(start);
1656
1677
  return {
1657
1678
  type: "ClassDeclaration",
@@ -1659,10 +1680,21 @@ var Parser = class {
1659
1680
  typeParams,
1660
1681
  superclass,
1661
1682
  superArguments,
1683
+ ...shapes ? { implements: shapes } : {},
1684
+ ...isAbstract ? { isAbstract } : {},
1662
1685
  members,
1663
1686
  ...spanFrom(start, this.previous())
1664
1687
  };
1665
1688
  }
1689
+ /** `class Name` or `abstract class Name` — all three words soft, so a name
1690
+ * has to follow for either to be a declaration: `class: T`, `class = 1`
1691
+ * and `abstract(x)` are names. */
1692
+ startsClassDeclaration() {
1693
+ const offset = this.checkIdentifierValue("abstract") ? 1 : 0;
1694
+ const word = this.peek(offset);
1695
+ if (word.type !== "Identifier" || word.value !== "class") return false;
1696
+ return this.peek(offset + 1).type === "Identifier" && !this.punctuatorAt(offset + 2, ":") && !this.operatorAt(offset + 2, "=") && !this.punctuatorAt(offset + 2, "(");
1697
+ }
1666
1698
  /** `class ... end` as a value. It may be named — the name is for the class
1667
1699
  * itself, not for the scope around it — and takes no type parameters,
1668
1700
  * since nothing could write the arguments. */
@@ -1674,8 +1706,25 @@ var Parser = class {
1674
1706
  this.error("A class written as a value takes no type parameters: nothing could write the arguments");
1675
1707
  }
1676
1708
  const { superclass, superArguments } = this.parseExtends();
1709
+ const shapes = this.parseImplements();
1677
1710
  const members = this.parseClassBody(start);
1678
- return { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
1711
+ return {
1712
+ type: "ClassExpression",
1713
+ name,
1714
+ superclass,
1715
+ superArguments,
1716
+ ...shapes ? { implements: shapes } : {},
1717
+ members,
1718
+ ...spanFrom(start, this.previous())
1719
+ };
1720
+ }
1721
+ /** `implements Shape, Named` — each a type, checked against the instance. */
1722
+ parseImplements() {
1723
+ if (!this.checkIdentifierValue("implements")) return void 0;
1724
+ this.advance();
1725
+ const shapes = [this.parseType()];
1726
+ while (this.matchPunctuator(",")) shapes.push(this.parseType());
1727
+ return shapes;
1679
1728
  }
1680
1729
  /** `extends Base` / `extends Box<number>`. */
1681
1730
  parseExtends() {
@@ -1728,23 +1777,66 @@ var Parser = class {
1728
1777
  parseClassMember() {
1729
1778
  const start = this.current();
1730
1779
  let accessibility;
1731
- if ((this.checkIdentifierValue("public") || this.checkIdentifierValue("private")) && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(")) {
1732
- accessibility = this.advance().value;
1780
+ const modifiers = /* @__PURE__ */ new Set();
1781
+ while (this.startsClassModifier()) {
1782
+ const token = this.advance();
1783
+ const word = token.value;
1784
+ if (ACCESSIBILITIES.has(word)) {
1785
+ if (accessibility) this.problemAt(token, `A member has one accessibility: '${accessibility}' is already written`);
1786
+ accessibility = word;
1787
+ } else {
1788
+ if (modifiers.has(word)) this.problemAt(token, `'${word}' is written twice`);
1789
+ modifiers.add(word);
1790
+ }
1733
1791
  }
1734
- const member = this.parseClassMemberAfterAccessibility(start);
1735
- if (!accessibility || !member) return member;
1792
+ const member = this.parseClassMemberAfterModifiers(start, modifiers);
1793
+ if (!member) return member;
1794
+ const misplaced = (message) => this.problemAt(start, message);
1736
1795
  if (member.type === "ClassConstructor") {
1737
- this.problem(`A constructor cannot be '${accessibility}'`);
1796
+ const written = [...accessibility ? [accessibility] : [], ...modifiers];
1797
+ if (written.length) misplaced(`A constructor cannot be '${written[0]}'`);
1738
1798
  return member;
1739
1799
  }
1740
- return { ...member, accessibility };
1800
+ if (modifiers.has("readonly") && member.type !== "ClassField") misplaced("Only a field can be 'readonly'");
1801
+ if (modifiers.has("abstract") && member.type !== "ClassMethod") misplaced("Only a method can be 'abstract'");
1802
+ if (modifiers.has("abstract") && member.isStatic) misplaced("A static method cannot be 'abstract'");
1803
+ if (modifiers.has("override") && member.isStatic) misplaced("A static member cannot be 'override'");
1804
+ return {
1805
+ ...member,
1806
+ ...accessibility ? { accessibility } : {},
1807
+ ...modifiers.has("readonly") && member.type === "ClassField" ? { isReadonly: true } : {},
1808
+ ...modifiers.has("override") ? { isOverride: true } : {}
1809
+ };
1741
1810
  }
1742
- parseClassMemberAfterAccessibility(start) {
1743
- const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
1744
- if (isStatic) this.advance();
1811
+ /** Is the word here a member modifier, rather than a member's name? */
1812
+ startsClassModifier() {
1813
+ const t = this.current();
1814
+ if (t.type !== "Identifier" || !CLASS_MODIFIERS.has(t.value)) return false;
1815
+ const next = this.peek(1);
1816
+ return next.type === "Identifier" || next.type === "Keyword" && next.value === "function";
1817
+ }
1818
+ parseClassMemberAfterModifiers(start, modifiers) {
1819
+ const isStatic = modifiers.has("static");
1745
1820
  if (this.checkKeyword("function")) {
1746
1821
  this.advance();
1747
1822
  const memberName = this.parseIdentifier();
1823
+ if (modifiers.has("abstract")) {
1824
+ const head = this.parseFunctionHead();
1825
+ const func = {
1826
+ type: "FunctionBody",
1827
+ generics: head.generics,
1828
+ params: head.params,
1829
+ hasVarargs: head.hasVarargs,
1830
+ returnType: head.returnType,
1831
+ predicate: head.predicate,
1832
+ body: { type: "Block", statements: [], ...spanFrom(head.start, this.previous()) },
1833
+ ...spanFrom(head.start, this.previous())
1834
+ };
1835
+ if (!isStatic) bindThis(func, start);
1836
+ if (!head.returnType) this.problem("An abstract method has no body to infer from: write its return type");
1837
+ if (this.checkPunctuator("{")) this.problem("An abstract method has no body");
1838
+ return { type: "ClassMethod", name: memberName, isStatic, func, isAbstract: true, ...spanFrom(start, this.previous()) };
1839
+ }
1748
1840
  const signatures = [];
1749
1841
  let written = memberName;
1750
1842
  while (true) {
@@ -1862,12 +1954,17 @@ var Parser = class {
1862
1954
  parseReturnStatement(stopAtNewline = false) {
1863
1955
  const start = this.current();
1864
1956
  this.expectKeyword("return");
1865
- let args = [];
1957
+ let argument;
1866
1958
  const pastLine = () => stopAtNewline && this.current().line.start > start.line.start;
1867
1959
  if (!pastLine() && this.isExpressionStart()) {
1868
- args = this.expressionListOr(pastLine);
1960
+ const values = this.expressionListOr(pastLine);
1961
+ argument = values[0];
1962
+ if (values.length > 1) {
1963
+ this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
1964
+ argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
1965
+ }
1869
1966
  }
1870
- return { type: "ReturnStatement", arguments: args, ...spanFrom(start, this.previous()) };
1967
+ return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
1871
1968
  }
1872
1969
  parseTypeAliasStatement() {
1873
1970
  const start = this.current();
@@ -1926,7 +2023,7 @@ var Parser = class {
1926
2023
  ...spanFrom(start, this.previous())
1927
2024
  };
1928
2025
  }
1929
- if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
2026
+ if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
1930
2027
  return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
1931
2028
  }
1932
2029
  let expression = first;
@@ -1979,6 +2076,15 @@ var Parser = class {
1979
2076
  }
1980
2077
  return false;
1981
2078
  }
2079
+ /** A string written straight before a method call — `"a":upper()`, or
2080
+ * `` `{n}`:rep(2) ``. Luau only takes `("a"):upper()`; the compiler adds
2081
+ * the parentheses. Anything else after a string (`"a".x`, `"a"(1)`) is
2082
+ * not a prefix expression here either. */
2083
+ startsStringMethodCall() {
2084
+ const t = this.current();
2085
+ const isString = t.type === "InterpolatedString" || t.type === "Literal" && t.kind === "string";
2086
+ return isString && this.punctuatorAt(1, ":") && this.startsMethodCall(1);
2087
+ }
1982
2088
  /** Does the next token start right where the current one ends? */
1983
2089
  touchesNext() {
1984
2090
  const current = this.current();
@@ -2165,6 +2271,7 @@ var Parser = class {
2165
2271
  }
2166
2272
  parseAtom() {
2167
2273
  const t = this.current();
2274
+ if (this.startsStringMethodCall()) return this.parsePrefixExpression();
2168
2275
  if (t.type === "Literal") {
2169
2276
  this.advance();
2170
2277
  const lit = t;
@@ -2184,8 +2291,9 @@ var Parser = class {
2184
2291
  return this.buildInterpolatedString(t);
2185
2292
  }
2186
2293
  if (t.type === "Operator" && t.value === "...") {
2294
+ this.problem(NO_DOTS);
2187
2295
  this.advance();
2188
- return { type: "VarargExpression", ...spanFrom(t, t) };
2296
+ return { type: "ErrorExpression", ...spanFrom(t, t) };
2189
2297
  }
2190
2298
  if (t.type === "Keyword" && t.value === "function") {
2191
2299
  this.advance();
@@ -2261,12 +2369,17 @@ var Parser = class {
2261
2369
  const start = this.current();
2262
2370
  let base;
2263
2371
  if (this.startsNew()) {
2264
- base = this.parseNewExpression();
2265
- } else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2372
+ this.problem("tilua has no 'new' operator: construct with 'Name.new(...)'");
2373
+ this.advance();
2374
+ }
2375
+ if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
2266
2376
  this.advance();
2267
2377
  base = { type: "SuperExpression", ...spanFrom(start, start) };
2268
2378
  } else if (this.checkType("Identifier")) {
2269
2379
  base = this.parseIdentifier();
2380
+ } else if (this.startsStringMethodCall()) {
2381
+ const t = this.advance();
2382
+ base = t.type === "InterpolatedString" ? this.buildInterpolatedString(t) : { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2270
2383
  } else if (this.matchPunctuator("(")) {
2271
2384
  const inner = this.inBrackets(() => this.parseExpression());
2272
2385
  this.expectPunctuator(")");
@@ -2285,6 +2398,21 @@ var Parser = class {
2285
2398
  base = { type: "MemberExpression", object: base, property: prop, optional: true, ...spanFrom(base, prop) };
2286
2399
  continue;
2287
2400
  }
2401
+ if (punct === "." && this.punctuatorAt(2, "[")) {
2402
+ this.advance();
2403
+ this.advance();
2404
+ this.expectPunctuator("[");
2405
+ const index = this.inBrackets(() => this.parseExpression());
2406
+ this.expectPunctuator("]");
2407
+ base = {
2408
+ type: "IndexExpression",
2409
+ object: base,
2410
+ index,
2411
+ optional: true,
2412
+ ...spanFrom(base, this.previous())
2413
+ };
2414
+ continue;
2415
+ }
2288
2416
  if (punct === "." && this.punctuatorAt(2, "(")) {
2289
2417
  this.advance();
2290
2418
  this.advance();
@@ -2377,9 +2505,9 @@ var Parser = class {
2377
2505
  }
2378
2506
  return base;
2379
2507
  }
2380
- /** `new` is a soft keyword: it starts a construction only when a name
2381
- * follows it, so a function or field called `new` — `Instance.new(x)`,
2382
- * and `Vec.new(1)` itself — is untouched. */
2508
+ /** `new Name(...)`, the way other languages build an instance — only to
2509
+ * say so. A function or field called `new` — `Instance.new(x)`, and
2510
+ * `Vec.new(1)` itself — is untouched. */
2383
2511
  startsNew() {
2384
2512
  return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
2385
2513
  }
@@ -2389,25 +2517,6 @@ var Parser = class {
2389
2517
  startsSuperUse() {
2390
2518
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2391
2519
  }
2392
- /** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
2393
- * name reached through a module — never an arbitrary expression, so the
2394
- * arguments are unambiguously the constructor's. */
2395
- parseNewExpression() {
2396
- const start = this.current();
2397
- this.advance();
2398
- let callee = this.parseIdentifier();
2399
- while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2400
- this.advance();
2401
- const property = this.parseIdentifier();
2402
- callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
2403
- }
2404
- const typeArguments = this.tryCallTypeArguments();
2405
- if (!this.checkPunctuator("(")) {
2406
- this.error("Expected '(' after the class being constructed: 'new Name(...)'");
2407
- }
2408
- const args = this.parseCallArguments();
2409
- return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
2410
- }
2411
2520
  /** After `...`, is there something to spread? Nothing following it means
2412
2521
  * the vararg pack, which is what `f(...)` has always passed on. */
2413
2522
  startsSpread() {
@@ -2449,8 +2558,8 @@ var Parser = class {
2449
2558
  const errors = this.errors.length;
2450
2559
  try {
2451
2560
  this.advance();
2452
- const list = [this.parseTypeArgument()];
2453
- while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseTypeArgument());
2561
+ const list = [this.parseType()];
2562
+ while (this.matchPunctuator(",") && !this.checkOperator(">")) list.push(this.parseType());
2454
2563
  this.expectOperator(">");
2455
2564
  if (!this.startsCallArguments()) throw new ParseRecover("not a call");
2456
2565
  return list;
@@ -2582,7 +2691,7 @@ var Parser = class {
2582
2691
  if (this.checkOperator("...")) {
2583
2692
  const dots = this.advance();
2584
2693
  if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
2585
- elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
2694
+ this.problemAt(dots, NO_DOTS);
2586
2695
  } else {
2587
2696
  const argument = this.expressionOr(stop);
2588
2697
  elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
@@ -2736,35 +2845,16 @@ var Parser = class {
2736
2845
  column: p.column
2737
2846
  };
2738
2847
  }
2739
- parseTypeOrTypePackReference() {
2848
+ /** `T...` written where a type goes: a pack, which tilua does not have. */
2849
+ rejectPackReference() {
2740
2850
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2741
- const start = this.current();
2742
- const base = this.expectIdentifier().value;
2851
+ this.problemAt(
2852
+ this.peek(1),
2853
+ "tilua has no type packs: several values are a tuple, '[A, B]', and a varying number an array, 'T[]'"
2854
+ );
2855
+ this.advance();
2743
2856
  this.advance();
2744
- const packRef = { type: "TypeReference", base, typeArguments: [], ...spanFrom(start, start) };
2745
- return {
2746
- type: "TypePackNode",
2747
- types: [],
2748
- hasVarargs: true,
2749
- // Wrap in `VariadicTypeNode`, matching the convention used by
2750
- // `parseFunctionTypeAfterParen`'s identifier-pack-reference
2751
- // branch, so the printer can tell `A...` (name-first, this
2752
- // case) apart from `...T` (dots-first) and append rather
2753
- // than prepend the `...`.
2754
- varargType: { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(start, this.previous()) },
2755
- ...spanFrom(start, this.previous())
2756
- };
2757
- }
2758
- return this.parseType();
2759
- }
2760
- parseTypeArgument() {
2761
- if (this.checkOperator("...")) {
2762
- return this.parseTypePack();
2763
- }
2764
- if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
2765
- return this.parseTypeOrTypePackReference();
2766
2857
  }
2767
- return this.parseType();
2768
2858
  }
2769
2859
  /** The part shared by a real function body and an overload signature:
2770
2860
  * `<generics>(params): ReturnType`, up to (but not including) the block. */
@@ -2777,7 +2867,6 @@ var Parser = class {
2777
2867
  this.expectPunctuator("(");
2778
2868
  const params = [];
2779
2869
  let hasVarargs = false;
2780
- let varargTypeAnnotation;
2781
2870
  if (!this.checkPunctuator(")")) {
2782
2871
  while (true) {
2783
2872
  if (this.checkOperator("...")) {
@@ -2785,14 +2874,14 @@ var Parser = class {
2785
2874
  hasVarargs = true;
2786
2875
  if (this.checkType("Identifier")) {
2787
2876
  const nameTok = this.expectIdentifier();
2788
- let typeAnnotation2;
2877
+ let typeAnnotation3;
2789
2878
  if (this.matchPunctuator(":")) {
2790
- typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
2879
+ typeAnnotation3 = this.typeOr(() => this.checkPunctuator(")"));
2791
2880
  }
2792
2881
  params.push({
2793
2882
  type: "FunctionParameter",
2794
2883
  name: nameTok.value,
2795
- typeAnnotation: typeAnnotation2,
2884
+ typeAnnotation: typeAnnotation3,
2796
2885
  rest: true,
2797
2886
  ...spanFrom(dots, this.previous())
2798
2887
  });
@@ -2801,9 +2890,20 @@ var Parser = class {
2801
2890
  }
2802
2891
  break;
2803
2892
  }
2893
+ this.problemAt(dots, NO_DOTS);
2894
+ let typeAnnotation2;
2804
2895
  if (this.matchPunctuator(":")) {
2805
- varargTypeAnnotation = this.parseTypeOrTypePackReference();
2896
+ this.rejectPackReference();
2897
+ const element = this.parseType();
2898
+ typeAnnotation2 = { type: "ArrayTypeNode", element, ...spanFrom(element, element) };
2806
2899
  }
2900
+ params.push({
2901
+ type: "FunctionParameter",
2902
+ name: "args",
2903
+ typeAnnotation: typeAnnotation2,
2904
+ rest: true,
2905
+ ...spanFrom(dots, this.previous())
2906
+ });
2807
2907
  break;
2808
2908
  }
2809
2909
  const paramStart = this.current();
@@ -2845,10 +2945,16 @@ var Parser = class {
2845
2945
  if (this.matchPunctuator(":")) {
2846
2946
  predicate = this.tryParseTypePredicate();
2847
2947
  if (!predicate) {
2848
- returnType = this.attempt(() => this.parseTypeOrTypePackReference(), () => false, () => void 0);
2948
+ returnType = this.attempt(() => this.parseReturnType(), () => false, () => void 0);
2849
2949
  }
2850
2950
  }
2851
- return { start, generics, params, hasVarargs, varargTypeAnnotation, returnType, predicate };
2951
+ return { start, generics, params, hasVarargs, returnType, predicate };
2952
+ }
2953
+ /** What a function returns: one type. `T...` is reported as the pack it
2954
+ * is, and the type after it read. */
2955
+ parseReturnType() {
2956
+ this.rejectPackReference();
2957
+ return this.parseType();
2852
2958
  }
2853
2959
  /** TypeScript-style type-guard return annotations, in return position only:
2854
2960
  *
@@ -2918,7 +3024,6 @@ var Parser = class {
2918
3024
  generics: [],
2919
3025
  params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
2920
3026
  hasVarargs: false,
2921
- varargTypeAnnotation: void 0,
2922
3027
  returnType: void 0,
2923
3028
  predicate: void 0
2924
3029
  };
@@ -2930,7 +3035,6 @@ var Parser = class {
2930
3035
  generics: head.generics,
2931
3036
  params: head.params,
2932
3037
  hasVarargs: head.hasVarargs,
2933
- varargTypeAnnotation: head.varargTypeAnnotation,
2934
3038
  returnType: head.returnType,
2935
3039
  predicate: head.predicate,
2936
3040
  body,
@@ -2942,7 +3046,7 @@ var Parser = class {
2942
3046
  returnOf(expression) {
2943
3047
  const statement = {
2944
3048
  type: "ReturnStatement",
2945
- arguments: [expression],
3049
+ argument: expression,
2946
3050
  ...spanFrom(expression, expression)
2947
3051
  };
2948
3052
  return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
@@ -2955,7 +3059,6 @@ var Parser = class {
2955
3059
  generics: head.generics,
2956
3060
  params: head.params,
2957
3061
  hasVarargs: head.hasVarargs,
2958
- varargTypeAnnotation: head.varargTypeAnnotation,
2959
3062
  returnType: head.returnType,
2960
3063
  predicate: head.predicate,
2961
3064
  body,
@@ -2968,7 +3071,6 @@ var Parser = class {
2968
3071
  generics: head.generics,
2969
3072
  params: head.params,
2970
3073
  hasVarargs: head.hasVarargs,
2971
- varargTypeAnnotation: head.varargTypeAnnotation,
2972
3074
  returnType: head.returnType,
2973
3075
  predicate: head.predicate,
2974
3076
  ...spanFrom(head.start, this.previous())
@@ -2986,7 +3088,6 @@ var Parser = class {
2986
3088
  generics: head.generics,
2987
3089
  params: head.params,
2988
3090
  hasVarargs: head.hasVarargs,
2989
- varargTypeAnnotation: head.varargTypeAnnotation,
2990
3091
  returnType: head.returnType,
2991
3092
  predicate: head.predicate,
2992
3093
  body,
@@ -3065,8 +3166,12 @@ var Parser = class {
3065
3166
  * `name?: T` for an optional property or parameter. */
3066
3167
  parseSuffixType() {
3067
3168
  if (this.checkIdentifierValue("readonly")) {
3169
+ const start = this.current();
3068
3170
  this.advance();
3069
- return this.parseSuffixType();
3171
+ const t2 = this.parseSuffixType();
3172
+ if (t2.type === "ArrayTypeNode" || t2.type === "TupleTypeNode") return { ...t2, isReadonly: true };
3173
+ this.problemAt(start, "'readonly' belongs on an array or a tuple: a property is marked where it is written");
3174
+ return t2;
3070
3175
  }
3071
3176
  let t = this.parsePrimaryType();
3072
3177
  while (true) {
@@ -3127,9 +3232,10 @@ var Parser = class {
3127
3232
  return this.parseFunctionTypeAfterParen(t, []);
3128
3233
  }
3129
3234
  if (t.type === "Operator" && t.value === "...") {
3235
+ this.problem("'...' in a type goes before a rest parameter, '...args: T[]', or at the end of a tuple, '[A, ...B[]]'");
3130
3236
  this.advance();
3131
- const inner = this.parseType();
3132
- return { type: "VariadicTypeNode", typeAnnotation: inner, ...spanFrom(t, this.previous()) };
3237
+ const element = this.parseType();
3238
+ return { type: "ArrayTypeNode", element, ...spanFrom(t, this.previous()) };
3133
3239
  }
3134
3240
  if (t.type === "Punctuator" && t.value === "{") {
3135
3241
  return this.parseTableType();
@@ -3137,13 +3243,22 @@ var Parser = class {
3137
3243
  if (t.type === "Punctuator" && t.value === "[") {
3138
3244
  this.advance();
3139
3245
  const elements = [];
3246
+ let rest;
3140
3247
  while (!this.checkPunctuator("]")) {
3248
+ if (this.matchOperator("...")) {
3249
+ rest = this.parseType();
3250
+ if (rest.type !== "ArrayTypeNode" && !(rest.type === "TypeReference" && rest.base !== "nil")) {
3251
+ this.problem("The rest of a tuple is an array type: '...T[]'");
3252
+ }
3253
+ this.matchPunctuator(",");
3254
+ break;
3255
+ }
3141
3256
  elements.push(this.parseType());
3142
3257
  if (this.matchPunctuator(",")) continue;
3143
3258
  break;
3144
3259
  }
3145
3260
  this.expectPunctuator("]");
3146
- return { type: "TupleTypeNode", elements, ...spanFrom(t, this.previous()) };
3261
+ return { type: "TupleTypeNode", elements, ...rest ? { rest } : {}, ...spanFrom(t, this.previous()) };
3147
3262
  }
3148
3263
  if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Punctuator" && this.peek(1).value === "(") {
3149
3264
  this.advance();
@@ -3190,9 +3305,9 @@ var Parser = class {
3190
3305
  if (this.checkOperator("<")) {
3191
3306
  this.advance();
3192
3307
  if (!this.checkOperator(">")) {
3193
- typeArguments.push(this.parseTypeArgument());
3308
+ typeArguments.push(this.parseType());
3194
3309
  while (this.matchPunctuator(",") && !this.checkOperator(">")) {
3195
- typeArguments.push(this.parseTypeArgument());
3310
+ typeArguments.push(this.parseType());
3196
3311
  }
3197
3312
  }
3198
3313
  this.expectOperator(">");
@@ -3203,13 +3318,12 @@ var Parser = class {
3203
3318
  }
3204
3319
  parseFunctionTypeAfterParen(start, generics) {
3205
3320
  const params = [];
3206
- let hasVarargs = false;
3207
- let varargType;
3321
+ let rested = false;
3208
3322
  if (!this.checkPunctuator(")")) {
3209
3323
  while (true) {
3210
3324
  if (this.checkOperator("...")) {
3211
3325
  const dots = this.advance();
3212
- hasVarargs = true;
3326
+ rested = true;
3213
3327
  if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
3214
3328
  const nameTok2 = this.expectIdentifier();
3215
3329
  this.advance();
@@ -3223,16 +3337,21 @@ var Parser = class {
3223
3337
  });
3224
3338
  break;
3225
3339
  }
3226
- varargType = this.parseType();
3340
+ this.problemAt(dots, "A function type takes the rest of its arguments as an array: '(...args: T[]) => R'");
3341
+ this.rejectPackReference();
3342
+ const element = this.parseType();
3343
+ params.push({
3344
+ type: "FunctionTypeParameter",
3345
+ name: "args",
3346
+ rest: true,
3347
+ typeAnnotation: { type: "ArrayTypeNode", element, ...spanFrom(element, element) },
3348
+ ...spanFrom(dots, this.previous())
3349
+ });
3227
3350
  break;
3228
3351
  }
3229
3352
  if (this.checkType("Identifier") && this.peek(1).type === "Operator" && this.peek(1).value === "...") {
3230
- const packStart = this.current();
3231
- const packRef = this.parseType();
3232
- this.advance();
3233
- hasVarargs = true;
3234
- varargType = { type: "VariadicTypeNode", typeAnnotation: packRef, ...spanFrom(packStart, this.previous()) };
3235
- break;
3353
+ this.rejectPackReference();
3354
+ continue;
3236
3355
  }
3237
3356
  let name;
3238
3357
  let optional2 = false;
@@ -3262,19 +3381,17 @@ var Parser = class {
3262
3381
  this.expectPunctuator(")");
3263
3382
  if (this.matchPunctuator("=>") || this.mistypedArrow()) {
3264
3383
  const predicate = this.tryParseTypePredicate();
3265
- const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
3384
+ const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseReturnType();
3266
3385
  return {
3267
3386
  type: "FunctionTypeNode",
3268
3387
  generics,
3269
3388
  params,
3270
- hasVarargs,
3271
- varargType,
3272
3389
  returnType,
3273
3390
  predicate,
3274
3391
  ...spanFrom(start, this.previous())
3275
3392
  };
3276
3393
  }
3277
- if (params.length === 1 && !params[0].name && !hasVarargs) {
3394
+ if (params.length === 1 && !params[0].name && !rested) {
3278
3395
  return {
3279
3396
  type: "ParenthesizedTypeNode",
3280
3397
  typeAnnotation: params[0].typeAnnotation,
@@ -3284,11 +3401,14 @@ var Parser = class {
3284
3401
  if (params.some((p) => p.name !== void 0)) {
3285
3402
  this.error("Expected '=>' for function type");
3286
3403
  }
3404
+ if (!params.length) {
3405
+ this.problemAt(start, "'()' is not a type: a function that returns nothing returns 'nil'");
3406
+ return { type: "TypeReference", base: "nil", typeArguments: [], ...spanFrom(start, this.previous()) };
3407
+ }
3408
+ this.problemAt(start, "Several types in parentheses are a pack, which tilua does not have: write a tuple, '[A, B]'");
3287
3409
  return {
3288
- type: "TypePackNode",
3289
- types: params.map((p) => p.typeAnnotation),
3290
- hasVarargs,
3291
- varargType,
3410
+ type: "TupleTypeNode",
3411
+ elements: params.map((p) => p.typeAnnotation),
3292
3412
  ...spanFrom(start, this.previous())
3293
3413
  };
3294
3414
  }
@@ -3426,40 +3546,15 @@ var Parser = class {
3426
3546
  this.expectPunctuator("}");
3427
3547
  return { type: "TableTypeNode", properties, ...spanFrom(start, this.previous()) };
3428
3548
  }
3429
- parseTypePack() {
3430
- const start = this.current();
3431
- if (this.matchOperator("...")) {
3432
- const varargType2 = this.parseType();
3433
- return { type: "TypePackNode", types: [], hasVarargs: true, varargType: varargType2, ...spanFrom(start, this.previous()) };
3434
- }
3435
- this.expectPunctuator("(");
3436
- const types = [];
3437
- let hasVarargs = false;
3438
- let varargType;
3439
- if (!(this.current().type === "Punctuator" && this.current().value === ")")) {
3440
- while (true) {
3441
- if (this.matchOperator("...")) {
3442
- hasVarargs = true;
3443
- varargType = this.parseType();
3444
- break;
3445
- }
3446
- types.push(this.parseType());
3447
- if (this.matchPunctuator(",")) continue;
3448
- break;
3449
- }
3450
- }
3451
- this.expectPunctuator(")");
3452
- return { type: "TypePackNode", types, hasVarargs, varargType, ...spanFrom(start, this.previous()) };
3453
- }
3454
3549
  parseGenericTypeParameterList() {
3455
3550
  const list = [];
3456
3551
  this.expectOperator("<");
3457
3552
  while (true) {
3458
3553
  const isConst = this.matchKeyword("const");
3459
3554
  const nameTok = this.expectIdentifier();
3460
- let isPack = false;
3461
- if (this.matchOperator("...")) {
3462
- isPack = true;
3555
+ const dots = this.checkOperator("...") ? this.advance() : void 0;
3556
+ if (dots) {
3557
+ this.problemAt(dots, "tilua has no pack parameters: write '<T extends unknown[]>', and take it as '...args: T'");
3463
3558
  }
3464
3559
  let constraint;
3465
3560
  if (this.checkIdentifierValue("extends")) {
@@ -3468,17 +3563,13 @@ var Parser = class {
3468
3563
  }
3469
3564
  let def;
3470
3565
  if (this.matchOperator("=")) {
3471
- if (isPack) {
3472
- def = this.parseTypePack();
3473
- } else {
3474
- def = this.parseType();
3475
- }
3566
+ this.rejectPackReference();
3567
+ def = this.parseType();
3476
3568
  }
3477
3569
  list.push({
3478
3570
  type: "GenericTypeParameter",
3479
3571
  name: nameTok.value,
3480
3572
  id: tokenIdentifier(nameTok),
3481
- isPack,
3482
3573
  isConst: isConst || void 0,
3483
3574
  constraint,
3484
3575
  default: def,
@@ -3569,7 +3660,7 @@ function childScope(parent) {
3569
3660
  var Analyzer = class {
3570
3661
  constructor(options) {
3571
3662
  this.options = options;
3572
- for (const name of options.builtinGlobals ?? []) {
3663
+ for (const name of [...LANGUAGE_GLOBALS, ...options.builtinGlobals ?? []]) {
3573
3664
  const id = this.getOrCreateGlobalBinding(name);
3574
3665
  this.bindings.get(id).isBuiltin = true;
3575
3666
  }
@@ -3631,31 +3722,34 @@ var Analyzer = class {
3631
3722
  * can call it, but a call straight in the block before the declaration
3632
3723
  * finds nothing there yet. At a module's top level the whole function is
3633
3724
  * hoisted, and this does not apply. */
3634
- checkUseBeforeDefine(identifier, id) {
3725
+ checkUseBeforeDefine(identifier, id, at = this.functionDepth) {
3635
3726
  const binding = this.bindings.get(id);
3636
- if (binding.declaredBy !== "function" && binding.declaredBy !== "class" || this.typeQueryDepth > 0) return;
3727
+ if (this.typeQueryDepth > 0) return;
3637
3728
  const declaration = binding.declarationNode;
3638
- const depth = declaration && this.hoisted.get(declaration);
3639
- if (depth === void 0 || depth !== this.functionDepth) return;
3729
+ if (!declaration) return;
3730
+ const hoisted = binding.declaredBy === "function" || binding.declaredBy === "class";
3731
+ const depth = hoisted ? this.hoisted.get(declaration) : this.declaredDepth.get(id);
3732
+ const readable = hoisted || binding.kind === "local";
3733
+ if (!readable || depth === void 0 || depth !== at) return;
3640
3734
  const before = identifier.line.start < declaration.line.start || identifier.line.start === declaration.line.start && identifier.column.start < declaration.column.start;
3641
3735
  if (!before) return;
3642
3736
  this.diagnostics.push({
3643
3737
  node: identifier,
3644
- message: `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run`,
3738
+ message: hoisted ? `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run` : `'${binding.name}' is used before its declaration, and holds nothing until that line has run`,
3645
3739
  kind: "use-before-define"
3646
3740
  });
3647
3741
  }
3648
3742
  noteDeferred(identifier, scope, id, assignment) {
3649
3743
  if (this.functionDepth === 0 && this.typeQueryDepth === 0) return;
3650
3744
  if (this.bindings.get(id).kind !== "global") return;
3651
- this.deferredGlobals.push({ node: identifier, scope, assignment });
3745
+ this.deferredGlobals.push({ node: identifier, scope, assignment, depth: this.functionDepth });
3652
3746
  }
3653
3747
  /** Point each deferred read of a global at the declaration of that name
3654
3748
  * that turned up later — in the module, or in any block around the code
3655
3749
  * that reads it. Such code runs after the declaration has: a closure
3656
3750
  * written inside a value reads the name the value is bound to. */
3657
3751
  resolveForwardReferences() {
3658
- for (const { node, scope, assignment } of this.deferredGlobals) {
3752
+ for (const { node, scope, assignment, depth } of this.deferredGlobals) {
3659
3753
  const localId = this.lookup(scope, node.name);
3660
3754
  const globalId = this.bindingOf.get(node);
3661
3755
  if (localId === void 0 || globalId === void 0 || localId === globalId) continue;
@@ -3670,7 +3764,10 @@ var Analyzer = class {
3670
3764
  this.bindingOf.set(node, localId);
3671
3765
  this.bindings.get(localId).references.push(node);
3672
3766
  if (assignment) this.checkConstAssign(localId, node);
3673
- else if (this.typeQueryDepth === 0) this.checkTypeOnly(localId, node);
3767
+ else if (this.typeQueryDepth === 0) {
3768
+ this.checkTypeOnly(localId, node);
3769
+ this.checkUseBeforeDefine(node, localId, depth);
3770
+ }
3674
3771
  }
3675
3772
  }
3676
3773
  /** Every read of a global nothing declares. A global assigned somewhere
@@ -3701,9 +3798,13 @@ var Analyzer = class {
3701
3798
  }
3702
3799
  const id = this.nextId++;
3703
3800
  this.bindings.set(id, { id, name, kind, declarationNode: node, references: [], isConst, declaredBy });
3801
+ this.declaredDepth.set(id, this.functionDepth);
3704
3802
  scope.declarations.set(name, id);
3705
3803
  return id;
3706
3804
  }
3805
+ /** Which function body each name was declared in. A read of a `const` from
3806
+ * the same body, but above it, is a read of nothing. */
3807
+ declaredDepth = /* @__PURE__ */ new Map();
3707
3808
  resolve(scope, name) {
3708
3809
  for (let s = scope; s; s = s.parent) {
3709
3810
  const id = s.declarations.get(name);
@@ -3971,14 +4072,14 @@ var Analyzer = class {
3971
4072
  return;
3972
4073
  }
3973
4074
  case "GenericForStatement": {
3974
- for (const it of stmt.iterators) this.visitExpression(it, scope);
4075
+ this.visitExpression(stmt.iterator, scope);
3975
4076
  const bodyScope = childScope(scope);
3976
- for (const v of stmt.variables) this.declarePattern(bodyScope, v, "for-generic", scope);
4077
+ this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
3977
4078
  this.visitBlock(stmt.body, bodyScope);
3978
4079
  return;
3979
4080
  }
3980
4081
  case "ReturnStatement":
3981
- for (const arg of stmt.arguments) this.visitExpression(arg, scope);
4082
+ if (stmt.argument) this.visitExpression(stmt.argument, scope);
3982
4083
  return;
3983
4084
  case "BreakStatement":
3984
4085
  case "ContinueStatement":
@@ -4046,7 +4147,6 @@ var Analyzer = class {
4046
4147
  this.declare(fnScope, param.name, kind, param);
4047
4148
  }
4048
4149
  });
4049
- this.visitType(func.varargTypeAnnotation, fnScope);
4050
4150
  this.visitType(func.returnType, fnScope);
4051
4151
  this.functionDepth++;
4052
4152
  try {
@@ -4074,6 +4174,7 @@ var Analyzer = class {
4074
4174
  this.visitGenerics(node.typeParams, scope);
4075
4175
  if (node.superclass) this.reference(outer, node.superclass);
4076
4176
  for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
4177
+ for (const shape of node.implements ?? []) this.visitType(shape, scope);
4077
4178
  for (const member of node.members) {
4078
4179
  switch (member.type) {
4079
4180
  case "ClassField":
@@ -4133,7 +4234,6 @@ var Analyzer = class {
4133
4234
  case "BooleanLiteral":
4134
4235
  case "NumberLiteral":
4135
4236
  case "StringLiteral":
4136
- case "VarargExpression":
4137
4237
  case "ErrorExpression":
4138
4238
  return;
4139
4239
  case "InterpolatedStringExpression":
@@ -4173,11 +4273,6 @@ var Analyzer = class {
4173
4273
  this.visitExpression(expr.callee, scope);
4174
4274
  for (const arg of expr.arguments) this.visitExpression(arg, scope);
4175
4275
  return;
4176
- case "NewExpression":
4177
- this.visitExpression(expr.callee, scope);
4178
- for (const argument of expr.arguments) this.visitExpression(argument, scope);
4179
- for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
4180
- return;
4181
4276
  case "SuperExpression":
4182
4277
  return;
4183
4278
  case "SpreadElement":
@@ -4231,6 +4326,7 @@ var Analyzer = class {
4231
4326
  }
4232
4327
  }
4233
4328
  };
4329
+ var LANGUAGE_GLOBALS = ["scriptArgs"];
4234
4330
  function analyzeScopes(program, options = {}) {
4235
4331
  return new Analyzer(options).run(program);
4236
4332
  }
@@ -4249,8 +4345,8 @@ type Exclude<T, U> = T - U
4249
4345
  type Extract<T, U> = T extends U ? T : never
4250
4346
  type NonNullable<T> = T - nil
4251
4347
 
4252
- type ReturnType<T> = T extends (...unknown) => infer R ? R : never
4253
- type Parameters<T> = T extends (...infer P) => unknown ? P : never
4348
+ type ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never
4349
+ type Parameters<T> = T extends (...args: infer P) => unknown ? P : never
4254
4350
 
4255
4351
  type Partial<T> = { [K in keyof T]?: T[K] }
4256
4352
  type Required<T> = { [K in keyof T]-?: T[K] }
@@ -4335,8 +4431,11 @@ function literal(value) {
4335
4431
  function arrayOf(element) {
4336
4432
  return { kind: "array", element };
4337
4433
  }
4338
- function tuple(elements, isPack) {
4339
- return { kind: "tuple", elements, isPack };
4434
+ function tuple(elements, rest) {
4435
+ return rest ? { kind: "tuple", elements, rest } : { kind: "tuple", elements };
4436
+ }
4437
+ function tupleMembers(t) {
4438
+ return t.rest ? [...t.elements, t.rest] : t.elements;
4340
4439
  }
4341
4440
  function objectType(entries, indexer, frozen) {
4342
4441
  return { kind: "object", properties: new Map(entries), indexer, frozen };
@@ -4358,9 +4457,9 @@ function substitute(t, subst) {
4358
4457
  case "typeParam":
4359
4458
  return subst.get(t.name) ?? t;
4360
4459
  case "array":
4361
- return arrayOf(substitute(t.element, subst));
4460
+ return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4362
4461
  case "tuple":
4363
- return tuple(t.elements.map((e) => substitute(e, subst)), t.isPack);
4462
+ return { ...tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst)), readonly: t.readonly };
4364
4463
  case "object": {
4365
4464
  const entries = [];
4366
4465
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
@@ -4384,14 +4483,20 @@ function substitute(t, subst) {
4384
4483
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
4385
4484
  let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
4386
4485
  let varargs = t.varargs && substitute(t.varargs, inner);
4387
- if (varargs?.kind === "tuple" && varargs.isPack) {
4486
+ let restIsWhole = t.restIsWhole;
4487
+ if (restIsWhole && varargs?.kind === "tuple") {
4388
4488
  params = [...params, ...varargs.elements.map((type) => ({ type }))];
4389
- varargs = void 0;
4489
+ varargs = varargs.rest;
4490
+ restIsWhole = void 0;
4491
+ } else if (restIsWhole && varargs?.kind === "array") {
4492
+ varargs = varargs.element;
4493
+ restIsWhole = void 0;
4390
4494
  }
4391
4495
  return {
4392
4496
  kind: "function",
4393
4497
  params,
4394
4498
  varargs,
4499
+ ...restIsWhole ? { restIsWhole } : {},
4395
4500
  returns: substitute(t.returns, inner),
4396
4501
  typeParams: t.typeParams,
4397
4502
  typeParamDefaults: t.typeParamDefaults,
@@ -4474,11 +4579,19 @@ function unify(param, arg, vars, out) {
4474
4579
  switch (param.kind) {
4475
4580
  case "array":
4476
4581
  if (arg.kind === "array") unify(param.element, arg.element, vars, out);
4477
- else if (arg.kind === "tuple") for (const e of arg.elements) unify(param.element, e, vars, out);
4582
+ else if (arg.kind === "tuple") for (const e of tupleMembers(arg)) unify(param.element, e, vars, out);
4478
4583
  return;
4479
4584
  case "tuple":
4480
- if (arg.kind === "tuple") param.elements.forEach((p, i) => arg.elements[i] && unify(p, arg.elements[i], vars, out));
4481
- else if (arg.kind === "array") for (const p of param.elements) unify(p, arg.element, vars, out);
4585
+ if (arg.kind === "tuple") {
4586
+ param.elements.forEach((p, i) => {
4587
+ const a = arg.elements[i] ?? arg.rest;
4588
+ if (a) unify(p, a, vars, out);
4589
+ });
4590
+ if (param.rest) for (const a of arg.elements.slice(param.elements.length)) unify(param.rest, a, vars, out);
4591
+ if (param.rest && arg.rest) unify(param.rest, arg.rest, vars, out);
4592
+ } else if (arg.kind === "array") {
4593
+ for (const p of tupleMembers(param)) unify(p, arg.element, vars, out);
4594
+ }
4482
4595
  return;
4483
4596
  case "function":
4484
4597
  if (arg.kind === "function") {
@@ -4511,6 +4624,20 @@ function unify(param, arg, vars, out) {
4511
4624
  return;
4512
4625
  }
4513
4626
  }
4627
+ function memberKey(t) {
4628
+ switch (t.kind) {
4629
+ case "literal":
4630
+ return `l${t.base}:${String(t.value)}`;
4631
+ case "primitive":
4632
+ return `p${t.name}`;
4633
+ case "any":
4634
+ case "unknown":
4635
+ case "never":
4636
+ return t.kind;
4637
+ default:
4638
+ return formatType(t);
4639
+ }
4640
+ }
4514
4641
  function union(types) {
4515
4642
  const flat = [];
4516
4643
  for (const t of types) {
@@ -4522,7 +4649,7 @@ function union(types) {
4522
4649
  const seen = /* @__PURE__ */ new Map();
4523
4650
  for (const t of flat) {
4524
4651
  if (t.kind === "never") continue;
4525
- const key = formatType(t);
4652
+ const key = memberKey(t);
4526
4653
  if (!seen.has(key)) seen.set(key, t);
4527
4654
  }
4528
4655
  let members = reduceSubtypes([...seen.values()]);
@@ -4551,7 +4678,7 @@ function intersection(types) {
4551
4678
  const seen = /* @__PURE__ */ new Map();
4552
4679
  for (const t of flat) {
4553
4680
  if (t.kind === "unknown") continue;
4554
- seen.set(formatType(t), t);
4681
+ seen.set(memberKey(t), t);
4555
4682
  }
4556
4683
  const members = [...seen.values()];
4557
4684
  if (members.length === 0) return unknownType;
@@ -4578,9 +4705,9 @@ function widen(t) {
4578
4705
  case "literal":
4579
4706
  return primitive(t.base);
4580
4707
  case "array":
4581
- return arrayOf(widen(t.element));
4708
+ return { ...arrayOf(widen(t.element)), readonly: t.readonly };
4582
4709
  case "tuple":
4583
- return tuple(t.elements.map(widen), t.isPack);
4710
+ return { ...tuple(t.elements.map(widen), t.rest && widen(t.rest)), readonly: t.readonly };
4584
4711
  case "object": {
4585
4712
  if (t.frozen || t.class) return t;
4586
4713
  const entries = [];
@@ -4678,15 +4805,21 @@ function isAssignableInner(a, b) {
4678
4805
  return b.kind === "primitive" && b.name === "string";
4679
4806
  }
4680
4807
  if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
4808
+ if ((a.kind === "array" || a.kind === "tuple") && a.readonly && (b.kind === "array" || b.kind === "tuple") && !b.readonly) return false;
4681
4809
  if (a.kind === "array") {
4682
4810
  if (b.kind === "array") return isAssignable(a.element, b.element);
4811
+ if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
4683
4812
  return false;
4684
4813
  }
4685
4814
  if (a.kind === "tuple") {
4686
4815
  if (b.kind === "tuple") {
4687
- return a.elements.length === b.elements.length && a.elements.every((t, i) => isAssignable(t, b.elements[i]));
4816
+ if (a.elements.length < b.elements.length) return false;
4817
+ if (!b.elements.every((t, i) => isAssignable(a.elements[i], t))) return false;
4818
+ const extra = [...a.elements.slice(b.elements.length), ...a.rest ? [a.rest] : []];
4819
+ if (!b.rest) return extra.length === 0;
4820
+ return extra.every((t) => isAssignable(t, b.rest));
4688
4821
  }
4689
- if (b.kind === "array") return a.elements.every((t) => isAssignable(t, b.element));
4822
+ if (b.kind === "array") return tupleMembers(a).every((t) => isAssignable(t, b.element));
4690
4823
  return false;
4691
4824
  }
4692
4825
  if (a.kind === "object") {
@@ -4735,7 +4868,7 @@ function sameRefTarget(a, b) {
4735
4868
  return a.name === b.name && (!a.origin || !b.origin || a.origin === b.origin);
4736
4869
  }
4737
4870
  function isNoValue(t) {
4738
- return t.kind === "tuple" && t.elements.length === 0;
4871
+ return t.kind === "tuple" && t.elements.length === 0 && !t.rest;
4739
4872
  }
4740
4873
  function equalTypes(a, b) {
4741
4874
  return formatType(a) === formatType(b);
@@ -4842,7 +4975,7 @@ function containsFreeTypeParam(t, seen, bound) {
4842
4975
  case "array":
4843
4976
  return containsTypeParam(t.element, seen, bound);
4844
4977
  case "tuple":
4845
- return t.elements.some((e) => containsTypeParam(e, seen, bound));
4978
+ return tupleMembers(t).some((e) => containsTypeParam(e, seen, bound));
4846
4979
  case "union":
4847
4980
  case "intersection":
4848
4981
  return t.types.some((m) => containsTypeParam(m, seen, bound));
@@ -4887,15 +5020,19 @@ function matchInfer(arg, pattern, out) {
4887
5020
  switch (pattern.kind) {
4888
5021
  case "array":
4889
5022
  if (arg.kind === "array") return matchInfer(arg.element, pattern.element, out);
4890
- if (arg.kind === "tuple") return matchInfer(union(arg.elements), pattern.element, out);
5023
+ if (arg.kind === "tuple") return matchInfer(union(tupleMembers(arg)), pattern.element, out);
4891
5024
  return false;
4892
5025
  case "tuple":
4893
5026
  if (arg.kind !== "tuple" || arg.elements.length !== pattern.elements.length) return false;
5027
+ if (pattern.rest && !matchInfer(arg.rest ?? neverType, pattern.rest, out)) return false;
4894
5028
  return pattern.elements.every((pt, i) => matchInfer(arg.elements[i], pt, out));
4895
5029
  case "function": {
4896
5030
  if (arg.kind !== "function") return false;
4897
5031
  if (pattern.varargs?.kind === "infer" && !pattern.params.length) {
4898
- out.set(pattern.varargs.name, tuple(arg.params.map((p) => p.type)));
5032
+ out.set(pattern.varargs.name, tuple(
5033
+ arg.params.map((p) => p.type),
5034
+ arg.varargs && !arg.restIsWhole ? arg.varargs : void 0
5035
+ ));
4899
5036
  } else {
4900
5037
  for (let i = 0; i < pattern.params.length; i++) {
4901
5038
  if (!arg.params[i]) return false;
@@ -4969,7 +5106,7 @@ function collectTypeParams(t, out, seen = /* @__PURE__ */ new Set()) {
4969
5106
  collectTypeParams(t.element, out, seen);
4970
5107
  return;
4971
5108
  case "tuple":
4972
- for (const e of t.elements) collectTypeParams(e, out, seen);
5109
+ for (const e of tupleMembers(t)) collectTypeParams(e, out, seen);
4973
5110
  return;
4974
5111
  case "union":
4975
5112
  case "intersection":
@@ -5014,11 +5151,10 @@ function formatTypeUncached(t) {
5014
5151
  case "literal":
5015
5152
  return t.base === "string" ? JSON.stringify(t.value) : String(t.value);
5016
5153
  case "array":
5017
- return `${formatAtom(t.element)}[]`;
5154
+ return `${t.readonly ? "readonly " : ""}${formatAtom(t.element)}[]`;
5018
5155
  case "tuple": {
5019
- if (!t.elements.length) return "()";
5020
- const inner = t.elements.map(formatType).join(", ");
5021
- return t.isPack ? `(${inner})` : `[${inner}]`;
5156
+ const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
5157
+ return `${t.readonly ? "readonly " : ""}[${inner.join(", ")}]`;
5022
5158
  }
5023
5159
  case "object": {
5024
5160
  if (t.name) {
@@ -5042,7 +5178,7 @@ function formatTypeUncached(t) {
5042
5178
  return `${consts.has(n) ? "const " : ""}${n}${constraint ? ` extends ${briefConstraint(constraint)}` : ""}`;
5043
5179
  }).join(", ")}>` : "";
5044
5180
  const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
5045
- if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
5181
+ if (t.varargs) ps.push(t.restIsWhole ? `...args: ${formatType(t.varargs)}` : `...args: ${formatAtom(t.varargs)}[]`);
5046
5182
  return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
5047
5183
  }
5048
5184
  case "typeParam":
@@ -5246,6 +5382,11 @@ function moduleExports(program, scopes, types, resolveModule) {
5246
5382
  }
5247
5383
  return { values, types: exportedTypes, default: defaultType };
5248
5384
  }
5385
+ function isBelow(k, key) {
5386
+ if (!k.startsWith(key) || k.length === key.length) return false;
5387
+ const next = k[key.length];
5388
+ return next === "." || next === "#" || next === "[";
5389
+ }
5249
5390
  function bindKey(id) {
5250
5391
  return `$${id}`;
5251
5392
  }
@@ -5263,9 +5404,6 @@ function mergeEnv(a, b, base) {
5263
5404
  function mergeIndexer(a, b) {
5264
5405
  return a ? { key: union([a.key, b.key]), value: union([a.value, b.value]) } : b;
5265
5406
  }
5266
- function producesMultipleValues(e) {
5267
- return e.type === "CallExpression" || e.type === "MethodCallExpression" || e.type === "VarargExpression";
5268
- }
5269
5407
  function isFreshLiteralExpr(e) {
5270
5408
  if (!e) return false;
5271
5409
  switch (e.type) {
@@ -5302,11 +5440,11 @@ function collectInferNames(node) {
5302
5440
  walk(n.element);
5303
5441
  return;
5304
5442
  case "ParenthesizedTypeNode":
5305
- case "VariadicTypeNode":
5306
5443
  walk(n.typeAnnotation);
5307
5444
  return;
5308
5445
  case "TupleTypeNode":
5309
5446
  n.elements.forEach(walk);
5447
+ walk(n.rest);
5310
5448
  return;
5311
5449
  case "UnionTypeNode":
5312
5450
  case "IntersectionTypeNode":
@@ -5324,7 +5462,6 @@ function collectInferNames(node) {
5324
5462
  return;
5325
5463
  case "FunctionTypeNode":
5326
5464
  n.params.forEach((pp) => walk(pp.typeAnnotation));
5327
- walk(n.varargType);
5328
5465
  walk(n.returnType);
5329
5466
  return;
5330
5467
  case "TableTypeNode":
@@ -5369,6 +5506,75 @@ function callsSuper(block) {
5369
5506
  });
5370
5507
  return found;
5371
5508
  }
5509
+ var ALL_FIELDS = /* @__PURE__ */ new Set();
5510
+ function definitelyAssigned(block) {
5511
+ const names = /* @__PURE__ */ new Set();
5512
+ for (const statement of block.statements) {
5513
+ switch (statement.type) {
5514
+ case "AssignmentStatement":
5515
+ case "CompoundAssignmentStatement":
5516
+ for (const name of assignedFields({ ...block, statements: [statement] })) names.add(name);
5517
+ break;
5518
+ case "DoStatement": {
5519
+ const inner = definitelyAssigned(statement.body);
5520
+ if (!inner) return void 0;
5521
+ for (const name of inner) names.add(name);
5522
+ break;
5523
+ }
5524
+ case "IfStatement": {
5525
+ if (!statement.alternate) break;
5526
+ const arms = [...statement.clauses.map((c) => c.body), statement.alternate].map(definitelyAssigned);
5527
+ const finishing = arms.filter((arm) => arm !== void 0);
5528
+ if (!finishing.length) return void 0;
5529
+ for (const name of finishing[0]) {
5530
+ if (finishing.every((arm) => arm.has(name))) names.add(name);
5531
+ }
5532
+ break;
5533
+ }
5534
+ case "ReturnStatement":
5535
+ return void 0;
5536
+ case "CallStatement": {
5537
+ const call = statement.expression;
5538
+ if (call.type === "CallExpression" && call.callee.type === "Identifier" && call.callee.name === "error") {
5539
+ return void 0;
5540
+ }
5541
+ break;
5542
+ }
5543
+ }
5544
+ }
5545
+ return names;
5546
+ }
5547
+ function withoutReceiver(t) {
5548
+ if (t.kind === "intersection") return intersection(t.types.map(withoutReceiver));
5549
+ if (t.kind !== "function") return t;
5550
+ const first = t.params[0]?.name;
5551
+ if (first !== "this" && first !== "self") return t;
5552
+ return { ...t, params: t.params.slice(1) };
5553
+ }
5554
+ var METAMETHOD_OPERANDS = {
5555
+ __add: 1,
5556
+ __sub: 1,
5557
+ __mul: 1,
5558
+ __div: 1,
5559
+ __idiv: 1,
5560
+ __mod: 1,
5561
+ __pow: 1,
5562
+ __concat: 1,
5563
+ __eq: 1,
5564
+ __lt: 1,
5565
+ __le: 1,
5566
+ __unm: 0,
5567
+ __len: 0,
5568
+ __tostring: 0,
5569
+ __iter: 0,
5570
+ __call: void 0
5571
+ };
5572
+ var METAMETHOD_RETURNS = {
5573
+ __tostring: stringType,
5574
+ __eq: booleanType,
5575
+ __lt: booleanType,
5576
+ __le: booleanType
5577
+ };
5372
5578
  function assignedFields(block) {
5373
5579
  const names = /* @__PURE__ */ new Set();
5374
5580
  walkNodes(block, (node) => {
@@ -5394,7 +5600,7 @@ function walkNodes(root, visit) {
5394
5600
  if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
5395
5601
  }
5396
5602
  }
5397
- var AliasMap = class extends Map {
5603
+ var LazyMap = class extends Map {
5398
5604
  pending = /* @__PURE__ */ new Map();
5399
5605
  defer(name, resolve5) {
5400
5606
  super.delete(name);
@@ -5471,7 +5677,7 @@ function expressionLabel(e, depth = 0) {
5471
5677
  case "IndexExpression": {
5472
5678
  const o = expressionLabel(e.object, depth + 1);
5473
5679
  const i = e.index.type === "StringLiteral" ? JSON.stringify(e.index.value) : e.index.type === "NumberLiteral" ? e.index.raw : e.index.type === "Identifier" ? e.index.name : "...";
5474
- return o === void 0 ? void 0 : `${o}[${i}]`;
5680
+ return o === void 0 ? void 0 : `${o}${e.optional ? "?." : ""}[${i}]`;
5475
5681
  }
5476
5682
  case "ParenthesizedExpression": {
5477
5683
  const inner = expressionLabel(e.expression, depth + 1);
@@ -5509,20 +5715,21 @@ var TypeAnalyzer = class {
5509
5715
  scopes;
5510
5716
  options;
5511
5717
  typeOf = /* @__PURE__ */ new Map();
5512
- bindingType = /* @__PURE__ */ new Map();
5718
+ bindingType = new LazyMap();
5513
5719
  narrowedTypeOf = /* @__PURE__ */ new Map();
5514
5720
  typeOfTypeNode = /* @__PURE__ */ new Map();
5515
5721
  expectedTypeOf = /* @__PURE__ */ new Map();
5516
- iteratesValues = /* @__PURE__ */ new Set();
5722
+ loops = /* @__PURE__ */ new Map();
5517
5723
  /** Public: each alias resolved once (generic aliases keep their params as
5518
5724
  * `typeParam` nodes in the body). */
5519
- aliases = new AliasMap();
5725
+ aliases = new LazyMap();
5520
5726
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
5521
5727
  aliasDefs = /* @__PURE__ */ new Map();
5522
5728
  /** Put on every ref to one of `aliasDefs`, so a module that imports the
5523
5729
  * type expands the ref here rather than by its name there. */
5524
5730
  origin = { expand: (t) => this.expand(t) };
5525
- /** See `resolveClass`. */
5731
+ /** See `resolveClass`. Shared with other analyses over the same libraries
5732
+ * when none of their names is redefined here; see `shareableClasses`. */
5526
5733
  classTypes = /* @__PURE__ */ new WeakMap();
5527
5734
  /** See `instanceType` — one instance type per `class ... end`. */
5528
5735
  instanceTypes = /* @__PURE__ */ new WeakMap();
@@ -5530,8 +5737,12 @@ var TypeAnalyzer = class {
5530
5737
  /** Generic parameters currently in lexical scope (alias body / generic fn),
5531
5738
  * with their `extends` constraints resolved. */
5532
5739
  typeParamScope = [];
5533
- /** Global types contributed by `declare` statements (libs, then this program). */
5534
- libGlobalTypes = /* @__PURE__ */ new Map();
5740
+ /** Global types contributed by `declare` statements (libs, then this program).
5741
+ * A library's are resolved when the name is first used: an engine's
5742
+ * definitions declare hundreds of globals, and a script uses a few. */
5743
+ libGlobalTypes = new LazyMap();
5744
+ /** Each library `declare` of a name, in order, until it is resolved. */
5745
+ libDeclares = /* @__PURE__ */ new Map();
5535
5746
  /** Declaration node -> binding, built once so `bindingIdByName` is O(1)
5536
5747
  * instead of a scan of every binding per declaration site. */
5537
5748
  bindingByDecl = /* @__PURE__ */ new Map();
@@ -5564,6 +5775,11 @@ var TypeAnalyzer = class {
5564
5775
  /** Recursion guard for `preVisitBody`. */
5565
5776
  preVisitDepth = 0;
5566
5777
  run() {
5778
+ const shared = shareableClasses(this.options.libs ?? [], this.program);
5779
+ if (shared) {
5780
+ this.classTypes = shared.classTypes;
5781
+ this.classMembers = shared.classMembers;
5782
+ }
5567
5783
  this.registerAliasDefs(preludeProgram().body, true);
5568
5784
  for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
5569
5785
  this.registerAliasDefs(this.program.body);
@@ -5576,8 +5792,11 @@ var TypeAnalyzer = class {
5576
5792
  this.indexDeclarations();
5577
5793
  for (const [name, id] of this.scopes.globalsByName) {
5578
5794
  if (this.deferredDeclares.has(name) && !this.options.globalTypes?.[name]) continue;
5579
- const t = this.options.globalTypes?.[name] ?? this.libGlobalTypes.get(name) ?? anyType;
5580
- this.bindingType.set(id, t);
5795
+ const given = this.options.globalTypes?.[name];
5796
+ if (given) this.bindingType.set(id, given);
5797
+ else if (this.libGlobalTypes.has(name)) this.bindingType.defer(id, () => this.libGlobalTypes.get(name) ?? anyType);
5798
+ else if (LANGUAGE_GLOBALS.includes(name)) this.bindingType.set(id, arrayOf(unknownType));
5799
+ else this.bindingType.set(id, anyType);
5581
5800
  }
5582
5801
  setAliasExpander((t) => this.expand(t));
5583
5802
  setDeferredBound((t) => this.deferredBound(t));
@@ -5597,7 +5816,7 @@ var TypeAnalyzer = class {
5597
5816
  typeOfTypeNode: this.typeOfTypeNode,
5598
5817
  expectedTypeOf: this.expectedTypeOf,
5599
5818
  aliases: this.resolveDeferredAliases(),
5600
- iteratesValues: this.iteratesValues,
5819
+ loops: this.loops,
5601
5820
  diagnostics: this.diagnostics
5602
5821
  };
5603
5822
  }
@@ -5949,28 +6168,55 @@ var TypeAnalyzer = class {
5949
6168
  type.name = `typeof ${this.className(stmt)}`;
5950
6169
  const shape = this.shapeOf(stmt);
5951
6170
  const local = this.superDecl(stmt);
5952
- const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
6171
+ const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsOf(stmt.superclass) : void 0;
5953
6172
  if (parent?.kind === "object") {
5954
6173
  for (const [key, property] of parent.properties) {
5955
6174
  if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
5956
6175
  }
5957
6176
  }
5958
6177
  for (const [key, property] of shape.statics) type.properties.set(key, property);
5959
- const constructor = this.constructorType(stmt);
6178
+ if (shape.filling) {
6179
+ (shape.filled ??= []).push(() => {
6180
+ for (const [key, property] of shape.statics) type.properties.set(key, property);
6181
+ });
6182
+ }
5960
6183
  const params = this.classTypeParams(stmt);
5961
- type.properties.set("new", {
5962
- type: fn(
5963
- constructor?.params.filter((p) => p.name !== "this") ?? [],
5964
- this.selfTypeOf(stmt),
5965
- constructor?.varargs,
5966
- params.map((p) => p.name)
5967
- ),
6184
+ let settled;
6185
+ const construct = {
5968
6186
  optional: false,
5969
- readonly: true
6187
+ readonly: true,
6188
+ // Kept, so a class extending it can read what it takes; reaching
6189
+ // it from outside is the error.
6190
+ ...stmt.isAbstract ? { abstract: true } : {}
6191
+ };
6192
+ Object.defineProperty(construct, "type", {
6193
+ enumerable: true,
6194
+ get: () => {
6195
+ if (settled) return settled;
6196
+ const constructor = this.constructorType(stmt);
6197
+ const made = fn(
6198
+ constructor?.params.filter((p) => p.name !== "this") ?? [],
6199
+ this.selfTypeOf(stmt),
6200
+ constructor?.varargs,
6201
+ params.map((p) => p.name)
6202
+ );
6203
+ if (!this.shapeOf(stmt).filling) settled = made;
6204
+ return made;
6205
+ }
5970
6206
  });
6207
+ type.properties.set("new", construct);
5971
6208
  type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
5972
6209
  return type;
5973
6210
  }
6211
+ /** The value side of the class `superclass` names when it is not
6212
+ * declared in this file: what the name it refers to holds — an import
6213
+ * brings the class table with it — or else a class of that name. */
6214
+ classStaticsOf(superclass) {
6215
+ const id = this.bindingIdOf(superclass);
6216
+ const bound = id !== void 0 ? this.expand(this.bindingType.get(id) ?? unknownType) : void 0;
6217
+ if (bound?.kind === "object") return bound;
6218
+ return this.classStaticsByNameType(superclass.name);
6219
+ }
5974
6220
  /** The value side of a class named by a binding rather than by a
5975
6221
  * declaration in this file — an imported one. */
5976
6222
  classStaticsByNameType(name) {
@@ -6031,20 +6277,26 @@ var TypeAnalyzer = class {
6031
6277
  } finally {
6032
6278
  this.emitDiagnostics = wasEmitting;
6033
6279
  shape.filling = false;
6280
+ for (const then of shape.filled ?? []) then();
6281
+ shape.filled = void 0;
6034
6282
  }
6035
6283
  return shape;
6036
6284
  }
6037
6285
  fillShape(stmt, shape) {
6038
6286
  const className = this.className(stmt);
6039
6287
  const put = (isStatic, name, property) => {
6040
- const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && m.accessibility === "private");
6041
- const marked = member ? { ...property, private: { owner: stmt, className } } : property;
6288
+ const member = stmt.members.find((m) => m.type !== "ClassConstructor" && m.isStatic === isStatic && m.name.name === name && (m.accessibility === "private" || m.accessibility === "protected"));
6289
+ const marked = member ? { ...property, private: { owner: stmt, className, ...member.accessibility === "protected" ? { protected: true } : {} } } : property;
6042
6290
  (isStatic ? shape.statics : shape.instance).set(name, marked);
6043
6291
  };
6044
6292
  for (const member of stmt.members) {
6045
6293
  if (member.type !== "ClassField") continue;
6046
6294
  const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
6047
- put(member.isStatic, member.name.name, { type, optional: false });
6295
+ put(member.isStatic, member.name.name, {
6296
+ type,
6297
+ optional: false,
6298
+ ...member.isReadonly ? { readonly: true } : {}
6299
+ });
6048
6300
  }
6049
6301
  for (const member of stmt.members) {
6050
6302
  switch (member.type) {
@@ -6053,7 +6305,11 @@ var TypeAnalyzer = class {
6053
6305
  case "ClassMethod": {
6054
6306
  this.paramsFromSignatures(member.func, member.signatures);
6055
6307
  const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
6056
- put(member.isStatic, member.name.name, { type, optional: false });
6308
+ put(member.isStatic, member.name.name, {
6309
+ type,
6310
+ optional: false,
6311
+ ...member.isAbstract ? { abstract: true } : {}
6312
+ });
6057
6313
  break;
6058
6314
  }
6059
6315
  case "ClassAccessor": {
@@ -6109,7 +6365,14 @@ var TypeAnalyzer = class {
6109
6365
  for (const signature of member.signatures ?? []) {
6110
6366
  this.checkParamOrder(signature.params, member);
6111
6367
  }
6112
- this.visitFunctionBody(member.func, env);
6368
+ if (member.type === "ClassMethod" && member.isAbstract) continue;
6369
+ const constructing = this.constructing;
6370
+ if (member.type === "ClassConstructor") this.constructing = stmt;
6371
+ try {
6372
+ this.visitFunctionBody(member.func, env);
6373
+ } finally {
6374
+ this.constructing = constructing;
6375
+ }
6113
6376
  }
6114
6377
  });
6115
6378
  }));
@@ -6156,13 +6419,148 @@ var TypeAnalyzer = class {
6156
6419
  if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
6157
6420
  report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
6158
6421
  }
6159
- const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
6422
+ this.checkAbstractMembers(stmt);
6423
+ this.checkOverrides(stmt);
6424
+ this.checkImplements(stmt);
6425
+ this.checkMetamethods(stmt);
6426
+ const assigned = constructor ? definitelyAssigned(constructor.func.body) ?? ALL_FIELDS : /* @__PURE__ */ new Set();
6160
6427
  for (const member of stmt.members) {
6161
6428
  if (member.type !== "ClassField" || member.isStatic || member.init) continue;
6162
- if (assigned.has(member.name.name)) continue;
6429
+ if (assigned === ALL_FIELDS || assigned.has(member.name.name)) continue;
6163
6430
  const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
6164
6431
  if (isAssignable(nilType, type)) continue;
6165
- report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
6432
+ report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor on every path, or let its type admit nil`);
6433
+ }
6434
+ }
6435
+ /** The class whose constructor is being checked: it, and only it, may
6436
+ * assign the `readonly` fields it declares. */
6437
+ constructing;
6438
+ /** A class that is not abstract has a `new`, so everything its instances
6439
+ * promise has to be there: no abstract member of its own, and every one
6440
+ * it inherits written. */
6441
+ checkAbstractMembers(stmt) {
6442
+ if (stmt.isAbstract) return;
6443
+ const name = this.className(stmt);
6444
+ for (const member of stmt.members) {
6445
+ if (member.type === "ClassMethod" && member.isAbstract) {
6446
+ this.diagnostics.push({
6447
+ node: member.name,
6448
+ message: `'${member.name.name}' is abstract, so '${name}' has to be an 'abstract class'`
6449
+ });
6450
+ }
6451
+ }
6452
+ const own = this.shapeOf(stmt).instance;
6453
+ const missing = [...this.instanceType(stmt).properties].filter(([key, property]) => property.abstract && !own.has(key)).map(([key]) => `'${key}'`);
6454
+ if (missing.length) {
6455
+ this.diagnostics.push({
6456
+ node: stmt.name ?? stmt,
6457
+ message: `'${name}' does not write the abstract ${missing.length === 1 ? "member" : "members"} ${missing.join(", ")} of the class it extends; write ${missing.length === 1 ? "it" : "them"}, or make '${name}' abstract too`
6458
+ });
6459
+ }
6460
+ }
6461
+ /** A member written again in a class that extends another has to fit
6462
+ * where the base's stood — anything reading an instance as the base
6463
+ * would otherwise be told wrong. `override` says a member is such a one,
6464
+ * and is wrong on a member that is not. */
6465
+ checkOverrides(stmt) {
6466
+ const base = this.baseInstance(stmt);
6467
+ const name = this.className(stmt);
6468
+ const own = this.shapeOf(stmt).instance;
6469
+ for (const member of stmt.members) {
6470
+ if (member.type === "ClassConstructor" || member.isStatic) continue;
6471
+ const key = member.name.name;
6472
+ const inherited = CLASS_LINKS.has(key) ? void 0 : base?.properties.get(key);
6473
+ if (member.isOverride && !inherited) {
6474
+ this.diagnostics.push({
6475
+ node: member.name,
6476
+ message: base ? `'${key}' is marked 'override', but '${stmt.superclass.name}' has no member of that name` : `'${key}' is marked 'override', but '${name}' does not extend another class`
6477
+ });
6478
+ continue;
6479
+ }
6480
+ const mine = own.get(key);
6481
+ if (!inherited || !mine || inherited.private && !inherited.private.protected) continue;
6482
+ const written = withoutReceiver(mine.type);
6483
+ const expected = withoutReceiver(inherited.type);
6484
+ if (isAssignable(written, expected)) continue;
6485
+ this.diagnostics.push({
6486
+ node: member.name,
6487
+ message: `'${key}' in '${name}' does not fit the '${key}' of '${stmt.superclass.name}' it replaces: '${briefType(written)}' is not assignable to '${briefType(expected)}'`
6488
+ });
6489
+ }
6490
+ }
6491
+ /** `implements Shape`: every member the shape names, of a type that fits.
6492
+ * A method's own `this` is left out of the comparison on both sides. */
6493
+ checkImplements(stmt) {
6494
+ if (!stmt.implements?.length) return;
6495
+ const name = this.className(stmt);
6496
+ const instance = this.instanceType(stmt).properties;
6497
+ for (const node of stmt.implements) {
6498
+ const shown = node.type === "TypeReference" ? node.namespace ? `${node.namespace}.${node.base}` : node.base : void 0;
6499
+ const target = this.expand(this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(node)));
6500
+ if (target.kind === "any") continue;
6501
+ if (target.kind !== "object") {
6502
+ this.diagnostics.push({
6503
+ node,
6504
+ message: `A class can only implement an object type or a class, not '${briefType(target)}'`
6505
+ });
6506
+ continue;
6507
+ }
6508
+ const label = shown ?? briefType(target);
6509
+ const missing = [];
6510
+ for (const [key, wanted] of target.properties) {
6511
+ const have = instance.get(key);
6512
+ if (!have) {
6513
+ if (!wanted.optional) missing.push(`'${key}'`);
6514
+ continue;
6515
+ }
6516
+ const written = withoutReceiver(have.type);
6517
+ const expected = withoutReceiver(wanted.optional ? optional(wanted.type) : wanted.type);
6518
+ if (isAssignable(written, expected)) continue;
6519
+ this.diagnostics.push({
6520
+ node,
6521
+ message: `'${name}' does not implement '${label}': its '${key}' is '${briefType(written)}', not '${briefType(expected)}'`
6522
+ });
6523
+ }
6524
+ if (missing.length) {
6525
+ this.diagnostics.push({
6526
+ node,
6527
+ message: `'${name}' does not implement '${label}': ${missing.join(", ")} ${missing.length === 1 ? "is" : "are"} missing`
6528
+ });
6529
+ }
6530
+ }
6531
+ }
6532
+ /** A method named for a metamethod *is* one — the class table is its
6533
+ * instances' metatable — so it has to take what Luau hands it and give
6534
+ * back what Luau expects. */
6535
+ checkMetamethods(stmt) {
6536
+ const instance = this.shapeOf(stmt).instance;
6537
+ for (const member of stmt.members) {
6538
+ if (member.type !== "ClassMethod" || !(member.name.name in METAMETHOD_OPERANDS)) continue;
6539
+ const key = member.name.name;
6540
+ if (member.isStatic) {
6541
+ this.diagnostics.push({
6542
+ node: member.name,
6543
+ message: `'${key}' is a metamethod of the instances; it cannot be static`
6544
+ });
6545
+ continue;
6546
+ }
6547
+ const operands = METAMETHOD_OPERANDS[key];
6548
+ const written = member.func.params.filter((p) => p.name !== "this").length;
6549
+ if (operands !== void 0 && (written !== operands || member.func.hasVarargs)) {
6550
+ this.diagnostics.push({
6551
+ node: member.name,
6552
+ message: operands === 0 ? `'${key}' takes nothing besides 'this'` : `'${key}' takes one operand besides 'this'`
6553
+ });
6554
+ continue;
6555
+ }
6556
+ const required = METAMETHOD_RETURNS[key];
6557
+ const returns = this.overloadsOf(instance.get(key)?.type ?? anyType)[0]?.returns;
6558
+ if (required && returns && !isAssignable(returns, required)) {
6559
+ this.diagnostics.push({
6560
+ node: member.name,
6561
+ message: `'${key}' has to return ${briefType(required)}, not '${briefType(returns)}'`
6562
+ });
6563
+ }
6166
6564
  }
6167
6565
  }
6168
6566
  /** `extends` must name a class, and the chain must end. */
@@ -6220,19 +6618,42 @@ var TypeAnalyzer = class {
6220
6618
  harvestDeclares(block, own = false) {
6221
6619
  for (const stmt of block.statements) {
6222
6620
  if (stmt.type !== "DeclareStatement") continue;
6223
- if (own && (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name)))) {
6621
+ if (!own) {
6622
+ const declares = this.libDeclares.get(stmt.name);
6623
+ if (declares) declares.push(stmt);
6624
+ else {
6625
+ this.libDeclares.set(stmt.name, [stmt]);
6626
+ this.libGlobalTypes.defer(stmt.name, () => this.resolveLibDeclares(stmt.name));
6627
+ }
6628
+ continue;
6629
+ }
6630
+ if (containsTypeQuery(stmt.valueType) || referencedTypeNames(stmt.valueType).some((name) => this.dependsOnTypeQuery(name))) {
6224
6631
  this.deferredDeclares.set(stmt.name, stmt);
6225
6632
  continue;
6226
6633
  }
6227
- const t = this.resolveType(stmt.valueType);
6228
- const prev = this.libGlobalTypes.get(stmt.name);
6229
- const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6230
- this.libGlobalTypes.set(
6231
- stmt.name,
6232
- overload ? intersection([prev, t]) : this.mergeDeclared(prev, t)
6233
- );
6634
+ this.libGlobalTypes.set(stmt.name, this.declaredOver(this.libGlobalTypes.get(stmt.name), stmt));
6234
6635
  }
6235
6636
  }
6637
+ /** Every library `declare` of `name`, in order. They are written at the
6638
+ * top level of their files, so they are read as if there — outside any
6639
+ * generic the use that asked for them happens to sit in. */
6640
+ resolveLibDeclares(name) {
6641
+ const scope = this.typeParamScope.splice(0);
6642
+ try {
6643
+ let type;
6644
+ for (const stmt of this.libDeclares.get(name) ?? []) type = this.declaredOver(type, stmt);
6645
+ return type ?? anyType;
6646
+ } finally {
6647
+ this.typeParamScope.push(...scope);
6648
+ }
6649
+ }
6650
+ /** A `declare` on top of what earlier ones said about the same name: a
6651
+ * function adds an overload, a table adds members. */
6652
+ declaredOver(prev, stmt) {
6653
+ const t = this.resolveType(stmt.valueType);
6654
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
6655
+ return overload ? intersection([prev, t]) : this.mergeDeclared(prev, t);
6656
+ }
6236
6657
  resolveAllAliases() {
6237
6658
  for (const [name, def] of this.aliasDefs) {
6238
6659
  if (def.class && !this.program.body.statements.includes(def.class)) {
@@ -6278,11 +6699,10 @@ var TypeAnalyzer = class {
6278
6699
  }
6279
6700
  imported;
6280
6701
  /** What a `return` gives, against what the function declared. */
6281
- checkReturn(stmt, declared, types, sources, env) {
6702
+ checkReturn(stmt, declared, actual, env) {
6282
6703
  if (!declared || !this.emitDiagnostics) return;
6283
6704
  if (declared.kind === "any" || declared.kind === "unknown" || this.namesNothing(declared)) return;
6284
- const actual = stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true);
6285
- const source = stmt.arguments.length === 1 ? sources[0] : void 0;
6705
+ const source = stmt.argument;
6286
6706
  const fits = source ? this.fitsAnnotation(source, declared, actual, env) : isAssignable(actual, declared) || isAssignable(widen(actual), declared);
6287
6707
  if (fits) return;
6288
6708
  this.diagnostics.push({
@@ -6430,21 +6850,12 @@ var TypeAnalyzer = class {
6430
6850
  this.instantiationDepth--;
6431
6851
  }
6432
6852
  }
6433
- /** Pair written type arguments with the parameters they instantiate. A
6434
- * pack parameter (`T...`) takes every argument from its position on, as
6435
- * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
6436
- * and `Signal<()>` to the empty pack. Left out, a parameter takes its
6437
- * default (`T... = ...any` is `any`), or `unknown`. */
6853
+ /** Pair written type arguments with the parameters they instantiate.
6854
+ * Left out, a parameter takes its default, or `unknown`. */
6438
6855
  bindTypeArguments(params, args) {
6439
6856
  const subst = /* @__PURE__ */ new Map();
6440
6857
  params.forEach((p, i) => {
6441
- let arg = args[i];
6442
- if (p.isPack && i < args.length) {
6443
- const rest = args.slice(i);
6444
- const single = rest.length === 1 ? rest[0] : void 0;
6445
- arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
6446
- }
6447
- subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
6858
+ subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
6448
6859
  });
6449
6860
  return subst;
6450
6861
  }
@@ -6534,10 +6945,18 @@ var TypeAnalyzer = class {
6534
6945
  return literal(node.value);
6535
6946
  case "TypeLiteralNumber":
6536
6947
  return literal(node.value);
6537
- case "ArrayTypeNode":
6538
- return arrayOf(this.resolveType(node.element));
6539
- case "TupleTypeNode":
6540
- return tuple(node.elements.map((e) => this.resolveType(e)));
6948
+ case "ArrayTypeNode": {
6949
+ const made = arrayOf(this.resolveType(node.element));
6950
+ return node.isReadonly ? { ...made, readonly: true } : made;
6951
+ }
6952
+ case "TupleTypeNode": {
6953
+ const rest = node.rest && this.expand(this.resolveType(node.rest));
6954
+ const made = tuple(
6955
+ node.elements.map((e) => this.resolveType(e)),
6956
+ rest ? rest.kind === "array" ? rest.element : rest.kind === "any" ? anyType : unknownType : void 0
6957
+ );
6958
+ return node.isReadonly && made.kind === "tuple" ? { ...made, readonly: true } : made;
6959
+ }
6541
6960
  case "UnionTypeNode":
6542
6961
  return union(node.types.map((t) => this.resolveType(t)));
6543
6962
  case "IntersectionTypeNode":
@@ -6573,15 +6992,13 @@ var TypeAnalyzer = class {
6573
6992
  type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
6574
6993
  optional: p.optional
6575
6994
  }));
6576
- const restParam = node.params.find((p) => p.rest);
6577
- const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
6578
- return this.withTypeParamDefaults(fn(
6995
+ return this.withTypeParamDefaults(this.withRest(fn(
6579
6996
  params,
6580
6997
  this.resolveType(node.returnType),
6581
- restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
6998
+ void 0,
6582
6999
  names,
6583
7000
  this.resolvePredicate(node.predicate, params)
6584
- ), node.generics);
7001
+ ), node.params), node.generics);
6585
7002
  });
6586
7003
  }
6587
7004
  case "TypeofTypeNode": {
@@ -6645,13 +7062,6 @@ var TypeAnalyzer = class {
6645
7062
  })
6646
7063
  );
6647
7064
  }
6648
- case "VariadicTypeNode":
6649
- return this.resolveType(node.typeAnnotation);
6650
- case "TypePackNode": {
6651
- if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
6652
- if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
6653
- return tuple(node.types.map((t) => this.resolveType(t)), true);
6654
- }
6655
7065
  }
6656
7066
  }
6657
7067
  // --------------------------------------------------------
@@ -6717,15 +7127,18 @@ var TypeAnalyzer = class {
6717
7127
  case "array":
6718
7128
  return arrayOf(this.reduceType(t.element));
6719
7129
  case "tuple":
6720
- return tuple(t.elements.map((e) => this.reduceType(e)), t.isPack);
7130
+ return tuple(t.elements.map((e) => this.reduceType(e)), t.rest && this.reduceType(t.rest));
6721
7131
  case "function":
6722
- return fn(
6723
- t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
6724
- this.reduceType(t.returns),
6725
- t.varargs && this.reduceType(t.varargs),
6726
- t.typeParams,
6727
- t.predicate
6728
- );
7132
+ return {
7133
+ ...fn(
7134
+ t.params.map((p) => ({ ...p, type: this.reduceType(p.type) })),
7135
+ this.reduceType(t.returns),
7136
+ t.varargs && this.reduceType(t.varargs),
7137
+ t.typeParams,
7138
+ t.predicate
7139
+ ),
7140
+ ...t.restIsWhole ? { restIsWhole: true } : {}
7141
+ };
6729
7142
  case "object": {
6730
7143
  if (t.class) return t;
6731
7144
  const entries = [];
@@ -6940,7 +7353,8 @@ var TypeAnalyzer = class {
6940
7353
  this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
6941
7354
  }
6942
7355
  });
6943
- const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
7356
+ const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7357
+ this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
6944
7358
  stmt.names.forEach((target, i) => {
6945
7359
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
6946
7360
  const source = sources[i];
@@ -7029,7 +7443,8 @@ var TypeAnalyzer = class {
7029
7443
  if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7030
7444
  }
7031
7445
  });
7032
- const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
7446
+ const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7447
+ this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7033
7448
  stmt.targets.forEach((target, i) => {
7034
7449
  const vt = valueTypes[i] ?? unknownType;
7035
7450
  const source = sources[i];
@@ -7116,23 +7531,26 @@ var TypeAnalyzer = class {
7116
7531
  return;
7117
7532
  }
7118
7533
  case "GenericForStatement": {
7119
- const iterTypes = stmt.iterators.map((it) => this.infer(it, env));
7534
+ const sourceType = this.infer(stmt.iterator, env);
7120
7535
  const bodyEnv = forkEnv(env);
7121
- const rows = stmt.variables.length >= 2 ? this.iterationRows(stmt.iterators[0], iterTypes[0]) : void 0;
7536
+ const rows = this.iterationRows(stmt.iterator, sourceType);
7122
7537
  if (rows) {
7123
- const [key, value] = stmt.variables;
7124
- this.bindPattern(key, union(rows.map((r) => r[0])), bodyEnv, "keep");
7125
- this.bindPattern(value, union(rows.map((r) => r[1])), bodyEnv, "keep");
7126
- stmt.variables.slice(2).forEach((v) => this.bindPattern(v, unknownType, bodyEnv, "widen"));
7127
- const keyId = key.type === "IdentifierPattern" ? this.bindingIdByName(key.name, key) : void 0;
7128
- const valueId = value.type === "IdentifierPattern" ? this.bindingIdByName(value.name, value) : void 0;
7129
- if (keyId !== void 0 && valueId !== void 0) this.correlateBindings(bodyEnv, [keyId, valueId], rows);
7538
+ this.bindPattern(
7539
+ stmt.variable,
7540
+ union(rows.map((row) => tuple(row))),
7541
+ bodyEnv,
7542
+ stmt.kind === "const" ? "keep" : "widen"
7543
+ );
7544
+ const parts = stmt.variable.type === "ArrayPattern" && !stmt.variable.rest ? stmt.variable.elements.slice(0, 2).map((el) => el?.value) : [];
7545
+ const ids = parts.map((p) => p?.type === "IdentifierPattern" ? this.bindingIdByName(p.name, p) : void 0);
7546
+ if (ids.length === 2 && ids.every((id) => id !== void 0)) {
7547
+ this.correlateBindings(bodyEnv, ids, rows);
7548
+ }
7549
+ this.loops.set(stmt, { walks: "iteration", viaIter: false });
7130
7550
  } else {
7131
- const [keyT, valT, iteratesValues] = this.iterationTypes(stmt.iterators[0], iterTypes[0], stmt.variables.length);
7132
- stmt.variables.forEach((v, i) => {
7133
- this.bindPattern(v, i === 0 ? keyT : i === 1 ? valT : unknownType, bodyEnv, "widen");
7134
- });
7135
- if (iteratesValues && stmt.iterators.length === 1) this.iteratesValues.add(stmt);
7551
+ const [item, form] = this.iterationOf(stmt.iterator, sourceType);
7552
+ this.bindPattern(stmt.variable, item, bodyEnv, "widen");
7553
+ this.loops.set(stmt, form);
7136
7554
  }
7137
7555
  this.visitBlock(stmt.body, bodyEnv);
7138
7556
  return;
@@ -7140,19 +7558,10 @@ var TypeAnalyzer = class {
7140
7558
  case "ReturnStatement": {
7141
7559
  const declared = this.declaredReturns[this.declaredReturns.length - 1];
7142
7560
  const wanted = declared ?? this.contextualReturns[this.contextualReturns.length - 1];
7143
- if (wanted) {
7144
- if (stmt.arguments.length === 1) {
7145
- this.applyContext(stmt.arguments[0], wanted);
7146
- } else if (wanted.kind === "tuple" && wanted.isPack) {
7147
- stmt.arguments.forEach((a, i) => this.applyContext(a, wanted.elements[i]));
7148
- }
7149
- }
7150
- const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
7151
- const { types, sources } = this.valueList(stmt.arguments, env, want);
7152
- this.checkReturn(stmt, declared, types, sources, env);
7153
- if (this.returnTypes) {
7154
- this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
7155
- }
7561
+ if (wanted && stmt.argument) this.applyContext(stmt.argument, wanted);
7562
+ const actual = stmt.argument ? this.infer(stmt.argument, env) : nilType;
7563
+ this.checkReturn(stmt, declared, actual, env);
7564
+ if (this.returnTypes) this.returnTypes.push(actual);
7156
7565
  return;
7157
7566
  }
7158
7567
  case "ExportStatement":
@@ -7239,7 +7648,7 @@ var TypeAnalyzer = class {
7239
7648
  * `sources` maps each produced value back to the expression it came from
7240
7649
  * (undefined for the 2nd and later values of a multi-value call), so the
7241
7650
  * caller can still do contextual typing against the written expression. */
7242
- valueList(exprs, env, want = 0) {
7651
+ valueList(exprs, env) {
7243
7652
  const types = [];
7244
7653
  const sources = [];
7245
7654
  exprs.forEach((e, i) => {
@@ -7255,31 +7664,29 @@ var TypeAnalyzer = class {
7255
7664
  }
7256
7665
  return;
7257
7666
  }
7258
- do {
7259
- types.push(t);
7260
- sources.push(e);
7261
- } while (last && types.length < want);
7262
- return;
7263
- }
7264
- if (last && e.type === "VarargExpression") {
7265
- do {
7266
- types.push(t);
7267
- sources.push(types.length - 1 === i ? e : void 0);
7268
- } while (types.length < want);
7269
- return;
7270
- }
7271
- if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
7272
- t.elements.forEach((el, j) => {
7273
- types.push(el);
7274
- sources.push(j === 0 ? e : void 0);
7275
- });
7667
+ types.push(t);
7668
+ sources.push(e);
7276
7669
  return;
7277
7670
  }
7278
- types.push(t.kind === "tuple" && t.isPack && producesMultipleValues(e) ? t.elements[0] ?? nilType : t);
7671
+ void last;
7672
+ types.push(t);
7279
7673
  sources.push(e);
7280
7674
  });
7281
7675
  return { types, sources };
7282
7676
  }
7677
+ /** `const a, b = x, y` pairs names with values, one each. A call is one
7678
+ * value — several come back as an array — so `const a, b = f()` is a
7679
+ * name short of a value, and says how to take them apart. */
7680
+ checkValueCount(node, names, values, types) {
7681
+ if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7682
+ const last = values[values.length - 1];
7683
+ const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7684
+ const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7685
+ this.diagnostics.push({
7686
+ node,
7687
+ message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
7688
+ });
7689
+ }
7283
7690
  /** Run `visit` with a fresh place to collect `break` states, and return
7284
7691
  * what it collected. */
7285
7692
  withBreakScope(visit) {
@@ -7368,8 +7775,19 @@ var TypeAnalyzer = class {
7368
7775
  if (p.rest) return arrayOf(unknownType);
7369
7776
  if (p.pattern) return this.patternToType(p.pattern, env);
7370
7777
  if (p.default) return widen(this.infer(p.default, env));
7371
- return this.contextualParams.get(p) ?? anyType;
7778
+ const contextual = this.contextualParams.get(p);
7779
+ if (contextual) return contextual;
7780
+ if (this.emitDiagnostics && !receiver && p.name && p.line && !this.untypedParamReported.has(p)) {
7781
+ this.untypedParamReported.add(p);
7782
+ this.diagnostics.push({
7783
+ node: p,
7784
+ message: `Parameter '${p.name}' has no type, so it is 'any': give it one, or a default to read it from`
7785
+ });
7786
+ }
7787
+ return anyType;
7372
7788
  }
7789
+ /** Parameters already spoken about: a body is visited more than once. */
7790
+ untypedParamReported = /* @__PURE__ */ new WeakSet();
7373
7791
  /** What a function expression's unannotated parameters are, from where
7374
7792
  * it is written — see `applyContext`. */
7375
7793
  contextualParams = /* @__PURE__ */ new WeakMap();
@@ -7428,18 +7846,29 @@ var TypeAnalyzer = class {
7428
7846
  * context. */
7429
7847
  contextualArrays = /* @__PURE__ */ new WeakMap();
7430
7848
  applyArrayContext(e, expected) {
7431
- const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
7849
+ const members = this.expectedMembers(expected);
7850
+ const tuples = members.filter((m) => m.kind === "tuple");
7851
+ const spread = e.elements.some((el) => el.type === "SpreadElement");
7852
+ const length = e.elements.length;
7853
+ const fitting = tuples.find((t) => !spread && !t.rest && t.elements.length === length) ?? tuples.find((t) => t.rest !== void 0 && (spread || length >= t.elements.length));
7854
+ const target = fitting ?? members.find((m) => m.kind === "array") ?? tuples[0];
7432
7855
  if (!target) return;
7856
+ if (target.kind === "tuple" && e.elements.length) this.tupleArrays.set(e, target);
7433
7857
  if (!e.elements.length) {
7434
7858
  this.contextualArrays.set(e, target);
7435
7859
  return;
7436
7860
  }
7437
7861
  e.elements.forEach((element, i) => {
7438
7862
  if (element.type === "SpreadElement") return;
7439
- const elementType = target.kind === "array" ? target.element : target.elements[i];
7863
+ const tupleTarget = target;
7864
+ const elementType = target.kind === "array" ? target.element : tupleTarget.elements[i] ?? tupleTarget.rest;
7440
7865
  this.applyContext(element, elementType);
7441
7866
  });
7442
7867
  }
7868
+ /** Array literals written where a tuple is wanted — a `return` of a
7869
+ * function that returns one, an argument, an annotated `const` — and
7870
+ * the tuple each is to be. */
7871
+ tupleArrays = /* @__PURE__ */ new WeakMap();
7443
7872
  /** `{ list: [] }` where `{ list: thread[] }` is expected: each field's
7444
7873
  * value gets its property's type as context. */
7445
7874
  applyTableContext(e, expected) {
@@ -7495,32 +7924,33 @@ var TypeAnalyzer = class {
7495
7924
  }
7496
7925
  return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
7497
7926
  }
7498
- /** What a vararg function's `...` holds, one value at a time.
7499
- *
7500
- * Written three ways, and they mean the same call: `...` says nothing,
7501
- * `...: T` says each value is a `T`, and `...rest: T[]` collects them
7502
- * into an array the body reads by name. Only the last changes what the
7503
- * body sees — the signature is the same either way. */
7504
- varargElement(func) {
7505
- if (!func.hasVarargs) return void 0;
7506
- const rest = func.params.find((p) => p.rest);
7507
- if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
7508
- const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
7509
- return declared?.kind === "array" ? declared.element : unknownType;
7510
- }
7511
- /** The type of `...` in each function body being walked. */
7512
- varargs = [];
7927
+ /** A function type with the rest parameter among `params` written in:
7928
+ * `...args: T[]` collects `T`s, and `...args: A` — `A` a tuple, or a
7929
+ * type parameter standing for one — is exactly `A`'s elements. */
7930
+ withRest(f, params) {
7931
+ const rest = params.find((p) => p.rest);
7932
+ if (!rest) return f;
7933
+ if (!rest.typeAnnotation) return { ...f, varargs: unknownType };
7934
+ const declared = this.resolveType(rest.typeAnnotation);
7935
+ const t = this.expand(declared);
7936
+ if (t.kind === "array") return { ...f, varargs: t.element };
7937
+ if (t.kind === "any") return { ...f, varargs: anyType };
7938
+ if (declared.kind === "infer") return { ...f, varargs: declared, restIsWhole: true };
7939
+ if (t.kind === "tuple") {
7940
+ return { ...f, params: [...f.params, ...t.elements.map((type) => ({ type }))], varargs: t.rest };
7941
+ }
7942
+ if (declared.kind === "typeParam") return { ...f, varargs: declared, restIsWhole: true };
7943
+ return { ...f, varargs: unknownType };
7944
+ }
7513
7945
  /** What each function body being walked declared it returns. */
7514
7946
  declaredReturns = [];
7515
- /** Run `body` with `...` and `return` as `func` declares them. */
7947
+ /** Run `body` with `return` as `func` declares it. */
7516
7948
  withVarargs(func, body) {
7517
- this.varargs.push(this.varargElement(func));
7518
7949
  this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
7519
7950
  this.contextualReturns.push(this.contextualReturnOf.get(func));
7520
7951
  try {
7521
7952
  return body();
7522
7953
  } finally {
7523
- this.varargs.pop();
7524
7954
  this.declaredReturns.pop();
7525
7955
  this.contextualReturns.pop();
7526
7956
  }
@@ -7579,7 +8009,7 @@ var TypeAnalyzer = class {
7579
8009
  if (type.kind !== "function") return type;
7580
8010
  const defaults = {};
7581
8011
  for (const generic of generics) {
7582
- if (generic.default && !generic.isPack) defaults[generic.name] = this.resolveType(generic.default);
8012
+ if (generic.default) defaults[generic.name] = this.resolveType(generic.default);
7583
8013
  }
7584
8014
  return Object.keys(defaults).length ? { ...type, typeParamDefaults: defaults } : type;
7585
8015
  }
@@ -7598,7 +8028,10 @@ var TypeAnalyzer = class {
7598
8028
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
7599
8029
  unify(p.type, preserve ? arg : widen(arg), vars, subst);
7600
8030
  });
7601
- if (f.varargs) {
8031
+ if (f.varargs && f.restIsWhole) {
8032
+ const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
8033
+ unify(f.varargs, tuple(rest), vars, subst);
8034
+ } else if (f.varargs) {
7602
8035
  const keeps = keepsLiterals(f.varargs);
7603
8036
  for (let i = f.params.length; i < argTypes.length; i++) {
7604
8037
  const arg = argTypes[i];
@@ -7649,6 +8082,11 @@ var TypeAnalyzer = class {
7649
8082
  * this returns `undefined` and the ordinary pick stands. */
7650
8083
  distributedReturn(fns, argTypes, picked, argsFor) {
7651
8084
  if (fns.length < 2) return void 0;
8085
+ if (argTypes.some((t) => this.expand(t).kind === "any")) {
8086
+ const taken = fns.filter((f) => this.overloadAccepts(f, argsFor(f, argTypes)));
8087
+ if (taken.length < 2) return void 0;
8088
+ return union(taken.map((f) => this.callReturn(f, argsFor(f, argTypes))));
8089
+ }
7652
8090
  const position = argTypes.findIndex((t) => this.expand(t).kind === "union");
7653
8091
  if (position < 0) return void 0;
7654
8092
  const members = this.expand(argTypes[position]).types;
@@ -7697,6 +8135,8 @@ var TypeAnalyzer = class {
7697
8135
  * lone argument cannot be checked against without false errors. */
7698
8136
  boundParams(f) {
7699
8137
  if (!f.typeParams?.length) return f.params.map((p) => p.type);
8138
+ const known = this.boundParamsCache.get(f);
8139
+ if (known) return known;
7700
8140
  const bounds = new Map(f.typeParams.map((name) => [name, anyType]));
7701
8141
  const seen = /* @__PURE__ */ new WeakSet();
7702
8142
  const walk = (value) => {
@@ -7714,8 +8154,11 @@ var TypeAnalyzer = class {
7714
8154
  for (const child of Object.values(value)) walk(child);
7715
8155
  };
7716
8156
  for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
7717
- return f.params.map((p) => this.reduceType(substitute(p.type, bounds)));
8157
+ const bound = f.params.map((p) => this.reduceType(substitute(p.type, bounds)));
8158
+ this.boundParamsCache.set(f, bound);
8159
+ return bound;
7718
8160
  }
8161
+ boundParamsCache = /* @__PURE__ */ new WeakMap();
7719
8162
  /** Record what each written argument is expected to be — see
7720
8163
  * `TypeAnalysis.expectedTypeOf`. */
7721
8164
  recordExpected(written, fns, selfOf, argsOf = () => []) {
@@ -7767,7 +8210,12 @@ var TypeAnalyzer = class {
7767
8210
  const arg = argTypes[i];
7768
8211
  const declared = declaredAt(i);
7769
8212
  if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
7770
- const expected = this.reduceType(substitute(declared, subst));
8213
+ let expected = this.reduceType(substitute(declared, subst));
8214
+ if (f.restIsWhole && i >= f.params.length) {
8215
+ const whole = this.expand(expected);
8216
+ expected = whole.kind === "tuple" ? whole.elements[i - f.params.length] ?? whole.rest : whole.kind === "array" ? whole.element : void 0;
8217
+ if (!expected) continue;
8218
+ }
7771
8219
  if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
7772
8220
  if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
7773
8221
  this.diagnostics.push({
@@ -7895,13 +8343,13 @@ var TypeAnalyzer = class {
7895
8343
  type: this.paramType(p, /* @__PURE__ */ new Map()),
7896
8344
  optional: p.optional || p.default !== void 0
7897
8345
  }));
7898
- return fn(
8346
+ return this.withRest(fn(
7899
8347
  params,
7900
8348
  sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
7901
- this.varargElement(sig),
8349
+ void 0,
7902
8350
  names,
7903
8351
  this.resolvePredicate(sig.predicate, params)
7904
- );
8352
+ ), sig.params);
7905
8353
  }));
7906
8354
  }
7907
8355
  /** `...rest: T[]` holds every argument from its position on, so its type
@@ -7909,7 +8357,7 @@ var TypeAnalyzer = class {
7909
8357
  checkRestType(p, node) {
7910
8358
  if (!this.emitDiagnostics || !p.typeAnnotation) return;
7911
8359
  const declared = this.resolveType(p.typeAnnotation);
7912
- if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
8360
+ if (declared.kind === "array" || declared.kind === "tuple" || declared.kind === "any" || declared.kind === "typeParam") return;
7913
8361
  this.diagnostics.push({
7914
8362
  node,
7915
8363
  message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
@@ -7965,13 +8413,13 @@ var TypeAnalyzer = class {
7965
8413
  return collected.length ? union(collected) : this.inferReturnType(func.body, bodyEnv);
7966
8414
  }));
7967
8415
  }
7968
- return fn(
8416
+ return this.withRest(fn(
7969
8417
  params,
7970
8418
  returns,
7971
- this.varargElement(func),
8419
+ void 0,
7972
8420
  names,
7973
8421
  this.resolvePredicate(func.predicate, params)
7974
- );
8422
+ ), func.params);
7975
8423
  });
7976
8424
  }
7977
8425
  /** Where the return types of the function being walked are collected, so
@@ -8013,17 +8461,16 @@ var TypeAnalyzer = class {
8013
8461
  this.preVisitDepth--;
8014
8462
  }
8015
8463
  }
8016
- /** The `[key, value]` pairs iterating a record yields, one per property —
8017
- * for `pairs(t)`, `next, t` and `for k, v in t` over an object type with
8018
- * no indexer. `undefined` for anything else (an array, a dictionary, an
8019
- * iterator function), whose keys have no names to list. */
8464
+ /** The `[key, value]` items `pairs(record)` hands out, one per property.
8465
+ * `undefined` for anything else (an array, a dictionary, an iterator
8466
+ * function), whose keys have no names to list. */
8020
8467
  iterationRows(iterNode, iterType) {
8021
8468
  let source;
8022
8469
  if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8023
- if (iterNode.callee.name !== "pairs" && iterNode.callee.name !== "next") return void 0;
8470
+ if (iterNode.callee.name !== "pairs") return void 0;
8024
8471
  source = this.typeOf.get(iterNode.arguments[0]);
8025
8472
  } else {
8026
- source = iterType;
8473
+ return void 0;
8027
8474
  }
8028
8475
  const t = source && this.expand(source);
8029
8476
  if (!t || t.kind !== "object" || t.class || t.indexer || !t.properties.size) return void 0;
@@ -8164,54 +8611,76 @@ var TypeAnalyzer = class {
8164
8611
  if (ids.length < 2) return;
8165
8612
  this.correlateBindings(env, ids, objects.map((member) => names.map((name) => this.propertyType(member, name))));
8166
8613
  }
8167
- /** `(keyType, valueType, iteratesValues)` yielded by a generic-for
8168
- * iterator. Handles `ipairs`/`pairs`/`next(t)` and Luau generalized
8169
- * iteration (`for … in t`). `varCount` is how many loop variables were
8170
- * written. `iteratesValues` reports the one case where the first variable
8171
- * is bound to the value rather than the key, which is what lowering has
8172
- * to know to emit the loop; see `TypeAnalysis.iteratesValues`. */
8173
- iterationTypes(iterNode, iterType, varCount) {
8174
- if (iterNode?.type === "CallExpression" && iterNode.callee.type === "Identifier" && iterNode.arguments[0]) {
8175
- const name = iterNode.callee.name;
8176
- const src = this.expand(this.typeOf.get(iterNode.arguments[0]) ?? unknownType);
8177
- if (name === "ipairs") return [numberType, this.elementType(src, 0), false];
8178
- if (name === "pairs" || name === "next") {
8614
+ /** `[step, state, first]` — what a call such as `pairs(t)` answers — as
8615
+ * the `step` function, or `undefined` for an array that is just an
8616
+ * array. Three places exactly, the first a function. */
8617
+ iterationTriple(t) {
8618
+ if (t.kind !== "tuple" || t.rest || t.elements.length !== 3) return void 0;
8619
+ const step = this.expand(t.elements[0]);
8620
+ return step.kind === "function" ? step : void 0;
8621
+ }
8622
+ /** What one `for (const item in source)` hands over each time, and how it
8623
+ * walks the source. `pairs`/`ipairs` are read from the table they are
8624
+ * given, so a record's keys stay the literals they are. */
8625
+ iterationOf(source, sourceType, report = true) {
8626
+ if (source.type === "CallExpression" && source.callee.type === "Identifier" && source.arguments[0]) {
8627
+ const name = source.callee.name;
8628
+ const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
8629
+ const iteration = { walks: "iteration", viaIter: false };
8630
+ if (name === "ipairs") return [tuple([numberType, this.elementType(src, 0)]), iteration];
8631
+ if (name === "pairs") {
8179
8632
  if (src.kind === "object") {
8180
- return [
8633
+ return [tuple([
8181
8634
  src.indexer?.key ?? stringType,
8182
- src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type)),
8183
- false
8184
- ];
8635
+ src.indexer?.value ?? union([...src.properties.values()].map((p) => p.type))
8636
+ ]), iteration];
8185
8637
  }
8186
- if (src.kind === "array") return [numberType, src.element, false];
8638
+ if (src.kind === "array") return [tuple([numberType, src.element]), iteration];
8187
8639
  }
8188
8640
  }
8189
- const iterator = this.expand(iterType);
8190
- if (iterator.kind === "function") {
8191
- const returns = this.expand(iterator.returns);
8192
- const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
8193
- const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
8194
- return [at(0), at(1), false];
8641
+ const handsOut = (t2, viaIter) => {
8642
+ const x = this.expand(t2);
8643
+ if (x.kind === "function") return [withoutNil(this.expand(x.returns)), { walks: "function", viaIter }];
8644
+ const step = this.iterationTriple(x);
8645
+ if (step) return [withoutNil(this.expand(step.returns)), { walks: "iteration", viaIter }];
8646
+ return void 0;
8647
+ };
8648
+ const direct = handsOut(sourceType, false);
8649
+ if (direct) return direct;
8650
+ const values = { walks: "values", viaIter: false };
8651
+ const t = this.expand(sourceType);
8652
+ if (t.kind === "any") return [anyType, values];
8653
+ const iter = t.kind === "object" ? t.properties.get("__iter") : void 0;
8654
+ if (iter) {
8655
+ const made = this.overloadsOf(iter.type)[0]?.returns ?? unknownType;
8656
+ return handsOut(made, true) ?? [unknownType, { walks: "values", viaIter: true }];
8657
+ }
8658
+ if (t.kind === "array") return [t.element, values];
8659
+ if (t.kind === "tuple") return [union([...t.elements, ...t.rest ? [t.rest] : []]), values];
8660
+ if (t.kind === "object") {
8661
+ return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
8195
8662
  }
8196
- const t = this.expand(iterType);
8197
- if (t.kind === "array") {
8198
- return varCount >= 2 ? [numberType, t.element, false] : [t.element, unknownType, true];
8663
+ if (t.kind === "union") {
8664
+ const each = t.types.map((member) => this.iterationOf(source, member, false));
8665
+ const [first] = each;
8666
+ if (first && each.every(([, form]) => form.walks === first[1].walks && form.viaIter === first[1].viaIter)) {
8667
+ return [union(each.map(([item]) => item)), first[1]];
8668
+ }
8199
8669
  }
8200
- if (t.kind === "object") {
8201
- const k = t.indexer?.key ?? stringType;
8202
- const v = t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type));
8203
- return varCount >= 2 ? [k, v, false] : [v, unknownType, true];
8670
+ if (t.kind !== "never" && report && this.emitDiagnostics) {
8671
+ this.diagnostics.push({
8672
+ node: source,
8673
+ message: `Cannot loop over '${formatType(sourceType)}': a loop walks an array, a table, an iterator function or an iteration such as 'pairs(t)'`
8674
+ });
8204
8675
  }
8205
- return [unknownType, unknownType, false];
8676
+ return [unknownType, values];
8206
8677
  }
8207
8678
  inferReturnType(body, env) {
8208
8679
  const returns = [];
8209
8680
  const walk = (block) => {
8210
8681
  for (const s of block.statements) {
8211
8682
  if (s.type === "ReturnStatement") {
8212
- if (s.arguments.length === 0) returns.push(nilType);
8213
- else if (s.arguments.length === 1) returns.push(this.infer(s.arguments[0], env));
8214
- else returns.push(tuple(s.arguments.map((a) => this.infer(a, env)), true));
8683
+ returns.push(s.argument ? this.infer(s.argument, env) : nilType);
8215
8684
  } else if (s.type === "IfStatement") {
8216
8685
  for (const c of s.clauses) walk(c.body);
8217
8686
  if (s.alternate) walk(s.alternate);
@@ -8233,8 +8702,9 @@ var TypeAnalyzer = class {
8233
8702
  * `infer` would have widened it to an array type). */
8234
8703
  fitsAnnotation(init, declared, inferred, env) {
8235
8704
  if (declared.kind === "tuple" && init.type === "ArrayExpression" && !init.elements.some((e) => e.type === "SpreadElement")) {
8236
- if (init.elements.length !== declared.elements.length) return false;
8237
- return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i]));
8705
+ const fits = declared.rest ? init.elements.length >= declared.elements.length : init.elements.length === declared.elements.length;
8706
+ if (!fits) return false;
8707
+ return init.elements.every((el, i) => isAssignable(widen(this.infer(el, env)), declared.elements[i] ?? declared.rest));
8238
8708
  }
8239
8709
  if (isAssignable(inferred, declared) || isAssignable(widen(inferred), declared)) return true;
8240
8710
  if (init.type === "TableExpression") return isAssignable(this.inferObject(init, env, true), declared);
@@ -8291,7 +8761,7 @@ var TypeAnalyzer = class {
8291
8761
  target.elements.forEach((el, i) => {
8292
8762
  if (el) this.reassignPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env);
8293
8763
  });
8294
- if (target.rest) this.reassignPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env);
8764
+ if (target.rest) this.reassignPattern(target.rest, this.restAfter(valueType, target.elements.length), env);
8295
8765
  return;
8296
8766
  }
8297
8767
  }
@@ -8327,7 +8797,7 @@ var TypeAnalyzer = class {
8327
8797
  if (!el) return;
8328
8798
  this.bindPattern(el.value, this.withDefault(this.elementType(valueType, i), el.default, env), env, mode);
8329
8799
  });
8330
- if (target.rest) this.bindPattern(target.rest, arrayOf(this.elementType(valueType, 0)), env, mode);
8800
+ if (target.rest) this.bindPattern(target.rest, this.restAfter(valueType, target.elements.length), env, mode);
8331
8801
  return;
8332
8802
  }
8333
8803
  }
@@ -8484,7 +8954,7 @@ var TypeAnalyzer = class {
8484
8954
  * not one of them is a mistake worth reporting, rather than the nil Lua
8485
8955
  * would hand back. */
8486
8956
  checkStringMember(node, object, key) {
8487
- if (!this.emitDiagnostics) return;
8957
+ if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
8488
8958
  const parts = this.stringParts(object);
8489
8959
  if (!parts) return;
8490
8960
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
@@ -8496,6 +8966,127 @@ var TypeAnalyzer = class {
8496
8966
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
8497
8967
  });
8498
8968
  }
8969
+ /** `game.Anything` on a `{ GetService: ... }`, or `a.a` on a number: a
8970
+ * value whose members are all known, and none of them is this one. Only
8971
+ * a type that is known through and through is reported — an indexer, an
8972
+ * `any`, or a part still waiting on a type parameter could each supply
8973
+ * the name. An empty `{}` is a table still being filled in, so it is left
8974
+ * alone; an array's and a string's members come from a library, so
8975
+ * without one they say nothing. A string on its own is `checkStringMember`'s. */
8976
+ checkMissingMember(node, object, name) {
8977
+ if (!this.emitDiagnostics || this.missingMemberReported.has(node)) return;
8978
+ if (!this.memberMissing(object, name)) return;
8979
+ this.missingMemberReported.add(node);
8980
+ this.diagnostics.push({
8981
+ node,
8982
+ message: `Property '${name}' does not exist on type '${briefType(withoutNil(this.expand(object)))}'`
8983
+ });
8984
+ }
8985
+ /** Is `name` certainly not a member of `object`? See `checkMissingMember`. */
8986
+ memberMissing(object, name) {
8987
+ if (this.stringParts(object)) return false;
8988
+ const raw = this.expand(object);
8989
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
8990
+ let missing = false;
8991
+ for (const part of parts) {
8992
+ switch (part.kind) {
8993
+ case "primitive":
8994
+ if (part.name === "nil") continue;
8995
+ if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
8996
+ if (part.name === "string" && this.builtInMethod(part, name)) continue;
8997
+ break;
8998
+ case "literal":
8999
+ case "templateLiteral":
9000
+ if (part.kind === "templateLiteral" || part.base === "string") {
9001
+ if (!this.aliasDefs.has("StringMethods")) return false;
9002
+ }
9003
+ if (this.builtInMethod(part, name)) continue;
9004
+ break;
9005
+ case "array":
9006
+ case "tuple":
9007
+ if (!this.aliasDefs.has("ArrayMethods")) return false;
9008
+ if (this.builtInMethod(part, name)) continue;
9009
+ break;
9010
+ case "object":
9011
+ if (part.properties.has(name)) continue;
9012
+ if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
9013
+ break;
9014
+ default:
9015
+ return false;
9016
+ }
9017
+ missing = true;
9018
+ }
9019
+ return missing;
9020
+ }
9021
+ /** `t[k]` read with a `k` that cannot be a key of `t`: an `unknown`, a
9022
+ * type none of `t`'s keys has (a number into a record, a string into an
9023
+ * array or a class), or a name `t` does not have — which is `t.name`'s
9024
+ * error. An `any` on either side says nothing, and neither does a type
9025
+ * parameter still to be decided. A plain `string` into a record is
9026
+ * allowed: it reads as one of the values or nil, and the nil has to be
9027
+ * dealt with. An empty `{}` is a table still being filled in, and is
9028
+ * left alone. Reports, and answers whether it did. */
9029
+ checkIndexKey(node, object, key) {
9030
+ if (!this.emitDiagnostics || this.badIndexReported.has(node)) return false;
9031
+ const index = this.expand(key);
9032
+ if (index.kind === "any" || index.kind === "never" || containsTypeParam(index)) return false;
9033
+ const keys = index.kind === "union" ? index.types.map((k) => this.expand(k)) : [index];
9034
+ const nameOf = (k) => k.kind === "literal" && typeof k.value === "string" ? k.value : void 0;
9035
+ const shown = briefType(withoutNil(this.expand(object)));
9036
+ const names = keys.map(nameOf);
9037
+ const others = keys.filter((_, i) => names[i] === void 0);
9038
+ let message;
9039
+ const listOnly = (t) => {
9040
+ const x = this.expand(t);
9041
+ if (x.kind === "union") return x.types.every(listOnly);
9042
+ return x.kind === "array" || x.kind === "tuple";
9043
+ };
9044
+ if (names.some((name) => name !== void 0) && listOnly(withoutNil(this.expand(object)))) {
9045
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
9046
+ } else if (others.length && this.cannotIndex(object, others)) {
9047
+ message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
9048
+ } else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
9049
+ message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
9050
+ }
9051
+ if (!message) return false;
9052
+ this.badIndexReported.add(node);
9053
+ this.diagnostics.push({ node, message });
9054
+ return true;
9055
+ }
9056
+ /** Can one of `keys` (string literals aside) not index some part of
9057
+ * `object`? `false` as soon as a part cannot be judged. */
9058
+ cannotIndex(object, keys) {
9059
+ const raw = this.expand(object);
9060
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.deferredAccess(this.expand(m)));
9061
+ const isNumber = (k) => k.kind === "primitive" && k.name === "number" || k.kind === "literal" && typeof k.value === "number";
9062
+ const isString = (k) => k.kind === "primitive" && k.name === "string" || k.kind === "templateLiteral";
9063
+ let bad = false;
9064
+ for (const part of parts) {
9065
+ switch (part.kind) {
9066
+ case "primitive":
9067
+ if (part.name === "nil") continue;
9068
+ return false;
9069
+ case "array":
9070
+ case "tuple":
9071
+ if (!keys.every(isNumber)) bad = true;
9072
+ continue;
9073
+ case "object": {
9074
+ if (!part.indexer && part.properties.size === 0 && !part.class) return false;
9075
+ const fits = (k) => part.indexer !== void 0 && isAssignable(k, part.indexer.key) || !part.indexer && !part.class && isString(k) || k.kind === "literal" && part.properties.has(String(k.value));
9076
+ if (!keys.every(fits)) bad = true;
9077
+ continue;
9078
+ }
9079
+ default:
9080
+ return false;
9081
+ }
9082
+ }
9083
+ return bad;
9084
+ }
9085
+ /** Indexes already reported: a loop body is visited more than once. */
9086
+ badIndexReported = /* @__PURE__ */ new WeakSet();
9087
+ /** Members already reported as missing: a loop body, or an assignment
9088
+ * target, is visited more than once. */
9089
+ missingMemberReported = /* @__PURE__ */ new WeakSet();
8499
9090
  /** A `private` member read from outside the class that declared it —
8500
9091
  * a subclass counts as outside, as in TypeScript. */
8501
9092
  checkPrivateMember(node, object, name) {
@@ -8506,18 +9097,46 @@ var TypeAnalyzer = class {
8506
9097
  if (t.kind !== "object") continue;
8507
9098
  const access = t.properties.get(name)?.private;
8508
9099
  if (!access || this.insideClass(access.owner)) continue;
9100
+ if (access.protected && this.insideSubclassOf(access.owner)) continue;
8509
9101
  this.diagnostics.push({
8510
9102
  node,
8511
- message: `Property '${name}' is private and only accessible within class '${access.className}'`
9103
+ message: access.protected ? `Property '${name}' is protected and only accessible within class '${access.className}' and the classes extending it` : `Property '${name}' is private and only accessible within class '${access.className}'`
8512
9104
  });
8513
9105
  return;
8514
9106
  }
8515
9107
  }
9108
+ /** `Shape.new` on an abstract class, and `super.area` on an abstract
9109
+ * method: each reaches something with nothing behind it. */
9110
+ checkAbstractAccess(expr, object) {
9111
+ if (!this.emitDiagnostics) return;
9112
+ const t = this.expand(object);
9113
+ if (t.kind !== "object") return;
9114
+ const name = expr.property.name;
9115
+ const property = t.properties.get(name);
9116
+ if (!property?.abstract) return;
9117
+ if (name === "new" && expr.object.type !== "SuperExpression") {
9118
+ const label = expressionLabel(expr.object) ?? formatType(t);
9119
+ this.diagnostics.push({
9120
+ node: expr,
9121
+ message: `'${label}' is an abstract class: build one of the classes extending it instead`
9122
+ });
9123
+ } else if (expr.object.type === "SuperExpression") {
9124
+ this.diagnostics.push({
9125
+ node: expr.property,
9126
+ message: `'${name}' is abstract in the class this one extends: there is nothing to call`
9127
+ });
9128
+ }
9129
+ }
8516
9130
  /** Is the code being checked written inside `owner`'s body — its own
8517
9131
  * methods, or a class or function nested in them? */
8518
9132
  insideClass(owner) {
8519
9133
  return this.enclosingClasses.includes(owner);
8520
9134
  }
9135
+ /** Is the code being checked inside a class that extends `owner`? */
9136
+ insideSubclassOf(owner) {
9137
+ const identity = this.classIdentity(owner);
9138
+ return this.enclosingClasses.some((cls) => this.instanceType(cls).class?.ancestors.includes(identity));
9139
+ }
8521
9140
  propertyType(raw, name) {
8522
9141
  const t = this.deferredAccess(this.expand(raw));
8523
9142
  if (t.kind === "object") {
@@ -8572,8 +9191,12 @@ var TypeAnalyzer = class {
8572
9191
  }
8573
9192
  if (containsTypeParam(index)) return this.reduceType({ kind: "indexedAccess", objectType: t, indexType: index });
8574
9193
  if (t.indexer) return t.indexer.value;
9194
+ const anyString = index.kind === "primitive" && index.name === "string" || index.kind === "templateLiteral";
9195
+ if (anyString && !t.class && t.properties.size > 0) {
9196
+ return union([...[...t.properties.values()].map((p) => p.type), nilType]);
9197
+ }
8575
9198
  }
8576
- return unknownType;
9199
+ return index.kind === "any" ? anyType : unknownType;
8577
9200
  }
8578
9201
  /** What a deferred `T[K]` can be: every property its index could name.
8579
9202
  * Reading a member of one, or calling it, sees that. */
@@ -8583,10 +9206,21 @@ var TypeAnalyzer = class {
8583
9206
  if (containsTypeParam(index)) return unknownType;
8584
9207
  return this.accessType(t.objectType, index);
8585
9208
  }
9209
+ /** `[a, ...rest]`: what `rest` holds — an array of whatever comes after
9210
+ * the first `from` places. */
9211
+ restAfter(raw, from) {
9212
+ const t = this.expand(raw);
9213
+ if (t.kind === "tuple") {
9214
+ const left = [...t.elements.slice(from), ...t.rest ? [t.rest] : []];
9215
+ return arrayOf(left.length ? union(left) : neverType);
9216
+ }
9217
+ if (t.kind === "union") return union(t.types.map((m) => this.restAfter(m, from)));
9218
+ return arrayOf(this.elementType(t, from));
9219
+ }
8586
9220
  elementType(raw, index) {
8587
9221
  const t = this.expand(raw);
8588
9222
  if (t.kind === "array") return t.element;
8589
- if (t.kind === "tuple") return t.elements[index] ?? unknownType;
9223
+ if (t.kind === "tuple") return t.elements[index] ?? t.rest ?? unknownType;
8590
9224
  if (t.kind === "union") return union(t.types.map((m) => this.elementType(m, index)));
8591
9225
  if (t.kind === "difference") return this.elementType(t.base, index);
8592
9226
  if (t.kind === "typeParam" && t.constraint) return this.elementType(t.constraint, index);
@@ -8616,8 +9250,6 @@ var TypeAnalyzer = class {
8616
9250
  return stringType;
8617
9251
  }
8618
9252
  // `...` holds what the function declared it takes.
8619
- case "VarargExpression":
8620
- return this.varargs[this.varargs.length - 1] ?? anyType;
8621
9253
  // `f(a, ...rest)` — every value the array holds, one after
8622
9254
  // another. Each of them is an element, so that is what the
8623
9255
  // parameters it fills are checked against.
@@ -8650,10 +9282,8 @@ var TypeAnalyzer = class {
8650
9282
  this.checkParamOrder(expr.func.params, expr);
8651
9283
  this.visitFunctionBody(expr.func, env);
8652
9284
  return this.inferFunctionBody(expr.func, env);
8653
- case "ParenthesizedExpression": {
8654
- const inner = this.infer(expr.expression, env);
8655
- return inner.kind === "tuple" && inner.isPack && producesMultipleValues(expr.expression) ? inner.elements[0] ?? nilType : inner;
8656
- }
9285
+ case "ParenthesizedExpression":
9286
+ return this.infer(expr.expression, env);
8657
9287
  case "TypeAssertionExpression": {
8658
9288
  this.infer(expr.expression, env);
8659
9289
  return this.resolveType(expr.typeAnnotation);
@@ -8685,10 +9315,18 @@ var TypeAnalyzer = class {
8685
9315
  switch (expr.operator) {
8686
9316
  case "not":
8687
9317
  return booleanType;
8688
- case "-":
8689
- return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
8690
- case "#":
8691
- return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
9318
+ case "-": {
9319
+ const answered = this.operatorResult(expr, "-", arg, void 0);
9320
+ if (answered) return answered;
9321
+ this.checkOperands(expr, "-", arg);
9322
+ return this.arithmeticOn([arg]);
9323
+ }
9324
+ case "#": {
9325
+ const answered = this.operatorResult(expr, "#", arg, void 0);
9326
+ if (answered) return answered;
9327
+ this.checkOperands(expr, "#", arg);
9328
+ return numberType;
9329
+ }
8692
9330
  }
8693
9331
  return arg;
8694
9332
  }
@@ -8704,7 +9342,11 @@ var TypeAnalyzer = class {
8704
9342
  const left = this.infer(expr.left, env);
8705
9343
  const { whenFalse } = this.narrowFromCondition(expr.left, env);
8706
9344
  const right = this.infer(expr.right, whenFalse);
8707
- return union([narrowTruthy(left), right]);
9345
+ const fallback = unwrapParens(expr.right);
9346
+ const empty = fallback.type === "TableExpression" && fallback.fields.length === 0 || fallback.type === "ArrayExpression" && fallback.elements.length === 0;
9347
+ const truthy = narrowTruthy(left);
9348
+ if (empty && truthy.kind !== "never" && truthy.kind !== "any" && isAssignable(right, truthy)) return truthy;
9349
+ return union([truthy, right]);
8708
9350
  }
8709
9351
  const l = this.infer(expr.left, env);
8710
9352
  const r = this.infer(expr.right, env);
@@ -8713,14 +9355,21 @@ var TypeAnalyzer = class {
8713
9355
  if (unwrapParens(expr.left).type === "StringLiteral") this.expectedTypeOf.set(unwrapParens(expr.left), r);
8714
9356
  }
8715
9357
  switch (op) {
8716
- case "..":
8717
- return this.operatorResult(expr, op, l, r) ?? stringType;
8718
- case "==":
8719
- case "~=":
9358
+ case "..": {
9359
+ const answered = this.operatorResult(expr, op, l, r);
9360
+ if (answered) return answered;
9361
+ this.checkOperands(expr, op, l, r);
9362
+ return stringType;
9363
+ }
8720
9364
  case "<":
8721
9365
  case ">":
8722
9366
  case "<=":
8723
9367
  case ">=":
9368
+ if (!this.checkComparison(expr, op, l, r)) this.checkOperands(expr, op, l, r);
9369
+ return booleanType;
9370
+ case "==":
9371
+ case "~=":
9372
+ this.checkOverlap(expr, op, l, r);
8724
9373
  return booleanType;
8725
9374
  case "+":
8726
9375
  case "-":
@@ -8728,17 +9377,23 @@ var TypeAnalyzer = class {
8728
9377
  case "/":
8729
9378
  case "//":
8730
9379
  case "%":
8731
- case "^":
8732
- return this.operatorResult(expr, op, l, r) ?? numberType;
9380
+ case "^": {
9381
+ const answered = this.operatorResult(expr, op, l, r);
9382
+ if (answered) return answered;
9383
+ this.checkOperands(expr, op, l, r);
9384
+ return this.arithmeticOn([l, r]);
9385
+ }
8733
9386
  }
8734
9387
  return union([l, r]);
8735
9388
  }
8736
9389
  case "MemberExpression": {
8737
9390
  const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
8738
9391
  this.checkPrivateMember(expr.property, obj, expr.property.name);
9392
+ this.checkAbstractAccess(expr, obj);
8739
9393
  const key = this.refKeyOf(expr);
8740
9394
  const narrowed = key === void 0 ? void 0 : env.get(key);
8741
9395
  this.checkStringMember(expr, obj, literal(expr.property.name));
9396
+ if (narrowed === void 0) this.checkMissingMember(expr.property, obj, expr.property.name);
8742
9397
  return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
8743
9398
  }
8744
9399
  case "IndexExpression": {
@@ -8747,6 +9402,7 @@ var TypeAnalyzer = class {
8747
9402
  const key = this.refKeyOf(expr);
8748
9403
  const narrowed = key === void 0 ? void 0 : env.get(key);
8749
9404
  this.checkStringMember(expr, obj, this.expand(idx));
9405
+ if (this.checkIndexKey(expr, obj, idx)) return this.chainResult(expr, anyType, shortCircuits);
8750
9406
  const member = this.stringMember(obj, this.expand(idx));
8751
9407
  if (member) return this.chainResult(expr, member, shortCircuits);
8752
9408
  return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
@@ -8756,8 +9412,6 @@ var TypeAnalyzer = class {
8756
9412
  const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
8757
9413
  return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
8758
9414
  }
8759
- case "NewExpression":
8760
- return this.inferNew(expr, env);
8761
9415
  case "ClassExpression":
8762
9416
  return this.visitClass(expr, env);
8763
9417
  case "SuperExpression": {
@@ -8794,18 +9448,14 @@ var TypeAnalyzer = class {
8794
9448
  /** `new Name(args)` is `Name.new(args)` — the same function, and the
8795
9449
  * same check. Saying so here rather than rewriting the tree keeps the
8796
9450
  * error messages pointing at what was written. */
8797
- inferNew(expr, env) {
8798
- const calleeType = this.infer(expr.callee, env);
8799
- const constructor = this.propertyType(calleeType, "new");
8800
- if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
8801
- if (this.emitDiagnostics) {
8802
- const label = expressionLabel(expr.callee) ?? formatType(calleeType);
8803
- this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
8804
- }
8805
- for (const argument of expr.arguments) this.infer(argument, env);
8806
- return anyType;
8807
- }
8808
- return this.inferCall(expr, constructor, env);
9451
+ /** `__call` of a value with one, as what calling the value calls. */
9452
+ callMetamethod(raw) {
9453
+ const t = this.expand(raw);
9454
+ if (t.kind !== "object" || this.overloadsOf(t).length) return void 0;
9455
+ const method = t.properties.get("__call");
9456
+ if (!method) return void 0;
9457
+ const bound = this.overloadsOf(method.type).map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f);
9458
+ return bound.length ? intersection(bound) : void 0;
8809
9459
  }
8810
9460
  /** `super(...)` — the base constructor, run on the instance being built. */
8811
9461
  inferSuperCall(expr, env) {
@@ -8829,8 +9479,9 @@ var TypeAnalyzer = class {
8829
9479
  this.inferCall(expr, callable, env);
8830
9480
  return nilType;
8831
9481
  }
8832
- inferCall(expr, callee, env) {
9482
+ inferCall(expr, called, env) {
8833
9483
  this.checkAmbiguousCall(expr);
9484
+ const callee = this.callMetamethod(called) ?? called;
8834
9485
  const united = this.unionSignatures(callee);
8835
9486
  const fns = united ?? this.overloadsOf(callee);
8836
9487
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -8853,8 +9504,31 @@ var TypeAnalyzer = class {
8853
9504
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
8854
9505
  return union(fns.map((f) => this.callReturn(f, argTypes, explicit)));
8855
9506
  }
9507
+ this.checkCallable(expr.callee, callee);
8856
9508
  return callee.kind === "any" ? anyType : unknownType;
8857
9509
  }
9510
+ /** `a()` where `a` is `1`: a value whose type is known and has no call
9511
+ * signature. A union is reported when any member cannot be called, but
9512
+ * only when every member is decided — `unknown`, `any` and anything still
9513
+ * generic say too little. */
9514
+ checkCallable(node, callee) {
9515
+ if (!this.emitDiagnostics || this.notCallableReported.has(node)) return;
9516
+ const raw = this.expand(callee);
9517
+ const parts = (raw.kind === "union" ? raw.types : [raw]).map((m) => this.expand(m));
9518
+ const notCallable = (m) => m.kind === "primitive" && m.name !== "nil" || m.kind === "literal" || m.kind === "templateLiteral" || m.kind === "array" || m.kind === "tuple" || m.kind === "object";
9519
+ const callable = (m) => this.overloadsOf(m).length > 0;
9520
+ const concrete = parts.filter((m) => !(m.kind === "primitive" && m.name === "nil"));
9521
+ if (!concrete.some(notCallable) || !concrete.every((m) => notCallable(m) || callable(m))) return;
9522
+ this.notCallableReported.add(node);
9523
+ const label = node.type === "Identifier" ? node.name : expressionLabel(node);
9524
+ this.diagnostics.push({
9525
+ node,
9526
+ message: `This expression is not callable: ${label === void 0 ? "" : `'${label}' `}is of type '${briefType(withoutNil(raw))}'`
9527
+ });
9528
+ }
9529
+ /** Callees already reported as not callable: a loop body is visited more
9530
+ * than once. */
9531
+ notCallableReported = /* @__PURE__ */ new WeakSet();
8858
9532
  /** A `(` on a line of its own continues the statement above it:
8859
9533
  *
8860
9534
  * const value = map[key]
@@ -8872,6 +9546,8 @@ var TypeAnalyzer = class {
8872
9546
  }
8873
9547
  inferMethodCall(expr, objType, env) {
8874
9548
  this.checkPrivateMember(expr.method, objType, expr.method.name);
9549
+ this.checkStringMember(expr.method, objType, literal(expr.method.name));
9550
+ this.checkMissingMember(expr.method, objType, expr.method.name);
8875
9551
  const method = this.propertyType(objType, expr.method.name);
8876
9552
  const united = this.unionSignatures(method);
8877
9553
  const fns = united ?? this.overloadsOf(method);
@@ -8905,6 +9581,7 @@ var TypeAnalyzer = class {
8905
9581
  if (arityFits && !picked) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
8906
9582
  return union(fns.map((f) => this.callReturn(f, withSelf(f), explicit)));
8907
9583
  }
9584
+ this.checkCallable(expr.method, method);
8908
9585
  return objType.kind === "any" ? anyType : unknownType;
8909
9586
  }
8910
9587
  // --------------------------------------------------------
@@ -8953,6 +9630,14 @@ var TypeAnalyzer = class {
8953
9630
  name = target.property.name;
8954
9631
  object = target.object;
8955
9632
  } else if (target.type === "IndexExpression") {
9633
+ const list = this.expand(this.infer(target.object, env));
9634
+ if ((list.kind === "array" || list.kind === "tuple") && list.readonly) {
9635
+ this.diagnostics.push({
9636
+ node: target,
9637
+ message: `Cannot assign to an element of '${formatType(list)}': it is read-only`
9638
+ });
9639
+ return;
9640
+ }
8956
9641
  const index = this.expand(this.infer(target.index, env));
8957
9642
  if (index.kind !== "literal" || typeof index.value !== "string") return;
8958
9643
  name = index.value;
@@ -8965,6 +9650,8 @@ var TypeAnalyzer = class {
8965
9650
  if (id !== void 0 && this.scopes.bindings.get(id)?.declaredBy === "namespace") return;
8966
9651
  }
8967
9652
  if (!this.isReadonlyProperty(this.expand(this.infer(object, env)), name)) return;
9653
+ const constructing = this.constructing;
9654
+ if (constructing && object.type === "Identifier" && object.name === "this" && constructing.members.some((m) => m.type === "ClassField" && !m.isStatic && m.isReadonly && m.name.name === name)) return;
8968
9655
  this.diagnostics.push({
8969
9656
  node: target,
8970
9657
  message: `Cannot assign to '${name}' because it is a read-only property`
@@ -9024,6 +9711,8 @@ var TypeAnalyzer = class {
9024
9711
  inferArray(expr, env, asConst) {
9025
9712
  const contextual = this.contextualArrays.get(expr);
9026
9713
  if (contextual && !asConst) return contextual;
9714
+ const wantedTuple = this.tupleArrays.get(expr);
9715
+ if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9027
9716
  const elems = [];
9028
9717
  let hadSpread = false;
9029
9718
  for (const el of expr.elements) {
@@ -9043,6 +9732,28 @@ var TypeAnalyzer = class {
9043
9732
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
9044
9733
  })) : unknownType);
9045
9734
  }
9735
+ /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
9736
+ * tuple's, and what comes after them — more elements, or a spread — is
9737
+ * its rest. */
9738
+ inferTupleLiteral(expr, env, wanted) {
9739
+ const fixed = [];
9740
+ const tail = [];
9741
+ let spread = false;
9742
+ expr.elements.forEach((element, i) => {
9743
+ if (element.type === "SpreadElement") {
9744
+ spread = true;
9745
+ const held = this.expand(this.infer(element.argument, env));
9746
+ if (held.kind === "array") tail.push(held.element);
9747
+ else if (held.kind === "tuple") tail.push(...tupleMembers(held));
9748
+ else tail.push(unknownType);
9749
+ return;
9750
+ }
9751
+ const t = this.widenUnlessAsked(this.infer(element, env), element);
9752
+ if (!spread && i < wanted.elements.length) fixed.push(t);
9753
+ else tail.push(t);
9754
+ });
9755
+ return tuple(fixed, tail.length ? union(tail) : void 0);
9756
+ }
9046
9757
  /** A literal written inside a fresh table or array widens — `{ n = 1 }` is
9047
9758
  * `{ n: number }` — unless the surroundings said a literal belongs there.
9048
9759
  * `request({ Method: "GET" })` keeps `"GET"` when `Method` is a union of
@@ -9099,9 +9810,27 @@ var TypeAnalyzer = class {
9099
9810
  if (s.indexer) indexer = mergeIndexer(indexer, s.indexer);
9100
9811
  }
9101
9812
  }
9813
+ this.checkDuplicateKey(expr, field, entries);
9102
9814
  }
9103
9815
  return objectType(entries, indexer, asConst || void 0);
9104
9816
  }
9817
+ /** `{ a: 1, a: 2 }` — the second wins and the first was written for
9818
+ * nothing, which is a typo far more often than a decision. A spread is
9819
+ * not one: overriding what it brought is exactly what it is for. */
9820
+ checkDuplicateKey(expr, field, entries) {
9821
+ if (!this.emitDiagnostics || this.duplicateKeyReported.has(field)) return;
9822
+ const written = field.type === "TableFieldNamed" ? field.key.type === "Identifier" ? field.key.name : field.key.value : field.type === "TableFieldShorthand" ? field.name.name : void 0;
9823
+ if (written === void 0) return;
9824
+ const spreadBefore = expr.fields.slice(0, expr.fields.indexOf(field)).some((f) => f.type === "TableFieldSpread");
9825
+ if (spreadBefore) return;
9826
+ if (entries.filter(([k]) => k === written).length < 2) return;
9827
+ this.duplicateKeyReported.add(field);
9828
+ this.diagnostics.push({
9829
+ node: field.type === "TableFieldNamed" ? field.key : field.name,
9830
+ message: `'${written}' is given twice in this table; only the last one is kept`
9831
+ });
9832
+ }
9833
+ duplicateKeyReported = /* @__PURE__ */ new WeakSet();
9105
9834
  /** A value inferred `as const`, widened back wherever `context` does not
9106
9835
  * ask for a literal: `satisfies`' result type. A property keeps `"circle"`
9107
9836
  * when the contract's property admits string literals, and becomes
@@ -9128,7 +9857,7 @@ var TypeAnalyzer = class {
9128
9857
  case "tuple": {
9129
9858
  const tupleContext = ctx && this.membersOf(ctx).find((m) => m.kind === "tuple");
9130
9859
  if (tupleContext?.kind === "tuple") {
9131
- return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.isPack);
9860
+ return tuple(value.elements.map((e, i) => this.keepContextualLiterals(e, tupleContext.elements[i])), value.rest);
9132
9861
  }
9133
9862
  const arrayContext = ctx && this.membersOf(ctx).find((m) => m.kind === "array");
9134
9863
  const element = arrayContext?.kind === "array" ? arrayContext.element : void 0;
@@ -9385,7 +10114,8 @@ var TypeAnalyzer = class {
9385
10114
  for (const f of candidates) {
9386
10115
  if (!f.predicate) continue;
9387
10116
  const arg = args[f.predicate.param];
9388
- if (!arg || this.refKeyOf(arg) === void 0) continue;
10117
+ const onCondition = f.predicate.asserts && !f.predicate.type;
10118
+ if (!arg || this.refKeyOf(arg) === void 0 && !onCondition) continue;
9389
10119
  if (f.typeParams?.length && f.predicate.type) {
9390
10120
  const subst = this.inferTypeArgs(f, argTypes);
9391
10121
  return {
@@ -9406,6 +10136,10 @@ var TypeAnalyzer = class {
9406
10136
  const found = this.predicateCallTarget(call, env);
9407
10137
  if (!found || !found.predicate.asserts) return;
9408
10138
  const filter = found.predicate.type;
10139
+ if (this.refKeyOf(found.arg) === void 0) {
10140
+ if (!filter) this.applyNarrowing(found.arg, env, env, forkEnv(env));
10141
+ return;
10142
+ }
9409
10143
  this.narrowRef(found.arg, env, env, forkEnv(env), (cur) => filter ? { yes: narrowTo(cur, filter), no: narrowExclude(cur, filter) } : { yes: narrowTruthy(cur), no: narrowFalsy(cur) });
9410
10144
  }
9411
10145
  // --------------------------------------------------------
@@ -9430,6 +10164,10 @@ var TypeAnalyzer = class {
9430
10164
  if (base === void 0) return void 0;
9431
10165
  if (expr.index.type === "StringLiteral") return `${base}.${expr.index.value}`;
9432
10166
  if (expr.index.type === "NumberLiteral") return `${base}#${expr.index.value}`;
10167
+ if (expr.index.type === "Identifier") {
10168
+ const id = this.bindingIdOf(expr.index);
10169
+ return id === void 0 ? void 0 : `${base}[${bindKey(id)}]`;
10170
+ }
9433
10171
  return void 0;
9434
10172
  }
9435
10173
  default:
@@ -9525,26 +10263,45 @@ var TypeAnalyzer = class {
9525
10263
  * that copied it no longer holds that value: forget the alias. */
9526
10264
  unalias(key) {
9527
10265
  for (const k of [...this.refAliases.keys()]) {
9528
- if (k !== key && !k.startsWith(`${key}.`) && !k.startsWith(`${key}#`)) continue;
10266
+ if (k !== key && !isBelow(k, key) && !k.includes(`[${key}]`)) continue;
9529
10267
  for (const other of this.refAliases.get(k) ?? []) this.refAliases.get(other)?.delete(k);
9530
10268
  this.refAliases.delete(k);
9531
10269
  }
9532
10270
  }
9533
- /** Drop every narrowing recorded for a path strictly under `key`. */
10271
+ /** Drop every narrowing recorded for a path strictly under `key` — and,
10272
+ * when `key` is a whole variable, for every `t[key]` indexed by it: that
10273
+ * now names a different slot. */
9534
10274
  invalidateBelow(env, key) {
10275
+ const asIndex = `[${key}]`;
9535
10276
  for (const k of [...env.keys()]) {
9536
- if (k.startsWith(`${key}.`) || k.startsWith(`${key}#`)) env.delete(k);
10277
+ if (isBelow(k, key) || k.includes(asIndex)) env.delete(k);
9537
10278
  }
9538
10279
  }
9539
10280
  /** An assignment through a reference invalidates it and everything under
9540
10281
  * it, then records the assigned type. */
9541
10282
  assignToRef(expr, value, env) {
10283
+ this.invalidateSiblings(expr, env);
9542
10284
  const key = this.refKeyOf(expr);
9543
10285
  if (key === void 0) return;
9544
10286
  this.invalidateBelow(env, key);
9545
10287
  this.unalias(key);
9546
10288
  env.set(key, value);
9547
10289
  }
10290
+ /** A write to one slot of a table can be a write to a slot narrowed under
10291
+ * another name: `t.x = nil` is `t[k] = nil` when `k` is `"x"`. So a
10292
+ * write by name forgets what is known through variable keys, and a write
10293
+ * through any computed key forgets everything known about the table's
10294
+ * slots. */
10295
+ invalidateSiblings(expr, env) {
10296
+ const inner = expr.type === "ParenthesizedExpression" ? expr.expression : expr;
10297
+ if (inner.type !== "MemberExpression" && inner.type !== "IndexExpression") return;
10298
+ const parent = this.refKeyOf(inner.object);
10299
+ if (parent === void 0) return;
10300
+ const computed = inner.type === "IndexExpression" && inner.index.type !== "StringLiteral" && inner.index.type !== "NumberLiteral";
10301
+ for (const k of [...env.keys()]) {
10302
+ if (computed ? isBelow(k, parent) : k.startsWith(`${parent}[`)) env.delete(k);
10303
+ }
10304
+ }
9548
10305
  // --------------------------------------------------------
9549
10306
  // Small helpers
9550
10307
  // --------------------------------------------------------
@@ -9559,6 +10316,92 @@ var TypeAnalyzer = class {
9559
10316
  this.selfType = saved;
9560
10317
  }
9561
10318
  }
10319
+ /** Arithmetic no declared metamethod answers: a number — unless an
10320
+ * operand is `any`, which may be a value with metamethods of its own.
10321
+ * `(a.Position - b.Position).Magnitude` on an `a` nothing typed is a
10322
+ * `Vector3` as far as anyone knows, not a number with no `Magnitude`. */
10323
+ arithmeticOn(operands) {
10324
+ return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
10325
+ }
10326
+ /** `a == b` where nothing `a` can hold is anything `b` can: the answer is
10327
+ * always the same, so the comparison was not the one meant. Only types
10328
+ * that say what they hold take part — `any`, `unknown` and a type
10329
+ * parameter say nothing, and a table compares by identity, so two of them
10330
+ * may well be the same table. */
10331
+ checkOverlap(node, op, l, r) {
10332
+ if (!this.emitDiagnostics) return;
10333
+ const left = this.expand(l);
10334
+ const right = this.expand(r);
10335
+ const judged = (t) => {
10336
+ const parts = t.kind === "union" ? t.types.map((m) => this.expand(m)) : [t];
10337
+ return parts.every((p) => p.kind === "literal" || p.kind === "primitive");
10338
+ };
10339
+ const isNil = (t) => t.kind === "primitive" && t.name === "nil";
10340
+ if (isNil(left) || isNil(right)) return;
10341
+ if (!judged(left) || !judged(right)) return;
10342
+ if (isAssignable(left, right) || isAssignable(right, left)) return;
10343
+ this.diagnostics.push({
10344
+ node,
10345
+ message: `This comparison is always ${op === "==" ? "false" : "true"}: '${formatType(l)}' and '${formatType(r)}' have no value in common`
10346
+ });
10347
+ }
10348
+ /** What an operator takes, where no metamethod answered for it. Luau
10349
+ * raises on the rest — `n * 2` with `n` nil is an error at the line it
10350
+ * runs, and a nil `n` is exactly what `number | nil` says may happen — so
10351
+ * it is said here instead. `any` passes: nothing is known about it. */
10352
+ checkOperands(node, op, left, right) {
10353
+ if (!this.emitDiagnostics) return;
10354
+ const fits = (t) => {
10355
+ const x = this.expand(t);
10356
+ if (x.kind === "any" || x.kind === "never") return true;
10357
+ switch (op) {
10358
+ // `..` joins strings, and Luau writes a number into one.
10359
+ case "..":
10360
+ return isAssignable(x, stringType) || isAssignable(x, numberType);
10361
+ // `#` counts a string's bytes or a table's entries.
10362
+ case "#":
10363
+ return x.kind === "array" || x.kind === "tuple" || x.kind === "object" || isAssignable(x, stringType);
10364
+ default:
10365
+ return isAssignable(x, numberType);
10366
+ }
10367
+ };
10368
+ if (op === "<" || op === ">" || op === "<=" || op === ">=") {
10369
+ const both = (t) => this.expand(t);
10370
+ const [l, r] = [both(left), both(right)];
10371
+ const loose = (t) => t.kind === "any" || t.kind === "never";
10372
+ const alike = (as) => isAssignable(l, as) && isAssignable(r, as);
10373
+ if (loose(l) || loose(r) || alike(numberType) || alike(stringType)) return;
10374
+ } else if (fits(left) && (right === void 0 || fits(right))) {
10375
+ return;
10376
+ }
10377
+ this.diagnostics.push({
10378
+ node,
10379
+ message: right === void 0 ? `Operator '${op}' cannot be applied to type '${formatType(left)}'` : `Operator '${op}' cannot be applied to types '${formatType(left)}' and '${formatType(right)}'`
10380
+ });
10381
+ }
10382
+ /** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
10383
+ * with the operands swapped for `>` and `>=`, and an instance without
10384
+ * one cannot be compared at all. */
10385
+ checkComparison(node, op, l, r) {
10386
+ if (!this.emitDiagnostics) return false;
10387
+ const left = this.expand(l);
10388
+ const right = this.expand(r);
10389
+ const instance = (t) => t.kind === "object" && t.class !== void 0;
10390
+ if (!instance(left) && !instance(right)) return false;
10391
+ const name = op === "<" || op === ">" ? "__lt" : "__le";
10392
+ const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
10393
+ for (const receiver of operands) {
10394
+ const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
10395
+ if (!method) continue;
10396
+ if (this.pickOverload(this.overloadsOf(method.type), operands)) return true;
10397
+ break;
10398
+ }
10399
+ this.diagnostics.push({
10400
+ node,
10401
+ message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
10402
+ });
10403
+ return true;
10404
+ }
9562
10405
  /** What an operator on a value with metamethods gives: `a + b` calls
9563
10406
  * `__add` on `a`, or failing that on `b` with the operands swapped — the
9564
10407
  * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
@@ -9844,7 +10687,56 @@ var TypeAnalyzer = class {
9844
10687
  return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
9845
10688
  }
9846
10689
  };
9847
- function referencedTypeNames(node, out = []) {
10690
+ var libraryNames = /* @__PURE__ */ new WeakMap();
10691
+ function namesDeclaredIn(program) {
10692
+ const known = libraryNames.get(program);
10693
+ if (known) return known;
10694
+ const names = /* @__PURE__ */ new Set();
10695
+ for (const stmt of program.body.statements) {
10696
+ const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
10697
+ if (declaration.type === "TypeAliasStatement") names.add(declaration.name.name);
10698
+ else if (declaration.type === "ExportTypeAliasStatement") names.add(declaration.alias.name.name);
10699
+ else if (declaration.type === "DeclareClassStatement") names.add(declaration.name.name);
10700
+ else if (declaration.type === "ClassDeclaration" && declaration.name) names.add(declaration.name.name);
10701
+ else if (stmt.type === "ImportStatement") {
10702
+ for (const s of stmt.specifiers) names.add(s.local.name);
10703
+ if (stmt.defaultImport) names.add(stmt.defaultImport.name);
10704
+ if (stmt.namespaceImport) names.add(stmt.namespaceImport.name);
10705
+ }
10706
+ }
10707
+ libraryNames.set(program, names);
10708
+ return names;
10709
+ }
10710
+ var sharedClassesByLib = /* @__PURE__ */ new WeakMap();
10711
+ function shareableClasses(libs, program) {
10712
+ if (!libs.length) return void 0;
10713
+ const mine = namesDeclaredIn(program);
10714
+ if (mine.size) {
10715
+ for (const lib of libs) {
10716
+ for (const name of namesDeclaredIn(lib)) if (mine.has(name)) return void 0;
10717
+ }
10718
+ }
10719
+ const existing = sharedClassesByLib.get(libs[0]);
10720
+ if (existing) {
10721
+ const same = existing.libs.length === libs.length && existing.libs.every((lib, i) => lib === libs[i]);
10722
+ if (same) return existing;
10723
+ return void 0;
10724
+ }
10725
+ const store = { libs: [...libs], classTypes: /* @__PURE__ */ new WeakMap(), classMembers: /* @__PURE__ */ new WeakMap() };
10726
+ sharedClassesByLib.set(libs[0], store);
10727
+ return store;
10728
+ }
10729
+ var referencedNamesOf = /* @__PURE__ */ new WeakMap();
10730
+ var typeQueryIn = /* @__PURE__ */ new WeakMap();
10731
+ function referencedTypeNames(node, out) {
10732
+ if (out === void 0 && node && typeof node === "object") {
10733
+ const known = referencedNamesOf.get(node);
10734
+ if (known) return known;
10735
+ const found = referencedTypeNames(node, []);
10736
+ referencedNamesOf.set(node, found);
10737
+ return found;
10738
+ }
10739
+ out = out ?? [];
9848
10740
  if (!node || typeof node !== "object") return out;
9849
10741
  if (Array.isArray(node)) {
9850
10742
  for (const item of node) referencedTypeNames(item, out);
@@ -9861,9 +10753,11 @@ function referencedTypeNames(node, out = []) {
9861
10753
  }
9862
10754
  function containsTypeQuery(node) {
9863
10755
  if (!node || typeof node !== "object") return false;
9864
- if (Array.isArray(node)) return node.some(containsTypeQuery);
9865
- if (node.type === "TypeofTypeNode") return true;
9866
- return Object.values(node).some(containsTypeQuery);
10756
+ const known = typeQueryIn.get(node);
10757
+ if (known !== void 0) return known;
10758
+ const found = Array.isArray(node) ? node.some(containsTypeQuery) : node.type === "TypeofTypeNode" || Object.values(node).some(containsTypeQuery);
10759
+ typeQueryIn.set(node, found);
10760
+ return found;
9867
10761
  }
9868
10762
  var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
9869
10763
  function briefType(t) {
@@ -9883,7 +10777,7 @@ function briefType(t) {
9883
10777
  }
9884
10778
 
9885
10779
  // src/project/host.ts
9886
- import { readFileSync, statSync } from "fs";
10780
+ import { readdirSync, readFileSync, statSync } from "fs";
9887
10781
  var nodeHost = {
9888
10782
  readFile(path) {
9889
10783
  try {
@@ -9891,6 +10785,13 @@ var nodeHost = {
9891
10785
  } catch {
9892
10786
  return void 0;
9893
10787
  }
10788
+ },
10789
+ readDirectory(path) {
10790
+ try {
10791
+ return readdirSync(path, { withFileTypes: true }).filter((entry) => entry.isDirectory() || entry.isSymbolicLink()).map((entry) => entry.name);
10792
+ } catch {
10793
+ return void 0;
10794
+ }
9894
10795
  }
9895
10796
  };
9896
10797
 
@@ -10082,13 +10983,31 @@ function resolveTypeLibraries(config, host = nodeHost) {
10082
10983
  else problems.push({ file: config.path, message: `'${entry}' has no ${ENTRY_FILE} (or 'tilua.types' in its package.json)`, ...entryPosition(config, entry) });
10083
10984
  continue;
10084
10985
  }
10085
- const name = entry.startsWith("@tilua-types/") ? entry : `@tilua-types/${entry}`;
10086
- const found = findPackage(name, config.directory, host);
10986
+ if (entry.includes("*")) {
10987
+ const matched = matchPackages(entry, config.directory, host);
10988
+ if (matched === void 0) {
10989
+ problems.push({
10990
+ file: config.path,
10991
+ message: `'${entry}' needs the folders listed, and this tool cannot list them; name the libraries one by one`,
10992
+ ...entryPosition(config, entry)
10993
+ });
10994
+ } else if (!matched.length) {
10995
+ problems.push({
10996
+ file: config.path,
10997
+ message: `No installed type library matches '${entry}'`,
10998
+ ...entryPosition(config, entry)
10999
+ });
11000
+ }
11001
+ for (const found2 of matched ?? []) addPackage(found2.directory, found2.file, /* @__PURE__ */ new Set());
11002
+ continue;
11003
+ }
11004
+ const names = entry.startsWith("@") ? [entry] : [`@tilua-types/${entry}`, entry];
11005
+ const found = names.map((name) => findPackage(name, config.directory, host)).find(Boolean);
10087
11006
  if (found) addPackage(found.directory, found.file, /* @__PURE__ */ new Set());
10088
11007
  else {
10089
11008
  problems.push({
10090
11009
  file: config.path,
10091
- message: `Cannot find type library '${name}'. Install it with: npm i -D ${name}`,
11010
+ message: names.length === 1 ? `Cannot find type library '${entry}'. Install it with: npm i -D ${entry}` : `Cannot find type library '${names[0]}' or '${entry}'. Install it with: npm i -D ${names[0]}`,
10092
11011
  ...entryPosition(config, entry)
10093
11012
  });
10094
11013
  }
@@ -10125,6 +11044,32 @@ function findPackage(name, from, host) {
10125
11044
  directory = parent;
10126
11045
  }
10127
11046
  }
11047
+ function matchPackages(pattern, from, host) {
11048
+ if (!host.readDirectory) return void 0;
11049
+ const slash = pattern.lastIndexOf("/");
11050
+ const scope = slash < 0 ? "" : pattern.slice(0, slash);
11051
+ const last = pattern.slice(slash + 1);
11052
+ const matches = new RegExp(`^${last.split("*").map(escapeRegExp2).join(".*")}$`);
11053
+ const found = /* @__PURE__ */ new Map();
11054
+ let directory = resolve2(from);
11055
+ for (; ; ) {
11056
+ const folder = join2(directory, "node_modules", ...scope ? scope.split("/") : []);
11057
+ for (const name of host.readDirectory(folder) ?? []) {
11058
+ const full = scope ? `${scope}/${name}` : name;
11059
+ if (!matches.test(name) || found.has(full)) continue;
11060
+ const candidate = join2(folder, name);
11061
+ const file = packageEntry(candidate, host);
11062
+ if (file) found.set(full, { directory: candidate, file });
11063
+ }
11064
+ const parent = dirname2(directory);
11065
+ if (parent === directory) break;
11066
+ directory = parent;
11067
+ }
11068
+ return [...found.keys()].sort().map((name) => found.get(name));
11069
+ }
11070
+ function escapeRegExp2(text) {
11071
+ return text.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
11072
+ }
10128
11073
  function dependencyNames(directory, host) {
10129
11074
  const manifest = readJson(join2(directory, "package.json"), host);
10130
11075
  const names = /* @__PURE__ */ new Set();
@@ -10223,7 +11168,8 @@ function sourceMapTypes(text, path, options) {
10223
11168
  for (const child of node.children ?? []) {
10224
11169
  if (!isNode(child)) continue;
10225
11170
  const childAlias = visit(child, [...segments, child.name], alias);
10226
- if (!IDENTIFIER.test(child.name) || taken.has(child.name) || named.has(child.name)) continue;
11171
+ const service = child.name === child.className && options.classes.has(child.className);
11172
+ if (!IDENTIFIER.test(child.name) || taken.has(child.name) && !service || named.has(child.name)) continue;
10227
11173
  named.add(child.name);
10228
11174
  members.push(`${child.name}: ${childAlias}`);
10229
11175
  }
@@ -10283,6 +11229,7 @@ export {
10283
11229
  BinaryOperators,
10284
11230
  CONFIG_FILE_NAMES,
10285
11231
  Keywords,
11232
+ LANGUAGE_GLOBALS,
10286
11233
  LexError,
10287
11234
  Operators,
10288
11235
  PRELUDE_SOURCE,
@@ -10351,6 +11298,7 @@ export {
10351
11298
  tilua,
10352
11299
  tokenize,
10353
11300
  tuple,
11301
+ tupleMembers,
10354
11302
  typeParam,
10355
11303
  unify,
10356
11304
  union,