@tilua/parser 3.0.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
@@ -3893,6 +3893,10 @@ var Analyzer = class {
3893
3893
  moduleScope = this.globalScope;
3894
3894
  /** How many function bodies enclose the walk. */
3895
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;
3896
3900
  /** Function declarations already declared by their block's hoisting, with
3897
3901
  * the function depth of that block. */
3898
3902
  hoisted = /* @__PURE__ */ new Map();
@@ -4245,11 +4249,11 @@ var Analyzer = class {
4245
4249
  return;
4246
4250
  case "WhileStatement":
4247
4251
  this.visitExpression(stmt.condition, scope);
4248
- this.visitBlockInNewScope(stmt.body, scope);
4252
+ this.inLoop(() => this.visitBlockInNewScope(stmt.body, scope));
4249
4253
  return;
4250
4254
  case "RepeatStatement": {
4251
4255
  const bodyScope = childScope(scope);
4252
- this.visitBlock(stmt.body, bodyScope);
4256
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4253
4257
  this.visitExpression(stmt.condition, bodyScope);
4254
4258
  return;
4255
4259
  }
@@ -4267,14 +4271,14 @@ var Analyzer = class {
4267
4271
  if (stmt.step) this.visitExpression(stmt.step, scope);
4268
4272
  const bodyScope = childScope(scope);
4269
4273
  this.declare(bodyScope, stmt.variable.name, "for-numeric", stmt.variable);
4270
- this.visitBlock(stmt.body, bodyScope);
4274
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4271
4275
  return;
4272
4276
  }
4273
4277
  case "GenericForStatement": {
4274
4278
  this.visitExpression(stmt.iterator, scope);
4275
4279
  const bodyScope = childScope(scope);
4276
4280
  this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
4277
- this.visitBlock(stmt.body, bodyScope);
4281
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4278
4282
  return;
4279
4283
  }
4280
4284
  case "ReturnStatement":
@@ -4282,6 +4286,15 @@ var Analyzer = class {
4282
4286
  return;
4283
4287
  case "BreakStatement":
4284
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;
4285
4298
  case "ErrorStatement":
4286
4299
  return;
4287
4300
  case "DeclareStatement":
@@ -4337,6 +4350,15 @@ var Analyzer = class {
4337
4350
  return;
4338
4351
  }
4339
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
+ }
4340
4362
  // ---------------- functions ----------------
4341
4363
  visitFunctionBody(func, outerScope, isMethod = false) {
4342
4364
  const fnScope = childScope(outerScope);
@@ -4352,11 +4374,14 @@ var Analyzer = class {
4352
4374
  }
4353
4375
  });
4354
4376
  this.visitType(func.returnType, fnScope);
4377
+ const loops = this.loopDepth;
4355
4378
  this.functionDepth++;
4379
+ this.loopDepth = 0;
4356
4380
  try {
4357
4381
  this.visitBlock(func.body, fnScope);
4358
4382
  } finally {
4359
4383
  this.functionDepth--;
4384
+ this.loopDepth = loops;
4360
4385
  }
4361
4386
  }
