@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.cjs CHANGED
@@ -734,7 +734,8 @@ var DECLARATION_STATEMENTS = /* @__PURE__ */ new Set([
734
734
  "ExportNamedStatement",
735
735
  "ExportAllStatement",
736
736
  "DeclareStatement",
737
- "DeclareClassStatement"
737
+ "DeclareClassStatement",
738
+ "DeclareMetatableStatement"
738
739
  ]);
739
740
  function spanFrom(start, end) {
740
741
  return {
@@ -1352,6 +1353,9 @@ var Parser = class {
1352
1353
  if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
1353
1354
  return this.parseDeclareClassStatement();
1354
1355
  }
1356
+ if (p1.type === "Identifier" && p1.value === "metatable" && !this.punctuatorAt(2, ":")) {
1357
+ return this.parseDeclareMetatableStatement();
1358
+ }
1355
1359
  if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
1356
1360
  return this.parseDeclareStatement();
1357
1361
  }
@@ -1404,6 +1408,17 @@ var Parser = class {
1404
1408
  }
1405
1409
  return tokenIdentifier(first);
1406
1410
  }
1411
+ // `declare metatable<T> T[]: { __index: { ... } }`
1412
+ parseDeclareMetatableStatement() {
1413
+ const start = this.current();
1414
+ this.advance();
1415
+ this.advance();
1416
+ const generics = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
1417
+ const target = this.parseType();
1418
+ this.expectPunctuator(":");
1419
+ const metatable = this.parseType();
1420
+ return { type: "DeclareMetatableStatement", generics, target, metatable, ...spanFrom(start, this.previous()) };
1421
+ }
1407
1422
  // `declare class Name extends Base { member: T, ... }`
