@tilua/parser 2.2.0 → 2.4.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(true) : this.parseArrayPattern(true);
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,11 +2559,11 @@ 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
- if (this.checkPunctuator("{")) return this.parseObjectPattern();
2544
- if (this.checkPunctuator("[")) return this.parseArrayPattern();
2565
+ if (this.checkPunctuator("{")) return this.parseObjectPattern(true);
2566
+ if (this.checkPunctuator("[")) return this.parseArrayPattern(true);
2545
2567
  return this.parsePrefixExpression();
2546
2568
  }
2547
2569
  /** Does a call's argument list start here? Lua's three forms: `(`, a
@@ -2720,13 +2742,17 @@ var Parser = class {
2720
2742
  /** Parses a binding target. When `topLevel`, also consumes a trailing
2721
2743
  * `<attr>` list (identifier only) and a `: Type` annotation — these are
2722
2744
  * only valid at the outermost level of a `local` / parameter binding,
2723
- * never nested inside another pattern. */
2724
- parseBindingTarget(topLevel) {
2745
+ * never nested inside another pattern. When `assign`, the pattern is a
2746
+ * destructuring assignment's (`[t[i], t[j]] = ...`), and a leaf is any
2747
+ * place a value can be assigned to: a name, a member or an index. */
2748
+ parseBindingTarget(topLevel, assign = false) {
2725
2749
  let target;
2726
2750
  if (this.checkPunctuator("{")) {
2727
- target = this.parseObjectPattern();
2751
+ target = this.parseObjectPattern(assign);
2728
2752
  } else if (this.checkPunctuator("[")) {
2729
- target = this.parseArrayPattern();
2753
+ target = this.parseArrayPattern(assign);
2754
+ } else if (assign) {
2755
+ return this.parseAssignPatternLeaf();
2730
2756
  } else {
2731
2757
  const nameTok = this.expectIdentifier();
2732
2758
  let attributes;
@@ -2748,10 +2774,25 @@ var Parser = class {
2748
2774
  }
2749
2775
  return target;
2750
2776
  }
2751
- parseObjectPattern() {
2752
- return this.inBrackets(() => this.parseObjectPatternInner());
2777
+ /** A leaf of a destructuring assignment: `a`, `a.b` or `a[i]`. */
2778
+ parseAssignPatternLeaf() {
2779
+ const expression = this.parsePrefixExpression();
2780
+ if (expression.type === "Identifier") {
2781
+ return { type: "IdentifierPattern", name: expression.name, line: expression.line, column: expression.column };
2782
+ }
2783
+ if (expression.type === "MemberExpression" || expression.type === "IndexExpression") {
2784
+ this.rejectOptionalTarget(expression);
2785
+ return expression;
2786
+ }
2787
+ const err = new ParseError("Only a name, a member or an index can be assigned to", expression.line.start, expression.column.start);
2788
+ if (!this.recover) throw err;
2789
+ this.record(err);
2790
+ return { type: "IdentifierPattern", name: "", line: expression.line, column: expression.column };
2791
+ }
2792
+ parseObjectPattern(assign = false) {
2793
+ return this.inBrackets(() => this.parseObjectPatternInner(assign));
2753
2794
  }
2754
- parseObjectPatternInner() {
2795
+ parseObjectPatternInner(assign) {
2755
2796
  const start = this.current();
2756
2797
  this.expectPunctuator("{");
2757
2798
  const properties = [];
@@ -2759,7 +2800,7 @@ var Parser = class {
2759
2800
  while (!this.checkPunctuator("}")) {
2760
2801
  if (this.checkOperator("...")) {
2761
2802
  this.advance();
2762
- rest = this.parseBindingTarget(false);
2803
+ rest = this.parseBindingTarget(false, assign);
2763
2804
  break;
2764
2805
  }
2765
2806
  const propStart = this.current();
@@ -2772,17 +2813,17 @@ var Parser = class {
2772
2813
  key = this.parseExpression();
2773
2814
  this.expectPunctuator("]");
2774
2815
  this.expectPunctuator(":");
2775
- value = this.parseBindingTarget(false);
2816
+ value = this.parseBindingTarget(false, assign);
2776
2817
  } else if (this.checkType("Literal") && this.current().kind === "string") {
2777
2818
  const t = this.advance();
2778
2819
  key = { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2779
2820
  this.expectPunctuator(":");
2780
- value = this.parseBindingTarget(false);
2821
+ value = this.parseBindingTarget(false, assign);
2781
2822
  } else {
2782
2823
  const nameTok = this.expectIdentifier();
2783
2824
  key = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
2784
2825
  if (this.matchPunctuator(":")) {
2785
- value = this.parseBindingTarget(false);
2826
+ value = this.parseBindingTarget(false, assign);
2786
2827
  } else {
2787
2828
  shorthand = true;
2788
2829
  value = { type: "IdentifierPattern", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
@@ -2805,10 +2846,10 @@ var Parser = class {
2805
2846
  this.expectPunctuator("}");
2806
2847
  return { type: "ObjectPattern", properties, rest, ...spanFrom(start, this.previous()) };
2807
2848
  }
2808
- parseArrayPattern() {
2809
- return this.inBrackets(() => this.parseArrayPatternInner());
2849
+ parseArrayPattern(assign = false) {
2850
+ return this.inBrackets(() => this.parseArrayPatternInner(assign));
2810
2851
  }
2811
- parseArrayPatternInner() {
2852
+ parseArrayPatternInner(assign) {
2812
2853
  const start = this.current();
2813
2854
  this.expectPunctuator("[");
2814
2855
  const elements = [];
@@ -2816,7 +2857,7 @@ var Parser = class {
2816
2857
  while (!this.checkPunctuator("]")) {
2817
2858
  if (this.checkOperator("...")) {
2818
2859
  this.advance();
2819
- rest = this.parseBindingTarget(false);
2860
+ rest = this.parseBindingTarget(false, assign);
2820
2861
  break;
2821
2862
  }
2822
2863
  if (this.checkPunctuator(",")) {
@@ -2825,7 +2866,7 @@ var Parser = class {
2825
2866
  continue;
2826
2867
  }
2827
2868
  const elStart = this.current();
2828
- const value = this.parseBindingTarget(false);
2869
+ const value = this.parseBindingTarget(false, assign);
2829
2870
  let def;
2830
2871
  if (this.matchOperator("=")) def = this.parseExpression();
2831
2872
  elements.push({ type: "ArrayPatternElement", value, default: def, ...spanFrom(elStart, this.previous()) });
@@ -3029,7 +3070,7 @@ var Parser = class {
3029
3070
  };
3030
3071
  })() : this.parseFunctionHead();
3031
3072
  this.expectPunctuator("=>");
3032
- const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
3073
+ const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.parseArrowExpressionBody();
3033
3074
  const func = {
3034
3075
  type: "FunctionBody",
3035
3076
  generics: head.generics,
@@ -3042,6 +3083,29 @@ var Parser = class {
3042
3083
  };
3043
3084
  return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
3044
3085
  }
3086
+ /** `() => value` returns the value. `() => a = 1` and `() => n += 1` are
3087
+ * an assignment, which is a statement: the body is that
3088
+ * statement, and the function returns nothing. */
3089
+ parseArrowExpressionBody() {
3090
+ const start = this.cursor;
3091
+ const errors = this.errors.length;
3092
+ const expression = this.parseExpression(0);
3093
+ const t = this.current();
3094
+ const assigns = t.type === "Operator" && (t.value === "=" || COMPOUND_ASSIGN_OPS.has(t.value));
3095
+ if (!assigns) return this.returnOf(expression);
3096
+ this.cursor = start;
3097
+ this.errors.length = errors;
3098
+ const first = this.current();
3099
+ const target = this.parsePrefixExpression();
3100
+ this.rejectOptionalTarget(target);
3101
+ const opToken = this.current();
3102
+ const op = opToken.value;
3103
+ if (opToken.type !== "Operator" || op !== "=" && !COMPOUND_ASSIGN_OPS.has(op)) this.error("Expected '='");
3104
+ this.advance();
3105
+ const value = this.expressionOr(() => false);
3106
+ const statement = op === "=" ? { type: "AssignmentStatement", target, value, ...spanFrom(first, this.previous()) } : { type: "CompoundAssignmentStatement", operator: op, target, value, ...spanFrom(first, this.previous()) };
3107
+ return { type: "Block", statements: [statement], ...spanFrom(statement, statement) };
3108
+ }
3045
3109
  /** A one-expression body: the value is what the function returns. */
3046
3110
  returnOf(expression) {
3047
3111
  const statement = {
@@ -3939,6 +4003,10 @@ var Analyzer = class {
3939
4003
  this.checkConstAssign(id, t);
3940
4004
  return;
3941
4005
  }
4006
+ case "MemberExpression":
4007
+ case "IndexExpression":
4008
+ this.visitExpression(t, scope);
4009
+ return;
3942
4010
  case "ObjectPattern":
3943
4011
  for (const p of t.properties) {
3944
4012
  if (p.computed) this.visitExpression(p.key, scope);
@@ -3973,10 +4041,11 @@ var Analyzer = class {
3973
4041
  visitStatement(stmt, scope) {
3974
4042
  switch (stmt.type) {
3975
4043
  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);
4044
+ if (stmt.init) this.visitExpression(stmt.init, scope);
4045
+ if (stmt.name.type !== "MemberExpression" && stmt.name.type !== "IndexExpression") {
4046
+ this.visitType(stmt.name.typeAnnotation, scope);
4047
+ }
4048
+ this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
3980
4049
  return;
3981
4050
  }
3982
4051
  case "FunctionDeclaration": {
@@ -4010,16 +4079,15 @@ var Analyzer = class {
4010
4079
  return;
4011
4080
  }
4012
4081
  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
- }
4082
+ this.visitExpression(stmt.value, scope);
4083
+ const target = stmt.target;
4084
+ if (target.type === "Identifier") {
4085
+ this.referenceAsAssignmentTarget(scope, target);
4086
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
4087
+ this.assignPattern(scope, target);
4088
+ } else {
4089
+ this.visitExpression(target, scope);
4090
+ this.checkModuleWrite(target);
4023
4091
  }
4024
4092
  return;
4025
4093
  }
@@ -5343,7 +5411,7 @@ function moduleExports(program, scopes, types, resolveModule) {
5343
5411
  params: declaration.typeParams.map((g) => g.name)
5344
5412
  });
5345
5413
  }
5346
- } else for (const target of declaration.names) exportPattern(target);
5414
+ } else exportPattern(declaration.name);
5347
5415
  } else if (stmt.type === "ExportTypeAliasStatement") {
5348
5416
  const name = stmt.alias.name.name;
5349
5417
  const type = types.aliases.get(name);
@@ -5582,12 +5650,10 @@ function assignedFields(block) {
5582
5650
  const names = /* @__PURE__ */ new Set();
5583
5651
  walkNodes(block, (node) => {
5584
5652
  const record = node;
5585
- const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
5586
- for (const target of targets ?? []) {
5587
- const member = target;
5588
- if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5589
- names.add(member.property.name);
5590
- }
5653
+ if (record.type !== "AssignmentStatement" && record.type !== "CompoundAssignmentStatement") return;
5654
+ const member = record.target;
5655
+ if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5656
+ names.add(member.property.name);
5591
5657
  }
5592
5658
  });
5593
5659
  return names;
@@ -6063,12 +6129,10 @@ var TypeAnalyzer = class {
6063
6129
  classDisplayNames = /* @__PURE__ */ new WeakMap();
6064
6130
  /** `const Name = class ... end` — the name the class will be known by. */
6065
6131
  nameClassExpressions(stmt) {
6066
- stmt.names.forEach((target, i) => {
6067
- const value = stmt.init[i];
6068
- if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6069
- this.classDisplayNames.set(value, target.name);
6070
- }
6071
- });
6132
+ const value = stmt.init;
6133
+ if (stmt.name.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6134
+ this.classDisplayNames.set(value, stmt.name.name);
6135
+ }
6072
6136
  }
6073
6137
  classTypeParams(stmt) {
6074
6138
  return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
@@ -6397,6 +6461,14 @@ var TypeAnalyzer = class {
6397
6461
  report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
6398
6462
  }
6399
6463
  }
6464
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
6465
+ this.withSelfType(this.selfTypeOf(stmt), () => {
6466
+ for (const member of stmt.members) {
6467
+ if (member.type !== "ClassMethod" || !member.signatures) continue;
6468
+ for (const signature of member.signatures) this.checkOverload(member.func, signature);
6469
+ }
6470
+ });
6471
+ }));
6400
6472
  for (const member of stmt.members) {
6401
6473
  if (member.type === "ClassConstructor") continue;
6402
6474
  if (CLASS_RESERVED.has(member.name.name)) {
@@ -7351,35 +7423,30 @@ var TypeAnalyzer = class {
7351
7423
  switch (stmt.type) {
7352
7424
  case "VariableDeclaration": {
7353
7425
  this.nameClassExpressions(stmt);
7354
- stmt.names.forEach((target, i) => {
7355
- if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
7356
- this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
7357
- }
7358
- });
7359
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7360
- this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
7361
- stmt.names.forEach((target, i) => {
7362
- const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
7363
- const source = sources[i];
7364
- if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7365
- const declared = this.resolveType(target.typeAnnotation);
7366
- if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7367
- this.diagnostics.push({
7368
- node: stmt,
7369
- message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7370
- });
7371
- } else if (declared.kind !== "any") {
7372
- this.reportExcessProperties(source, declared);
7373
- }
7374
- }
7375
- const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7376
- this.bindPattern(target, inferred, env, mode);
7377
- if (stmt.kind === "const") {
7378
- this.correlateDestructuring(target, inferred, env);
7379
- this.correlateIndexed(target, source, env);
7380
- this.aliasReference(target, source);
7426
+ const target = stmt.name;
7427
+ const source = stmt.init;
7428
+ if (target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7429
+ this.applyContext(source, this.resolveType(target.typeAnnotation));
7430
+ }
7431
+ const inferred = source ? this.infer(source, env) : nilType;
7432
+ if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7433
+ const declared = this.resolveType(target.typeAnnotation);
7434
+ if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7435
+ this.diagnostics.push({
7436
+ node: stmt,
7437
+ message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7438
+ });
7439
+ } else if (declared.kind !== "any") {
7440
+ this.reportExcessProperties(source, declared);
7381
7441
  }
7382
- });
7442
+ }
7443
+ const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7444
+ this.bindPattern(target, inferred, env, mode);
7445
+ if (stmt.kind === "const") {
7446
+ this.correlateDestructuring(target, inferred, env);
7447
+ this.correlateIndexed(target, source, env);
7448
+ this.aliasReference(target, source);
7449
+ }
7383
7450
  return;
7384
7451
  }
7385
7452
  case "ClassDeclaration": {
@@ -7436,48 +7503,41 @@ var TypeAnalyzer = class {
7436
7503
  return;
7437
7504
  }
7438
7505
  case "AssignmentStatement": {
7439
- stmt.targets.forEach((target, i) => {
7440
- const value = stmt.values[i];
7441
- if (!value) return;
7442
- if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7443
- this.applyContext(value, this.infer(target, env));
7444
- } else if (target.type === "Identifier") {
7445
- const id = this.bindingIdOf(target);
7446
- if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7447
- }
7448
- });
7449
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7450
- this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7451
- stmt.targets.forEach((target, i) => {
7452
- const vt = valueTypes[i] ?? unknownType;
7453
- const source = sources[i];
7454
- if (target.type === "Identifier") {
7455
- const id = this.bindingIdOf(target);
7456
- if (id !== void 0) {
7457
- this.uncorrelate(id);
7458
- const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7459
- if (this.annotated.has(id)) {
7460
- const declared = this.bindingType.get(id);
7461
- if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7462
- this.diagnostics.push({
7463
- node: stmt,
7464
- message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7465
- });
7466
- }
7467
- this.setBinding(env, id, narrowTo(declared, next));
7468
- } else {
7469
- this.setBinding(env, id, next);
7470
- this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7506
+ const target = stmt.target;
7507
+ const source = stmt.value;
7508
+ if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7509
+ this.applyContext(source, this.infer(target, env));
7510
+ } else if (target.type === "Identifier") {
7511
+ const id = this.bindingIdOf(target);
7512
+ if (id !== void 0 && this.annotated.has(id)) this.applyContext(source, this.bindingType.get(id));
7513
+ }
7514
+ const vt = this.infer(source, env);
7515
+ if (target.type === "Identifier") {
7516
+ const id = this.bindingIdOf(target);
7517
+ if (id !== void 0) {
7518
+ this.uncorrelate(id);
7519
+ const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7520
+ if (this.annotated.has(id)) {
7521
+ const declared = this.bindingType.get(id);
7522
+ if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7523
+ this.diagnostics.push({
7524
+ node: stmt,
7525
+ message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7526
+ });
7471
7527
  }
7528
+ this.setBinding(env, id, narrowTo(declared, next));
7529
+ } else {
7530
+ this.setBinding(env, id, next);
7531
+ this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7472
7532
  }
7473
- } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7474
- this.infer(target, env);
7475
- this.checkReadonlyAssign(target, env);
7476
- this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7477
- } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7478
- this.reassignPattern(target, vt, env);
7479
7533
  }
7480
- });
7534
+ } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7535
+ this.infer(target, env);
7536
+ this.checkReadonlyAssign(target, env);
7537
+ this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7538
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7539
+ this.reassignPattern(target, vt, env);
7540
+ }
7481
7541
  return;
7482
7542
  }
7483
7543
  case "CompoundAssignmentStatement":
@@ -7641,55 +7701,6 @@ var TypeAnalyzer = class {
7641
7701
  return;
7642
7702
  }
7643
7703
  }
7644
- /** Flatten an expression list into the values it actually produces.
7645
- *
7646
- * Lua's adjustment rule: every expression but the last contributes one
7647
- * value, and the last contributes all of its values if it is a call or
7648
- * `...`. That is what makes `local ok, err = pcall(f)` work, and it is
7649
- * why `local x = f()` takes only the first value.
7650
- *
7651
- * `sources` maps each produced value back to the expression it came from
7652
- * (undefined for the 2nd and later values of a multi-value call), so the
7653
- * caller can still do contextual typing against the written expression. */
7654
- valueList(exprs, env) {
7655
- const types = [];
7656
- const sources = [];
7657
- exprs.forEach((e, i) => {
7658
- const t = this.infer(e, env);
7659
- const last = i === exprs.length - 1;
7660
- if (e.type === "SpreadElement") {
7661
- const held = this.typeOf.get(e.argument);
7662
- const expanded = held && this.expand(held);
7663
- if (expanded?.kind === "tuple") {
7664
- for (const element of expanded.elements) {
7665
- types.push(element);
7666
- sources.push(e);
7667
- }
7668
- return;
7669
- }
7670
- types.push(t);
7671
- sources.push(e);
7672
- return;
7673
- }
7674
- void last;
7675
- types.push(t);
7676
- sources.push(e);
7677
- });
7678
- return { types, sources };
7679
- }
7680
- /** `const a, b = x, y` pairs names with values, one each. A call is one
7681
- * value — several come back as an array — so `const a, b = f()` is a
7682
- * name short of a value, and says how to take them apart. */
7683
- checkValueCount(node, names, values, types) {
7684
- if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7685
- const last = values[values.length - 1];
7686
- const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7687
- const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7688
- this.diagnostics.push({
7689
- node,
7690
- 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`
7691
- });
7692
- }
7693
7704
  /** Run `visit` with a fresh place to collect `break` states, and return
7694
7705
  * what it collected. */
7695
7706
  withBreakScope(visit) {
@@ -7964,7 +7975,7 @@ var TypeAnalyzer = class {
7964
7975
  /** Synthesize a type from a destructuring pattern used without an
7965
7976
  * annotation (`function f({ a, b = 1 })`). */
7966
7977
  patternToType(target, env) {
7967
- const leaf = (v, def) => v.type !== "IdentifierPattern" ? this.patternToType(v, env) : v.typeAnnotation ? this.resolveType(v.typeAnnotation) : def ? widen(this.infer(def, env)) : anyType;
7978
+ const leaf = (v, def) => v.type === "ObjectPattern" || v.type === "ArrayPattern" ? this.patternToType(v, env) : v.type !== "IdentifierPattern" ? anyType : v.typeAnnotation ? this.resolveType(v.typeAnnotation) : def ? widen(this.infer(def, env)) : anyType;
7968
7979
  if (target.type === "ObjectPattern") {
7969
7980
  const entries = [];
7970
7981
  for (const p of target.properties) {
@@ -8370,6 +8381,7 @@ var TypeAnalyzer = class {
8370
8381
  * can actually be. An annotation, a pattern or a default still wins. */
8371
8382
  paramsFromSignatures(func, signatures) {
8372
8383
  if (!signatures?.length) return;
8384
+ for (const signature of signatures) this.checkOverload(func, signature);
8373
8385
  const resolved = signatures.map((sig) => this.signatureToFnType(sig));
8374
8386
  func.params.forEach((param, i) => {
8375
8387
  if (param.typeAnnotation || param.pattern || param.default) return;
@@ -8383,6 +8395,64 @@ var TypeAnalyzer = class {
8383
8395
  if (candidates.length) this.contextualParams.set(param, union(candidates));
8384
8396
  });
8385
8397
  }
8398
+ /** The implementation is what runs for every signature of an overload
8399
+ * set, so each signature must be one it can serve: no parameter it does
8400
+ * not have, no fewer arguments than it requires, no argument type it does
8401
+ * not accept, and a return it can give. Stricter than TypeScript, which
8402
+ * lets a signature name parameters the implementation never reads. */
8403
+ checkOverload(func, signature) {
8404
+ if (!this.emitDiagnostics) return;
8405
+ const node = signature;
8406
+ const report = (message) => {
8407
+ this.diagnostics.push({ node, message });
8408
+ };
8409
+ const own = (params) => params.filter((p, i) => !p.rest && !(i === 0 && !p.typeAnnotation && (p.name === "self" || p.name === "this")));
8410
+ const implementation = own(func.params);
8411
+ const offered = own(signature.params);
8412
+ const implementationRest = func.params.some((p) => p.rest);
8413
+ const signatureRest = signature.params.some((p) => p.rest);
8414
+ const label = (p) => p.pattern ? "a destructured parameter" : `'${p.name}'`;
8415
+ if (!implementationRest) {
8416
+ const extra = offered[implementation.length];
8417
+ if (extra) {
8418
+ report(`This overload takes parameter ${label(extra)}, which the implementation does not have: it takes ${implementation.length} parameter${implementation.length === 1 ? "" : "s"}`);
8419
+ return;
8420
+ }
8421
+ if (signatureRest) {
8422
+ report("This overload takes a rest parameter, which the implementation does not have");
8423
+ return;
8424
+ }
8425
+ }
8426
+ if (!signatureRest) {
8427
+ let required = 0;
8428
+ implementation.forEach((p, i) => {
8429
+ if (!p.optional && p.default === void 0) required = i + 1;
8430
+ });
8431
+ if (offered.length < required) {
8432
+ report(`The implementation requires ${required} argument${required === 1 ? "" : "s"}, and this overload passes at most ${offered.length}`);
8433
+ return;
8434
+ }
8435
+ }
8436
+ if (signature.generics.length || func.generics.length) return;
8437
+ for (let i = 0; i < offered.length && i < implementation.length; i++) {
8438
+ const target = implementation[i];
8439
+ if (!target.typeAnnotation && target.default === void 0) continue;
8440
+ let accepted = this.paramType(target, /* @__PURE__ */ new Map());
8441
+ if (target.default !== void 0) accepted = optional(accepted);
8442
+ const given = this.paramType(offered[i], /* @__PURE__ */ new Map());
8443
+ if (!isAssignable(given, accepted)) {
8444
+ report(`Parameter ${label(offered[i])} of this overload is '${briefType(given)}', which the implementation's '${briefType(accepted)}' does not accept`);
8445
+ return;
8446
+ }
8447
+ }
8448
+ if (signature.returnType && func.returnType) {
8449
+ const promised = this.resolveType(signature.returnType);
8450
+ const returned = this.resolveType(func.returnType);
8451
+ if (!isAssignable(promised, returned) && !isAssignable(returned, promised)) {
8452
+ report(`This overload returns '${briefType(promised)}', which the implementation's '${briefType(returned)}' cannot be`);
8453
+ }
8454
+ }
8455
+ }
8386
8456
  signatureToFnType(sig) {
8387
8457
  const names = sig.generics.map((g) => g.name);
8388
8458
  const record = (type) => {
@@ -8807,6 +8877,12 @@ var TypeAnalyzer = class {
8807
8877
  }
8808
8878
  return;
8809
8879
  }
8880
+ case "MemberExpression":
8881
+ case "IndexExpression":
8882
+ this.infer(target, env);
8883
+ this.checkReadonlyAssign(target, env);
8884
+ this.assignToRef(target, widen(valueType), env);
8885
+ return;
8810
8886
  case "ObjectPattern": {
8811
8887
  for (const p of target.properties) {
8812
8888
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
@@ -9673,7 +9749,35 @@ var TypeAnalyzer = class {
9673
9749
  }
9674
9750
  const read = this.expand(type);
9675
9751
  if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
9676
- return { type, shortCircuits: inChain !== void 0 || link.optional === true };
9752
+ const optionalNil = link.optional === true && this.mayBeNil(inChain ?? full);
9753
+ return { type, shortCircuits: inChain !== void 0 || optionalNil };
9754
+ }
9755
+ /** Whether a value of `raw` can be nil — `includesNil`, and also the types
9756
+ * that do not say: `any`, `unknown`, an unconstrained type parameter. */
9757
+ mayBeNil(raw) {
9758
+ const t = this.expand(raw);
9759
+ switch (t.kind) {
9760
+ case "primitive":
9761
+ return t.name === "nil";
9762
+ case "any":
9763
+ case "unknown":
9764
+ return true;
9765
+ case "typeParam":
9766
+ return t.constraint === void 0 || this.mayBeNil(t.constraint);
9767
+ case "union":
9768
+ return t.types.some((m) => this.mayBeNil(m));
9769
+ case "never":
9770
+ case "literal":
9771
+ case "array":
9772
+ case "tuple":
9773
+ case "object":
9774
+ case "function":
9775
+ case "intersection":
9776
+ case "templateLiteral":
9777
+ return false;
9778
+ default:
9779
+ return true;
9780
+ }
9677
9781
  }
9678
9782
  /** Objects already reported as possibly nil: a loop body is visited more
9679
9783
  * than once. */
@@ -10595,13 +10699,12 @@ var TypeAnalyzer = class {
10595
10699
  computingAhead = /* @__PURE__ */ new Set();
10596
10700
  declaredAhead(id) {
10597
10701
  this.aheadDeclarations ??= this.indexAheadDeclarations();
10598
- const found = this.aheadDeclarations.get(id);
10599
- if (!found || this.computingAhead.has(id)) return void 0;
10702
+ const statement = this.aheadDeclarations.get(id);
10703
+ if (!statement || this.computingAhead.has(id)) return void 0;
10600
10704
  this.computingAhead.add(id);
10601
10705
  const wasEmitting = this.emitDiagnostics;
10602
10706
  this.emitDiagnostics = false;
10603
10707
  try {
10604
- const { statement, index } = found;
10605
10708
  let type;
10606
10709
  if (statement.type === "DeclareStatement") {
10607
10710
  type = this.resolveType(statement.valueType);
@@ -10610,11 +10713,11 @@ var TypeAnalyzer = class {
10610
10713
  } else if (statement.type === "ClassDeclaration") {
10611
10714
  type = this.classValueType(statement);
10612
10715
  } else if (statement.type === "VariableDeclaration") {
10613
- const target = statement.names[index];
10716
+ const target = statement.name;
10614
10717
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
10615
10718
  type = this.resolveType(target.typeAnnotation);
10616
- } else if (statement.init[index]) {
10617
- const value = this.infer(statement.init[index], /* @__PURE__ */ new Map());
10719
+ } else if (statement.init) {
10720
+ const value = this.infer(statement.init, /* @__PURE__ */ new Map());
10618
10721
  type = statement.kind === "const" ? value : widen(value);
10619
10722
  }
10620
10723
  }
@@ -10632,11 +10735,10 @@ var TypeAnalyzer = class {
10632
10735
  for (const statement of this.program.body.statements) {
10633
10736
  const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
10634
10737
  if (declaration.type !== "VariableDeclaration") continue;
10635
- declaration.names.forEach((target, index) => {
10636
- if (target.type !== "IdentifierPattern") return;
10637
- const id = this.bindingIdByName(target.name, target);
10638
- if (id !== void 0) out.set(id, { statement: declaration, index });
10639
- });
10738
+ const target = declaration.name;
10739
+ if (target.type !== "IdentifierPattern") continue;
10740
+ const id = this.bindingIdByName(target.name, target);
10741
+ if (id !== void 0) out.set(id, declaration);
10640
10742
  }
10641
10743
  const visit = (node) => {
10642
10744
  if (!node || typeof node !== "object") return;
@@ -10647,11 +10749,11 @@ var TypeAnalyzer = class {
10647
10749
  const record = node;
10648
10750
  if (record.type === "ClassDeclaration" && record.name) {
10649
10751
  const id = this.bindingIdByName(record.name.name, record.name);
10650
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10752
+ if (id !== void 0) out.set(id, node);
10651
10753
  }
10652
10754
  if (record.type === "FunctionDeclaration" && record.name) {
10653
10755
  const id = this.bindingIdByName(record.name.name, record.name);
10654
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10756
+ if (id !== void 0) out.set(id, node);
10655
10757
  }
10656
10758
  for (const [key, value] of Object.entries(node)) {
10657
10759
  if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
@@ -10660,7 +10762,7 @@ var TypeAnalyzer = class {
10660
10762
  visit(this.program.body);
10661
10763
  for (const [name, statement] of this.deferredDeclares) {
10662
10764
  const id = this.scopes.globalsByName.get(name);
10663
- if (id !== void 0) out.set(id, { statement, index: 0 });
10765
+ if (id !== void 0) out.set(id, statement);
10664
10766
  }
10665
10767
  return out;
10666
10768
  }