4362
4387
  /** An overload signature: no body and no bindings, but its types can hold
@@ -4610,11 +4635,14 @@ declare metatable string: {
4610
4635
  -- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
4611
4636
  -- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
4612
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.
4613
4640
  declare metatable<T> T[]: {
4614
4641
  __index: {
4615
4642
  find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
4616
4643
  findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
4617
- filter: (self: T[], test: (value: T, index: number) => boolean) => T[],
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[]),
4618
4646
  map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
4619
4647
  forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
4620
4648
  some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
@@ -4753,8 +4781,14 @@ function substitute(t, subst) {
4753
4781
  if (subst.size === 0) return t;
4754
4782
  if (!containsTypeParam(t)) return t;
4755
4783
  switch (t.kind) {
4756
- case "typeParam":
4757
- 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
+ }
4758
4792
  case "array":
4759
4793
  return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4760
4794
  case "tuple":
@@ -4899,6 +4933,10 @@ function unify(param, arg, vars, out, how = "union") {
4899
4933
  if (arg.kind === "function") {
4900
4934
  param.params.forEach((p, i) => arg.params[i] && unify(p.type, arg.params[i].type, vars, out));
4901
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
+ }
4902
4940
  }
4903
4941
  return;
4904
4942
  case "object":
@@ -5154,6 +5192,11 @@ function isAssignableInner(a, b) {
5154
5192
  for (let i = 0; i < n; i++) {
5155
5193
  if (!isAssignable(a.params[i].type, b.params[i].type) && !isAssignable(b.params[i].type, a.params[i].type)) return false;
5156
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
+ }
5157
5200
  return isAssignable(a.returns, b.returns);
5158
5201
  }
5159
5202
  if (b.kind === "typeParam") return false;
@@ -5288,7 +5331,7 @@ function containsFreeTypeParam(t, seen, bound) {
5288
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));
5289
5332
  case "function": {
5290
5333
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
5291
- 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);
5292
5335
  }
5293
5336
  case "genericRef":
5294
5337
  return t.typeArguments.some((a) => containsTypeParam(a, seen, bound));
@@ -5713,9 +5756,14 @@ function isFreshLiteralExpr(e) {
5713
5756
  case "StringLiteral":
5714
5757
  case "BooleanLiteral":
5715
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.
5716
5764
  case "TableExpression":
5717
5765
  case "ArrayExpression":
5718
- return true;
5766
+ return false;
5719
5767
  case "ParenthesizedExpression":
5720
5768
  return isFreshLiteralExpr(e.expression);
5721
5769
  case "UnaryExpression":
@@ -5730,6 +5778,30 @@ function isFreshLiteralExpr(e) {
5730
5778
  return false;
5731
5779
  }
5732
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
+ }
5733
5805
  function collectInferNames(node) {
5734
5806
  const out = [];
5735
5807
  const walk = (n) => {
@@ -8112,7 +8184,9 @@ var TypeAnalyzer = class {
8112
8184
  const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
8113
8185
  if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
8114
8186
  const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
8115
- 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);
8116
8190
  const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
8117
8191
  written.forEach((a, i) => {
8118
8192
  if (!waits[i]) return;
@@ -8303,6 +8377,16 @@ var TypeAnalyzer = class {
8303
8377
  }
8304
8378
  const id = this.bindingIdByName(p.name, p);
8305
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
+ }
8306
8390
  if (id !== void 0) {
8307
8391
  this.bindingType.set(id, t);
8308
8392
  this.setBinding(env, id, t);
@@ -8400,9 +8484,9 @@ var TypeAnalyzer = class {
8400
8484
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
8401
8485
  * `<T>(v: T) -> string` and still pick the precise one. */
8402
8486
  pickOverload(fns, argTypes, argsFor, spread) {
8403
- for (const generic of [false, true]) {
8487
+ for (const tier of [0, 1, 2]) {
8404
8488
  for (const f of fns) {
8405
- if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
8489
+ if (overloadTier(f) !== tier) continue;
8406
8490
  if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
8407
8491
  }
8408
8492
  }
@@ -8428,7 +8512,7 @@ var TypeAnalyzer = class {
8428
8512
  if (position < 0) return void 0;
8429
8513
  const members = this.expand(argTypes[position]).types;
8430
8514
  if (members.length > 32) return void 0;
8431
- 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);
8432
8516
  const limit = picked ? rank(picked) : Infinity;
8433
8517
  const results = [];
8434
8518
  for (const member of members) {
@@ -8821,10 +8905,38 @@ var TypeAnalyzer = class {
8821
8905
  returns,
8822
8906
  void 0,
8823
8907
  names,
8824
- this.resolvePredicate(func.predicate, params)
8908
+ func.predicate || func.returnType ? this.resolvePredicate(func.predicate, params) : this.inferPredicate(func, params, returns, bodyEnv)
8825
8909
  ), func.params);
8826
8910
  });