1408
1423
  parseDeclareClassStatement() {
1409
1424
  const start = this.current();
@@ -2112,6 +2127,14 @@ var Parser = class {
2112
2127
  parseExpressionStatement() {
2113
2128
  const start = this.current();
2114
2129
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
2130
+ const call = this.tryParse(() => {
2131
+ const errors = this.errors.length;
2132
+ const expression2 = this.parseAtom();
2133
+ if (expression2.type !== "MethodCallExpression" && expression2.type !== "CallExpression") throw new ParseRecover("not a call");
2134
+ if (this.errors.length > errors) throw new ParseRecover("not a call");
2135
+ return expression2;
2136
+ });
2137
+ if (call) return { type: "CallStatement", expression: call, ...spanFrom(start, this.previous()) };
2115
2138
  const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
2116
2139
  return this.parseAssignmentRest(start, target);
2117
2140
  }
@@ -2433,11 +2456,9 @@ var Parser = class {
2433
2456
  if (t.type === "Identifier" && t.value === "class") {
2434
2457
  return this.parseClassExpression();
2435
2458
  }
2436
- if (t.type === "Punctuator" && t.value === "{") {
2437
- return this.parseTableExpression();
2438
- }
2439
- if (t.type === "Punctuator" && t.value === "[") {
2440
- return this.parseArrayExpression();
2459
+ if (t.type === "Punctuator" && (t.value === "{" || t.value === "[")) {
2460
+ const literal2 = t.value === "{" ? this.parseTableExpression() : this.parseArrayExpression();
2461
+ return this.checkPunctuator(":") && this.startsMethodCall() ? this.parseSuffixes(literal2) : literal2;
2441
2462
  }
2442
2463
  if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
2443
2464
  if (this.checkPunctuator("(") || this.checkOperator("<")) {
@@ -2514,6 +2535,11 @@ var Parser = class {
2514
2535
  } else {
2515
2536
  this.error("Expected identifier or '('");
2516
2537
  }
2538
+ return this.parseSuffixes(base);
2539
+ }
2540
+ /** What follows a prefix expression's base: `.name`, `[key]`, `:m(...)`,
2541
+ * a call, and their optional forms. */
2542
+ parseSuffixes(base) {
2517
2543
  while (true) {
2518
2544
  if (this.checkPunctuator("?") && this.touchesNext()) {
2519
2545
  const next = this.peek(1);
@@ -3867,6 +3893,10 @@ var Analyzer = class {
3867
3893
  moduleScope = this.globalScope;
3868
3894
  /** How many function bodies enclose the walk. */
3869
3895
  functionDepth = 0;
3896
+ /** How many loops enclose the walk inside the nearest function body. A
3897
+ * function starts again at 0: `continue` in `xs:map(x => ...)` has no
3898
+ * loop to go on with, whatever loop the call is written in. */
3899
+ loopDepth = 0;
3870
3900
  /** Function declarations already declared by their block's hoisting, with
3871
3901
  * the function depth of that block. */
3872
3902
  hoisted = /* @__PURE__ */ new Map();
@@ -4219,11 +4249,11 @@ var Analyzer = class {
4219
4249
  return;
4220
4250
  case "WhileStatement":
4221
4251
  this.visitExpression(stmt.condition, scope);
4222
- this.visitBlockInNewScope(stmt.body, scope);
4252
+ this.inLoop(() => this.visitBlockInNewScope(stmt.body, scope));
4223
4253
  return;
4224
4254
  case "RepeatStatement": {
4225
4255
  const bodyScope = childScope(scope);
4226
- this.visitBlock(stmt.body, bodyScope);
4256
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4227
4257
  this.visitExpression(stmt.condition, bodyScope);
4228
4258
  return;
4229
4259
  }
@@ -4241,14 +4271,14 @@ var Analyzer = class {
4241
4271
  if (stmt.step) this.visitExpression(stmt.step, scope);
4242
4272
  const bodyScope = childScope(scope);
4243
4273
  this.declare(bodyScope, stmt.variable.name, "for-numeric", stmt.variable);
4244
- this.visitBlock(stmt.body, bodyScope);
4274
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4245
4275
  return;
4246
4276
  }
4247
4277
  case "GenericForStatement": {
4248
4278
  this.visitExpression(stmt.iterator, scope);
4249
4279
  const bodyScope = childScope(scope);
4250
4280
  this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
4251
- this.visitBlock(stmt.body, bodyScope);
4281
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4252
4282
  return;
4253
4283
  }
4254
4284
  case "ReturnStatement":
@@ -4256,6 +4286,15 @@ var Analyzer = class {
4256
4286
  return;
4257
4287
  case "BreakStatement":
4258
4288
  case "ContinueStatement":
4289
+ if (this.loopDepth === 0) {
4290
+ const word = stmt.type === "BreakStatement" ? "break" : "continue";
4291
+ this.diagnostics.push({
4292
+ node: stmt,
4293
+ 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`,
4294
+ kind: "outside-loop"
4295
+ });
4296
+ }
4297
+ return;
4259
4298
  case "ErrorStatement":
4260
4299
  return;
4261
4300
  case "DeclareStatement":
@@ -4264,6 +4303,11 @@ var Analyzer = class {
4264
4303
  case "DeclareClassStatement":
4265
4304
  this.visitType(stmt.body, scope);
4266
4305
  return;
4306
+ case "DeclareMetatableStatement":
4307
+ this.visitGenerics(stmt.generics, scope);
4308
+ this.visitType(stmt.target, scope);
4309
+ this.visitType(stmt.metatable, scope);
4310
+ return;
4267
4311
  case "TypeAliasStatement":
4268
4312
  case "ExportTypeAliasStatement":
4269
4313
  this.visitGenerics(
@@ -4306,6 +4350,15 @@ var Analyzer = class {
4306
4350
  return;
4307
4351
  }
4308
4352
  }
4353
+ /** Visit a loop's body, where `break` and `continue` have a loop. */
4354
+ inLoop(visit) {
4355
+ this.loopDepth++;
4356
+ try {
4357
+ visit();
4358
+ } finally {
4359
+ this.loopDepth--;
4360
+ }
4361
+ }
4309
4362
  // ---------------- functions ----------------
4310
4363
  visitFunctionBody(func, outerScope, isMethod = false) {
4311
4364
  const fnScope = childScope(outerScope);
@@ -4321,11 +4374,14 @@ var Analyzer = class {
4321
4374
  }
4322
4375
  });
4323
4376
  this.visitType(func.returnType, fnScope);
4377
+ const loops = this.loopDepth;
4324
4378
  this.functionDepth++;
4379
+ this.loopDepth = 0;
4325
4380
  try {
4326
4381
  this.visitBlock(func.body, fnScope);
4327
4382
  } finally {
4328
4383
  this.functionDepth--;
4384
+ this.loopDepth = loops;
4329
4385
  }
4330
4386
  }
4331
4387
  /** An overload signature: no body and no bindings, but its types can hold
@@ -4529,6 +4585,104 @@ type Mutable<T> = { -readonly [K in keyof T]: T[K] }
4529
4585
  type Pick<T, K> = { [P in K]: T[P] }
4530
4586
  type Omit<T, K> = Pick<T, Exclude<keyof T, K>>
4531
4587
  type Record<K, V> = { [P in K]: V }
4588
+
4589
+ -- ============================================================
4590
+ -- The metatables of the language's own values
4591
+ -- ============================================================
4592
+
4593
+ -- A string's methods. The first group is Lua's string library, which a
4594
+ -- string really answers to; where Lua answers several values, tilua answers
4595
+ -- one, as the global functions do (\`find\` is an array, \`gsub\` the new
4596
+ -- string). The second group is written the way JavaScript writes it.
4597
+ --
4598
+ -- Positions are Lua's: \`sub\` and \`slice\` count from 1, and \`indexOf\` is
4599
+ -- \`nil\` when the text is not there.
4600
+ declare metatable string: {
4601
+ __index: {
4602
+ upper: (self: string) => string,
4603
+ lower: (self: string) => string,
4604
+ len: (self: string) => number,
4605
+ sub: (self: string, i: number, j?: number) => string,
4606
+ rep: (self: string, n: number, separator?: string) => string,
4607
+ reverse: (self: string) => string,
4608
+ format: (self: string, ...args: unknown[]) => string,
4609
+ byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),
4610
+ find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,
4611
+ match: (self: string, pattern: string, init?: number) => [...string[]] | nil,
4612
+ gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,
4613
+ gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,
4614
+
4615
+ trim: (self: string) => string,
4616
+ trimStart: (self: string) => string,
4617
+ trimEnd: (self: string) => string,
4618
+ startsWith: (self: string, text: string) => boolean,
4619
+ endsWith: (self: string, text: string) => boolean,
4620
+ includes: (self: string, text: string) => boolean,
4621
+ indexOf: (self: string, text: string, start?: number) => number | nil,
4622
+ slice: (self: string, start?: number, stop?: number) => string,
4623
+ replace: (self: string, text: string, replacement: string) => string,
4624
+ replaceAll: (self: string, text: string, replacement: string) => string,
4625
+ padStart: (self: string, length: number, padding?: string) => string,
4626
+ padEnd: (self: string, length: number, padding?: string) => string,
4627
+ },
4628
+ }
4629
+
4630
+ -- An array's methods. Lua gives an array no metatable: the compiler lowers
4631
+ -- each call to a plain function, so they work on any array, one a Lua
4632
+ -- library made included.
4633
+ --
4634
+ -- Indices are Lua's: the first element is 1, and \`indexOf\` / \`findIndex\`
4635
+ -- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
4636
+ -- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
4637
+ -- \`sort\` and \`reverse\` change the array; everything else returns a new one.
4638
+ -- A \`filter\` whose test is a guard \u2014 \`v is S\`, or \`v => v ~= nil\` \u2014
4639
+ -- answers an array of what it let through.
4640
+ declare metatable<T> T[]: {
4641
+ __index: {
4642
+ find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
4643
+ findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
4644
+ filter: (<S extends T>(self: T[], test: (value: T, index: number) => value is S) => S[])
4645
+ & ((self: T[], test: (value: T, index: number) => boolean) => T[]),
4646
+ map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
4647
+ forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
4648
+ some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
4649
+ every: (self: T[], test: (value: T, index: number) => boolean) => boolean,
4650
+ reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,
4651
+ includes: (self: T[], value: T) => boolean,
4652
+ indexOf: (self: T[], value: T, start?: number) => number | nil,
4653
+ join: (self: T[], separator?: string) => string,
4654
+ concat: (self: T[], ...others: T[][]) => T[],
4655
+ slice: (self: T[], start?: number, stop?: number) => T[],
4656
+ flat: (self: T[]) => T[],
4657
+ reverse: (self: T[]) => T[],
4658
+ sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],
4659
+ push: (self: T[], ...args: T[]) => number,
4660
+ pop: (self: T[]) => T | nil,
4661
+ shift: (self: T[]) => T | nil,
4662
+ unshift: (self: T[], ...args: T[]) => number,
4663
+ },
4664
+ }
4665
+
4666
+ -- A table's methods. Unlike JavaScript's \`Object.keys\`, they answer a
4667
+ -- tuple, in the order the table's type wrote its members:
4668
+ --
4669
+ -- const point = { x: 1, y: "up" }
4670
+ -- point:keys() -- ["x", "y"]
4671
+ -- point:values() -- [number, string]
4672
+ -- point:entries() -- [["x", number], ["y", string]]
4673
+ --
4674
+ -- A Lua table has no order of its own, so the compiler hands its runtime the
4675
+ -- keys as the type lists them. An optional member keeps its place, holding
4676
+ -- \`nil\` when it is missing; an indexer's keys follow the named ones. A
4677
+ -- member of the table's own with one of these names is what a call reaches,
4678
+ -- and a class instance, which has a metatable of its own, has none of them.
4679
+ declare metatable<T extends {}> T: {
4680
+ __index: {
4681
+ keys: (self: T) => ObjectKeys<T>,
4682
+ values: (self: T) => ObjectValues<T>,
4683
+ entries: (self: T) => ObjectEntries<T>,
4684
+ },
4685
+ }
4532
4686
  `;
4533
4687
  var prelude;
4534
4688
  function preludeProgram() {
@@ -4627,8 +4781,14 @@ function substitute(t, subst) {
4627
4781
  if (subst.size === 0) return t;
4628
4782
  if (!containsTypeParam(t)) return t;
4629
4783
  switch (t.kind) {
4630
- case "typeParam":
4631
- return subst.get(t.name) ?? t;
4784
+ case "typeParam": {
4785
+ const bound = subst.get(t.name);
4786
+ if (bound) return bound;
4787
+ if (t.constraint && containsTypeParam(t.constraint)) {
4788
+ return { ...t, constraint: substitute(t.constraint, new Map([...subst, [t.name, t]])) };
4789
+ }
4790
+ return t;
4791
+ }
4632
4792
  case "array":
4633
4793
  return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4634
4794
  case "tuple":
@@ -4773,6 +4933,10 @@ function unify(param, arg, vars, out, how = "union") {
4773
4933
  if (arg.kind === "function") {
4774
4934
  param.params.forEach((p, i) => arg.params[i] && unify(p.type, arg.params[i].type, vars, out));
4775
4935
  unify(param.returns, arg.returns, vars, out);
4936
+ const wanted = param.predicate, given = arg.predicate;
4937
+ if (wanted?.type && given?.type && wanted.param === given.param && !wanted.asserts && !given.asserts) {
4938
+ unify(wanted.type, given.type, vars, out);
4939
+ }
4776
4940
  }
4777
4941
  return;
4778
4942
  case "object":
@@ -5028,6 +5192,11 @@ function isAssignableInner(a, b) {
5028
5192
  for (let i = 0; i < n; i++) {
5029
5193
  if (!isAssignable(a.params[i].type, b.params[i].type) && !isAssignable(b.params[i].type, a.params[i].type)) return false;
5030
5194
  }
5195
+ const wanted = b.predicate;
5196
+ if (wanted?.type && !wanted.asserts) {
5197
+ const given = a.predicate;
5198
+ if (!given?.type || given.asserts || given.param !== wanted.param || !isAssignable(given.type, wanted.type)) return false;
5199
+ }
5031
5200
  return isAssignable(a.returns, b.returns);
5032
5201
  }
5033
5202
  if (b.kind === "typeParam") return false;
@@ -5162,7 +5331,7 @@ function containsFreeTypeParam(t, seen, bound) {
5162
5331
  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));
5163
5332
  case "function": {
5164
5333
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
5165
- return t.params.some((p) => containsTypeParam(p.type, seen, inner)) || !!t.varargs && containsTypeParam(t.varargs, seen, inner) || containsTypeParam(t.returns, seen, inner);
5334
+ 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);
5166
5335
  }
5167
5336
  case "genericRef":
5168
5337
  return t.typeArguments.some((a) => containsTypeParam(a, seen, bound));
@@ -5587,9 +5756,14 @@ function isFreshLiteralExpr(e) {
5587
5756
  case "StringLiteral":
5588
5757
  case "BooleanLiteral":
5589
5758
  case "InterpolatedStringExpression":
5759
+ return true;
5760
+ // A table or array literal widened the literals written in it as it
5761
+ // was read (`widenUnlessAsked`), and keeps what it read from
5762
+ // elsewhere — `{ key: name }`, `[...names]` — so there is nothing
5763
+ // left for the binding to widen.
5590
5764
  case "TableExpression":
5591
5765
  case "ArrayExpression":
5592
- return true;
5766
+ return false;
5593
5767
  case "ParenthesizedExpression":
5594
5768
  return isFreshLiteralExpr(e.expression);
5595
5769
  case "UnaryExpression":
@@ -5604,6 +5778,30 @@ function isFreshLiteralExpr(e) {
5604
5778
  return false;
5605
5779
  }
5606
5780
  }
5781
+ function overloadTier(f) {
5782
+ const takesGuard = f.params.some((p) => p.type.kind === "function" && p.type.predicate?.type && !p.type.predicate.asserts);
5783
+ if (takesGuard) return 0;
5784
+ return (f.typeParams?.length ?? 0) > 0 ? 2 : 1;
5785
+ }
5786
+ function writesLiteral(e) {
5787
+ switch (e.type) {
5788
+ case "NumberLiteral":
5789
+ case "StringLiteral":
5790
+ case "BooleanLiteral":
5791
+ case "InterpolatedStringExpression":
5792
+ return true;
5793
+ case "ParenthesizedExpression":
5794
+ return writesLiteral(e.expression);
5795
+ case "UnaryExpression":
5796
+ return writesLiteral(e.argument);
5797
+ case "IfElseExpression":
5798
+ return e.clauses.some((c) => writesLiteral(c.body)) || writesLiteral(e.alternate);
5799
+ case "BinaryExpression":
5800
+ return (e.operator === "or" || e.operator === "and") && (writesLiteral(e.left) || writesLiteral(e.right));
5801
+ default:
5802
+ return false;
5803
+ }
5804
+ }
5607
5805
  function collectInferNames(node) {
5608
5806
  const out = [];
5609
5807
  const walk = (n) => {
@@ -5894,6 +6092,10 @@ var TypeAnalyzer = class {
5894
6092
  typeOfTypeNode = /* @__PURE__ */ new Map();
5895
6093
  expectedTypeOf = /* @__PURE__ */ new Map();
5896
6094
  loops = /* @__PURE__ */ new Map();
6095
+ /** Every `declare metatable`, libraries first, in declaration order. */
6096
+ metatables = [];
6097
+ metatableCache = /* @__PURE__ */ new WeakMap();
6098
+ methodSources = /* @__PURE__ */ new Map();
5897
6099
  /** Public: each alias resolved once (generic aliases keep their params as
5898
6100
  * `typeParam` nodes in the body). */
5899
6101
  aliases = new LazyMap();
@@ -5960,6 +6162,9 @@ var TypeAnalyzer = class {
5960
6162
  this.registerNestedClasses();
5961
6163
  this.registerNestedAliases();
5962
6164
  for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
6165
+ this.harvestMetatables(preludeProgram().body, { origin: "language" });
6166
+ (this.options.libs ?? []).forEach((lib, i) => this.harvestMetatables(lib.body, { origin: "library", library: i }));
6167
+ this.harvestMetatables(this.program.body, { origin: "program" });
5963
6168
  this.registerImportedTypes();
5964
6169
  this.resolveAllAliases();
5965
6170
  this.harvestDeclares(this.program.body, true);
@@ -5991,6 +6196,8 @@ var TypeAnalyzer = class {
5991
6196
  expectedTypeOf: this.expectedTypeOf,
5992
6197
  aliases: this.resolveDeferredAliases(),
5993
6198
  loops: this.loops,
6199
+ methodSources: this.methodSources,
6200
+ metatableMembers: (type) => this.metatableMembers(type),
5994
6201
  diagnostics: this.diagnostics
5995
6202
  };
5996
6203
  }
@@ -6048,8 +6255,7 @@ var TypeAnalyzer = class {
6048
6255
  }
6049
6256
  /** `layering` is on for the prelude and for definitions files: a second
6050
6257
  * library that declares an alias already declared *adds* to it, the way a
6051
- * second `declare` of a table's name does, so `@tilua-types/roblox` can give
6052
- * `StringMethods` Luau's `split` without restating Lua's. The file being
6258
+ * second `declare` of a table's name does. The file being
6053
6259
  * analysed is not a layer: its own alias replaces what the libraries
6054
6260
  * gave, which is how a project opts out of a set. */
6055
6261
  registerAliasDefs(block, layering = false) {
@@ -6967,7 +7173,7 @@ var TypeAnalyzer = class {
6967
7173
  if (record.type === "TypeReference" && typeof record.base === "string") {
6968
7174
  const name = typeof record.namespace === "string" ? `${record.namespace}.${record.base}` : record.base;
6969
7175
  const resolved = this.typeOfTypeNode.get(node);
6970
- 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]);
7176
+ 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]);
6971
7177
  const at = node;
6972
7178
  const key = `${at.line.start}:${at.column.start}`;
6973
7179
  if (unresolved && !reported.has(key)) {
@@ -7085,7 +7291,7 @@ var TypeAnalyzer = class {
7085
7291
  const tp = this.lookupTypeParam(node.base);
7086
7292
  if (tp) return typeParam(tp.name, tp.constraint, tp.isConst);
7087
7293
  if (node.typeArguments.length === 1 && !this.aliasDefs.has(node.base)) {
7088
- const intrinsic = this.applyStringIntrinsic(
7294
+ const intrinsic = this.applyIntrinsic(
7089
7295
  node.base,
7090
7296
  this.resolveType(node.typeArguments[0])
7091
7297
  );
@@ -7292,7 +7498,7 @@ var TypeAnalyzer = class {
7292
7498
  return difference(this.reduceType(t.base), this.reduceType(t.excluded));
7293
7499
  case "genericRef": {
7294
7500
  if (t.typeArguments.length !== 1 || this.aliasDefs.has(t.name)) return t;
7295
- return this.applyStringIntrinsic(t.name, t.typeArguments[0]) ?? t;
7501
+ return this.applyIntrinsic(t.name, t.typeArguments[0]) ?? t;
7296
7502
  }
7297
7503
  case "conditional":
7298
7504
  return this.reduceConditional(t);
@@ -7405,6 +7611,41 @@ var TypeAnalyzer = class {
7405
7611
  }
7406
7612
  return union(combos.map((c) => literal(c)));
7407
7613
  }
7614
+ /** A type function the analyzer supplies, since tilua cannot write it. */
7615
+ applyIntrinsic(name, arg) {
7616
+ return this.applyStringIntrinsic(name, arg) ?? this.applyObjectIntrinsic(name, arg);
7617
+ }
7618
+ /** `ObjectKeys<T>`, `ObjectValues<T>`, `ObjectEntries<T>`: an object's
7619
+ * members as a tuple, in the order they were written — `{ a: 1, b: "x" }`
7620
+ * has keys `["a", "b"]`, values `[number, string]` and entries
7621
+ * `[["a", number], ["b", string]]`. TypeScript answers `string[]` for
7622
+ * `Object.keys`, since its objects have no order; tilua's lowering hands
7623
+ * the runtime the written order, so the tuple is what runs. An indexer's
7624
+ * keys follow the named ones, as the tuple's rest. */
7625
+ applyObjectIntrinsic(name, arg) {
7626
+ if (!OBJECT_INTRINSICS.has(name)) return void 0;
7627
+ const t = this.expand(this.reduceType(arg));
7628
+ if (containsTypeParam(t)) return void 0;
7629
+ if (t.kind === "union") return union(t.types.map((m) => this.applyObjectIntrinsic(name, m) ?? unknownType));
7630
+ const parts = t.kind === "intersection" ? t.types.map((m) => this.expand(m)) : [t];
7631
+ if (!parts.every((m) => m.kind === "object" && !m.class)) return unknownType;
7632
+ const properties = /* @__PURE__ */ new Map();
7633
+ let indexer;
7634
+ for (const part of parts) {
7635
+ for (const [key, property] of part.properties) properties.set(key, property);
7636
+ indexer ??= part.indexer;
7637
+ }
7638
+ const entries = [...properties].map(([key, p]) => [literal(key), p.optional ? optional(p.type) : p.type]);
7639
+ const listed = (elements, rest) => !elements.length && rest ? arrayOf(rest) : tuple(elements, rest);
7640
+ switch (name) {
7641
+ case "ObjectKeys":
7642
+ return listed(entries.map(([key]) => key), indexer?.key);
7643
+ case "ObjectValues":
7644
+ return listed(entries.map(([, value]) => value), indexer?.value);
7645
+ default:
7646
+ return listed(entries.map((entry) => tuple([...entry])), indexer && tuple([indexer.key, indexer.value]));
7647
+ }
7648
+ }
7408
7649
  /** TypeScript's four string intrinsics. They cannot be written in tilua —
7409
7650
  * there is no character-level type arithmetic — so the analyzer supplies
7410
7651
  * them, and only them, as named type functions. */
@@ -7943,7 +8184,9 @@ var TypeAnalyzer = class {
7943
8184
  const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
7944
8185
  if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
7945
8186
  const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
7946
- const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
8187
+ const spread = this.spreadOf(written, selfOf(fns[0]));
8188
+ const provisional = argTypes.map((t, i) => waits[i] ? anyType : t);
8189
+ const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), spread) ?? this.pickOverload(fns, provisional, (f) => extra(f, provisional), spread);
7947
8190
  const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
7948
8191
  written.forEach((a, i) => {
7949
8192
  if (!waits[i]) return;
@@ -8134,6 +8377,16 @@ var TypeAnalyzer = class {
8134
8377
  }
8135
8378
  const id = this.bindingIdByName(p.name, p);
8136
8379
  const t = this.paramType(p, env);
8380
+ if (p.default && p.typeAnnotation) {
8381
+ const given = this.infer(p.default, env);
8382
+ const declared = this.resolveType(p.typeAnnotation);
8383
+ if (this.emitDiagnostics && !isAssignable(given, declared)) {
8384
+ this.diagnostics.push({
8385
+ node: p.default,
8386
+ message: `Type '${briefType(given)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(given, declared)
8387
+ });
8388
+ }
8389
+ }
8137
8390
  if (id !== void 0) {
8138
8391
  this.bindingType.set(id, t);
8139
8392
  this.setBinding(env, id, t);
@@ -8231,9 +8484,9 @@ var TypeAnalyzer = class {
8231
8484
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
8232
8485
  * `<T>(v: T) -> string` and still pick the precise one. */
8233
8486
  pickOverload(fns, argTypes, argsFor, spread) {
8234
- for (const generic of [false, true]) {
8487
+ for (const tier of [0, 1, 2]) {
8235
8488
  for (const f of fns) {
8236
- if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
8489
+ if (overloadTier(f) !== tier) continue;
8237
8490
  if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
8238
8491
  }
8239
8492
  }
@@ -8259,7 +8512,7 @@ var TypeAnalyzer = class {
8259
8512
  if (position < 0) return void 0;
8260
8513
  const members = this.expand(argTypes[position]).types;
8261
8514
  if (members.length > 32) return void 0;
8262
- const rank = (f) => ((f.typeParams?.length ?? 0) > 0 ? fns.length : 0) + fns.indexOf(f);
8515
+ const rank = (f) => overloadTier(f) * fns.length + fns.indexOf(f);
8263
8516
  const limit = picked ? rank(picked) : Infinity;
8264
8517
  const results = [];
8265
8518
  for (const member of members) {
@@ -8652,10 +8905,38 @@ var TypeAnalyzer = class {
8652
8905
  returns,
8653
8906
  void 0,
8654
8907
  names,
8655
- this.resolvePredicate(func.predicate, params)
8908
+ func.predicate || func.returnType ? this.resolvePredicate(func.predicate, params) : this.inferPredicate(func, params, returns, bodyEnv)
8656
8909
  ), func.params);
8657
8910
  });
8658
8911
  }
8912
+ /** `v => v ~= nil` is a guard without saying so, as TypeScript reads it:
8913
+ * a body that is one `return` of a boolean which, true, narrows a
8914
+ * parameter — and, false, leaves it exactly what that excludes. Then
8915
+ * `list:filter(v => v ~= nil)` knows what it kept. */
8916
+ inferPredicate(func, params, returns, env) {
8917
+ const only = func.body.statements.length === 1 ? func.body.statements[0] : void 0;
8918
+ if (only?.type !== "ReturnStatement" || !only.argument) return void 0;
8919
+ if (returns.kind === "any" || returns.kind === "never" || !isAssignable(returns, booleanType)) return void 0;
8920
+ const cond = only.argument;
8921
+ for (let i = 0; i < params.length; i++) {
8922
+ const p = func.params[i];
8923
+ if (p.pattern || p.rest) continue;
8924
+ const id = this.bindingIdByName(p.name, p);
8925
+ if (id === void 0) continue;
8926
+ const declared = params[i].type;
8927
+ const key = bindKey(id);
8928
+ const t = forkEnv(env);
8929
+ const f = forkEnv(env);
8930
+ this.silently(() => this.applyNarrowing(cond, env, t, f));
8931
+ const yes = t.get(key);
8932
+ if (!yes || equalTypes(yes, declared) || !isAssignable(yes, declared)) continue;
8933
+ const no = this.expand(f.get(key) ?? declared);
8934
+ const rest = no.kind === "union" ? no.types : [no];
8935
+ if (rest.some((m) => m.kind !== "never" && isAssignable(m, yes))) continue;
8936
+ return { param: i, type: yes, asserts: false };
8937
+ }
8938
+ return void 0;
8939
+ }
8659
8940
  /** Where the return types of the function being walked are collected, so
8660
8941
  * each is read where it is written — inside the branch that narrowed it —
8661
8942
  * rather than in whatever state the body ends in. */
@@ -9086,28 +9367,122 @@ var TypeAnalyzer = class {
9086
9367
  this.resolvingAliases.delete(t.name);
9087
9368
  }
9088
9369
  }
9089
- /** `names:filter(f)`, `text:trim()` — the methods arrays and strings have.
9090
- * They are written in the prelude as `ArrayMethods<T>` and
9091
- * `StringMethods`, so a file (or a type library) that declares one of
9092
- * those names again replaces the whole set, and nothing here is a special
9093
- * case in the analyzer. The build lowers each call to a plain function. */
9094
- builtInMethod(t, name) {
9095
- const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
9096
- const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(m.elements) : void 0);
9097
- const element = elements.every((e) => e !== void 0) ? union(elements) : void 0;
9098
- const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
9099
- const methodTable = element !== void 0 ? "ArrayMethods" : parts.every(isString) ? "StringMethods" : void 0;
9100
- const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
9101
- if (!def || def.class) return void 0;
9102
- const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
9103
- const layers = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
9104
- for (let i = layers.length - 1; i >= 0; i--) {
9105
- const part = layers[i];
9106
- const property = part.kind === "object" ? part.properties.get(name) : void 0;
9107
- if (property) return property.type;
9370
+ // --------------------------------------------------------
9371
+ // Metatables
9372
+ // --------------------------------------------------------
9373
+ harvestMetatables(block, source) {
9374
+ for (const stmt of block.statements) {
9375
+ if (stmt.type === "DeclareMetatableStatement") this.metatables.push({ node: stmt, source });
9376
+ }
9377
+ }
9378
+ resolvedMetatable(entry) {
9379
+ entry.resolved ??= this.withTypeParams(entry.node.generics, () => ({
9380
+ target: this.resolveType(entry.node.target),
9381
+ metatable: this.resolveType(entry.node.metatable)
9382
+ }));
9383
+ return entry.resolved;
9384
+ }
9385
+ /** Does a value (`whole`, whose union members are `parts`) have the
9386
+ * metatable `entry` declares — and if so how specifically, and what its
9387
+ * type parameters stand for there. Three kinds of target:
9388
+ *
9389
+ * - `<T> T[]`: every array and tuple, `T` what they hold;
9390
+ * - `<T extends C> T`: every value that is a `C` other than a class
9391
+ * instance, which has a metatable of its own; `T` the value itself;
9392
+ * - a type with no parameters (`string`): every value that is one.
9393
+ *
9394
+ * A bare parameter matches the most, so it is the least specific: an
9395
+ * array that is also a `{}` takes an array's metatable. */
9396
+ matchMetatable(entry, whole, parts) {
9397
+ if (!parts.length || parts.some((m) => m.kind === "any" || m.kind === "unknown" || m.kind === "never")) return void 0;
9398
+ const { target } = this.resolvedMetatable(entry);
9399
+ if (target.kind === "array" && target.element.kind === "typeParam") {
9400
+ const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(tupleMembers(m)) : void 0);
9401
+ if (!elements.every((e) => e !== void 0)) return void 0;
9402
+ return { specificity: 2, bindings: /* @__PURE__ */ new Map([[target.element.name, union(elements)]]) };
9403
+ }
9404
+ if (target.kind === "typeParam") {
9405
+ const constraint = target.constraint;
9406
+ if (parts.some((m) => m.kind === "object" && m.class)) return void 0;
9407
+ if (constraint && !parts.every((m) => isAssignable(m, constraint))) return void 0;
9408
+ return { specificity: 1, bindings: /* @__PURE__ */ new Map([[target.name, whole]]) };
9409
+ }
9410
+ if (containsTypeParam(target)) return void 0;
9411
+ return parts.every((m) => isAssignable(m, target)) ? { specificity: 2, bindings: /* @__PURE__ */ new Map() } : void 0;
9412
+ }
9413
+ /** The metatables `t` has: every declaration of the most specific target
9414
+ * it matches, instantiated for it, in declaration order — a later one
9415
+ * adds to an earlier, the way a library layers on another. A type
9416
+ * parameter is read at its constraint. */
9417
+ metatablesOf(t) {
9418
+ if (!this.metatables.length) return [];
9419
+ const cached = this.metatableCache.get(t);
9420
+ if (cached) return cached;
9421
+ const expanded = this.expand(t);
9422
+ 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);
9423
+ const whole = union(parts);
9424
+ const matches = this.metatables.flatMap((entry) => {
9425
+ const match = this.matchMetatable(entry, whole, parts);
9426
+ return match ? [{ entry, ...match }] : [];
9427
+ });
9428
+ const best = Math.max(0, ...matches.map((m) => m.specificity));
9429
+ const found = matches.filter((m) => m.specificity === best).map(({ entry, bindings }) => ({
9430
+ type: this.expand(this.reduceType(substitute(this.resolvedMetatable(entry).metatable, bindings))),
9431
+ source: entry.source
9432
+ }));
9433
+ this.metatableCache.set(t, found);
9434
+ return found;
9435
+ }
9436
+ /** The `__index` tables of `t`'s metatables, each with the library that
9437
+ * declared it, in declaration order. */
9438
+ metatableIndexes(t) {
9439
+ return this.metatablesOf(t).flatMap(({ type, source }) => {
9440
+ const parts = type.kind === "intersection" ? type.types.map((m) => this.expand(m)) : [type];
9441
+ return parts.flatMap((part) => {
9442
+ const index = part.kind === "object" ? part.properties.get("__index") : void 0;
9443
+ if (!index) return [];
9444
+ const table = this.expand(index.type);
9445
+ return (table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table]).map((m) => ({ table: m, source }));
9446
+ });
9447
+ });
9448
+ }
9449
+ /** `name` read through `t`'s metatable: `text:upper()` is `upper` in the
9450
+ * string metatable's `__index`. The last declaration to give the name
9451
+ * wins. The language's own metatables are declared in the prelude, in
9452
+ * tilua, like any library's; a library adds to them the same way. */
9453
+ metatableMember(t, name) {
9454
+ const indexes = this.metatableIndexes(t);
9455
+ for (let i = indexes.length - 1; i >= 0; i--) {
9456
+ const { table, source } = indexes[i];
9457
+ const property = table.kind === "object" ? table.properties.get(name) : void 0;
9458
+ if (property) return { type: property.type, source };
9108
9459
  }
9109
9460
  return void 0;
9110
9461
  }
9462
+ metatableMethod(t, name) {
9463
+ return this.metatableMember(t, name)?.type;
9464
+ }
9465
+ hasMetatable(t) {
9466
+ return this.metatablesOf(t).length > 0;
9467
+ }
9468
+ metatableMembers(t) {
9469
+ const members = /* @__PURE__ */ new Map();
9470
+ for (const { table } of this.metatableIndexes(t)) {
9471
+ if (table.kind !== "object") continue;
9472
+ for (const [name, property] of table.properties) members.set(name, property.type);
9473
+ }
9474
+ return members;
9475
+ }
9476
+ /** `text:upper()`, `scores:keys()`: the method a value's metatable gives
9477
+ * it, when the value has no member of that name of its own. Called with
9478
+ * `:`, it answers before an object's indexer, since a metatable's
9479
+ * `__index` is only read for a key the table does not hold. */
9480
+ receiverMetatableMember(objType, name) {
9481
+ const t = this.expand(objType);
9482
+ const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
9483
+ if (parts.some((m) => m.kind === "object" && m.properties.has(name))) return void 0;
9484
+ return this.metatableMember(t, name);
9485
+ }
9111
9486
  /** The most a deferred type could turn out to be. A conditional is one of
9112
9487
  * its branches, and the true branch stands for a member of what was
9113
9488
  * tested (`T` in `T extends U ? T : never`); an indexed access reads
@@ -9173,7 +9548,7 @@ var TypeAnalyzer = class {
9173
9548
  if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
9174
9549
  const parts = this.stringParts(object);
9175
9550
  if (!parts) return void 0;
9176
- const found = parts.map((part) => this.builtInMethod(part, index.value));
9551
+ const found = parts.map((part) => this.metatableMethod(part, index.value));
9177
9552
  return found.every((t) => t !== void 0) ? union(found) : void 0;
9178
9553
  }
9179
9554
  /** The members of `t` when every one of them is a string — `"a" | "b"` is
@@ -9194,13 +9569,13 @@ var TypeAnalyzer = class {
9194
9569
  * not one of them is a mistake worth reporting, rather than the nil Lua
9195
9570
  * would hand back. */
9196
9571
  checkStringMember(node, object, key) {
9197
- if (!this.emitDiagnostics || !this.aliasDefs.has("StringMethods")) return;
9572
+ if (!this.emitDiagnostics || !this.hasMetatable(stringType)) return;
9198
9573
  const parts = this.stringParts(object);
9199
9574
  if (!parts) return;
9200
9575
  const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
9201
9576
  if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
9202
9577
  const names = keys.map((m) => String(m.value));
9203
- if (names.some((name) => parts.some((part) => this.builtInMethod(part, name)))) return;
9578
+ if (names.some((name) => parts.some((part) => this.metatableMethod(part, name)))) return;
9204
9579
  this.diagnostics.push({
9205
9580
  node,
9206
9581
  message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
@@ -9232,24 +9607,24 @@ var TypeAnalyzer = class {
9232
9607
  switch (part.kind) {
9233
9608
  case "primitive":
9234
9609
  if (part.name === "nil") continue;
9235
- if (part.name === "string" && !this.aliasDefs.has("StringMethods")) return false;
9236
- if (part.name === "string" && this.builtInMethod(part, name)) continue;
9610
+ if (part.name === "string" && !this.hasMetatable(part)) return false;
9611
+ if (part.name === "string" && this.metatableMethod(part, name)) continue;
9237
9612
  break;
9238
9613
  case "literal":
9239
9614
  case "templateLiteral":
9240
9615
  if (part.kind === "templateLiteral" || part.base === "string") {
9241
- if (!this.aliasDefs.has("StringMethods")) return false;
9616
+ if (!this.hasMetatable(part)) return false;
9242
9617
  }
9243
- if (this.builtInMethod(part, name)) continue;
9618
+ if (this.metatableMethod(part, name)) continue;
9244
9619
  break;
9245
9620
  case "array":
9246
9621
  case "tuple":
9247
- if (!this.aliasDefs.has("ArrayMethods")) return false;
9248
- if (this.builtInMethod(part, name)) continue;
9622
+ if (!this.hasMetatable(part)) return false;
9623
+ if (this.metatableMethod(part, name)) continue;
9249
9624
  break;
9250
9625
  case "object":
9251
9626
  if (part.properties.has(name)) continue;
9252
- if (part.indexer || part.properties.size === 0 && !part.class || this.builtInMethod(part, name)) return false;
9627
+ if (part.indexer || part.properties.size === 0 && !part.class || this.metatableMethod(part, name)) return false;
9253
9628
  break;
9254
9629
  default:
9255
9630
  return false;
@@ -9384,10 +9759,10 @@ var TypeAnalyzer = class {
9384
9759
  if (p) return p.optional ? optional(p.type) : p.type;
9385
9760
  if (t.indexer) return t.indexer.value;
9386
9761
  }
9387
- const built = this.builtInMethod(t, name);
9762
+ const built = this.metatableMethod(t, name);
9388
9763
  if (built) return built;
9389
9764
  if (t.kind === "union") {
9390
- const built2 = this.builtInMethod(t, name);
9765
+ const built2 = this.metatableMethod(t, name);
9391
9766
  if (built2) return built2;
9392
9767
  return union(t.types.map((m) => this.propertyType(m, name)));
9393
9768
  }
@@ -9683,6 +10058,27 @@ var TypeAnalyzer = class {
9683
10058
  elseEnv = whenFalse;
9684
10059
  }
9685
10060
  branches.push(this.infer(expr.alternate, elseEnv));
10061
+ const bodies = [...expr.clauses.map((c) => c.body), expr.alternate].map(unwrapParens);
10062
+ const isEmpty = (e) => e.type === "TableExpression" && e.fields.length === 0 || e.type === "ArrayExpression" && e.elements.length === 0;
10063
+ const others = union(branches.filter((_, i) => !isEmpty(bodies[i])));
10064
+ const parts = (others.kind === "union" ? others.types : [others]).map((m) => this.expand(m)).filter((m) => !(m.kind === "primitive" && m.name === "nil"));
10065
+ const fits = (e) => {
10066
+ if (!parts.length) return void 0;
10067
+ if (e.type === "ArrayExpression") {
10068
+ if (parts.every((m) => m.kind === "array")) return others;
10069
+ return parts.every((m) => m.kind === "array" || m.kind === "tuple") ? tuple([]) : void 0;
10070
+ }
10071
+ return parts.every((m) => m.kind === "object" && !m.class && [...m.properties.values()].every((prop) => prop.optional)) ? others : void 0;
10072
+ };
10073
+ if (bodies.some(isEmpty) && others.kind !== "never" && others.kind !== "any" && bodies.every((body) => !isEmpty(body) || fits(body))) {
10074
+ const typed = bodies.map((body, i) => {
10075
+ if (!isEmpty(body)) return branches[i];
10076
+ const t = fits(body);
10077
+ this.typeOf.set(body, t);
10078
+ return t;
10079
+ });
10080
+ return union(typed);
10081
+ }
9686
10082
  return union(branches);
9687
10083
  }
9688
10084
  }
@@ -9788,7 +10184,9 @@ var TypeAnalyzer = class {
9788
10184
  this.checkPrivateMember(expr.method, objType, expr.method.name);
9789
10185
  this.checkStringMember(expr.method, objType, literal(expr.method.name));
9790
10186
  this.checkMissingMember(expr.method, objType, expr.method.name);
9791
- const method = this.propertyType(objType, expr.method.name);
10187
+ const fromMetatable = this.receiverMetatableMember(objType, expr.method.name);
10188
+ if (fromMetatable) this.methodSources.set(expr, fromMetatable.source);
10189
+ const method = fromMetatable?.type ?? this.propertyType(objType, expr.method.name);
9792
10190
  const united = this.unionSignatures(method);
9793
10191
  const fns = united ?? this.overloadsOf(method);
9794
10192
  const explicit = this.explicitTypeArguments(expr, fns);
@@ -9987,23 +10385,87 @@ var TypeAnalyzer = class {
9987
10385
  if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
9988
10386
  const elems = [];
9989
10387
  let hadSpread = false;
10388
+ const written = [];
9990
10389
  for (const el of expr.elements) {
9991
10390
  if (el.type === "SpreadElement") {
9992
10391
  hadSpread = true;
9993
- const s = this.infer(el.argument, env);
9994
- if (s.kind === "array") elems.push(s.element);
9995
- else if (s.kind === "tuple") elems.push(...s.elements);
9996
- else elems.push(unknownType);
10392
+ const spread = this.infer(el.argument, env);
10393
+ written.push({ spread: true, type: spread });
10394
+ elems.push(...this.spreadElements(spread));
9997
10395
  } else {
9998
- elems.push(asConst ? this.inferAsConst(el, env) : this.infer(el, env));
10396
+ const t = asConst ? this.inferAsConst(el, env) : this.infer(el, env);
10397
+ written.push({ spread: false, type: asConst ? t : this.widenUnlessAsked(t, el) });
10398
+ elems.push(t);
9999
10399
  }
10000
10400
  }
10001
10401
  if (asConst && !hadSpread) return tuple(elems);
10402
+ if (hadSpread) {
10403
+ const tuples = this.spreadTuples(written);
10404
+ if (tuples) return union(tuples.map((elements) => tuple(elements)));
10405
+ }
10002
10406
  return arrayOf(elems.length ? union(elems.map((t, i) => {
10003
10407
  const element = expr.elements[i];
10004
10408
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
10005
10409
  })) : unknownType);
10006
10410
  }
10411
+ /** The tuples an array literal with spreads is, when every spread is a
10412
+ * tuple of known length or a union of them: one per way the unions go.
10413
+ * `undefined` when a spread's length is not known (an array, a tuple
10414
+ * with a rest), or the ways multiply past what is worth listing. */
10415
+ spreadTuples(written) {
10416
+ const MAX = 32;
10417
+ let variants = [[]];
10418
+ for (const { spread, type } of written) {
10419
+ if (!spread) {
10420
+ variants = variants.map((v) => [...v, type]);
10421
+ continue;
10422
+ }
10423
+ const options = this.fixedTuples(type);
10424
+ if (!options) return void 0;
10425
+ const next = [];
10426
+ for (const v of variants) for (const option of options) next.push([...v, ...option]);
10427
+ if (next.length > MAX) return void 0;
10428
+ variants = next;
10429
+ }
10430
+ return variants;
10431
+ }
10432
+ /** A tuple of known length's members, or a union of such tuples' — each
10433
+ * a way the value can be. `nil` in a union spreads nothing. */
10434
+ fixedTuples(t, depth = 0) {
10435
+ if (depth > 8) return void 0;
10436
+ const held = this.expand(t);
10437
+ if (held.kind === "tuple") return held.rest ? void 0 : [held.elements];
10438
+ if (held.kind !== "union") return void 0;
10439
+ const out = [];
10440
+ for (const member of held.types) {
10441
+ if (member.kind === "primitive" && member.name === "nil") continue;
10442
+ const options = this.fixedTuples(member, depth + 1);
10443
+ if (!options) return void 0;
10444
+ out.push(...options);
10445
+ }
10446
+ return out.length ? out : void 0;
10447
+ }
10448
+ /** What `...xs` puts into an array: an array's element, a tuple's members,
10449
+ * and for a union each member's — `...(host ? extra : [])` adds `extra`'s
10450
+ * elements and the empty tuple nothing. Anything else, `unknown`. */
10451
+ spreadElements(t, depth = 0) {
10452
+ const held = this.expand(t);
10453
+ if (depth > 8) return [unknownType];
10454
+ switch (held.kind) {
10455
+ case "array":
10456
+ return [held.element];
10457
+ case "tuple":
10458
+ return tupleMembers(held);
10459
+ case "union":
10460
+ return held.types.filter((m) => !(m.kind === "primitive" && m.name === "nil")).flatMap((m) => this.spreadElements(m, depth + 1));
10461
+ case "typeParam":
10462
+ return held.constraint ? this.spreadElements(held.constraint, depth + 1) : [unknownType];
10463
+ case "any":
10464
+ return [anyType];
10465
+ default:
10466
+ return [unknownType];
10467
+ }
10468
+ }
10007
10469
  /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
10008
10470
  * tuple's, and what comes after them — more elements, or a spread — is
10009
10471
  * its rest. */
@@ -10014,10 +10476,7 @@ var TypeAnalyzer = class {
10014
10476
  expr.elements.forEach((element, i) => {
10015
10477
  if (element.type === "SpreadElement") {
10016
10478
  spread = true;
10017
- const held = this.expand(this.infer(element.argument, env));
10018
- if (held.kind === "array") tail.push(held.element);
10019
- else if (held.kind === "tuple") tail.push(...tupleMembers(held));
10020
- else tail.push(unknownType);
10479
+ tail.push(...this.spreadElements(this.infer(element.argument, env)));
10021
10480
  return;
10022
10481
  }
10023
10482
  const t = this.widenUnlessAsked(this.infer(element, env), element);
@@ -10034,6 +10493,7 @@ var TypeAnalyzer = class {
10034
10493
  * The context was recorded by `applyContext` before the value was
10035
10494
  * inferred, so this is a lookup rather than a second pass. */
10036
10495
  widenUnlessAsked(value, at) {
10496
+ if (!writesLiteral(at)) return value;
10037
10497
  const wanted = this.expectedTypeOf.get(at);
10038
10498
  if (value.kind === "literal") {
10039
10499
  const arms = this.contextArms.get(at);
@@ -11030,6 +11490,8 @@ function containsTypeQuery(node) {
11030
11490
  return found;
11031
11491
  }
11032
11492
  var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
11493
+ var OBJECT_INTRINSICS = /* @__PURE__ */ new Set(["ObjectKeys", "ObjectValues", "ObjectEntries"]);
11494
+ var INTRINSICS = /* @__PURE__ */ new Set([...STRING_INTRINSICS, ...OBJECT_INTRINSICS]);
11033
11495
  function briefType(t) {
11034
11496
  if (t.kind === "union" && t.types.length > 8) {
11035
11497
  const shown = t.types.slice(0, 6).map(formatType).join(" | ");
@@ -11237,7 +11699,8 @@ function resolveTypeLibraries(config, host = nodeHost) {
11237
11699
  }
11238
11700
  addFile(entryFile);
11239
11701
  const lowering = loweringModule(directory, host, problems, config);
11240
- if (lowering) lowerings.push(lowering);
11702
+ const library = files.findIndex((file) => pathKey(file) === pathKey(entryFile));
11703
+ if (lowering) lowerings.push({ ...lowering, library });
11241
11704
  };
11242
11705
  for (const entry of config.types) {
11243
11706
  const relative = entry.startsWith("./") || entry.startsWith("../") || entry.startsWith("/") || /^[A-Za-z]:[\\/]/.test(entry);