@tilua/parser 2.4.0 → 3.0.1

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
@@ -629,7 +629,8 @@ var DECLARATION_STATEMENTS = /* @__PURE__ */ new Set([
629
629
  "ExportNamedStatement",
630
630
  "ExportAllStatement",
631
631
  "DeclareStatement",
632
- "DeclareClassStatement"
632
+ "DeclareClassStatement",
633
+ "DeclareMetatableStatement"
633
634
  ]);
634
635
  function spanFrom(start, end) {
635
636
  return {
@@ -1247,6 +1248,9 @@ var Parser = class {
1247
1248
  if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
1248
1249
  return this.parseDeclareClassStatement();
1249
1250
  }
1251
+ if (p1.type === "Identifier" && p1.value === "metatable" && !this.punctuatorAt(2, ":")) {
1252
+ return this.parseDeclareMetatableStatement();
1253
+ }
1250
1254
  if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
1251
1255
  return this.parseDeclareStatement();
1252
1256
  }
@@ -1299,6 +1303,17 @@ var Parser = class {
1299
1303
  }
1300
1304
  return tokenIdentifier(first);
1301
1305
  }
1306
+ // `declare metatable<T> T[]: { __index: { ... } }`
1307
+ parseDeclareMetatableStatement() {
1308
+ const start = this.current();
1309
+ this.advance();
1310
+ this.advance();
1311
+ const generics = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1312
+ const target = this.parseType();
1313
+ this.expectPunctuator(":");
1314
+ const metatable = this.parseType();
1315
+ return { type: "DeclareMetatableStatement", generics, target, metatable, ...spanFrom(start, this.previous()) };
1316
+ }
1302
1317
  // `declare class Name extends Base { member: T, ... }`