8827
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
+ }
8828
8940
  /** Where the return types of the function being walked are collected, so
8829
8941
  * each is read where it is written — inside the branch that narrowed it —
8830
8942
  * rather than in whatever state the body ends in. */
@@ -9946,6 +10058,27 @@ var TypeAnalyzer = class {
9946
10058
  elseEnv = whenFalse;
9947
10059
  }
9948
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
+ }
9949
10082
  return union(branches);
9950
10083
  }
9951
10084
  }
@@ -10252,23 +10385,87 @@ var TypeAnalyzer = class {
10252
10385
  if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
10253
10386
  const elems = [];
10254
10387
  let hadSpread = false;
10388
+ const written = [];
10255
10389
  for (const el of expr.elements) {
10256
10390
  if (el.type === "SpreadElement") {
10257
10391
  hadSpread = true;
10258
- const s = this.infer(el.argument, env);
10259
- if (s.kind === "array") elems.push(s.element);
10260
- else if (s.kind === "tuple") elems.push(...s.elements);
10261
- 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));
10262
10395
  } else {
10263
- 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);
10264
10399
  }
10265
10400
  }
10266
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
+ }
10267
10406
  return arrayOf(elems.length ? union(elems.map((t, i) => {
10268
10407
  const element = expr.elements[i];
10269
10408
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
10270
10409
  })) : unknownType);
10271
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
+ }
10272
10469
  /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
10273
10470
  * tuple's, and what comes after them — more elements, or a spread — is
10274
10471
  * its rest. */
