@tilua/parser 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -864,10 +864,10 @@ var Parser = class {
864
864
  expressionOr(stop) {
865
865
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
866
866
  }
867
- /** A comma-separated list of values: a `return`'s, a declaration's, an
868
- * assignment's. Each is one value; a spread belongs in a call's
869
- * arguments or an array, and is reported here — then read as one, so
870
- * the rest still means something. */
867
+ /** A comma-separated list of values where one belongs — a `return`'s, a
868
+ * declaration's, an assignment's — read whole, so that writing several
869
+ * is reported once and the rest still means something. A spread belongs
870
+ * in a call's arguments or an array, and is reported here too. */
871
871
  expressionListOr(stop) {
872
872
  const until = () => stop() || this.checkPunctuator(",");
873
873
  const item = () => {
@@ -1459,7 +1459,7 @@ var Parser = class {
1459
1459
  }
1460
1460
  this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
1461
1461
  }
1462
- // `const x = ...` / `let x, y = ...`.
1462
+ // `const x = ...` / `let [a, b] = ...`.
1463
1463
  // tilua has no `local` — `const` bindings are immutable, `let` mutable.
1464
1464
  /** `kind` reads the leading word as that keyword (recovery's `local`). */
1465
1465
  parseVariableDeclaration(as) {
@@ -1473,14 +1473,34 @@ var Parser = class {
1473
1473
  while (this.matchPunctuator(",")) {
1474
1474
  names.push(this.parseBindingTarget(true));
1475
1475
  }
1476
- let init = [];
1476
+ let values = [];
1477
1477
  if (this.matchOperator("=")) {
1478
- init = this.expressionListOr(() => false);
1478
+ values = this.expressionListOr(() => false);
1479
1479
  } else if (kind === "const") {
1480
1480
  if (!this.recover) this.error("'const' declaration requires an initializer");
1481
1481
  this.softError("'const' declaration requires an initializer");
1482
1482
  }
1483
- return { type: "VariableDeclaration", kind, names, init, ...spanFrom(start, this.previous()) };
1483
+ let name = names[0];
1484
+ let init = values[0];
1485
+ if (names.length > 1 || values.length > 1) {
1486
+ this.problemAt(start, `A declaration takes one value: take several from an array, '${kind} [a, b] = [x, y]'`);
1487
+ if (names.length > 1) name = this.namesAsPattern(names);
1488
+ if (values.length > 1) init = this.valuesAsArray(values);
1489
+ }
1490
+ return { type: "VariableDeclaration", kind, name, init, ...spanFrom(start, this.previous()) };
1491
+ }
1492
+ /** `a, b` written where one name goes, read as `[a, b]`. */
1493
+ namesAsPattern(names) {
1494
+ const elements = names.map((value) => ({
1495
+ type: "ArrayPatternElement",
1496
+ value,
1497
+ ...spanFrom(value, value)
1498
+ }));
1499
+ return { type: "ArrayPattern", elements, ...spanFrom(names[0], names[names.length - 1]) };
1500
+ }
1501
+ /** `x, y` written where one value goes, read as `[x, y]`. */
1502
+ valuesAsArray(values) {
1503
+ return { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
1484
1504
  }
1485
1505
  parseIfStatement() {
1486
1506
  const start = this.current();
@@ -1961,7 +1981,7 @@ var Parser = class {
1961
1981
  argument = values[0];
1962
1982
  if (values.length > 1) {
1963
1983
  this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
1964
- argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
1984
+ argument = this.valuesAsArray(values);
1965
1985
  }
1966
1986
  }
1967
1987
  return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
@@ -1987,28 +2007,15 @@ var Parser = class {
1987
2007
  parseExpressionStatement() {
1988
2008
  const start = this.current();
1989
2009
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
1990
- const targets = [
1991
- this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern()
1992
- ];
1993
- while (this.matchPunctuator(",")) {
1994
- targets.push(this.parseAssignTarget());
1995
- }
1996
- this.expectOperator("=");
1997
- const values = this.expressionListOr(() => false);
1998
- return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
2010
+ const target = this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern();
2011
+ return this.parseAssignmentRest(start, target);
1999
2012
  }
2000
2013
  const statementStart = this.cursor;
2001
2014
  const errorsAtStart = this.errors.length;
2002
2015
  const first = this.parsePrefixExpression();
2003
2016
  if (this.checkOperator("=") || this.checkPunctuator(",")) {
2004
- const targets = [first];
2005
- while (this.matchPunctuator(",")) {
2006
- targets.push(this.parseAssignTarget());
2007
- }
2008
- for (const target of targets) this.rejectOptionalTarget(target);
2009
- this.expectOperator("=");
2010
- const values = this.expressionListOr(() => false);
2011
- return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
2017
+ this.rejectOptionalTarget(first);
2018
+ return this.parseAssignmentRest(start, first);
2012
2019
  }
2013
2020
  const t = this.current();
2014
2021
  if (t.type === "Operator" && COMPOUND_ASSIGN_OPS.has(t.value)) {
@@ -2034,6 +2041,21 @@ var Parser = class {
2034
2041
  }
2035
2042
  return { type: "ExpressionStatement", expression, ...spanFrom(start, this.previous()) };
2036
2043
  }
2044
+ /** From after an assignment's target: `= value`. Luau's `a, b = x, y`
2045
+ * is reported, and read as its first target taking its first value. */
2046
+ parseAssignmentRest(start, target) {
2047
+ let several = false;
2048
+ while (this.matchPunctuator(",")) {
2049
+ this.parseAssignTarget();
2050
+ several = true;
2051
+ }
2052
+ this.expectOperator("=");
2053
+ const values = this.expressionListOr(() => false);
2054
+ if (several || values.length > 1) {
2055
+ this.problemAt(start, "An assignment takes one value: take several from an array, '[a, b] = [x, y]'");
2056
+ }
2057
+ return { type: "AssignmentStatement", target, value: values[0], ...spanFrom(start, this.previous()) };
2058
+ }
2037
2059
  // ============================================================
2038
2060
  // Expressions
2039
2061
  // ============================================================
@@ -2517,8 +2539,8 @@ var Parser = class {
2517
2539
  startsSuperUse() {
2518
2540
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2519
2541
  }
2520
- /** After `...`, is there something to spread? Nothing following it means
2521
- * the vararg pack, which is what `f(...)` has always passed on. */
2542
+ /** After `...`, is there something to spread? tilua has no bare `...`:
2543
+ * a varying number of arguments is a rest parameter, `...args: T[]`. */
2522
2544
  startsSpread() {
2523
2545
  const next = this.peek(1);
2524
2546
  if (next.type === "Punctuator") {
@@ -2537,8 +2559,8 @@ var Parser = class {
2537
2559
  const token = this.peek(ahead);
2538
2560
  return token.type === "Punctuator" && token.value === value;
2539
2561
  }
2540
- /** An assignment target after the first: a prefix expression (`a.b`,
2541
- * `a[i]`, `a`) or a nested destructuring pattern. */
2562
+ /** An assignment target after the first — Luau's `a, b = ...`, read only
2563
+ * to be reported: a prefix expression or a destructuring pattern. */
2542
2564
  parseAssignTarget() {
2543
2565
  if (this.checkPunctuator("{")) return this.parseObjectPattern();
2544
2566
  if (this.checkPunctuator("[")) return this.parseArrayPattern();
@@ -3029,7 +3051,7 @@ var Parser = class {
3029
3051
  };
3030
3052
  })() : this.parseFunctionHead();
3031
3053
  this.expectPunctuator("=>");
3032
- const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
3054
+ const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.parseArrowExpressionBody();
3033
3055
  const func = {
3034
3056
  type: "FunctionBody",
3035
3057
  generics: head.generics,
@@ -3042,6 +3064,29 @@ var Parser = class {
3042
3064
  };
3043
3065
  return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
3044
3066
  }
3067
+ /** `() => value` returns the value. `() => a = 1` and `() => n += 1` are
3068
+ * an assignment, which is a statement: the body is that
3069
+ * statement, and the function returns nothing. */
3070
+ parseArrowExpressionBody() {
3071
+ const start = this.cursor;
3072
+ const errors = this.errors.length;
3073
+ const expression = this.parseExpression(0);
3074
+ const t = this.current();
3075
+ const assigns = t.type === "Operator" && (t.value === "=" || COMPOUND_ASSIGN_OPS.has(t.value));
3076
+ if (!assigns) return this.returnOf(expression);
3077
+ this.cursor = start;
3078
+ this.errors.length = errors;
3079
+ const first = this.current();
3080
+ const target = this.parsePrefixExpression();
3081
+ this.rejectOptionalTarget(target);
3082
+ const opToken = this.current();
3083
+ const op = opToken.value;
3084
+ if (opToken.type !== "Operator" || op !== "=" && !COMPOUND_ASSIGN_OPS.has(op)) this.error("Expected '='");
3085
+ this.advance();
3086
+ const value = this.expressionOr(() => false);
3087
+ const statement = op === "=" ? { type: "AssignmentStatement", target, value, ...spanFrom(first, this.previous()) } : { type: "CompoundAssignmentStatement", operator: op, target, value, ...spanFrom(first, this.previous()) };
3088
+ return { type: "Block", statements: [statement], ...spanFrom(statement, statement) };
3089
+ }
3045
3090
  /** A one-expression body: the value is what the function returns. */
3046
3091
  returnOf(expression) {
3047
3092
  const statement = {
@@ -3973,10 +4018,9 @@ var Analyzer = class {
3973
4018
  visitStatement(stmt, scope) {
3974
4019
  switch (stmt.type) {
3975
4020
  case "VariableDeclaration": {
3976
- for (const init of stmt.init) this.visitExpression(init, scope);
3977
- for (const name of stmt.names) this.visitType(name.typeAnnotation, scope);
3978
- const isConst = stmt.kind === "const";
3979
- for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
4021
+ if (stmt.init) this.visitExpression(stmt.init, scope);
4022
+ this.visitType(stmt.name.typeAnnotation, scope);
4023
+ this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
3980
4024
  return;
3981
4025
  }
3982
4026
  case "FunctionDeclaration": {
@@ -4010,16 +4054,15 @@ var Analyzer = class {
4010
4054
  return;
4011
4055
  }
4012
4056
  case "AssignmentStatement": {
4013
- for (const value of stmt.values) this.visitExpression(value, scope);
4014
- for (const target of stmt.targets) {
4015
- if (target.type === "Identifier") {
4016
- this.referenceAsAssignmentTarget(scope, target);
4017
- } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
4018
- this.assignPattern(scope, target);
4019
- } else {
4020
- this.visitExpression(target, scope);
4021
- this.checkModuleWrite(target);
4022
- }
4057
+ this.visitExpression(stmt.value, scope);
4058
+ const target = stmt.target;
4059
+ if (target.type === "Identifier") {
4060
+ this.referenceAsAssignmentTarget(scope, target);
4061
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
4062
+ this.assignPattern(scope, target);
4063
+ } else {
4064
+ this.visitExpression(target, scope);
4065
+ this.checkModuleWrite(target);
4023
4066
  }
4024
4067
  return;
4025
4068
  }
@@ -4565,14 +4608,17 @@ function classArguments(t, name) {
4565
4608
  if (t.kind === "object") return t.class?.typeArguments?.get(name);
4566
4609
  return void 0;
4567
4610
  }
4568
- function unify(param, arg, vars, out) {
4611
+ function unify(param, arg, vars, out, how = "union") {
4569
4612
  if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
4570
- unify(param.constraint, arg, vars, out);
4613
+ unify(param.constraint, arg, vars, out, how);
4571
4614
  return;
4572
4615
  }
4573
4616
  if (param.kind === "typeParam" && vars.has(param.name)) {
4574
4617
  const prev = out.get(param.name);
4575
- out.set(param.name, prev ? union([prev, arg]) : arg);
4618
+ if (!prev) out.set(param.name, arg);
4619
+ else if (isAssignable(arg, prev)) {
4620
+ } else if (isAssignable(prev, arg)) out.set(param.name, arg);
4621
+ else if (how === "union") out.set(param.name, union([prev, arg]));
4576
4622
  return;
4577
4623
  }
4578
4624
  if (arg.kind === "any" || arg.kind === "never") return;
@@ -5340,7 +5386,7 @@ function moduleExports(program, scopes, types, resolveModule) {
5340
5386
  params: declaration.typeParams.map((g) => g.name)
5341
5387
  });
5342
5388
  }
5343
- } else for (const target of declaration.names) exportPattern(target);
5389
+ } else exportPattern(declaration.name);
5344
5390
  } else if (stmt.type === "ExportTypeAliasStatement") {
5345
5391
  const name = stmt.alias.name.name;
5346
5392
  const type = types.aliases.get(name);
@@ -5579,12 +5625,10 @@ function assignedFields(block) {
5579
5625
  const names = /* @__PURE__ */ new Set();
5580
5626
  walkNodes(block, (node) => {
5581
5627
  const record = node;
5582
- const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
5583
- for (const target of targets ?? []) {
5584
- const member = target;
5585
- if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5586
- names.add(member.property.name);
5587
- }
5628
+ if (record.type !== "AssignmentStatement" && record.type !== "CompoundAssignmentStatement") return;
5629
+ const member = record.target;
5630
+ if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5631
+ names.add(member.property.name);
5588
5632
  }
5589
5633
  });
5590
5634
  return names;
@@ -6060,12 +6104,10 @@ var TypeAnalyzer = class {
6060
6104
  classDisplayNames = /* @__PURE__ */ new WeakMap();
6061
6105
  /** `const Name = class ... end` — the name the class will be known by. */
6062
6106
  nameClassExpressions(stmt) {
6063
- stmt.names.forEach((target, i) => {
6064
- const value = stmt.init[i];
6065
- if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6066
- this.classDisplayNames.set(value, target.name);
6067
- }
6068
- });
6107
+ const value = stmt.init;
6108
+ if (stmt.name.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6109
+ this.classDisplayNames.set(value, stmt.name.name);
6110
+ }
6069
6111
  }
6070
6112
  classTypeParams(stmt) {
6071
6113
  return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
@@ -7348,35 +7390,30 @@ var TypeAnalyzer = class {
7348
7390
  switch (stmt.type) {
7349
7391
  case "VariableDeclaration": {
7350
7392
  this.nameClassExpressions(stmt);
7351
- stmt.names.forEach((target, i) => {
7352
- if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
7353
- this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
7354
- }
7355
- });
7356
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7357
- this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
7358
- stmt.names.forEach((target, i) => {
7359
- const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
7360
- const source = sources[i];
7361
- if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7362
- const declared = this.resolveType(target.typeAnnotation);
7363
- if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7364
- this.diagnostics.push({
7365
- node: stmt,
7366
- message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7367
- });
7368
- } else if (declared.kind !== "any") {
7369
- this.reportExcessProperties(source, declared);
7370
- }
7371
- }
7372
- const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7373
- this.bindPattern(target, inferred, env, mode);
7374
- if (stmt.kind === "const") {
7375
- this.correlateDestructuring(target, inferred, env);
7376
- this.correlateIndexed(target, source, env);
7377
- this.aliasReference(target, source);
7393
+ const target = stmt.name;
7394
+ const source = stmt.init;
7395
+ if (target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7396
+ this.applyContext(source, this.resolveType(target.typeAnnotation));
7397
+ }
7398
+ const inferred = source ? this.infer(source, env) : nilType;
7399
+ if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7400
+ const declared = this.resolveType(target.typeAnnotation);
7401
+ if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7402
+ this.diagnostics.push({
7403
+ node: stmt,
7404
+ message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7405
+ });
7406
+ } else if (declared.kind !== "any") {
7407
+ this.reportExcessProperties(source, declared);
7378
7408
  }
7379
- });
7409
+ }
7410
+ const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7411
+ this.bindPattern(target, inferred, env, mode);
7412
+ if (stmt.kind === "const") {
7413
+ this.correlateDestructuring(target, inferred, env);
7414
+ this.correlateIndexed(target, source, env);
7415
+ this.aliasReference(target, source);
7416
+ }
7380
7417
  return;
7381
7418
  }
7382
7419
  case "ClassDeclaration": {
@@ -7433,48 +7470,41 @@ var TypeAnalyzer = class {
7433
7470
  return;
7434
7471
  }
7435
7472
  case "AssignmentStatement": {
7436
- stmt.targets.forEach((target, i) => {
7437
- const value = stmt.values[i];
7438
- if (!value) return;
7439
- if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7440
- this.applyContext(value, this.infer(target, env));
7441
- } else if (target.type === "Identifier") {
7442
- const id = this.bindingIdOf(target);
7443
- if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7444
- }
7445
- });
7446
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7447
- this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7448
- stmt.targets.forEach((target, i) => {
7449
- const vt = valueTypes[i] ?? unknownType;
7450
- const source = sources[i];
7451
- if (target.type === "Identifier") {
7452
- const id = this.bindingIdOf(target);
7453
- if (id !== void 0) {
7454
- this.uncorrelate(id);
7455
- const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7456
- if (this.annotated.has(id)) {
7457
- const declared = this.bindingType.get(id);
7458
- if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7459
- this.diagnostics.push({
7460
- node: stmt,
7461
- message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7462
- });
7463
- }
7464
- this.setBinding(env, id, narrowTo(declared, next));
7465
- } else {
7466
- this.setBinding(env, id, next);
7467
- this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7473
+ const target = stmt.target;
7474
+ const source = stmt.value;
7475
+ if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7476
+ this.applyContext(source, this.infer(target, env));
7477
+ } else if (target.type === "Identifier") {
7478
+ const id = this.bindingIdOf(target);
7479
+ if (id !== void 0 && this.annotated.has(id)) this.applyContext(source, this.bindingType.get(id));
7480
+ }
7481
+ const vt = this.infer(source, env);
7482
+ if (target.type === "Identifier") {
7483
+ const id = this.bindingIdOf(target);
7484
+ if (id !== void 0) {
7485
+ this.uncorrelate(id);
7486
+ const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7487
+ if (this.annotated.has(id)) {
7488
+ const declared = this.bindingType.get(id);
7489
+ if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7490
+ this.diagnostics.push({
7491
+ node: stmt,
7492
+ message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7493
+ });
7468
7494
  }
7495
+ this.setBinding(env, id, narrowTo(declared, next));
7496
+ } else {
7497
+ this.setBinding(env, id, next);
7498
+ this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7469
7499
  }
7470
- } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7471
- this.infer(target, env);
7472
- this.checkReadonlyAssign(target, env);
7473
- this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7474
- } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7475
- this.reassignPattern(target, vt, env);
7476
7500
  }
7477
- });
7501
+ } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7502
+ this.infer(target, env);
7503
+ this.checkReadonlyAssign(target, env);
7504
+ this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7505
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7506
+ this.reassignPattern(target, vt, env);
7507
+ }
7478
7508
  return;
7479
7509
  }
7480
7510
  case "CompoundAssignmentStatement":
@@ -7638,55 +7668,6 @@ var TypeAnalyzer = class {
7638
7668
  return;
7639
7669
  }
7640
7670
  }
7641
- /** Flatten an expression list into the values it actually produces.
7642
- *
7643
- * Lua's adjustment rule: every expression but the last contributes one
7644
- * value, and the last contributes all of its values if it is a call or
7645
- * `...`. That is what makes `local ok, err = pcall(f)` work, and it is
7646
- * why `local x = f()` takes only the first value.
7647
- *
7648
- * `sources` maps each produced value back to the expression it came from
7649
- * (undefined for the 2nd and later values of a multi-value call), so the
7650
- * caller can still do contextual typing against the written expression. */
7651
- valueList(exprs, env) {
7652
- const types = [];
7653
- const sources = [];
7654
- exprs.forEach((e, i) => {
7655
- const t = this.infer(e, env);
7656
- const last = i === exprs.length - 1;
7657
- if (e.type === "SpreadElement") {
7658
- const held = this.typeOf.get(e.argument);
7659
- const expanded = held && this.expand(held);
7660
- if (expanded?.kind === "tuple") {
7661
- for (const element of expanded.elements) {
7662
- types.push(element);
7663
- sources.push(e);
7664
- }
7665
- return;
7666
- }
7667
- types.push(t);
7668
- sources.push(e);
7669
- return;
7670
- }
7671
- void last;
7672
- types.push(t);
7673
- sources.push(e);
7674
- });
7675
- return { types, sources };
7676
- }
7677
- /** `const a, b = x, y` pairs names with values, one each. A call is one
7678
- * value — several come back as an array — so `const a, b = f()` is a
7679
- * name short of a value, and says how to take them apart. */
7680
- checkValueCount(node, names, values, types) {
7681
- if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7682
- const last = values[values.length - 1];
7683
- const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7684
- const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7685
- this.diagnostics.push({
7686
- node,
7687
- message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
7688
- });
7689
- }
7690
7671
  /** Run `visit` with a fresh place to collect `break` states, and return
7691
7672
  * what it collected. */
7692
7673
  withBreakScope(visit) {
@@ -7757,8 +7738,10 @@ var TypeAnalyzer = class {
7757
7738
  // --------------------------------------------------------
7758
7739
  // Functions
7759
7740
  // --------------------------------------------------------
7760
- visitFunctionBody(func, outerEnv) {
7761
- this.withTypeParams(func.generics, () => this.visitFunctionBodyInner(func, outerEnv));
7741
+ /** `returns`, when given, collects the type of each `return` the walk
7742
+ * reads — see `inferFunctionBody`. */
7743
+ visitFunctionBody(func, outerEnv, returns) {
7744
+ this.withTypeParams(func.generics, () => this.visitFunctionBodyInner(func, outerEnv, returns));
7762
7745
  }
7763
7746
  /** A parameter's type: annotation, else a shape synthesized from a
7764
7747
  * destructuring pattern, else inferred from its default, else `any`. */
@@ -7807,6 +7790,53 @@ var TypeAnalyzer = class {
7807
7790
  * it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
7808
7791
  * `player` from `Connect`'s callback type. Anything else is inferred as
7809
7792
  * usual. */
7793
+ /** The arguments of a call, in two passes where one is needed.
7794
+ *
7795
+ * A function written at the call site with a parameter nothing annotates
7796
+ * reads that parameter from the signature — and where the signature says
7797
+ * `(v: T) => U`, `T` is only known once the other arguments have been
7798
+ * read. So such an argument is read once quietly, to get a shape to pick
7799
+ * a signature with, and then again with `T` in hand: `map(xs, v => v + 1)`
7800
+ * has its `v` a number by then, and `U` follows from what the body
7801
+ * answers. Everything else is read once, as it always was. */
7802
+ inferArguments(written, fns, selfOf, extra, env) {
7803
+ const expected = this.expectedArguments(written, fns, selfOf);
7804
+ written.forEach((a, i) => this.applyContext(a, expected[i]));
7805
+ const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
7806
+ if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
7807
+ const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
7808
+ const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
7809
+ const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
7810
+ written.forEach((a, i) => {
7811
+ if (!waits[i]) return;
7812
+ if (picked && subst) {
7813
+ const self = selfOf(picked);
7814
+ const declared = i + self < picked.params.length ? picked.params[i + self].type : picked.varargs;
7815
+ if (declared) this.applyContext(a, this.reduceType(substitute(declared, subst)));
7816
+ }
7817
+ argTypes[i] = this.infer(a, env);
7818
+ });
7819
+ return argTypes;
7820
+ }
7821
+ /** Is this argument a function whose own type waits on the call's? */
7822
+ waitsOnInference(arg, expected) {
7823
+ if (!expected || !containsTypeParam(expected)) return false;
7824
+ let e = arg;
7825
+ while (e.type === "ParenthesizedExpression") e = e.expression;
7826
+ if (e.type !== "FunctionExpression") return false;
7827
+ return e.func.params.some((p) => !p.typeAnnotation && !p.default && !p.pattern && p.name !== "self" && p.name !== "this");
7828
+ }
7829
+ /** Read something without saying anything about it: a first pass whose
7830
+ * only purpose is a shape to go on. */
7831
+ quietly(read) {
7832
+ const was = this.emitDiagnostics;
7833
+ this.emitDiagnostics = false;
7834
+ try {
7835
+ return read();
7836
+ } finally {
7837
+ this.emitDiagnostics = was;
7838
+ }
7839
+ }
7810
7840
  applyContext(expr, expected) {
7811
7841
  let e = expr;
7812
7842
  while (e.type === "ParenthesizedExpression") e = e.expression;
@@ -7955,7 +7985,7 @@ var TypeAnalyzer = class {
7955
7985
  this.contextualReturns.pop();
7956
7986
  }
7957
7987
  }
7958
- visitFunctionBodyInner(func, outerEnv) {
7988
+ visitFunctionBodyInner(func, outerEnv, returns) {
7959
7989
  const env = forkEnv(outerEnv);
7960
7990
  for (const p of func.params) {
7961
7991
  if (p.pattern) {
@@ -7972,7 +8002,7 @@ var TypeAnalyzer = class {
7972
8002
  if (p.typeAnnotation) this.annotated.add(id);
7973
8003
  }
7974
8004
  }
7975
- this.withVarargs(func, () => this.collectReturns(void 0, () => {
8005
+ this.withVarargs(func, () => this.collectReturns(returns, () => {
7976
8006
  this.visitBlock(func.body, env);
7977
8007
  this.checkReturnsAtAll(func, this.declaredReturns[this.declaredReturns.length - 1]);
7978
8008
  }));
@@ -8026,7 +8056,7 @@ var TypeAnalyzer = class {
8026
8056
  if (arg === void 0) return;
8027
8057
  const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
8028
8058
  const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
8029
- unify(p.type, preserve ? arg : widen(arg), vars, subst);
8059
+ unify(p.type, preserve ? arg : widen(arg), vars, subst, "first");
8030
8060
  });
8031
8061
  if (f.varargs && f.restIsWhole) {
8032
8062
  const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
@@ -8377,7 +8407,12 @@ var TypeAnalyzer = class {
8377
8407
  asserts: node.asserts
8378
8408
  };
8379
8409
  }
8380
- inferFunctionBody(func, env) {
8410
+ /** `visited`, when given, is what the real walk of the body just
8411
+ * collected from its `return`s: a function expression is walked before
8412
+ * its type is asked for, so there is nothing left to guess, and walking
8413
+ * the body a second time only to read them made every callback nested in
8414
+ * another cost twice its parent's. */
8415
+ inferFunctionBody(func, env, visited) {
8381
8416
  const names = func.generics.map((g) => g.name);
8382
8417
  return this.withTypeParams(func.generics, () => {
8383
8418
  const params = func.params.flatMap((p) => {
@@ -8406,6 +8441,8 @@ var TypeAnalyzer = class {
8406
8441
  returns = this.resolveType(func.returnType);
8407
8442
  } else if (func.predicate) {
8408
8443
  returns = booleanType;
8444
+ } else if (visited) {
8445
+ returns = visited.length ? union([...visited]) : this.withVarargs(func, () => this.silently(() => this.inferReturnType(func.body, bodyEnv)));
8409
8446
  } else {
8410
8447
  const collected = [];
8411
8448
  returns = this.withVarargs(func, () => this.silently(() => {
@@ -9278,10 +9315,12 @@ var TypeAnalyzer = class {
9278
9315
  return this.inferArray(expr, env, false);
9279
9316
  case "TableExpression":
9280
9317
  return this.inferObject(expr, env, false);
9281
- case "FunctionExpression":
9318
+ case "FunctionExpression": {
9282
9319
  this.checkParamOrder(expr.func.params, expr);
9283
- this.visitFunctionBody(expr.func, env);
9284
- return this.inferFunctionBody(expr.func, env);
9320
+ const returned = [];
9321
+ this.visitFunctionBody(expr.func, env, returned);
9322
+ return this.inferFunctionBody(expr.func, env, returned);
9323
+ }
9285
9324
  case "ParenthesizedExpression":
9286
9325
  return this.infer(expr.expression, env);
9287
9326
  case "TypeAssertionExpression": {
@@ -9485,9 +9524,7 @@ var TypeAnalyzer = class {
9485
9524
  const united = this.unionSignatures(callee);
9486
9525
  const fns = united ?? this.overloadsOf(callee);
9487
9526
  const explicit = this.explicitTypeArguments(expr, fns);
9488
- const expected = this.expectedArguments(expr.arguments, fns, () => 0);
9489
- expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
9490
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
9527
+ const argTypes = this.inferArguments(expr.arguments, fns, () => 0, (_, args) => args, env);
9491
9528
  if (fns.length) {
9492
9529
  this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
9493
9530
  const spread = this.spreadOf(expr.arguments);
@@ -9552,9 +9589,13 @@ var TypeAnalyzer = class {
9552
9589
  const united = this.unionSignatures(method);
9553
9590
  const fns = united ?? this.overloadsOf(method);
9554
9591
  const explicit = this.explicitTypeArguments(expr, fns);
9555
- const expected = this.expectedArguments(expr.arguments, fns, (f) => this.takesSelf(f) ? 1 : 0);
9556
- expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
9557
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
9592
+ const argTypes = this.inferArguments(
9593
+ expr.arguments,
9594
+ fns,
9595
+ (f) => this.takesSelf(f) ? 1 : 0,
9596
+ (f, args) => this.takesSelf(f) ? [objType, ...args] : args,
9597
+ env
9598
+ );
9558
9599
  if (fns.length) {
9559
9600
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
9560
9601
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
@@ -9610,7 +9651,35 @@ var TypeAnalyzer = class {
9610
9651
  }
9611
9652
  const read = this.expand(type);
9612
9653
  if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
9613
- return { type, shortCircuits: inChain !== void 0 || link.optional === true };
9654
+ const optionalNil = link.optional === true && this.mayBeNil(inChain ?? full);
9655
+ return { type, shortCircuits: inChain !== void 0 || optionalNil };
9656
+ }
9657
+ /** Whether a value of `raw` can be nil — `includesNil`, and also the types
9658
+ * that do not say: `any`, `unknown`, an unconstrained type parameter. */
9659
+ mayBeNil(raw) {
9660
+ const t = this.expand(raw);
9661
+ switch (t.kind) {
9662
+ case "primitive":
9663
+ return t.name === "nil";
9664
+ case "any":
9665
+ case "unknown":
9666
+ return true;
9667
+ case "typeParam":
9668
+ return t.constraint === void 0 || this.mayBeNil(t.constraint);
9669
+ case "union":
9670
+ return t.types.some((m) => this.mayBeNil(m));
9671
+ case "never":
9672
+ case "literal":
9673
+ case "array":
9674
+ case "tuple":
9675
+ case "object":
9676
+ case "function":
9677
+ case "intersection":
9678
+ case "templateLiteral":
9679
+ return false;
9680
+ default:
9681
+ return true;
9682
+ }
9614
9683
  }
9615
9684
  /** Objects already reported as possibly nil: a loop body is visited more
9616
9685
  * than once. */
@@ -10532,13 +10601,12 @@ var TypeAnalyzer = class {
10532
10601
  computingAhead = /* @__PURE__ */ new Set();
10533
10602
  declaredAhead(id) {
10534
10603
  this.aheadDeclarations ??= this.indexAheadDeclarations();
10535
- const found = this.aheadDeclarations.get(id);
10536
- if (!found || this.computingAhead.has(id)) return void 0;
10604
+ const statement = this.aheadDeclarations.get(id);
10605
+ if (!statement || this.computingAhead.has(id)) return void 0;
10537
10606
  this.computingAhead.add(id);
10538
10607
  const wasEmitting = this.emitDiagnostics;
10539
10608
  this.emitDiagnostics = false;
10540
10609
  try {
10541
- const { statement, index } = found;
10542
10610
  let type;
10543
10611
  if (statement.type === "DeclareStatement") {
10544
10612
  type = this.resolveType(statement.valueType);
@@ -10547,11 +10615,11 @@ var TypeAnalyzer = class {
10547
10615
  } else if (statement.type === "ClassDeclaration") {
10548
10616
  type = this.classValueType(statement);
10549
10617
  } else if (statement.type === "VariableDeclaration") {
10550
- const target = statement.names[index];
10618
+ const target = statement.name;
10551
10619
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
10552
10620
  type = this.resolveType(target.typeAnnotation);
10553
- } else if (statement.init[index]) {
10554
- const value = this.infer(statement.init[index], /* @__PURE__ */ new Map());
10621
+ } else if (statement.init) {
10622
+ const value = this.infer(statement.init, /* @__PURE__ */ new Map());
10555
10623
  type = statement.kind === "const" ? value : widen(value);
10556
10624
  }
10557
10625
  }
@@ -10569,11 +10637,10 @@ var TypeAnalyzer = class {
10569
10637
  for (const statement of this.program.body.statements) {
10570
10638
  const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
10571
10639
  if (declaration.type !== "VariableDeclaration") continue;
10572
- declaration.names.forEach((target, index) => {
10573
- if (target.type !== "IdentifierPattern") return;
10574
- const id = this.bindingIdByName(target.name, target);
10575
- if (id !== void 0) out.set(id, { statement: declaration, index });
10576
- });
10640
+ const target = declaration.name;
10641
+ if (target.type !== "IdentifierPattern") continue;
10642
+ const id = this.bindingIdByName(target.name, target);
10643
+ if (id !== void 0) out.set(id, declaration);
10577
10644
  }
10578
10645
  const visit = (node) => {
10579
10646
  if (!node || typeof node !== "object") return;
@@ -10584,11 +10651,11 @@ var TypeAnalyzer = class {
10584
10651
  const record = node;
10585
10652
  if (record.type === "ClassDeclaration" && record.name) {
10586
10653
  const id = this.bindingIdByName(record.name.name, record.name);
10587
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10654
+ if (id !== void 0) out.set(id, node);
10588
10655
  }
10589
10656
  if (record.type === "FunctionDeclaration" && record.name) {
10590
10657
  const id = this.bindingIdByName(record.name.name, record.name);
10591
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10658
+ if (id !== void 0) out.set(id, node);
10592
10659
  }
10593
10660
  for (const [key, value] of Object.entries(node)) {
10594
10661
  if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
@@ -10597,7 +10664,7 @@ var TypeAnalyzer = class {
10597
10664
  visit(this.program.body);
10598
10665
  for (const [name, statement] of this.deferredDeclares) {
10599
10666
  const id = this.scopes.globalsByName.get(name);
10600
- if (id !== void 0) out.set(id, { statement, index: 0 });
10667
+ if (id !== void 0) out.set(id, statement);
10601
10668
  }
10602
10669
  return out;
10603
10670
  }