1303
1318
  parseDeclareClassStatement() {
1304
1319
  const start = this.current();
@@ -2007,6 +2022,14 @@ var Parser = class {
2007
2022
  parseExpressionStatement() {
2008
2023
  const start = this.current();
2009
2024
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
2025
+ const call = this.tryParse(() => {
2026
+ const errors = this.errors.length;
2027
+ const expression2 = this.parseAtom();
2028
+ if (expression2.type !== "MethodCallExpression" && expression2.type !== "CallExpression") throw new ParseRecover("not a call");
2029
+ if (this.errors.length > errors) throw new ParseRecover("not a call");
2030
+ return expression2;
2031
+ });
2032
+ if (call) return { type: "CallStatement", expression: call, ...spanFrom(start, this.previous()) };
2010
2033
  const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
2011
2034
  return this.parseAssignmentRest(start, target);
2012
2035
  }
@@ -2328,11 +2351,9 @@ var Parser = class {
2328
2351
  if (t.type === "Identifier" && t.value === "class") {
2329
2352
  return this.parseClassExpression();
2330
2353
  }
2331
- if (t.type === "Punctuator" && t.value === "{") {
2332
- return this.parseTableExpression();
2333
- }
2334
- if (t.type === "Punctuator" && t.value === "[") {
2335
- return this.parseArrayExpression();
2354
+ if (t.type === "Punctuator" && (t.value === "{" || t.value === "[")) {
2355
+ const literal2 = t.value === "{" ? this.parseTableExpression() : this.parseArrayExpression();
2356
+ return this.checkPunctuator(":") && this.startsMethodCall() ? this.parseSuffixes(literal2) : literal2;
2336
2357
  }
2337
2358
  if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
2338
2359
  if (this.checkPunctuator("(") || this.checkOperator("<")) {
@@ -2409,6 +2430,11 @@ var Parser = class {
2409
2430
  } else {
2410
2431
  this.error("Expected identifier or '('");
2411
2432
  }
2433
+ return this.parseSuffixes(base);
2434
+ }
2435
+ /** What follows a prefix expression's base: `.name`, `[key]`, `:m(...)`,
2436
+ * a call, and their optional forms. */
2437
+ parseSuffixes(base) {
2412
2438
  while (true) {
2413
2439
  if (this.checkPunctuator("?") && this.touchesNext()) {
2414
2440
  const next = this.peek(1);
@@ -3762,6 +3788,10 @@ var Analyzer = class {
3762
3788
  moduleScope = this.globalScope;
3763
3789
  /** How many function bodies enclose the walk. */
3764
3790
  functionDepth = 0;
3791
+ /** How many loops enclose the walk inside the nearest function body. A
3792
+ * function starts again at 0: `continue` in `xs:map(x => ...)` has no
3793
+ * loop to go on with, whatever loop the call is written in. */
3794
+ loopDepth = 0;
3765
3795
  /** Function declarations already declared by their block's hoisting, with
3766
3796
  * the function depth of that block. */
3767
3797
  hoisted = /* @__PURE__ */ new Map();
@@ -4114,11 +4144,11 @@ var Analyzer = class {
4114
4144
  return;
4115
4145
  case "WhileStatement":
4116
4146
  this.visitExpression(stmt.condition, scope);
4117
- this.visitBlockInNewScope(stmt.body, scope);
4147
+ this.inLoop(() => this.visitBlockInNewScope(stmt.body, scope));
4118
4148
  return;
4119
4149
  case "RepeatStatement": {
4120
4150
  const bodyScope = childScope(scope);
4121
- this.visitBlock(stmt.body, bodyScope);
4151
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4122
4152
  this.visitExpression(stmt.condition, bodyScope);
4123
4153
  return;
4124
4154
  }
@@ -4136,14 +4166,14 @@ var Analyzer = class {
4136
4166
  if (stmt.step) this.visitExpression(stmt.step, scope);
4137
4167
  const bodyScope = childScope(scope);
4138
4168
  this.declare(bodyScope, stmt.variable.name, "for-numeric", stmt.variable);
4139
- this.visitBlock(stmt.body, bodyScope);
4169
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4140
4170
  return;
4141
4171
  }
4142
4172
  case "GenericForStatement": {
4143
4173
  this.visitExpression(stmt.iterator, scope);
4144
4174
  const bodyScope = childScope(scope);
4145
4175
  this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
4146
- this.visitBlock(stmt.body, bodyScope);
4176
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4147
4177
  return;
4148
4178
  }
4149
4179
  case "ReturnStatement":
@@ -4151,6 +4181,15 @@ var Analyzer = class {
4151
4181
  return;
4152
4182
  case "BreakStatement":
4153
4183
  case "ContinueStatement":
4184
+ if (this.loopDepth === 0) {
4185
+ const word = stmt.type === "BreakStatement" ? "break" : "continue";
4186
+ this.diagnostics.push({
4187
+ node: stmt,
4188
+ message: this.functionDepth > 0 ? `'${word}' is not inside a loop of this function; to skip a value in a callback, 'return' from it` : `'${word}' is not inside a loop`,
4189
+ kind: "outside-loop"
4190
+ });
4191
+ }
4192
+ return;
4154
4193
  case "ErrorStatement":
4155
4194
  return;
4156
4195
  case "DeclareStatement":
@@ -4159,6 +4198,11 @@ var Analyzer = class {
4159
4198
  case "DeclareClassStatement":
4160
4199
  this.visitType(stmt.body, scope);
4161
4200
  return;
4201
+ case "DeclareMetatableStatement":
4202
+ this.visitGenerics(stmt.generics, scope);
4203
+ this.visitType(stmt.target, scope);
4204
+ this.visitType(stmt.metatable, scope);
4205
+ return;
4162
4206
  case "TypeAliasStatement":
4163
4207
  case "ExportTypeAliasStatement":
4164
4208
  this.visitGenerics(
@@ -4201,6 +4245,15 @@ var Analyzer = class {
4201
4245
  return;
4202
4246
  }
4203
4247
  }
4248
+ /** Visit a loop's body, where `break` and `continue` have a loop. */
4249
+ inLoop(visit) {
4250
+ this.loopDepth++;
4251
+ try {
4252
+ visit();
4253
+ } finally {
4254
+ this.loopDepth--;
4255
+ }
4256
+ }
4204
4257
  // ---------------- functions ----------------
4205
4258
  visitFunctionBody(func, outerScope, isMethod = false) {
4206
4259
  const fnScope = childScope(outerScope);
@@ -4216,11 +4269,14 @@ var Analyzer = class {
4216
4269
  }
4217
4270
  });
4218
4271
  this.visitType(func.returnType, fnScope);
4272
+ const loops = this.loopDepth;
4219
4273
  this.functionDepth++;
4274
+ this.loopDepth = 0;
4220
4275
  try {
4221
4276
  this.visitBlock(func.body, fnScope);
4222
4277
  } finally {
4223
4278
  this.functionDepth--;
4279
+ this.loopDepth = loops;
4224
4280
  }
4225
4281
  }
4226
4282
  /** An overload signature: no body and no bindings, but its types can hold
@@ -4424,6 +4480,104 @@ type Mutable<T> = { -readonly [K in keyof T]: T[K] }
4424
4480
  type Pick<T, K> = { [P in K]: T[P] }
4425
4481
  type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
4426
4482
  type Record<K, V> = { [P in K]: V }
4483
+
4484
+ -- ============================================================
4485
+ -- The metatables of the language's own values
4486
+ -- ============================================================
4487
+
4488
+ -- A string's methods. The first group is Lua's string library, which a
4489
+ -- string really answers to; where Lua answers several values, tilua answers
4490
+ -- one, as the global functions do (\`find\` is an array, \`gsub\` the new
4491
+ -- string). The second group is written the way JavaScript writes it.
4492
+ --
4493
+ -- Positions are Lua's: \`sub\` and \`slice\` count from 1, and \`indexOf\` is
4494
+ -- \`nil\` when the text is not there.
4495
+ declare metatable string: {
4496
+ __index: {
4497
+ upper: (self: string) => string,
4498
+ lower: (self: string) => string,
4499
+ len: (self: string) => number,
4500
+ sub: (self: string, i: number, j?: number) => string,
4501
+ rep: (self: string, n: number, separator?: string) => string,
4502
+ reverse: (self: string) => string,
4503
+ format: (self: string, ...args: unknown[]) => string,
4504
+ byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),
4505
+ find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,
4506
+ match: (self: string, pattern: string, init?: number) => [...string[]] | nil,
4507
+ gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,
4508
+ gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,
4509
+
4510
+ trim: (self: string) => string,
4511
+ trimStart: (self: string) => string,
4512
+ trimEnd: (self: string) => string,
4513
+ startsWith: (self: string, text: string) => boolean,
4514
+ endsWith: (self: string, text: string) => boolean,
4515
+ includes: (self: string, text: string) => boolean,
4516
+ indexOf: (self: string, text: string, start?: number) => number | nil,
4517
+ slice: (self: string, start?: number, stop?: number) => string,
4518
+ replace: (self: string, text: string, replacement: string) => string,
4519
+ replaceAll: (self: string, text: string, replacement: string) => string,
4520
+ padStart: (self: string, length: number, padding?: string) => string,
4521
+ padEnd: (self: string, length: number, padding?: string) => string,
4522
+ },
4523
+ }
4524
+
4525
+ -- An array's methods. Lua gives an array no metatable: the compiler lowers
4526
+ -- each call to a plain function, so they work on any array, one a Lua
4527
+ -- library made included.
4528
+ --
4529
+ -- Indices are Lua's: the first element is 1, and \`indexOf\` / \`findIndex\`
4530
+ -- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
4531
+ -- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
4532
+ -- \`sort\` and \`reverse\` change the array; everything else returns a new one.
4533
+ -- A \`filter\` whose test is a guard \u2014 \`v is S\`, or \`v => v ~= nil\` \u2014
4534
+ -- answers an array of what it let through.
4535
+ declare metatable<T> T[]: {
4536
+ __index: {
4537
+ find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
4538
+ findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
4539
+ filter: (<S extends T>(self: T[], test: (value: T, index: number) => value is S) => S[])
4540
+ & ((self: T[], test: (value: T, index: number) => boolean) => T[]),
4541
+ map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
4542
+ forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
4543
+ some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
4544
+ every: (self: T[], test: (value: T, index: number) => boolean) => boolean,
4545
+ reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,
4546
+ includes: (self: T[], value: T) => boolean,
4547
+ indexOf: (self: T[], value: T, start?: number) => number | nil,
4548
+ join: (self: T[], separator?: string) => string,
4549
+ concat: (self: T[], ...others: T[][]) => T[],
4550
+ slice: (self: T[], start?: number, stop?: number) => T[],
4551
+ flat: (self: T[]) => T[],
4552
+ reverse: (self: T[]) => T[],
4553
+ sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],
4554
+ push: (self: T[], ...args: T[]) => number,
4555
+ pop: (self: T[]) => T | nil,
4556
+ shift: (self: T[]) => T | nil,
4557
+ unshift: (self: T[], ...args: T[]) => number,
4558
+ },
4559
+ }
4560
+
4561
+ -- A table's methods. Unlike JavaScript's \`Object.keys\`, they answer a
4562
+ -- tuple, in the order the table's type wrote its members:
4563
+ --
4564
+ -- const point = { x: 1, y: "up" }
4565
+ -- point:keys() -- ["x", "y"]
4566
+ -- point:values() -- [number, string]
4567
+ -- point:entries() -- [["x", number], ["y", string]]
4568
+ --
4569
+ -- A Lua table has no order of its own, so the compiler hands its runtime the
4570
+ -- keys as the type lists them. An optional member keeps its place, holding
4571
+ -- \`nil\` when it is missing; an indexer's keys follow the named ones. A
4572
+ -- member of the table's own with one of these names is what a call reaches,
4573
+ -- and a class instance, which has a metatable of its own, has none of them.
4574
+ declare metatable<T extends {}> T: {
4575
+ __index: {
4576
+ keys: (self: T) => ObjectKeys<T>,
4577
+ values: (self: T) => ObjectValues<T>,
4578
+ entries: (self: T) => ObjectEntries<T>,
4579
+ },
4580
+ }
4427
4581
  `;
4428
4582
  var prelude;
4429
4583
  function preludeProgram() {
@@ -4522,8 +4676,14 @@ function substitute(t, subst) {
4522
4676
  if (subst.size === 0) return t;
4523
4677
  if (!containsTypeParam(t)) return t;
4524
4678
  switch (t.kind) {
4525
- case "typeParam":
4526
- return subst.get(t.name) ?? t;
4679
+ case "typeParam": {
4680
+ const bound = subst.get(t.name);
4681
+ if (bound) return bound;
4682
+ if (t.constraint && containsTypeParam(t.constraint)) {
4683
+ return { ...t, constraint: substitute(t.constraint, new Map([...subst, [t.name, t]])) };
4684
+ }
4685
+ return t;
4686
+ }
4527
4687
  case "array":
4528
4688
  return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4529
4689
  case "tuple":
@@ -4668,6 +4828,10 @@ function unify(param, arg, vars, out, how = "union") {
4668
4828
  if (arg.kind === "function") {
4669
4829
  param.params.forEach((p, i) => arg.params[i] && unify(p.type, arg.params[i].type, vars, out));
4670
4830
  unify(param.returns, arg.returns, vars, out);
4831
+ const wanted = param.predicate, given = arg.predicate;
4832
+ if (wanted?.type && given?.type && wanted.param === given.param && !wanted.asserts && !given.asserts) {
4833
+ unify(wanted.type, given.type, vars, out);
4834
+ }
4671
4835
  }
4672
4836
  return;
4673
4837
  case "object":
@@ -4923,6 +5087,11 @@ function isAssignableInner(a, b) {
4923
5087
  for (let i = 0; i < n; i++) {
4924
5088
  if (!isAssignable(a.params[i].type, b.params[i].type) && !isAssignable(b.params[i].type, a.params[i].type)) return false;
4925
5089
  }
5090
+ const wanted = b.predicate;
5091
+ if (wanted?.type && !wanted.asserts) {
5092
+ const given = a.predicate;
5093
+ if (!given?.type || given.asserts || given.param !== wanted.param || !isAssignable(given.type, wanted.type)) return false;
5094
+ }
4926
5095
  return isAssignable(a.returns, b.returns);
4927
5096
  }
4928
5097
  if (b.kind === "typeParam") return false;
@@ -5057,7 +5226,7 @@ function containsFreeTypeParam(t, seen, bound) {
5057
5226
  return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
5058
5227
  case "function": {
5059
5228
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
5060
- return t.params.some((p) => containsTypeParam(p.type, seen, inner)) || !!t.varargs && containsTypeParam(t.varargs, seen, inner) || containsTypeParam(t.returns, seen, inner);
5229
+ return t.params.some((p) => containsTypeParam(p.type, seen, inner)) || !!t.varargs && containsTypeParam(t.varargs, seen, inner) || containsTypeParam(t.returns, seen, inner) || !!t.predicate?.type && containsTypeParam(t.predicate.type, seen, inner);
5061
5230
  }
5062
5231
  case "genericRef":
5063
5232
  return t.typeArguments.some((a) => containsTypeParam(a, seen, bound));
@@ -5482,9 +5651,14 @@ function isFreshLiteralExpr(e) {
5482
5651
  case "StringLiteral":
5483
5652
  case "BooleanLiteral":
5484
5653
  case "InterpolatedStringExpression":
5654
+ return true;
5655
+ // A table or array literal widened the literals written in it as it
5656
+ // was read (`widenUnlessAsked`), and keeps what it read from
5657
+ // elsewhere — `{ key: name }`, `[...names]` — so there is nothing
5658
+ // left for the binding to widen.
5485
5659
  case "TableExpression":
5486
5660
  case "ArrayExpression":
5487
- return true;
5661
+ return false;
5488
5662
  case "ParenthesizedExpression":
5489
5663
  return isFreshLiteralExpr(e.expression);
5490
5664
  case "UnaryExpression":
@@ -5499,6 +5673,30 @@ function isFreshLiteralExpr(e) {
5499
5673
  return false;
5500
5674
  }
5501
5675
  }
5676
+ function overloadTier(f) {
5677
+ const takesGuard = f.params.some((p) => p.type.kind === "function" && p.type.predicate?.type && !p.type.predicate.asserts);
5678
+ if (takesGuard) return 0;
5679
+ return (f.typeParams?.length ?? 0) > 0 ? 2 : 1;
5680
+ }
5681
+ function writesLiteral(e) {
5682
+ switch (e.type) {
5683
+ case "NumberLiteral":
5684
+ case "StringLiteral":
5685
+ case "BooleanLiteral":
5686
+ case "InterpolatedStringExpression":
5687
+ return true;
5688
+ case "ParenthesizedExpression":
5689
+ return writesLiteral(e.expression);
5690
+ case "UnaryExpression":
5691
+ return writesLiteral(e.argument);
5692
+ case "IfElseExpression":
5693
+ return e.clauses.some((c) => writesLiteral(c.body)) || writesLiteral(e.alternate);
5694
+ case "BinaryExpression":
5695
+ return (e.operator === "or" || e.operator === "and") && (writesLiteral(e.left) || writesLiteral(e.right));
5696
+ default:
5697
+ return false;
5698
+ }
5699
+ }
5502
5700
  function collectInferNames(node) {
5503
5701
  const out = [];
5504
5702
  const walk = (n) => {
@@ -5789,6 +5987,10 @@ var TypeAnalyzer = class {
5789
5987
  typeOfTypeNode = /* @__PURE__ */ new Map();
5790
5988
  expectedTypeOf = /* @__PURE__ */ new Map();
5791
5989
  loops = /* @__PURE__ */ new Map();
5990
+ /** Every `declare metatable`, libraries first, in declaration order. */
5991
+ metatables = [];
5992
+ metatableCache = /* @__PURE__ */ new WeakMap();
5993
+ methodSources = /* @__PURE__ */ new Map();
5792
5994
  /** Public: each alias resolved once (generic aliases keep their params as
5793
5995
  * `typeParam` nodes in the body). */
5794
5996
  aliases = new LazyMap();
@@ -5855,6 +6057,9 @@ var TypeAnalyzer = class {
5855
6057
  this.registerNestedClasses();
5856
6058
  this.registerNestedAliases();
5857
6059
  for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
6060
+ this.harvestMetatables(preludeProgram().body, { origin: "language" });
6061
+ (this.options.libs ?? []).forEach((lib, i) => this.harvestMetatables(lib.body, { origin: "library", library: i }));
6062
+ this.harvestMetatables(this.program.body, { origin: "program" });
5858
6063
  this.registerImportedTypes();
5859
6064
  this.resolveAllAliases();
5860
6065
  this.harvestDeclares(this.program.body, true);
@@ -5886,6 +6091,8 @@ var TypeAnalyzer = class {
5886
6091
  expectedTypeOf: this.expectedTypeOf,
5887
6092
  aliases: this.resolveDeferredAliases(),
5888
6093
  loops: this.loops,
6094
+ methodSources: this.methodSources,
6095
+ metatableMembers: (type) => this.metatableMembers(type),
5889
6096
  diagnostics: this.diagnostics
5890
6097
  };
5891
6098
  }
@@ -5943,8 +6150,7 @@ var TypeAnalyzer = class {
5943
6150
  }
5944
6151
  /** `layering` is on for the prelude and for definitions files: a second
5945
6152
  * library that declares an alias already declared *adds* to it, the way a
5946
- * second `declare` of a table's name does, so `@tilua-types/roblox` can give
5947
- * `StringMethods` Luau's `split` without restating Lua's. The file being
6153
+ * second `declare` of a table's name does. The file being
5948
6154
  * analysed is not a layer: its own alias replaces what the libraries
5949
6155
  * gave, which is how a project opts out of a set. */
5950
6156
  registerAliasDefs(block, layering = false) {
@@ -6862,7 +7068,7 @@ var TypeAnalyzer = class {
6862
7068
  if (record.type === "TypeReference" && typeof record.base === "string") {
6863
7069
  const name = typeof record.namespace === "string" ? `${record.namespace}.${record.base}` : record.base;
6864
7070
  const resolved = this.typeOfTypeNode.get(node);
6865
- const unresolved = resolved?.kind === "genericRef" && resolved.name === name && !this.aliasDefs.has(name) && !this.importedTypes.has(name) && this.options.libTypes?.[name] === void 0 && !STRING_INTRINSICS.has(name) && !this.importedNames().has(name.split(".")[0]);
7071
+ const unresolved = resolved?.kind === "genericRef" && resolved.name === name && !this.aliasDefs.has(name) && !this.importedTypes.has(name) && this.options.libTypes?.[name] === void 0 && !INTRINSICS.has(name) && !this.importedNames().has(name.split(".")[0]);
6866
7072
  const at = node;
6867
7073
  const key = `${at.line.start}:${at.column.start}`;
6868
7074
  if (unresolved && !reported.has(key)) {
@@ -6980,7 +7186,7 @@ var TypeAnalyzer = class {
6980
7186
  const tp = this.lookupTypeParam(node.base);
6981
7187
  if (tp) return typeParam(tp.name, tp.constraint, tp.isConst);
6982
7188
  if (node.typeArguments.length === 1 && !this.aliasDefs.has(node.base)) {
6983
- const intrinsic = this.applyStringIntrinsic(
7189
+ const intrinsic = this.applyIntrinsic(
6984
7190
  node.base,
6985
7191
  this.resolveType(node.typeArguments[0])
6986
7192
  );
@@ -7187,7 +7393,7 @@ var TypeAnalyzer = class {
7187
7393
  return difference(this.reduceType(t.base), this.reduceType(t.excluded));
7188
7394
  case "genericRef": {
7189
7395
  if (t.typeArguments.length !== 1 || this.aliasDefs.has(t.name)) return t;
7190
- return this.applyStringIntrinsic(t.name, t.typeArguments[0]) ?? t;
7396
+ return this.applyIntrinsic(t.name, t.typeArguments[0]) ?? t;
7191
7397
  }
7192
7398
  case "conditional":
7193
7399
  return this.reduceConditional(t);
@@ -7300,6 +7506,41 @@ var TypeAnalyzer = class {
7300
7506
  }
7301
7507
  return union(combos.map((c) => literal(c)));
7302
7508
  }
7509
+ /** A type function the analyzer supplies, since tilua cannot write it. */
7510
+ applyIntrinsic(name, arg) {
7511
+ return this.applyStringIntrinsic(name, arg) ?? this.applyObjectIntrinsic(name, arg);
7512
+ }
7513
+ /** `ObjectKeys<T>`, `ObjectValues<T>`, `ObjectEntries<T>`: an object's
7514
+ * members as a tuple, in the order they were written — `{ a: 1, b: "x" }`
7515
+ * has keys `["a", "b"]`, values `[number, string]` and entries
7516
+ * `[["a", number], ["b", string]]`. TypeScript answers `string[]` for
7517
+ * `Object.keys`, since its objects have no order; tilua's lowering hands
7518
+ * the runtime the written order, so the tuple is what runs. An indexer's
7519
+ * keys follow the named ones, as the tuple's rest. */
7520
+ applyObjectIntrinsic(name, arg) {
7521
+ if (!OBJECT_INTRINSICS.has(name)) return void 0;
7522
+ const t = this.expand(this.reduceType(arg));
7523
+ if (containsTypeParam(t)) return void 0;
7524
+ if (t.kind === "union") return union(t.types.map((m) => this.applyObjectIntrinsic(name, m) ?? unknownType));
7525
+ const parts = t.kind === "intersection" ? t.types.map((m) => this.expand(m)) : [t];
7526
+ if (!parts.every((m) => m.kind === "object" && !m.class)) return unknownType;
7527
+ const properties = /* @__PURE__ */ new Map();
7528
+ let indexer;
7529
+ for (const part of parts) {
7530
+ for (const [key, property] of part.properties) properties.set(key, property);
7531
+ indexer ??= part.indexer;
7532
+ }
7533
+ const entries = [...properties].map(([key, p]) => [literal(key), p.optional ? optional(p.type) : p.type]);
7534
+ const listed = (elements, rest) => !elements.length && rest ? arrayOf(rest) : tuple(elements, rest);
7535
+ switch (name) {
7536
+ case "ObjectKeys":
7537
+ return listed(entries.map(([key]) => key), indexer?.key);
7538
+ case "ObjectValues":
7539
+ return listed(entries.map(([, value]) => value), indexer?.value);
7540
+ default:
7541
+ return listed(entries.map((entry) => tuple([...entry])), indexer && tuple([indexer.key, indexer.value]));
7542
+ }
7543
+ }
7303
7544
  /** TypeScript's four string intrinsics. They cannot be written in tilua —
7304
7545
  * there is no character-level type arithmetic — so the analyzer supplies
7305
7546
  * them, and only them, as named type functions. */
@@ -7838,7 +8079,9 @@ var TypeAnalyzer = class {
7838
8079
  const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
7839
8080
  if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
7840
8081
  const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
7841
- const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
8082
+ const spread = this.spreadOf(written, selfOf(fns[0]));
8083
+ const provisional = argTypes.map((t, i) => waits[i] ? anyType : t);
8084
+ const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), spread) ?? this.pickOverload(fns, provisional, (f) => extra(f, provisional), spread);
7842
8085
  const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
7843
8086
  written.forEach((a, i) => {
7844
8087
  if (!waits[i]) return;
@@ -8029,6 +8272,16 @@ var TypeAnalyzer = class {
8029
8272
  }
8030
8273
  const id = this.bindingIdByName(p.name, p);
8031
8274
  const t = this.paramType(p, env);
8275
+ if (p.default && p.typeAnnotation) {
8276
+ const given = this.infer(p.default, env);
8277
+ const declared = this.resolveType(p.typeAnnotation);
8278
+ if (this.emitDiagnostics && !isAssignable(given, declared)) {
8279
+ this.diagnostics.push({
8280
+ node: p.default,
8281
+ message: `Type '${briefType(given)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(given, declared)
8282
+ });
8283
+ }
8284
+ }
8032
8285
  if (id !== void 0) {
8033
8286
  this.bindingType.set(id, t);
8034
8287
  this.setBinding(env, id, t);
@@ -8126,9 +8379,9 @@ var TypeAnalyzer = class {
8126
8379
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
8127
8380
  * `<T>(v: T) -> string` and still pick the precise one. */
8128
8381
  pickOverload(fns, argTypes, argsFor, spread) {
8129
- for (const generic of [false, true]) {
8382
+ for (const tier of [0, 1, 2]) {
8130
8383
  for (const f of fns) {
8131
- if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
8384
+ if (overloadTier(f) !== tier) continue;
8132
8385
  if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
8133
8386
  }
8134
8387
  }
@@ -8154,7 +8407,7 @@ var TypeAnalyzer = class {
8154
8407
  if (position < 0) return void 0;
8155
8408
  const members = this.expand(argTypes[position]).types;
8156
8409
  if (members.length > 32) return void 0;
8157
- const rank = (f) => ((f.typeParams?.length ?? 0) > 0 ? fns.length : 0) + fns.indexOf(f);
8410
+ const rank = (f) => overloadTier(f) * fns.length + fns.indexOf(f);
8158
8411
  const limit = picked ? rank(picked) : Infinity;
8159
8412
  const results = [];
8160
8413
  for (const member of members) {
@@ -8547,10 +8800,38 @@ var TypeAnalyzer = class {
8547
8800
  returns,
8548
8801
  void 0,
8549
8802
  names,
8550
- this.resolvePredicate(func.predicate, params)
8803
+ func.predicate || func.returnType ? this.resolvePredicate(func.predicate, params) : this.inferPredicate(func, params, returns, bodyEnv)
8551
8804
  ), func.params);
8552
8805
  });
8553
8806
  }
8807
+ /** `v => v ~= nil` is a guard without saying so, as TypeScript reads it:
8808
+ * a body that is one `return` of a boolean which, true, narrows a
8809
+ * parameter — and, false, leaves it exactly what that excludes. Then
8810
+ * `list:filter(v => v ~= nil)` knows what it kept. */
8811
+ inferPredicate(func, params, returns, env) {
8812
+ const only = func.body.statements.length === 1 ? func.body.statements[0] : void 0;
8813
+ if (only?.type !== "ReturnStatement" || !only.argument) return void 0;
8814
+ if (returns.kind === "any" || returns.kind === "never" || !isAssignable(returns, booleanType)) return void 0;
8815
+ const cond = only.argument;
8816
+ for (let i = 0; i < params.length; i++) {
8817
+ const p = func.params[i];
8818
+ if (p.pattern || p.rest) continue;
8819
+ const id = this.bindingIdByName(p.name, p);
8820
+ if (id === void 0) continue;
8821
+ const declared = params[i].type;
8822
+ const key = bindKey(id);
8823
+ const t = forkEnv(env);
8824
+ const f = forkEnv(env);
8825
+ this.silently(() => this.applyNarrowing(cond, env, t, f));
8826
+ const yes = t.get(key);
8827
+ if (!yes || equalTypes(yes, declared) || !isAssignable(yes, declared)) continue;
8828
+ const no = this.expand(f.get(key) ?? declared);
8829
+ const rest = no.kind === "union" ? no.types : [no];
8830
+ if (rest.some((m) => m.kind !== "never" && isAssignable(m, yes))) continue;
8831
+ return { param: i, type: yes, asserts: false };
8832
+ }
8833
+ return void 0;
8834
+ }
8554
8835
  /** Where the return types of the function being walked are collected, so
8555
8836
  * each is read where it is written — inside the branch that narrowed it —
8556
8837
  * rather than in whatever state the body ends in. */
@@ -8981,28 +9262,122 @@ var TypeAnalyzer = class {
8981
9262
  this.resolvingAliases.delete(t.name);
8982
9263
  }
8983
9264
  }
8984
- /** `names:filter(f)`, `text:trim()` — the methods arrays and strings have.
8985
- * They are written in the prelude as `ArrayMethods<T>` and
8986
- * `StringMethods`, so a file (or a type library) that declares one of
8987
- * those names again replaces the whole set, and nothing here is a special
8988
- * case in the analyzer. The build lowers each call to a plain function. */
8989
- builtInMethod(t, name) {
8990
- const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
8991
- const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(m.elements) : void 0);
8992
- const element = elements.every((e) => e !== void 0) ? union(elements) : void 0;
8993
- const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
8994
- const methodTable = element !== void 0 ? "ArrayMethods" : parts.every(isString) ? "StringMethods" : void 0;
8995
- const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
8996
- if (!def || def.class) return void 0;
8997
- const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
8998
- const layers = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
8999
- for (let i = layers.length - 1; i >= 0; i--) {
9000
- const part = layers[i];
9001
- const property = part.kind === "object" ? part.properties.get(name) : void 0;
9002
- if (property) return property.type;
9265
+ // --------------------------------------------------------
9266
+ // Metatables
9267
+ // --------------------------------------------------------
9268
+ harvestMetatables(block, source) {
9269
+ for (const stmt of block.statements) {
9270
+ if (stmt.type === "DeclareMetatableStatement") this.metatables.push({ node: stmt, source });
9271
+ }
9272
+ }
9273
+ resolvedMetatable(entry) {
9274
+ entry.resolved ??= this.withTypeParams(entry.node.generics, () => ({
9275
+ target: this.resolveType(entry.node.target),
9276
+ metatable: this.resolveType(entry.node.metatable)
9277
+ }));
9278
+ return entry.resolved;
9279
+ }
9280
+ /** Does a value (`whole`, whose union members are `parts`) have the
9281
+ * metatable `entry` declares — and if so how specifically, and what its
9282
+ * type parameters stand for there. Three kinds of target:
9283
+ *
9284
+ * - `<T> T[]`: every array and tuple, `T` what they hold;
9285
+ * - `<T extends C> T`: every value that is a `C` other than a class
9286
+ * instance, which has a metatable of its own; `T` the value itself;
9287
+ * - a type with no parameters (`string`): every value that is one.
9288
+ *
9289
+ * A bare parameter matches the most, so it is the least specific: an
9290
+ * array that is also a `{}` takes an array's metatable. */
9291
+ matchMetatable(entry, whole, parts) {
9292
+ if (!parts.length || parts.some((m) => m.kind === "any" || m.kind === "unknown" || m.kind === "never")) return void 0;
9293
+ const { target } = this.resolvedMetatable(entry);
9294
+ if (target.kind === "array" && target.element.kind === "typeParam") {
9295
+ const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(tupleMembers(m)) : void 0);
9296
+ if (!elements.every((e) => e !== void 0)) return void 0;
9297
+ return { specificity: 2, bindings: /* @__PURE__ */ new Map([[target.element.name, union(elements)]]) };
9298
+ }
9299
+ if (target.kind === "typeParam") {
9300
+ const constraint = target.constraint;
9301
+ if (parts.some((m) => m.kind === "object" && m.class)) return void 0;
9302
+ if (constraint && !parts.every((m) => isAssignable(m, constraint))) return void 0;
9303
+ return { specificity: 1, bindings: /* @__PURE__ */ new Map([[target.name, whole]]) };
9304
+ }
9305
+ if (containsTypeParam(target)) return void 0;
9306
+ return parts.every((m) => isAssignable(m, target)) ? { specificity: 2, bindings: /* @__PURE__ */ new Map() } : void 0;
9307
+ }
9308
+ /** The metatables `t` has: every declaration of the most specific target
9309
+ * it matches, instantiated for it, in declaration order — a later one
9310
+ * adds to an earlier, the way a library layers on another. A type
9311
+ * parameter is read at its constraint. */
9312
+ metatablesOf(t) {
9313
+ if (!this.metatables.length) return [];
9314
+ const cached = this.metatableCache.get(t);
9315
+ if (cached) return cached;
9316
+ const expanded = this.expand(t);
9317
+ const parts = (expanded.kind === "union" ? expanded.types : [expanded]).map((m) => this.expand(m)).map((m) => m.kind === "typeParam" && m.constraint ? this.expand(m.constraint) : m);
9318
+ const whole = union(parts);
9319
+ const matches = this.metatables.flatMap((entry) => {
9320
+ const match = this.matchMetatable(entry, whole, parts);
9321
+ return match ? [{ entry, ...match }] : [];
9322
+ });
9323
+ const best = Math.max(0, ...matches.map((m) => m.specificity));
9324
+ const found = matches.filter((m) => m.specificity === best).map(({ entry, bindings }) => ({
9325
+ type: this.expand(this.reduceType(substitute(this.resolvedMetatable(entry).metatable, bindings))),
9326
+ source: entry.source
9327
+ }));
9328
+ this.metatableCache.set(t, found);
9329
+ return found;
9330
+ }
9331
+ /** The `__index` tables of `t`'s metatables, each with the library that
9332
+ * declared it, in declaration order. */
9333
+ metatableIndexes(t) {
9334
+ return this.metatablesOf(t).flatMap(({ type, source }) => {
9335
+ const parts = type.kind === "intersection" ? type.types.map((m) => this.expand(m)) : [type];
9336
+ return parts.flatMap((part) => {
9337
+ const index = part.kind === "object" ? part.properties.get("__index") : void 0;
9338
+ if (!index) return [];
9339
+ const table = this.expand(index.type);
9340
+ return (table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table]).map((m) => ({ table: m, source }));
9341
+ });
9342
+ });
9343
+ }
9344
+ /** `name` read through `t`'s metatable: `text:upper()` is `upper` in the
9345
+ * string metatable's `__index`. The last declaration to give the name
9346
+ * wins. The language's own metatables are declared in the prelude, in
9347
+ * tilua, like any library's; a library adds to them the same way. */
9348
+ metatableMember(t, name) {
9349
+ const indexes = this.metatableIndexes(t);
9350
+ for (let i = indexes.length - 1; i >= 0; i--) {
9351
+ const { table, source } = indexes[i];
9352
+ const property = table.kind === "object" ? table.properties.get(name) : void 0;
9353
+ if (property) return { type: property.type, source };
9003
9354
  }
9004
9355
  return void 0;
9005
9356
  }
9357
+ metatableMethod(t, name) {
9358
+ return this.metatableMember(t, name)?.type;
9359
+ }
9360
+ hasMetatable(t) {
9361
+ return this.metatablesOf(t).length > 0;
9362
+ }
9363
+ metatableMembers(t) {
9364
+ const members = /* @__PURE__ */ new Map();
9365
+ for (const { table } of this.metatableIndexes(t)) {
9366
+ if (table.kind !== "object") continue;
9367
+ for (const [name, property] of table.properties) members.set(name, property.type);
9368
+ }
9369
+ return members;
9370
+ }
9371
+ /** `text:upper()`, `scores:keys()`: the method a value's metatable gives
9372
+ * it, when the value has no member of that name of its own. Called with
9373
+ * `:`, it answers before an object's indexer, since a metatable's
9374
+ * `__index` is only read for a key the table does not hold. */
9375
+ receiverMetatableMember(objType, name) {
9376
+ const t = this.expand(objType);
9377
+ const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
9378
+ if (parts.some((m) => m.kind === "object" && m.properties.has(name))) return void 0;
9379
+ return this.metatableMember(t, name);
9380
+ }
9006
9381
  /** The most a deferred type could turn out to be. A conditional is one of
9007
9382
  * its branches, and the true branch stands for a member of what was
9008
9383
  * tested (`T` in `T extends U ? T : never`); an indexed access reads
@@ -9068,7 +9443,7 @@ var TypeAnalyzer = class {
9068
9443
  if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
9069
9444
  const parts = this.stringParts(object);
9070
9445
  if (!parts) return void 0;
9071
- const found = parts.map((part) => this.builtInMethod(part, index.value));
9446
+ const found = parts.map((part) => this.metatableMethod(part, index.value));
9072
9447
  return found.every((t) => t !== void 0) ? union(found) : void 0;
9073
9448
  }
9074
9449
  /** The members of `t` when every one of them is a string — `"a" | "b"` is
@@ -9089,13 +9464,13 @@ var TypeAnalyzer = class {
9089
9464
  * not one of them is a mistake worth reporting, rather than the nil Lua
9090
9465
  * would hand back. */
9091
9466
  checkStringMember(node, object, key) {
9092
- if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
9467
+ if (!this.emitDiagnostics || !this.hasMetatable(stringType)) return;
9093
9468
  const parts = this.stringParts(object);
9094
9469
  if (!parts) return;
9095
9470
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
9096
9471
  if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
9097
9472
  const names = keys.map((m) => String(m.value));
9098
- if (names.some((name) => parts.some((part) => this.builtInMethod(part, name)))) return;
9473
+ if (names.some((name) => parts.some((part) => this.metatableMethod(part, name)))) return;
9099
9474
  this.diagnostics.push({
9100
9475
  node,
9101
9476
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
@@ -9127,24 +9502,24 @@ var TypeAnalyzer = class {
9127
9502
  switch (part.kind) {
9128
9503
  case "primitive":
9129
9504
  if (part.name === "nil") continue;
9130
- if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
9131
- if (part.name === "string" && this.builtInMethod(part, name)) continue;
9505
+ if (part.name === "string" && !this.hasMetatable(part)) return false;
9506
+ if (part.name === "string" && this.metatableMethod(part, name)) continue;
9132
9507
  break;
9133
9508
  case "literal":
9134
9509
  case "templateLiteral":
9135
9510
  if (part.kind === "templateLiteral" || part.base === "string") {
9136
- if (!this.aliasDefs.has("StringMethods")) return false;
9511
+ if (!this.hasMetatable(part)) return false;
9137
9512
  }
9138
- if (this.builtInMethod(part, name)) continue;
9513
+ if (this.metatableMethod(part, name)) continue;
9139
9514
  break;
9140
9515
  case "array":
9141
9516
  case "tuple":
9142
- if (!this.aliasDefs.has("ArrayMethods")) return false;
9143
- if (this.builtInMethod(part, name)) continue;
9517
+ if (!this.hasMetatable(part)) return false;
9518
+ if (this.metatableMethod(part, name)) continue;
9144
9519
  break;
9145
9520
  case "object":
9146
9521
  if (part.properties.has(name)) continue;
9147
- if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
9522
+ if (part.indexer || part.properties.size === 0 && !part.class || this.metatableMethod(part, name)) return false;
9148
9523
  break;
9149
9524
  default:
9150
9525
  return false;
@@ -9279,10 +9654,10 @@ var TypeAnalyzer = class {
9279
9654
  if (p) return p.optional ? optional(p.type) : p.type;
9280
9655
  if (t.indexer) return t.indexer.value;
9281
9656
  }
9282
- const built = this.builtInMethod(t, name);
9657
+ const built = this.metatableMethod(t, name);
9283
9658
  if (built) return built;
9284
9659
  if (t.kind === "union") {
9285
- const built2 = this.builtInMethod(t, name);
9660
+ const built2 = this.metatableMethod(t, name);
9286
9661
  if (built2) return built2;
9287
9662
  return union(t.types.map((m) => this.propertyType(m, name)));
9288
9663
  }
@@ -9578,6 +9953,27 @@ var TypeAnalyzer = class {
9578
9953
  elseEnv = whenFalse;
9579
9954
  }
9580
9955
  branches.push(this.infer(expr.alternate, elseEnv));
9956
+ const bodies = [...expr.clauses.map((c) => c.body), expr.alternate].map(unwrapParens);
9957
+ const isEmpty = (e) => e.type === "TableExpression" && e.fields.length === 0 || e.type === "ArrayExpression" && e.elements.length === 0;
9958
+ const others = union(branches.filter((_, i) => !isEmpty(bodies[i])));
9959
+ const parts = (others.kind === "union" ? others.types : [others]).map((m) => this.expand(m)).filter((m) => !(m.kind === "primitive" && m.name === "nil"));
9960
+ const fits = (e) => {
9961
+ if (!parts.length) return void 0;
9962
+ if (e.type === "ArrayExpression") {
9963
+ if (parts.every((m) => m.kind === "array")) return others;
9964
+ return parts.every((m) => m.kind === "array" || m.kind === "tuple") ? tuple([]) : void 0;
9965
+ }
9966
+ return parts.every((m) => m.kind === "object" && !m.class && [...m.properties.values()].every((prop) => prop.optional)) ? others : void 0;
9967
+ };
9968
+ if (bodies.some(isEmpty) && others.kind !== "never" && others.kind !== "any" && bodies.every((body) => !isEmpty(body) || fits(body))) {
9969
+ const typed = bodies.map((body, i) => {
9970
+ if (!isEmpty(body)) return branches[i];
9971
+ const t = fits(body);
9972
+ this.typeOf.set(body, t);
9973
+ return t;
9974
+ });
9975
+ return union(typed);
9976
+ }
9581
9977
  return union(branches);
9582
9978
  }
9583
9979
  }
@@ -9683,7 +10079,9 @@ var TypeAnalyzer = class {
9683
10079
  this.checkPrivateMember(expr.method, objType, expr.method.name);
9684
10080
  this.checkStringMember(expr.method, objType, literal(expr.method.name));
9685
10081
  this.checkMissingMember(expr.method, objType, expr.method.name);
9686
- const method = this.propertyType(objType, expr.method.name);
10082
+ const fromMetatable = this.receiverMetatableMember(objType, expr.method.name);
10083
+ if (fromMetatable) this.methodSources.set(expr, fromMetatable.source);
10084
+ const method = fromMetatable?.type ?? this.propertyType(objType, expr.method.name);
9687
10085
  const united = this.unionSignatures(method);
9688
10086
  const fns = united ?? this.overloadsOf(method);
9689
10087
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -9882,23 +10280,87 @@ var TypeAnalyzer = class {
9882
10280
  if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9883
10281
  const elems = [];
9884
10282
  let hadSpread = false;
10283
+ const written = [];
9885
10284
  for (const el of expr.elements) {
9886
10285
  if (el.type === "SpreadElement") {
9887
10286
  hadSpread = true;
9888
- const s = this.infer(el.argument, env);
9889
- if (s.kind === "array") elems.push(s.element);
9890
- else if (s.kind === "tuple") elems.push(...s.elements);
9891
- else elems.push(unknownType);
10287
+ const spread = this.infer(el.argument, env);
10288
+ written.push({ spread: true, type: spread });
10289
+ elems.push(...this.spreadElements(spread));
9892
10290
  } else {
9893
- elems.push(asConst ? this.inferAsConst(el, env) : this.infer(el, env));
10291
+ const t = asConst ? this.inferAsConst(el, env) : this.infer(el, env);
10292
+ written.push({ spread: false, type: asConst ? t : this.widenUnlessAsked(t, el) });
10293
+ elems.push(t);
9894
10294
  }
9895
10295
  }
9896
10296
  if (asConst && !hadSpread) return tuple(elems);
10297
+ if (hadSpread) {
10298
+ const tuples = this.spreadTuples(written);
10299
+ if (tuples) return union(tuples.map((elements) => tuple(elements)));
10300
+ }
9897
10301
  return arrayOf(elems.length ? union(elems.map((t, i) => {
9898
10302
  const element = expr.elements[i];
9899
10303
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
9900
10304
  })) : unknownType);
9901
10305
  }
10306
+ /** The tuples an array literal with spreads is, when every spread is a
10307
+ * tuple of known length or a union of them: one per way the unions go.
10308
+ * `undefined` when a spread's length is not known (an array, a tuple
10309
+ * with a rest), or the ways multiply past what is worth listing. */
10310
+ spreadTuples(written) {
10311
+ const MAX = 32;
10312
+ let variants = [[]];
10313
+ for (const { spread, type } of written) {
10314
+ if (!spread) {
10315
+ variants = variants.map((v) => [...v, type]);
10316
+ continue;
10317
+ }
10318
+ const options = this.fixedTuples(type);
10319
+ if (!options) return void 0;
10320
+ const next = [];
10321
+ for (const v of variants) for (const option of options) next.push([...v, ...option]);
10322
+ if (next.length > MAX) return void 0;
10323
+ variants = next;
10324
+ }
10325
+ return variants;
10326
+ }
10327
+ /** A tuple of known length's members, or a union of such tuples' — each
10328
+ * a way the value can be. `nil` in a union spreads nothing. */
10329
+ fixedTuples(t, depth = 0) {
10330
+ if (depth > 8) return void 0;
10331
+ const held = this.expand(t);
10332
+ if (held.kind === "tuple") return held.rest ? void 0 : [held.elements];
10333
+ if (held.kind !== "union") return void 0;
10334
+ const out = [];
10335
+ for (const member of held.types) {
10336
+ if (member.kind === "primitive" && member.name === "nil") continue;
10337
+ const options = this.fixedTuples(member, depth + 1);
10338
+ if (!options) return void 0;
10339
+ out.push(...options);
10340
+ }
10341
+ return out.length ? out : void 0;
10342
+ }
10343
+ /** What `...xs` puts into an array: an array's element, a tuple's members,
10344
+ * and for a union each member's — `...(host ? extra : [])` adds `extra`'s
10345
+ * elements and the empty tuple nothing. Anything else, `unknown`. */
10346
+ spreadElements(t, depth = 0) {
10347
+ const held = this.expand(t);
10348
+ if (depth > 8) return [unknownType];
10349
+ switch (held.kind) {
10350
+ case "array":
10351
+ return [held.element];
10352
+ case "tuple":
10353
+ return tupleMembers(held);
10354
+ case "union":
10355
+ return held.types.filter((m) => !(m.kind === "primitive" && m.name === "nil")).flatMap((m) => this.spreadElements(m, depth + 1));
10356
+ case "typeParam":
10357
+ return held.constraint ? this.spreadElements(held.constraint, depth + 1) : [unknownType];
10358
+ case "any":
10359
+ return [anyType];
10360
+ default:
10361
+ return [unknownType];
10362
+ }
10363
+ }
9902
10364
  /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
9903
10365
  * tuple's, and what comes after them — more elements, or a spread — is
9904
10366
  * its rest. */
@@ -9909,10 +10371,7 @@ var TypeAnalyzer = class {
9909
10371
  expr.elements.forEach((element, i) => {
9910
10372
  if (element.type === "SpreadElement") {
9911
10373
  spread = true;
9912
- const held = this.expand(this.infer(element.argument, env));
9913
- if (held.kind === "array") tail.push(held.element);
9914
- else if (held.kind === "tuple") tail.push(...tupleMembers(held));
9915
- else tail.push(unknownType);
10374
+ tail.push(...this.spreadElements(this.infer(element.argument, env)));
9916
10375
  return;
9917
10376
  }
9918
10377
  const t = this.widenUnlessAsked(this.infer(element, env), element);
@@ -9929,6 +10388,7 @@ var TypeAnalyzer = class {
9929
10388
  * The context was recorded by `applyContext` before the value was
9930
10389
  * inferred, so this is a lookup rather than a second pass. */
9931
10390
  widenUnlessAsked(value, at) {
10391
+ if (!writesLiteral(at)) return value;
9932
10392
  const wanted = this.expectedTypeOf.get(at);
9933
10393
  if (value.kind === "literal") {
9934
10394
  const arms = this.contextArms.get(at);
@@ -10925,6 +11385,8 @@ function containsTypeQuery(node) {
10925
11385
  return found;
10926
11386
  }
10927
11387
  var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
11388
+ var OBJECT_INTRINSICS = /* @__PURE__ */ new Set(["ObjectKeys", "ObjectValues", "ObjectEntries"]);
11389
+ var INTRINSICS = /* @__PURE__ */ new Set([...STRING_INTRINSICS, ...OBJECT_INTRINSICS]);
10928
11390
  function briefType(t) {
10929
11391
  if (t.kind === "union" && t.types.length > 8) {
10930
11392
  const shown = t.types.slice(0, 6).map(formatType).join(" | ");
@@ -11132,7 +11594,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
11132
11594
  }
11133
11595
  addFile(entryFile);
11134
11596
  const lowering = loweringModule(directory, host, problems, config);
11135
- if (lowering) lowerings.push(lowering);
11597
+ const library = files.findIndex((file) => pathKey(file) === pathKey(entryFile));
11598
+ if (lowering) lowerings.push({ ...lowering, library });
11136
11599
  };
11137
11600
  for (const entry of config.types) {
11138
11601
  const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);