@@ -10279,10 +10476,7 @@ var TypeAnalyzer = class {
10279
10476
  expr.elements.forEach((element, i) => {
10280
10477
  if (element.type === "SpreadElement") {
10281
10478
  spread = true;
10282
- const held = this.expand(this.infer(element.argument, env));
10283
- if (held.kind === "array") tail.push(held.element);
10284
- else if (held.kind === "tuple") tail.push(...tupleMembers(held));
10285
- else tail.push(unknownType);
10479
+ tail.push(...this.spreadElements(this.infer(element.argument, env)));
10286
10480
  return;
10287
10481
  }
10288
10482
  const t = this.widenUnlessAsked(this.infer(element, env), element);
@@ -10299,6 +10493,7 @@ var TypeAnalyzer = class {
10299
10493
  * The context was recorded by `applyContext` before the value was
10300
10494
  * inferred, so this is a lookup rather than a second pass. */
10301
10495
  widenUnlessAsked(value, at) {
10496
+ if (!writesLiteral(at)) return value;
10302
10497
  const wanted = this.expectedTypeOf.get(at);
10303
10498
  if (value.kind === "literal") {
10304
10499
  const arms = this.contextArms.get(at);
package/dist/index.d.cts CHANGED
@@ -1064,7 +1064,7 @@ interface ScopeDiagnostic {
1064
1064
  };
1065
1065
  };
1066
1066
  message: string;
1067
- kind: "redeclare" | "const-assign" | "type-only" | "undeclared" | "use-before-define";
1067
+ kind: "redeclare" | "const-assign" | "type-only" | "undeclared" | "use-before-define" | "outside-loop";
1068
1068
  }
1069
1069
  interface ScopeAnalysis {
1070
1070
  /** Every Identifier that appears in a variable *usage* position (i.e.
@@ -1601,7 +1601,7 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
1601
1601
  * `ObjectValues<T>` and `ObjectEntries<T>`, an object's members as tuples in
1602
1602
  * the order they were written.
1603
1603
  */
1604
- declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (self: T[], test: (value: T, index: number) => boolean) => T[],\n map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n includes: (self: T[], value: T) => boolean,\n indexOf: (self: T[], value: T, start?: number) => number | nil,\n join: (self: T[], separator?: string) => string,\n concat: (self: T[], ...others: T[][]) => T[],\n slice: (self: T[], start?: number, stop?: number) => T[],\n flat: (self: T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n },\n}\n";
1604
+ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n includes: (self: T[], value: T) => boolean,\n indexOf: (self: T[], value: T, start?: number) => number | nil,\n join: (self: T[], separator?: string) => string,\n concat: (self: T[], ...others: T[][]) => T[],\n slice: (self: T[], start?: number, stop?: number) => T[],\n flat: (self: T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n },\n}\n";
1605
1605
 
1606
1606
  interface ProjectHost {
1607
1607
  /** A file's text, or `undefined` when there is no such file. */
package/dist/index.d.ts CHANGED
@@ -1064,7 +1064,7 @@ interface ScopeDiagnostic {
1064
1064
  };
1065
1065
  };
1066
1066
  message: string;
1067
- kind: "redeclare" | "const-assign" | "type-only" | "undeclared" | "use-before-define";
1067
+ kind: "redeclare" | "const-assign" | "type-only" | "undeclared" | "use-before-define" | "outside-loop";
1068
1068
  }
1069
1069
  interface ScopeAnalysis {
1070
1070
  /** Every Identifier that appears in a variable *usage* position (i.e.
@@ -1601,7 +1601,7 @@ resolveModule?: (specifier: string) => ModuleExports | undefined): ModuleExports
1601
1601
  * `ObjectValues<T>` and `ObjectEntries<T>`, an object's members as tuples in
1602
1602
  * the order they were written.
1603
1603
  */
1604
- declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (self: T[], test: (value: T, index: number) => boolean) => T[],\n map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n includes: (self: T[], value: T) => boolean,\n indexOf: (self: T[], value: T, start?: number) => number | nil,\n join: (self: T[], separator?: string) => string,\n concat: (self: T[], ...others: T[][]) => T[],\n slice: (self: T[], start?: number, stop?: number) => T[],\n flat: (self: T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n },\n}\n";
1604
+ declare const PRELUDE_SOURCE = "\n-- In Luau only `nil` and `false` are falsy: `0` and `\"\"` are truthy.\n-- These are what truthiness narrowing computes, made available to write down.\ntype Falsy = nil | false\ntype Truthy<T> = T - Falsy\n\n-- `-` is set difference. Over a union it drops members; over a concrete type\n-- it simplifies away; over an opaque type (`unknown`, an unresolved parameter)\n-- it is kept, so `Exclude<unknown, 1>` stays `unknown - 1`.\ntype Exclude<T, U> = T - U\ntype Extract<T, U> = T extends U ? T : never\ntype NonNullable<T> = T - nil\n\ntype ReturnType<T> = T extends (...args: unknown[]) => infer R ? R : never\ntype Parameters<T> = T extends (...args: infer P) => unknown ? P : never\n\ntype Partial<T> = { [K in keyof T]?: T[K] }\ntype Required<T> = { [K in keyof T]-?: T[K] }\ntype Readonly<T> = { readonly [K in keyof T]: T[K] }\ntype Mutable<T> = { -readonly [K in keyof T]: T[K] }\n\ntype Pick<T, K> = { [P in K]: T[P] }\ntype Omit<T, K> = Pick<T, Exclude<keyof T, K>>\ntype Record<K, V> = { [P in K]: V }\n\n-- ============================================================\n-- The metatables of the language's own values\n-- ============================================================\n\n-- A string's methods. The first group is Lua's string library, which a\n-- string really answers to; where Lua answers several values, tilua answers\n-- one, as the global functions do (`find` is an array, `gsub` the new\n-- string). The second group is written the way JavaScript writes it.\n--\n-- Positions are Lua's: `sub` and `slice` count from 1, and `indexOf` is\n-- `nil` when the text is not there.\ndeclare metatable string: {\n __index: {\n upper: (self: string) => string,\n lower: (self: string) => string,\n len: (self: string) => number,\n sub: (self: string, i: number, j?: number) => string,\n rep: (self: string, n: number, separator?: string) => string,\n reverse: (self: string) => string,\n format: (self: string, ...args: unknown[]) => string,\n byte: ((self: string, i?: number) => number) & ((self: string, i: number, j: number) => number[]),\n find: (self: string, pattern: string, init?: number, plain?: boolean) => [number, number, ...string[]] | nil,\n match: (self: string, pattern: string, init?: number) => [...string[]] | nil,\n gmatch: (self: string, pattern: string) => () => [string, ...string[]] | nil,\n gsub: (self: string, pattern: string, replacement: unknown, n?: number) => string,\n\n trim: (self: string) => string,\n trimStart: (self: string) => string,\n trimEnd: (self: string) => string,\n startsWith: (self: string, text: string) => boolean,\n endsWith: (self: string, text: string) => boolean,\n includes: (self: string, text: string) => boolean,\n indexOf: (self: string, text: string, start?: number) => number | nil,\n slice: (self: string, start?: number, stop?: number) => string,\n replace: (self: string, text: string, replacement: string) => string,\n replaceAll: (self: string, text: string, replacement: string) => string,\n padStart: (self: string, length: number, padding?: string) => string,\n padEnd: (self: string, length: number, padding?: string) => string,\n },\n}\n\n-- An array's methods. Lua gives an array no metatable: the compiler lowers\n-- each call to a plain function, so they work on any array, one a Lua\n-- library made included.\n--\n-- Indices are Lua's: the first element is 1, and `indexOf` / `findIndex`\n-- answer `nil` rather than JavaScript's -1. `sort` takes Lua's comparator\n-- (true when `a` comes first). `push`, `pop`, `shift`, `unshift`,\n-- `sort` and `reverse` change the array; everything else returns a new one.\n-- A `filter` whose test is a guard \u2014 `v is S`, or `v => v ~= nil` \u2014\n-- answers an array of what it let through.\ndeclare metatable<T> T[]: {\n __index: {\n find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,\n findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,\n filter: (<S extends T>(self: T[], test: (value: T, index: number) => value is S) => S[])\n & ((self: T[], test: (value: T, index: number) => boolean) => T[]),\n map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],\n forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,\n some: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n every: (self: T[], test: (value: T, index: number) => boolean) => boolean,\n reduce: <U>(self: T[], step: (total: U, value: T, index: number) => U, initial: U) => U,\n includes: (self: T[], value: T) => boolean,\n indexOf: (self: T[], value: T, start?: number) => number | nil,\n join: (self: T[], separator?: string) => string,\n concat: (self: T[], ...others: T[][]) => T[],\n slice: (self: T[], start?: number, stop?: number) => T[],\n flat: (self: T[]) => T[],\n reverse: (self: T[]) => T[],\n sort: (self: T[], compare?: (a: T, b: T) => boolean) => T[],\n push: (self: T[], ...args: T[]) => number,\n pop: (self: T[]) => T | nil,\n shift: (self: T[]) => T | nil,\n unshift: (self: T[], ...args: T[]) => number,\n },\n}\n\n-- A table's methods. Unlike JavaScript's `Object.keys`, they answer a\n-- tuple, in the order the table's type wrote its members:\n--\n-- const point = { x: 1, y: \"up\" }\n-- point:keys() -- [\"x\", \"y\"]\n-- point:values() -- [number, string]\n-- point:entries() -- [[\"x\", number], [\"y\", string]]\n--\n-- A Lua table has no order of its own, so the compiler hands its runtime the\n-- keys as the type lists them. An optional member keeps its place, holding\n-- `nil` when it is missing; an indexer's keys follow the named ones. A\n-- member of the table's own with one of these names is what a call reaches,\n-- and a class instance, which has a metatable of its own, has none of them.\ndeclare metatable<T extends {}> T: {\n __index: {\n keys: (self: T) => ObjectKeys<T>,\n values: (self: T) => ObjectValues<T>,\n entries: (self: T) => ObjectEntries<T>,\n },\n}\n";
1605
1605
 
1606
1606
  interface ProjectHost {
1607
1607
  /** A file's text, or `undefined` when there is no such file. */
package/dist/index.js CHANGED
@@ -3788,6 +3788,10 @@ var Analyzer = class {
3788
3788
  moduleScope = this.globalScope;
3789
3789
  /** How many function bodies enclose the walk. */
3790
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;
3791
3795
  /** Function declarations already declared by their block's hoisting, with
3792
3796
  * the function depth of that block. */
3793
3797
  hoisted = /* @__PURE__ */ new Map();
@@ -4140,11 +4144,11 @@ var Analyzer = class {
4140
4144
  return;
4141
4145
  case "WhileStatement":
4142
4146
  this.visitExpression(stmt.condition, scope);
4143
- this.visitBlockInNewScope(stmt.body, scope);
4147
+ this.inLoop(() => this.visitBlockInNewScope(stmt.body, scope));
4144
4148
  return;
4145
4149
  case "RepeatStatement": {
4146
4150
  const bodyScope = childScope(scope);
4147
- this.visitBlock(stmt.body, bodyScope);
4151
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4148
4152
  this.visitExpression(stmt.condition, bodyScope);
4149
4153
  return;
4150
4154
  }
@@ -4162,14 +4166,14 @@ var Analyzer = class {
4162
4166
  if (stmt.step) this.visitExpression(stmt.step, scope);
4163
4167
  const bodyScope = childScope(scope);
4164
4168
  this.declare(bodyScope, stmt.variable.name, "for-numeric", stmt.variable);
4165
- this.visitBlock(stmt.body, bodyScope);
4169
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4166
4170
  return;
4167
4171
  }
4168
4172
  case "GenericForStatement": {
4169
4173
  this.visitExpression(stmt.iterator, scope);
4170
4174
  const bodyScope = childScope(scope);
4171
4175
  this.declarePattern(bodyScope, stmt.variable, "for-generic", scope, stmt.kind === "const");
4172
- this.visitBlock(stmt.body, bodyScope);
4176
+ this.inLoop(() => this.visitBlock(stmt.body, bodyScope));
4173
4177
  return;
4174
4178
  }
4175
4179
  case "ReturnStatement":
@@ -4177,6 +4181,15 @@ var Analyzer = class {
4177
4181
  return;
4178
4182
  case "BreakStatement":
4179
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;
4180
4193
  case "ErrorStatement":
4181
4194
  return;
4182
4195
  case "DeclareStatement":
@@ -4232,6 +4245,15 @@ var Analyzer = class {
4232
4245
  return;
4233
4246
  }
4234
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
+ }
4235
4257
  // ---------------- functions ----------------
4236
4258
  visitFunctionBody(func, outerScope, isMethod = false) {
4237
4259
  const fnScope = childScope(outerScope);
@@ -4247,11 +4269,14 @@ var Analyzer = class {
4247
4269
  }
4248
4270
  });
4249
4271
  this.visitType(func.returnType, fnScope);
4272
+ const loops = this.loopDepth;
4250
4273
  this.functionDepth++;
4274
+ this.loopDepth = 0;
4251
4275
  try {
4252
4276
  this.visitBlock(func.body, fnScope);
4253
4277
  } finally {
4254
4278
  this.functionDepth--;
4279
+ this.loopDepth = loops;
4255
4280
  }
4256
4281
  }
4257
4282
  /** An overload signature: no body and no bindings, but its types can hold
@@ -4505,11 +4530,14 @@ declare metatable string: {
4505
4530
  -- answer \`nil\` rather than JavaScript's -1. \`sort\` takes Lua's comparator
4506
4531
  -- (true when \`a\` comes first). \`push\`, \`pop\`, \`shift\`, \`unshift\`,
4507
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.
4508
4535
  declare metatable<T> T[]: {
4509
4536
  __index: {
4510
4537
  find: (self: T[], test: (value: T, index: number) => boolean) => T | nil,
4511
4538
  findIndex: (self: T[], test: (value: T, index: number) => boolean) => number | nil,
4512
- filter: (self: T[], test: (value: T, index: number) => boolean) => T[],
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[]),
4513
4541
  map: <U>(self: T[], transform: (value: T, index: number) => U) => U[],
4514
4542
  forEach: (self: T[], visit: (value: T, index: number) => nil) => nil,
4515
4543
  some: (self: T[], test: (value: T, index: number) => boolean) => boolean,
@@ -4648,8 +4676,14 @@ function substitute(t, subst) {
4648
4676
  if (subst.size === 0) return t;
4649
4677
  if (!containsTypeParam(t)) return t;
4650
4678
  switch (t.kind) {
4651
- case "typeParam":
4652
- 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
+ }
4653
4687
  case "array":
4654
4688
  return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
4655
4689
  case "tuple":
@@ -4794,6 +4828,10 @@ function unify(param, arg, vars, out, how = "union") {
4794
4828
  if (arg.kind === "function") {
4795
4829
  param.params.forEach((p, i) => arg.params[i] && unify(p.type, arg.params[i].type, vars, out));
4796
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
+ }
4797
4835
  }
4798
4836
  return;
4799
4837
  case "object":
@@ -5049,6 +5087,11 @@ function isAssignableInner(a, b) {
5049
5087
  for (let i = 0; i < n; i++) {
5050
5088
  if (!isAssignable(a.params[i].type, b.params[i].type) && !isAssignable(b.params[i].type, a.params[i].type)) return false;
5051
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
+ }
5052
5095
  return isAssignable(a.returns, b.returns);
5053
5096
  }
5054
5097
  if (b.kind === "typeParam") return false;
@@ -5183,7 +5226,7 @@ function containsFreeTypeParam(t, seen, bound) {
5183
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));
5184
5227
  case "function": {
5185
5228
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
5186
- 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);
5187
5230
  }
5188
5231
  case "genericRef":
5189
5232
  return t.typeArguments.some((a) => containsTypeParam(a, seen, bound));
@@ -5608,9 +5651,14 @@ function isFreshLiteralExpr(e) {
5608
5651
  case "StringLiteral":
5609
5652
  case "BooleanLiteral":
5610
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.
5611
5659
  case "TableExpression":
5612
5660
  case "ArrayExpression":
5613
- return true;
5661
+ return false;
5614
5662
  case "ParenthesizedExpression":
5615
5663
  return isFreshLiteralExpr(e.expression);
5616
5664
  case "UnaryExpression":
@@ -5625,6 +5673,30 @@ function isFreshLiteralExpr(e) {
5625
5673
  return false;
5626
5674
  }
5627
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
+ }
5628
5700
  function collectInferNames(node) {
5629
5701
  const out = [];
5630
5702
  const walk = (n) => {
@@ -8007,7 +8079,9 @@ var TypeAnalyzer = class {
8007
8079
  const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
8008
8080
  if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
8009
8081
  const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
8010
- 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);
8011
8085
  const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
8012
8086
  written.forEach((a, i) => {
8013
8087
  if (!waits[i]) return;
@@ -8198,6 +8272,16 @@ var TypeAnalyzer = class {
8198
8272
  }
8199
8273
  const id = this.bindingIdByName(p.name, p);
8200
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
+ }
8201
8285
  if (id !== void 0) {
8202
8286
  this.bindingType.set(id, t);
8203
8287
  this.setBinding(env, id, t);
@@ -8295,9 +8379,9 @@ var TypeAnalyzer = class {
8295
8379
  * lets `typeof` declare `(v: number) -> "number"` alongside a trailing
8296
8380
  * `<T>(v: T) -> string` and still pick the precise one. */
8297
8381
  pickOverload(fns, argTypes, argsFor, spread) {
8298
- for (const generic of [false, true]) {
8382
+ for (const tier of [0, 1, 2]) {
8299
8383
  for (const f of fns) {
8300
- if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
8384
+ if (overloadTier(f) !== tier) continue;
8301
8385
  if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
8302
8386
  }
8303
8387
  }
@@ -8323,7 +8407,7 @@ var TypeAnalyzer = class {
8323
8407
  if (position < 0) return void 0;
8324
8408
  const members = this.expand(argTypes[position]).types;
8325
8409
  if (members.length > 32) return void 0;
8326
- 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);
8327
8411
  const limit = picked ? rank(picked) : Infinity;
8328
8412
  const results = [];
8329
8413
  for (const member of members) {
@@ -8716,10 +8800,38 @@ var TypeAnalyzer = class {
8716
8800
  returns,
8717
8801
  void 0,
8718
8802
  names,
8719
- this.resolvePredicate(func.predicate, params)
8803
+ func.predicate || func.returnType ? this.resolvePredicate(func.predicate, params) : this.inferPredicate(func, params, returns, bodyEnv)
8720
8804
  ), func.params);
8721
8805
  });
8722
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
+ }
8723
8835
  /** Where the return types of the function being walked are collected, so
8724
8836
  * each is read where it is written — inside the branch that narrowed it —
8725
8837
  * rather than in whatever state the body ends in. */
@@ -9841,6 +9953,27 @@ var TypeAnalyzer = class {
9841
9953
  elseEnv = whenFalse;
9842
9954
  }
9843
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
+ }
9844
9977
  return union(branches);
9845
9978
  }
