@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.cjs CHANGED
@@ -969,10 +969,10 @@ var Parser = class {
969
969
  expressionOr(stop) {
970
970
  return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
971
971
  }
972
- /** A comma-separated list of values: a `return`'s, a declaration's, an
973
- * assignment's. Each is one value; a spread belongs in a call's
974
- * arguments or an array, and is reported here — then read as one, so
975
- * the rest still means something. */
972
+ /** A comma-separated list of values where one belongs — a `return`'s, a
973
+ * declaration's, an assignment's — read whole, so that writing several
974
+ * is reported once and the rest still means something. A spread belongs
975
+ * in a call's arguments or an array, and is reported here too. */
976
976
  expressionListOr(stop) {
977
977
  const until = () => stop() || this.checkPunctuator(",");
978
978
  const item = () => {
@@ -1564,7 +1564,7 @@ var Parser = class {
1564
1564
  }
1565
1565
  this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
1566
1566
  }
1567
- // `const x = ...` / `let x, y = ...`.
1567
+ // `const x = ...` / `let [a, b] = ...`.
1568
1568
  // tilua has no `local` — `const` bindings are immutable, `let` mutable.
1569
1569
  /** `kind` reads the leading word as that keyword (recovery's `local`). */
1570
1570
  parseVariableDeclaration(as) {
@@ -1578,14 +1578,34 @@ var Parser = class {
1578
1578
  while (this.matchPunctuator(",")) {
1579
1579
  names.push(this.parseBindingTarget(true));
1580
1580
  }
1581
- let init = [];
1581
+ let values = [];
1582
1582
  if (this.matchOperator("=")) {
1583
- init = this.expressionListOr(() => false);
1583
+ values = this.expressionListOr(() => false);
1584
1584
  } else if (kind === "const") {
1585
1585
  if (!this.recover) this.error("'const' declaration requires an initializer");
1586
1586
  this.softError("'const' declaration requires an initializer");
1587
1587
  }
1588
- return { type: "VariableDeclaration", kind, names, init, ...spanFrom(start, this.previous()) };
1588
+ let name = names[0];
1589
+ let init = values[0];
1590
+ if (names.length > 1 || values.length > 1) {
1591
+ this.problemAt(start, `A declaration takes one value: take several from an array, '${kind} [a, b] = [x, y]'`);
1592
+ if (names.length > 1) name = this.namesAsPattern(names);
1593
+ if (values.length > 1) init = this.valuesAsArray(values);
1594
+ }
1595
+ return { type: "VariableDeclaration", kind, name, init, ...spanFrom(start, this.previous()) };
1596
+ }
1597
+ /** `a, b` written where one name goes, read as `[a, b]`. */
1598
+ namesAsPattern(names) {
1599
+ const elements = names.map((value) => ({
1600
+ type: "ArrayPatternElement",
1601
+ value,
1602
+ ...spanFrom(value, value)
1603
+ }));
1604
+ return { type: "ArrayPattern", elements, ...spanFrom(names[0], names[names.length - 1]) };
1605
+ }
1606
+ /** `x, y` written where one value goes, read as `[x, y]`. */
1607
+ valuesAsArray(values) {
1608
+ return { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
1589
1609
  }
1590
1610
  parseIfStatement() {
1591
1611
  const start = this.current();
@@ -2066,7 +2086,7 @@ var Parser = class {
2066
2086
  argument = values[0];
2067
2087
  if (values.length > 1) {
2068
2088
  this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
2069
- argument = { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
2089
+ argument = this.valuesAsArray(values);
2070
2090
  }
2071
2091
  }
2072
2092
  return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
@@ -2092,28 +2112,15 @@ var Parser = class {
2092
2112
  parseExpressionStatement() {
2093
2113
  const start = this.current();
2094
2114
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
2095
- const targets = [
2096
- this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern()
2097
- ];
2098
- while (this.matchPunctuator(",")) {
2099
- targets.push(this.parseAssignTarget());
2100
- }
2101
- this.expectOperator("=");
2102
- const values = this.expressionListOr(() => false);
2103
- return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
2115
+ const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
2116
+ return this.parseAssignmentRest(start, target);
2104
2117
  }
2105
2118
  const statementStart = this.cursor;
2106
2119
  const errorsAtStart = this.errors.length;
2107
2120
  const first = this.parsePrefixExpression();
2108
2121
  if (this.checkOperator("=") || this.checkPunctuator(",")) {
2109
- const targets = [first];
2110
- while (this.matchPunctuator(",")) {
2111
- targets.push(this.parseAssignTarget());
2112
- }
2113
- for (const target of targets) this.rejectOptionalTarget(target);
2114
- this.expectOperator("=");
2115
- const values = this.expressionListOr(() => false);
2116
- return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
2122
+ this.rejectOptionalTarget(first);
2123
+ return this.parseAssignmentRest(start, first);
2117
2124
  }
2118
2125
  const t = this.current();
2119
2126
  if (t.type === "Operator" && COMPOUND_ASSIGN_OPS.has(t.value)) {
@@ -2139,6 +2146,21 @@ var Parser = class {
2139
2146
  }
2140
2147
  return { type: "ExpressionStatement", expression, ...spanFrom(start, this.previous()) };
2141
2148
  }
2149
+ /** From after an assignment's target: `= value`. Luau's `a, b = x, y`
2150
+ * is reported, and read as its first target taking its first value. */
2151
+ parseAssignmentRest(start, target) {
2152
+ let several = false;
2153
+ while (this.matchPunctuator(",")) {
2154
+ this.parseAssignTarget();
2155
+ several = true;
2156
+ }
2157
+ this.expectOperator("=");
2158
+ const values = this.expressionListOr(() => false);
2159
+ if (several || values.length > 1) {
2160
+ this.problemAt(start, "An assignment takes one value: take several from an array, '[a, b] = [x, y]'");
2161
+ }
2162
+ return { type: "AssignmentStatement", target, value: values[0], ...spanFrom(start, this.previous()) };
2163
+ }
2142
2164
  // ============================================================
2143
2165
  // Expressions
2144
2166
  // ============================================================
@@ -2622,8 +2644,8 @@ var Parser = class {
2622
2644
  startsSuperUse() {
2623
2645
  return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
2624
2646
  }
2625
- /** After `...`, is there something to spread? Nothing following it means
2626
- * the vararg pack, which is what `f(...)` has always passed on. */
2647
+ /** After `...`, is there something to spread? tilua has no bare `...`:
2648
+ * a varying number of arguments is a rest parameter, `...args: T[]`. */
2627
2649
  startsSpread() {
2628
2650
  const next = this.peek(1);
2629
2651
  if (next.type === "Punctuator") {
@@ -2642,11 +2664,11 @@ var Parser = class {
2642
2664
  const token = this.peek(ahead);
2643
2665
  return token.type === "Punctuator" && token.value === value;
2644
2666
  }
2645
- /** An assignment target after the first: a prefix expression (`a.b`,
2646
- * `a[i]`, `a`) or a nested destructuring pattern. */
2667
+ /** An assignment target after the first — Luau's `a, b = ...`, read only
2668
+ * to be reported: a prefix expression or a destructuring pattern. */
2647
2669
  parseAssignTarget() {
2648
- if (this.checkPunctuator("{")) return this.parseObjectPattern();
2649
- if (this.checkPunctuator("[")) return this.parseArrayPattern();
2670
+ if (this.checkPunctuator("{")) return this.parseObjectPattern(true);
2671
+ if (this.checkPunctuator("[")) return this.parseArrayPattern(true);
2650
2672
  return this.parsePrefixExpression();
2651
2673
  }
2652
2674
  /** Does a call's argument list start here? Lua's three forms: `(`, a
@@ -2825,13 +2847,17 @@ var Parser = class {
2825
2847
  /** Parses a binding target. When `topLevel`, also consumes a trailing
2826
2848
  * `<attr>` list (identifier only) and a `: Type` annotation — these are
2827
2849
  * only valid at the outermost level of a `local` / parameter binding,
2828
- * never nested inside another pattern. */
2829
- parseBindingTarget(topLevel) {
2850
+ * never nested inside another pattern. When `assign`, the pattern is a
2851
+ * destructuring assignment's (`[t[i], t[j]] = ...`), and a leaf is any
2852
+ * place a value can be assigned to: a name, a member or an index. */
2853
+ parseBindingTarget(topLevel, assign = false) {
2830
2854
  let target;
2831
2855
  if (this.checkPunctuator("{")) {
2832
- target = this.parseObjectPattern();
2856
+ target = this.parseObjectPattern(assign);
2833
2857
  } else if (this.checkPunctuator("[")) {
2834
- target = this.parseArrayPattern();
2858
+ target = this.parseArrayPattern(assign);
2859
+ } else if (assign) {
2860
+ return this.parseAssignPatternLeaf();
2835
2861
  } else {
2836
2862
  const nameTok = this.expectIdentifier();
2837
2863
  let attributes;
@@ -2853,10 +2879,25 @@ var Parser = class {
2853
2879
  }
2854
2880
  return target;
2855
2881
  }
2856
- parseObjectPattern() {
2857
- return this.inBrackets(() => this.parseObjectPatternInner());
2882
+ /** A leaf of a destructuring assignment: `a`, `a.b` or `a[i]`. */
2883
+ parseAssignPatternLeaf() {
2884
+ const expression = this.parsePrefixExpression();
2885
+ if (expression.type === "Identifier") {
2886
+ return { type: "IdentifierPattern", name: expression.name, line: expression.line, column: expression.column };
2887
+ }
2888
+ if (expression.type === "MemberExpression" || expression.type === "IndexExpression") {
2889
+ this.rejectOptionalTarget(expression);
2890
+ return expression;
2891
+ }
2892
+ const err = new ParseError("Only a name, a member or an index can be assigned to", expression.line.start, expression.column.start);
2893
+ if (!this.recover) throw err;
2894
+ this.record(err);
2895
+ return { type: "IdentifierPattern", name: "", line: expression.line, column: expression.column };
2896
+ }
2897
+ parseObjectPattern(assign = false) {
2898
+ return this.inBrackets(() => this.parseObjectPatternInner(assign));
2858
2899
  }
2859
- parseObjectPatternInner() {
2900
+ parseObjectPatternInner(assign) {
2860
2901
  const start = this.current();
2861
2902
  this.expectPunctuator("{");
2862
2903
  const properties = [];
@@ -2864,7 +2905,7 @@ var Parser = class {
2864
2905
  while (!this.checkPunctuator("}")) {
2865
2906
  if (this.checkOperator("...")) {
2866
2907
  this.advance();
2867
- rest = this.parseBindingTarget(false);
2908
+ rest = this.parseBindingTarget(false, assign);
2868
2909
  break;
2869
2910
  }
2870
2911
  const propStart = this.current();
@@ -2877,17 +2918,17 @@ var Parser = class {
2877
2918
  key = this.parseExpression();
2878
2919
  this.expectPunctuator("]");
2879
2920
  this.expectPunctuator(":");
2880
- value = this.parseBindingTarget(false);
2921
+ value = this.parseBindingTarget(false, assign);
2881
2922
  } else if (this.checkType("Literal") && this.current().kind === "string") {
2882
2923
  const t = this.advance();
2883
2924
  key = { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2884
2925
  this.expectPunctuator(":");
2885
- value = this.parseBindingTarget(false);
2926
+ value = this.parseBindingTarget(false, assign);
2886
2927
  } else {
2887
2928
  const nameTok = this.expectIdentifier();
2888
2929
  key = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
2889
2930
  if (this.matchPunctuator(":")) {
2890
- value = this.parseBindingTarget(false);
2931
+ value = this.parseBindingTarget(false, assign);
2891
2932
  } else {
2892
2933
  shorthand = true;
2893
2934
  value = { type: "IdentifierPattern", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
@@ -2910,10 +2951,10 @@ var Parser = class {
2910
2951
  this.expectPunctuator("}");
2911
2952
  return { type: "ObjectPattern", properties, rest, ...spanFrom(start, this.previous()) };
2912
2953
  }
2913
- parseArrayPattern() {
2914
- return this.inBrackets(() => this.parseArrayPatternInner());
2954
+ parseArrayPattern(assign = false) {
2955
+ return this.inBrackets(() => this.parseArrayPatternInner(assign));
2915
2956
  }
2916
- parseArrayPatternInner() {
2957
+ parseArrayPatternInner(assign) {
2917
2958
  const start = this.current();
2918
2959
  this.expectPunctuator("[");
2919
2960
  const elements = [];
@@ -2921,7 +2962,7 @@ var Parser = class {
2921
2962
  while (!this.checkPunctuator("]")) {
2922
2963
  if (this.checkOperator("...")) {
2923
2964
  this.advance();
2924
- rest = this.parseBindingTarget(false);
2965
+ rest = this.parseBindingTarget(false, assign);
2925
2966
  break;
2926
2967
  }
2927
2968
  if (this.checkPunctuator(",")) {
@@ -2930,7 +2971,7 @@ var Parser = class {
2930
2971
  continue;
2931
2972
  }
2932
2973
  const elStart = this.current();
2933
- const value = this.parseBindingTarget(false);
2974
+ const value = this.parseBindingTarget(false, assign);
2934
2975
  let def;
2935
2976
  if (this.matchOperator("=")) def = this.parseExpression();
2936
2977
  elements.push({ type: "ArrayPatternElement", value, default: def, ...spanFrom(elStart, this.previous()) });
@@ -3134,7 +3175,7 @@ var Parser = class {
3134
3175
  };
3135
3176
  })() : this.parseFunctionHead();
3136
3177
  this.expectPunctuator("=>");
3137
- const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
3178
+ const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.parseArrowExpressionBody();
3138
3179
  const func = {
3139
3180
  type: "FunctionBody",
3140
3181
  generics: head.generics,
@@ -3147,6 +3188,29 @@ var Parser = class {
3147
3188
  };
3148
3189
  return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
3149
3190
  }
3191
+ /** `() => value` returns the value. `() => a = 1` and `() => n += 1` are
3192
+ * an assignment, which is a statement: the body is that
3193
+ * statement, and the function returns nothing. */
3194
+ parseArrowExpressionBody() {
3195
+ const start = this.cursor;
3196
+ const errors = this.errors.length;
3197
+ const expression = this.parseExpression(0);
3198
+ const t = this.current();
3199
+ const assigns = t.type === "Operator" && (t.value === "=" || COMPOUND_ASSIGN_OPS.has(t.value));
3200
+ if (!assigns) return this.returnOf(expression);
3201
+ this.cursor = start;
3202
+ this.errors.length = errors;
3203
+ const first = this.current();
3204
+ const target = this.parsePrefixExpression();
3205
+ this.rejectOptionalTarget(target);
3206
+ const opToken = this.current();
3207
+ const op = opToken.value;
3208
+ if (opToken.type !== "Operator" || op !== "=" && !COMPOUND_ASSIGN_OPS.has(op)) this.error("Expected '='");
3209
+ this.advance();
3210
+ const value = this.expressionOr(() => false);
3211
+ const statement = op === "=" ? { type: "AssignmentStatement", target, value, ...spanFrom(first, this.previous()) } : { type: "CompoundAssignmentStatement", operator: op, target, value, ...spanFrom(first, this.previous()) };
3212
+ return { type: "Block", statements: [statement], ...spanFrom(statement, statement) };
3213
+ }
3150
3214
  /** A one-expression body: the value is what the function returns. */
3151
3215
  returnOf(expression) {
3152
3216
  const statement = {
@@ -4044,6 +4108,10 @@ var Analyzer = class {
4044
4108
  this.checkConstAssign(id, t);
4045
4109
  return;
4046
4110
  }
4111
+ case "MemberExpression":
4112
+ case "IndexExpression":
4113
+ this.visitExpression(t, scope);
4114
+ return;
4047
4115
  case "ObjectPattern":
4048
4116
  for (const p of t.properties) {
4049
4117
  if (p.computed) this.visitExpression(p.key, scope);
@@ -4078,10 +4146,11 @@ var Analyzer = class {
4078
4146
  visitStatement(stmt, scope) {
4079
4147
  switch (stmt.type) {
4080
4148
  case "VariableDeclaration": {
4081
- for (const init of stmt.init) this.visitExpression(init, scope);
4082
- for (const name of stmt.names) this.visitType(name.typeAnnotation, scope);
4083
- const isConst = stmt.kind === "const";
4084
- for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
4149
+ if (stmt.init) this.visitExpression(stmt.init, scope);
4150
+ if (stmt.name.type !== "MemberExpression" && stmt.name.type !== "IndexExpression") {
4151
+ this.visitType(stmt.name.typeAnnotation, scope);
4152
+ }
4153
+ this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
4085
4154
  return;
4086
4155
  }
4087
4156
  case "FunctionDeclaration": {
@@ -4115,16 +4184,15 @@ var Analyzer = class {
4115
4184
  return;
4116
4185
  }
4117
4186
  case "AssignmentStatement": {
4118
- for (const value of stmt.values) this.visitExpression(value, scope);
4119
- for (const target of stmt.targets) {
4120
- if (target.type === "Identifier") {
4121
- this.referenceAsAssignmentTarget(scope, target);
4122
- } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
4123
- this.assignPattern(scope, target);
4124
- } else {
4125
- this.visitExpression(target, scope);
4126
- this.checkModuleWrite(target);
4127
- }
4187
+ this.visitExpression(stmt.value, scope);
4188
+ const target = stmt.target;
4189
+ if (target.type === "Identifier") {
4190
+ this.referenceAsAssignmentTarget(scope, target);
4191
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
4192
+ this.assignPattern(scope, target);
4193
+ } else {
4194
+ this.visitExpression(target, scope);
4195
+ this.checkModuleWrite(target);
4128
4196
  }
4129
4197
  return;
4130
4198
  }
@@ -5448,7 +5516,7 @@ function moduleExports(program, scopes, types, resolveModule) {
5448
5516
  params: declaration.typeParams.map((g) => g.name)
5449
5517
  });
5450
5518
  }
5451
- } else for (const target of declaration.names) exportPattern(target);
5519
+ } else exportPattern(declaration.name);
5452
5520
  } else if (stmt.type === "ExportTypeAliasStatement") {
5453
5521
  const name = stmt.alias.name.name;
5454
5522
  const type = types.aliases.get(name);
@@ -5687,12 +5755,10 @@ function assignedFields(block) {
5687
5755
  const names = /* @__PURE__ */ new Set();
5688
5756
  walkNodes(block, (node) => {
5689
5757
  const record = node;
5690
- const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
5691
- for (const target of targets ?? []) {
5692
- const member = target;
5693
- if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5694
- names.add(member.property.name);
5695
- }
5758
+ if (record.type !== "AssignmentStatement" && record.type !== "CompoundAssignmentStatement") return;
5759
+ const member = record.target;
5760
+ if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
5761
+ names.add(member.property.name);
5696
5762
  }
5697
5763
  });
5698
5764
  return names;
@@ -6168,12 +6234,10 @@ var TypeAnalyzer = class {
6168
6234
  classDisplayNames = /* @__PURE__ */ new WeakMap();
6169
6235
  /** `const Name = class ... end` — the name the class will be known by. */
6170
6236
  nameClassExpressions(stmt) {
6171
- stmt.names.forEach((target, i) => {
6172
- const value = stmt.init[i];
6173
- if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6174
- this.classDisplayNames.set(value, target.name);
6175
- }
6176
- });
6237
+ const value = stmt.init;
6238
+ if (stmt.name.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
6239
+ this.classDisplayNames.set(value, stmt.name.name);
6240
+ }
6177
6241
  }
6178
6242
  classTypeParams(stmt) {
6179
6243
  return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
@@ -6502,6 +6566,14 @@ var TypeAnalyzer = class {
6502
6566
  report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
6503
6567
  }
6504
6568
  }
6569
+ this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
6570
+ this.withSelfType(this.selfTypeOf(stmt), () => {
6571
+ for (const member of stmt.members) {
6572
+ if (member.type !== "ClassMethod" || !member.signatures) continue;
6573
+ for (const signature of member.signatures) this.checkOverload(member.func, signature);
6574
+ }
6575
+ });
6576
+ }));
6505
6577
  for (const member of stmt.members) {
6506
6578
  if (member.type === "ClassConstructor") continue;
6507
6579
  if (CLASS_RESERVED.has(member.name.name)) {
@@ -7456,35 +7528,30 @@ var TypeAnalyzer = class {
7456
7528
  switch (stmt.type) {
7457
7529
  case "VariableDeclaration": {
7458
7530
  this.nameClassExpressions(stmt);
7459
- stmt.names.forEach((target, i) => {
7460
- if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
7461
- this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
7462
- }
7463
- });
7464
- const { types: valueTypes, sources } = this.valueList(stmt.init, env);
7465
- this.checkValueCount(stmt, stmt.names.length, stmt.init, valueTypes);
7466
- stmt.names.forEach((target, i) => {
7467
- const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
7468
- const source = sources[i];
7469
- if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7470
- const declared = this.resolveType(target.typeAnnotation);
7471
- if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7472
- this.diagnostics.push({
7473
- node: stmt,
7474
- message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7475
- });
7476
- } else if (declared.kind !== "any") {
7477
- this.reportExcessProperties(source, declared);
7478
- }
7479
- }
7480
- const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7481
- this.bindPattern(target, inferred, env, mode);
7482
- if (stmt.kind === "const") {
7483
- this.correlateDestructuring(target, inferred, env);
7484
- this.correlateIndexed(target, source, env);
7485
- this.aliasReference(target, source);
7531
+ const target = stmt.name;
7532
+ const source = stmt.init;
7533
+ if (target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7534
+ this.applyContext(source, this.resolveType(target.typeAnnotation));
7535
+ }
7536
+ const inferred = source ? this.infer(source, env) : nilType;
7537
+ if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
7538
+ const declared = this.resolveType(target.typeAnnotation);
7539
+ if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
7540
+ this.diagnostics.push({
7541
+ node: stmt,
7542
+ message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
7543
+ });
7544
+ } else if (declared.kind !== "any") {
7545
+ this.reportExcessProperties(source, declared);
7486
7546
  }
7487
- });
7547
+ }
7548
+ const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
7549
+ this.bindPattern(target, inferred, env, mode);
7550
+ if (stmt.kind === "const") {
7551
+ this.correlateDestructuring(target, inferred, env);
7552
+ this.correlateIndexed(target, source, env);
7553
+ this.aliasReference(target, source);
7554
+ }
7488
7555
  return;
7489
7556
  }
7490
7557
  case "ClassDeclaration": {
@@ -7541,48 +7608,41 @@ var TypeAnalyzer = class {
7541
7608
  return;
7542
7609
  }
7543
7610
  case "AssignmentStatement": {
7544
- stmt.targets.forEach((target, i) => {
7545
- const value = stmt.values[i];
7546
- if (!value) return;
7547
- if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7548
- this.applyContext(value, this.infer(target, env));
7549
- } else if (target.type === "Identifier") {
7550
- const id = this.bindingIdOf(target);
7551
- if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
7552
- }
7553
- });
7554
- const { types: valueTypes, sources } = this.valueList(stmt.values, env);
7555
- this.checkValueCount(stmt, stmt.targets.length, stmt.values, valueTypes);
7556
- stmt.targets.forEach((target, i) => {
7557
- const vt = valueTypes[i] ?? unknownType;
7558
- const source = sources[i];
7559
- if (target.type === "Identifier") {
7560
- const id = this.bindingIdOf(target);
7561
- if (id !== void 0) {
7562
- this.uncorrelate(id);
7563
- const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7564
- if (this.annotated.has(id)) {
7565
- const declared = this.bindingType.get(id);
7566
- if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7567
- this.diagnostics.push({
7568
- node: stmt,
7569
- message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7570
- });
7571
- }
7572
- this.setBinding(env, id, narrowTo(declared, next));
7573
- } else {
7574
- this.setBinding(env, id, next);
7575
- this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7611
+ const target = stmt.target;
7612
+ const source = stmt.value;
7613
+ if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7614
+ this.applyContext(source, this.infer(target, env));
7615
+ } else if (target.type === "Identifier") {
7616
+ const id = this.bindingIdOf(target);
7617
+ if (id !== void 0 && this.annotated.has(id)) this.applyContext(source, this.bindingType.get(id));
7618
+ }
7619
+ const vt = this.infer(source, env);
7620
+ if (target.type === "Identifier") {
7621
+ const id = this.bindingIdOf(target);
7622
+ if (id !== void 0) {
7623
+ this.uncorrelate(id);
7624
+ const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
7625
+ if (this.annotated.has(id)) {
7626
+ const declared = this.bindingType.get(id);
7627
+ if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
7628
+ this.diagnostics.push({
7629
+ node: stmt,
7630
+ message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
7631
+ });
7576
7632
  }
7633
+ this.setBinding(env, id, narrowTo(declared, next));
7634
+ } else {
7635
+ this.setBinding(env, id, next);
7636
+ this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
7577
7637
  }
7578
- } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7579
- this.infer(target, env);
7580
- this.checkReadonlyAssign(target, env);
7581
- this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7582
- } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7583
- this.reassignPattern(target, vt, env);
7584
7638
  }
7585
- });
7639
+ } else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
7640
+ this.infer(target, env);
7641
+ this.checkReadonlyAssign(target, env);
7642
+ this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
7643
+ } else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
7644
+ this.reassignPattern(target, vt, env);
7645
+ }
7586
7646
  return;
7587
7647
  }
7588
7648
  case "CompoundAssignmentStatement":
@@ -7746,55 +7806,6 @@ var TypeAnalyzer = class {
7746
7806
  return;
7747
7807
  }
7748
7808
  }
7749
- /** Flatten an expression list into the values it actually produces.
7750
- *
7751
- * Lua's adjustment rule: every expression but the last contributes one
7752
- * value, and the last contributes all of its values if it is a call or
7753
- * `...`. That is what makes `local ok, err = pcall(f)` work, and it is
7754
- * why `local x = f()` takes only the first value.
7755
- *
7756
- * `sources` maps each produced value back to the expression it came from
7757
- * (undefined for the 2nd and later values of a multi-value call), so the
7758
- * caller can still do contextual typing against the written expression. */
7759
- valueList(exprs, env) {
7760
- const types = [];
7761
- const sources = [];
7762
- exprs.forEach((e, i) => {
7763
- const t = this.infer(e, env);
7764
- const last = i === exprs.length - 1;
7765
- if (e.type === "SpreadElement") {
7766
- const held = this.typeOf.get(e.argument);
7767
- const expanded = held && this.expand(held);
7768
- if (expanded?.kind === "tuple") {
7769
- for (const element of expanded.elements) {
7770
- types.push(element);
7771
- sources.push(e);
7772
- }
7773
- return;
7774
- }
7775
- types.push(t);
7776
- sources.push(e);
7777
- return;
7778
- }
7779
- void last;
7780
- types.push(t);
7781
- sources.push(e);
7782
- });
7783
- return { types, sources };
7784
- }
7785
- /** `const a, b = x, y` pairs names with values, one each. A call is one
7786
- * value — several come back as an array — so `const a, b = f()` is a
7787
- * name short of a value, and says how to take them apart. */
7788
- checkValueCount(node, names, values, types) {
7789
- if (!this.emitDiagnostics || !values.length || types.length >= names) return;
7790
- const last = values[values.length - 1];
7791
- const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
7792
- const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
7793
- this.diagnostics.push({
7794
- node,
7795
- 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`
7796
- });
7797
- }
7798
7809
  /** Run `visit` with a fresh place to collect `break` states, and return
7799
7810
  * what it collected. */
7800
7811
  withBreakScope(visit) {
@@ -8069,7 +8080,7 @@ var TypeAnalyzer = class {
8069
8080
  /** Synthesize a type from a destructuring pattern used without an
8070
8081
  * annotation (`function f({ a, b = 1 })`). */
8071
8082
  patternToType(target, env) {
8072
- const leaf = (v, def) => v.type !== "IdentifierPattern" ? this.patternToType(v, env) : v.typeAnnotation ? this.resolveType(v.typeAnnotation) : def ? widen(this.infer(def, env)) : anyType;
8083
+ 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;
8073
8084
  if (target.type === "ObjectPattern") {
8074
8085
  const entries = [];
8075
8086
  for (const p of target.properties) {
@@ -8475,6 +8486,7 @@ var TypeAnalyzer = class {
8475
8486
  * can actually be. An annotation, a pattern or a default still wins. */
8476
8487
  paramsFromSignatures(func, signatures) {
8477
8488
  if (!signatures?.length) return;
8489
+ for (const signature of signatures) this.checkOverload(func, signature);
8478
8490
  const resolved = signatures.map((sig) => this.signatureToFnType(sig));
8479
8491
  func.params.forEach((param, i) => {
8480
8492
  if (param.typeAnnotation || param.pattern || param.default) return;
@@ -8488,6 +8500,64 @@ var TypeAnalyzer = class {
8488
8500
  if (candidates.length) this.contextualParams.set(param, union(candidates));
8489
8501
  });
8490
8502
  }
8503
+ /** The implementation is what runs for every signature of an overload
8504
+ * set, so each signature must be one it can serve: no parameter it does
8505
+ * not have, no fewer arguments than it requires, no argument type it does
8506
+ * not accept, and a return it can give. Stricter than TypeScript, which
8507
+ * lets a signature name parameters the implementation never reads. */
8508
+ checkOverload(func, signature) {
8509
+ if (!this.emitDiagnostics) return;
8510
+ const node = signature;
8511
+ const report = (message) => {
8512
+ this.diagnostics.push({ node, message });
8513
+ };
8514
+ const own = (params) => params.filter((p, i) => !p.rest && !(i === 0 && !p.typeAnnotation && (p.name === "self" || p.name === "this")));
8515
+ const implementation = own(func.params);
8516
+ const offered = own(signature.params);
8517
+ const implementationRest = func.params.some((p) => p.rest);
8518
+ const signatureRest = signature.params.some((p) => p.rest);
8519
+ const label = (p) => p.pattern ? "a destructured parameter" : `'${p.name}'`;
8520
+ if (!implementationRest) {
8521
+ const extra = offered[implementation.length];
8522
+ if (extra) {
8523
+ report(`This overload takes parameter ${label(extra)}, which the implementation does not have: it takes ${implementation.length} parameter${implementation.length === 1 ? "" : "s"}`);
8524
+ return;
8525
+ }
8526
+ if (signatureRest) {
8527
+ report("This overload takes a rest parameter, which the implementation does not have");
8528
+ return;
8529
+ }
8530
+ }
8531
+ if (!signatureRest) {
8532
+ let required = 0;
8533
+ implementation.forEach((p, i) => {
8534
+ if (!p.optional && p.default === void 0) required = i + 1;
8535
+ });
8536
+ if (offered.length < required) {
8537
+ report(`The implementation requires ${required} argument${required === 1 ? "" : "s"}, and this overload passes at most ${offered.length}`);
8538
+ return;
8539
+ }
8540
+ }
8541
+ if (signature.generics.length || func.generics.length) return;
8542
+ for (let i = 0; i < offered.length && i < implementation.length; i++) {
8543
+ const target = implementation[i];
8544
+ if (!target.typeAnnotation && target.default === void 0) continue;
8545
+ let accepted = this.paramType(target, /* @__PURE__ */ new Map());
8546
+ if (target.default !== void 0) accepted = optional(accepted);
8547
+ const given = this.paramType(offered[i], /* @__PURE__ */ new Map());
8548
+ if (!isAssignable(given, accepted)) {
8549
+ report(`Parameter ${label(offered[i])} of this overload is '${briefType(given)}', which the implementation's '${briefType(accepted)}' does not accept`);
8550
+ return;
8551
+ }
8552
+ }
8553
+ if (signature.returnType && func.returnType) {
8554
+ const promised = this.resolveType(signature.returnType);
8555
+ const returned = this.resolveType(func.returnType);
8556
+ if (!isAssignable(promised, returned) && !isAssignable(returned, promised)) {
8557
+ report(`This overload returns '${briefType(promised)}', which the implementation's '${briefType(returned)}' cannot be`);
8558
+ }
8559
+ }
8560
+ }
8491
8561
  signatureToFnType(sig) {
8492
8562
  const names = sig.generics.map((g) => g.name);
8493
8563
  const record = (type) => {
@@ -8912,6 +8982,12 @@ var TypeAnalyzer = class {
8912
8982
  }
8913
8983
  return;
8914
8984
  }
8985
+ case "MemberExpression":
8986
+ case "IndexExpression":
8987
+ this.infer(target, env);
8988
+ this.checkReadonlyAssign(target, env);
8989
+ this.assignToRef(target, widen(valueType), env);
8990
+ return;
8915
8991
  case "ObjectPattern": {
8916
8992
  for (const p of target.properties) {
8917
8993
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
@@ -9778,7 +9854,35 @@ var TypeAnalyzer = class {
9778
9854
  }
9779
9855
  const read = this.expand(type);
9780
9856
  if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
9781
- return { type, shortCircuits: inChain !== void 0 || link.optional === true };
9857
+ const optionalNil = link.optional === true && this.mayBeNil(inChain ?? full);
9858
+ return { type, shortCircuits: inChain !== void 0 || optionalNil };
9859
+ }
9860
+ /** Whether a value of `raw` can be nil — `includesNil`, and also the types
9861
+ * that do not say: `any`, `unknown`, an unconstrained type parameter. */
9862
+ mayBeNil(raw) {
9863
+ const t = this.expand(raw);
9864
+ switch (t.kind) {
9865
+ case "primitive":
9866
+ return t.name === "nil";
9867
+ case "any":
9868
+ case "unknown":
9869
+ return true;
9870
+ case "typeParam":
9871
+ return t.constraint === void 0 || this.mayBeNil(t.constraint);
9872
+ case "union":
9873
+ return t.types.some((m) => this.mayBeNil(m));
9874
+ case "never":
9875
+ case "literal":
9876
+ case "array":
9877
+ case "tuple":
9878
+ case "object":
9879
+ case "function":
9880
+ case "intersection":
9881
+ case "templateLiteral":
9882
+ return false;
9883
+ default:
9884
+ return true;
9885
+ }
9782
9886
  }
9783
9887
  /** Objects already reported as possibly nil: a loop body is visited more
9784
9888
  * than once. */
@@ -10700,13 +10804,12 @@ var TypeAnalyzer = class {
10700
10804
  computingAhead = /* @__PURE__ */ new Set();
10701
10805
  declaredAhead(id) {
10702
10806
  this.aheadDeclarations ??= this.indexAheadDeclarations();
10703
- const found = this.aheadDeclarations.get(id);
10704
- if (!found || this.computingAhead.has(id)) return void 0;
10807
+ const statement = this.aheadDeclarations.get(id);
10808
+ if (!statement || this.computingAhead.has(id)) return void 0;
10705
10809
  this.computingAhead.add(id);
10706
10810
  const wasEmitting = this.emitDiagnostics;
10707
10811
  this.emitDiagnostics = false;
10708
10812
  try {
10709
- const { statement, index } = found;
10710
10813
  let type;
10711
10814
  if (statement.type === "DeclareStatement") {
10712
10815
  type = this.resolveType(statement.valueType);
@@ -10715,11 +10818,11 @@ var TypeAnalyzer = class {
10715
10818
  } else if (statement.type === "ClassDeclaration") {
10716
10819
  type = this.classValueType(statement);
10717
10820
  } else if (statement.type === "VariableDeclaration") {
10718
- const target = statement.names[index];
10821
+ const target = statement.name;
10719
10822
  if (target.type === "IdentifierPattern" && target.typeAnnotation) {
10720
10823
  type = this.resolveType(target.typeAnnotation);
10721
- } else if (statement.init[index]) {
10722
- const value = this.infer(statement.init[index], /* @__PURE__ */ new Map());
10824
+ } else if (statement.init) {
10825
+ const value = this.infer(statement.init, /* @__PURE__ */ new Map());
10723
10826
  type = statement.kind === "const" ? value : widen(value);
10724
10827
  }
10725
10828
  }
@@ -10737,11 +10840,10 @@ var TypeAnalyzer = class {
10737
10840
  for (const statement of this.program.body.statements) {
10738
10841
  const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
10739
10842
  if (declaration.type !== "VariableDeclaration") continue;
10740
- declaration.names.forEach((target, index) => {
10741
- if (target.type !== "IdentifierPattern") return;
10742
- const id = this.bindingIdByName(target.name, target);
10743
- if (id !== void 0) out.set(id, { statement: declaration, index });
10744
- });
10843
+ const target = declaration.name;
10844
+ if (target.type !== "IdentifierPattern") continue;
10845
+ const id = this.bindingIdByName(target.name, target);
10846
+ if (id !== void 0) out.set(id, declaration);
10745
10847
  }
10746
10848
  const visit = (node) => {
10747
10849
  if (!node || typeof node !== "object") return;
@@ -10752,11 +10854,11 @@ var TypeAnalyzer = class {
10752
10854
  const record = node;
10753
10855
  if (record.type === "ClassDeclaration" && record.name) {
10754
10856
  const id = this.bindingIdByName(record.name.name, record.name);
10755
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10857
+ if (id !== void 0) out.set(id, node);
10756
10858
  }
10757
10859
  if (record.type === "FunctionDeclaration" && record.name) {
10758
10860
  const id = this.bindingIdByName(record.name.name, record.name);
10759
- if (id !== void 0) out.set(id, { statement: node, index: 0 });
10861
+ if (id !== void 0) out.set(id, node);
10760
10862
  }
10761
10863
  for (const [key, value] of Object.entries(node)) {
10762
10864
  if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
@@ -10765,7 +10867,7 @@ var TypeAnalyzer = class {
10765
10867
  visit(this.program.body);
10766
10868
  for (const [name, statement] of this.deferredDeclares) {
10767
10869
  const id = this.scopes.globalsByName.get(name);
10768
- if (id !== void 0) out.set(id, { statement, index: 0 });
10870
+ if (id !== void 0) out.set(id, statement);
10769
10871
  }
10770
10872
  return out;
10771
10873
  }