9846
9979
  }
@@ -10147,23 +10280,87 @@ var TypeAnalyzer = class {
10147
10280
  if (wantedTuple && !asConst) return this.inferTupleLiteral(expr, env, wantedTuple);
10148
10281
  const elems = [];
10149
10282
  let hadSpread = false;
10283
+ const written = [];
10150
10284
  for (const el of expr.elements) {
10151
10285
  if (el.type === "SpreadElement") {
10152
10286
  hadSpread = true;
10153
- const s = this.infer(el.argument, env);
10154
- if (s.kind === "array") elems.push(s.element);
10155
- else if (s.kind === "tuple") elems.push(...s.elements);
10156
- 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));
10157
10290
  } else {
10158
- 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);
10159
10294
  }
10160
10295
  }
10161
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
+ }
10162
10301
  return arrayOf(elems.length ? union(elems.map((t, i) => {
10163
10302
  const element = expr.elements[i];
10164
10303
  return asConst || !element || element.type === "SpreadElement" ? t : this.widenUnlessAsked(t, element);
10165
10304
  })) : unknownType);
10166
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
+ }
10167
10364
  /** `[a, b, ...more]` where a tuple is wanted: the places it names are the
10168
10365
  * tuple's, and what comes after them — more elements, or a spread — is
10169
10366
  * its rest. */
@@ -10174,10 +10371,7 @@ var TypeAnalyzer = class {
10174
10371
  expr.elements.forEach((element, i) => {
10175
10372
  if (element.type === "SpreadElement") {
10176
10373
  spread = true;
10177
- const held = this.expand(this.infer(element.argument, env));
10178
- if (held.kind === "array") tail.push(held.element);
10179
- else if (held.kind === "tuple") tail.push(...tupleMembers(held));
10180
- else tail.push(unknownType);
10374
+ tail.push(...this.spreadElements(this.infer(element.argument, env)));
10181
10375
  return;
10182
10376
  }
10183
10377
  const t = this.widenUnlessAsked(this.infer(element, env), element);
@@ -10194,6 +10388,7 @@ var TypeAnalyzer = class {
10194
10388
  * The context was recorded by `applyContext` before the value was
10195
10389
  * inferred, so this is a lookup rather than a second pass. */
10196
10390
  widenUnlessAsked(value, at) {
10391
+ if (!writesLiteral(at)) return value;
10197
10392
  const wanted = this.expectedTypeOf.get(at);
10198
10393
  if (value.kind === "literal") {
10199
10394
  const arms = this.contextArms.get(at);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/parser",
3
- "version": "3.0.0",
3
+ "version": "3.0.1",
4
4
  "description": "tilua parser for roblox",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",