@tilua/parser 2.3.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
@@ -2112,7 +2112,7 @@ var Parser = class {
2112
2112
  parseExpressionStatement() {
2113
2113
  const start = this.current();
2114
2114
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
2115
- const target = this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern();
2115
+ const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
2116
2116
  return this.parseAssignmentRest(start, target);
2117
2117
  }
2118
2118
  const statementStart = this.cursor;
@@ -2667,8 +2667,8 @@ var Parser = class {
2667
2667
  /** An assignment target after the first — Luau's `a, b = ...`, read only
2668
2668
  * to be reported: a prefix expression or a destructuring pattern. */
2669
2669
  parseAssignTarget() {
2670
- if (this.checkPunctuator("{")) return this.parseObjectPattern();
2671
- if (this.checkPunctuator("[")) return this.parseArrayPattern();
2670
+ if (this.checkPunctuator("{")) return this.parseObjectPattern(true);
2671
+ if (this.checkPunctuator("[")) return this.parseArrayPattern(true);
2672
2672
  return this.parsePrefixExpression();
2673
2673
  }
2674
2674
  /** Does a call's argument list start here? Lua's three forms: `(`, a
@@ -2847,13 +2847,17 @@ var Parser = class {
2847
2847
  /** Parses a binding target. When `topLevel`, also consumes a trailing
2848
2848
  * `<attr>` list (identifier only) and a `: Type` annotation — these are
2849
2849
  * only valid at the outermost level of a `local` / parameter binding,
2850
- * never nested inside another pattern. */
2851
- 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) {
2852
2854
  let target;
2853
2855
  if (this.checkPunctuator("{")) {
2854
- target = this.parseObjectPattern();
2856
+ target = this.parseObjectPattern(assign);
2855
2857
  } else if (this.checkPunctuator("[")) {
2856
- target = this.parseArrayPattern();
2858
+ target = this.parseArrayPattern(assign);
2859
+ } else if (assign) {
2860
+ return this.parseAssignPatternLeaf();
2857
2861
  } else {
2858
2862
  const nameTok = this.expectIdentifier();
2859
2863
  let attributes;
@@ -2875,10 +2879,25 @@ var Parser = class {
2875
2879
  }
2876
2880
  return target;
2877
2881
  }
2878
- parseObjectPattern() {
2879
- 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 };
2880
2896
  }
2881
- parseObjectPatternInner() {
2897
+ parseObjectPattern(assign = false) {
2898
+ return this.inBrackets(() => this.parseObjectPatternInner(assign));
2899
+ }
2900
+ parseObjectPatternInner(assign) {
2882
2901
  const start = this.current();
2883
2902
  this.expectPunctuator("{");
2884
2903
  const properties = [];
@@ -2886,7 +2905,7 @@ var Parser = class {
2886
2905
  while (!this.checkPunctuator("}")) {
2887
2906
  if (this.checkOperator("...")) {
2888
2907
  this.advance();
2889
- rest = this.parseBindingTarget(false);
2908
+ rest = this.parseBindingTarget(false, assign);
2890
2909
  break;
2891
2910
  }
2892
2911
  const propStart = this.current();
@@ -2899,17 +2918,17 @@ var Parser = class {
2899
2918
  key = this.parseExpression();
2900
2919
  this.expectPunctuator("]");
2901
2920
  this.expectPunctuator(":");
2902
- value = this.parseBindingTarget(false);
2921
+ value = this.parseBindingTarget(false, assign);
2903
2922
  } else if (this.checkType("Literal") && this.current().kind === "string") {
2904
2923
  const t = this.advance();
2905
2924
  key = { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2906
2925
  this.expectPunctuator(":");
2907
- value = this.parseBindingTarget(false);
2926
+ value = this.parseBindingTarget(false, assign);
2908
2927
  } else {
2909
2928
  const nameTok = this.expectIdentifier();
2910
2929
  key = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
2911
2930
  if (this.matchPunctuator(":")) {
2912
- value = this.parseBindingTarget(false);
2931
+ value = this.parseBindingTarget(false, assign);
2913
2932
  } else {
2914
2933
  shorthand = true;
2915
2934
  value = { type: "IdentifierPattern", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
@@ -2932,10 +2951,10 @@ var Parser = class {
2932
2951
  this.expectPunctuator("}");
2933
2952
  return { type: "ObjectPattern", properties, rest, ...spanFrom(start, this.previous()) };
2934
2953
  }
2935
- parseArrayPattern() {
2936
- return this.inBrackets(() => this.parseArrayPatternInner());
2954
+ parseArrayPattern(assign = false) {
2955
+ return this.inBrackets(() => this.parseArrayPatternInner(assign));
2937
2956
  }
2938
- parseArrayPatternInner() {
2957
+ parseArrayPatternInner(assign) {
2939
2958
  const start = this.current();
2940
2959
  this.expectPunctuator("[");
2941
2960
  const elements = [];
@@ -2943,7 +2962,7 @@ var Parser = class {
2943
2962
  while (!this.checkPunctuator("]")) {
2944
2963
  if (this.checkOperator("...")) {
2945
2964
  this.advance();
2946
- rest = this.parseBindingTarget(false);
2965
+ rest = this.parseBindingTarget(false, assign);
2947
2966
  break;
2948
2967
  }
2949
2968
  if (this.checkPunctuator(",")) {
@@ -2952,7 +2971,7 @@ var Parser = class {
2952
2971
  continue;
2953
2972
  }
2954
2973
  const elStart = this.current();
2955
- const value = this.parseBindingTarget(false);
2974
+ const value = this.parseBindingTarget(false, assign);
2956
2975
  let def;
2957
2976
  if (this.matchOperator("=")) def = this.parseExpression();
2958
2977
  elements.push({ type: "ArrayPatternElement", value, default: def, ...spanFrom(elStart, this.previous()) });
@@ -4089,6 +4108,10 @@ var Analyzer = class {
4089
4108
  this.checkConstAssign(id, t);
4090
4109
  return;
4091
4110
  }
4111
+ case "MemberExpression":
4112
+ case "IndexExpression":
4113
+ this.visitExpression(t, scope);
4114
+ return;
4092
4115
  case "ObjectPattern":
4093
4116
  for (const p of t.properties) {
4094
4117
  if (p.computed) this.visitExpression(p.key, scope);
@@ -4124,7 +4147,9 @@ var Analyzer = class {
4124
4147
  switch (stmt.type) {
4125
4148
  case "VariableDeclaration": {
4126
4149
  if (stmt.init) this.visitExpression(stmt.init, scope);
4127
- this.visitType(stmt.name.typeAnnotation, scope);
4150
+ if (stmt.name.type !== "MemberExpression" && stmt.name.type !== "IndexExpression") {
4151
+ this.visitType(stmt.name.typeAnnotation, scope);
4152
+ }
4128
4153
  this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
4129
4154
  return;
4130
4155
  }
@@ -6541,6 +6566,14 @@ var TypeAnalyzer = class {
6541
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}'`);
6542
6567
  }
6543
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
+ }));
6544
6577
  for (const member of stmt.members) {
6545
6578
  if (member.type === "ClassConstructor") continue;
6546
6579
  if (CLASS_RESERVED.has(member.name.name)) {
@@ -8047,7 +8080,7 @@ var TypeAnalyzer = class {
8047
8080
  /** Synthesize a type from a destructuring pattern used without an
8048
8081
  * annotation (`function f({ a, b = 1 })`). */
8049
8082
  patternToType(target, env) {
8050
- 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;
8051
8084
  if (target.type === "ObjectPattern") {
8052
8085
  const entries = [];
8053
8086
  for (const p of target.properties) {
@@ -8453,6 +8486,7 @@ var TypeAnalyzer = class {
8453
8486
  * can actually be. An annotation, a pattern or a default still wins. */
8454
8487
  paramsFromSignatures(func, signatures) {
8455
8488
  if (!signatures?.length) return;
8489
+ for (const signature of signatures) this.checkOverload(func, signature);
8456
8490
  const resolved = signatures.map((sig) => this.signatureToFnType(sig));
8457
8491
  func.params.forEach((param, i) => {
8458
8492
  if (param.typeAnnotation || param.pattern || param.default) return;
@@ -8466,6 +8500,64 @@ var TypeAnalyzer = class {
8466
8500
  if (candidates.length) this.contextualParams.set(param, union(candidates));
8467
8501
  });
8468
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
+ }
8469
8561
  signatureToFnType(sig) {
8470
8562
  const names = sig.generics.map((g) => g.name);
8471
8563
  const record = (type) => {
@@ -8890,6 +8982,12 @@ var TypeAnalyzer = class {
8890
8982
  }
8891
8983
  return;
8892
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;
8893
8991
  case "ObjectPattern": {
8894
8992
  for (const p of target.properties) {
8895
8993
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
package/dist/index.d.cts CHANGED
@@ -254,7 +254,9 @@ interface VariableDeclaration extends BaseNode {
254
254
  /** Absent only on `let x`. */
255
255
  init?: Expression;
256
256
  }
257
- type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern;
257
+ /** A member or an index (`t.a`, `t[i]`) is a leaf only in a destructuring
258
+ * assignment (`[t[i], t[j]] = [t[j], t[i]]`): a declaration binds names. */
259
+ type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern | MemberExpression | IndexExpression;
258
260
  interface IdentifierPattern extends BaseNode {
259
261
  type: "IdentifierPattern";
260
262
  name: string;
package/dist/index.d.ts CHANGED
@@ -254,7 +254,9 @@ interface VariableDeclaration extends BaseNode {
254
254
  /** Absent only on `let x`. */
255
255
  init?: Expression;
256
256
  }
257
- type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern;
257
+ /** A member or an index (`t.a`, `t[i]`) is a leaf only in a destructuring
258
+ * assignment (`[t[i], t[j]] = [t[j], t[i]]`): a declaration binds names. */
259
+ type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern | MemberExpression | IndexExpression;
258
260
  interface IdentifierPattern extends BaseNode {
259
261
  type: "IdentifierPattern";
260
262
  name: string;
package/dist/index.js CHANGED
@@ -2007,7 +2007,7 @@ var Parser = class {
2007
2007
  parseExpressionStatement() {
2008
2008
  const start = this.current();
2009
2009
  if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
2010
- const target = this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern();
2010
+ const target = this.checkPunctuator("{") ? this.parseObjectPattern(true) : this.parseArrayPattern(true);
2011
2011
  return this.parseAssignmentRest(start, target);
2012
2012
  }
2013
2013
  const statementStart = this.cursor;
@@ -2562,8 +2562,8 @@ var Parser = class {
2562
2562
  /** An assignment target after the first — Luau's `a, b = ...`, read only
2563
2563
  * to be reported: a prefix expression or a destructuring pattern. */
2564
2564
  parseAssignTarget() {
2565
- if (this.checkPunctuator("{")) return this.parseObjectPattern();
2566
- if (this.checkPunctuator("[")) return this.parseArrayPattern();
2565
+ if (this.checkPunctuator("{")) return this.parseObjectPattern(true);
2566
+ if (this.checkPunctuator("[")) return this.parseArrayPattern(true);
2567
2567
  return this.parsePrefixExpression();
2568
2568
  }
2569
2569
  /** Does a call's argument list start here? Lua's three forms: `(`, a
@@ -2742,13 +2742,17 @@ var Parser = class {
2742
2742
  /** Parses a binding target. When `topLevel`, also consumes a trailing
2743
2743
  * `<attr>` list (identifier only) and a `: Type` annotation — these are
2744
2744
  * only valid at the outermost level of a `local` / parameter binding,
2745
- * never nested inside another pattern. */
2746
- 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) {
2747
2749
  let target;
2748
2750
  if (this.checkPunctuator("{")) {
2749
- target = this.parseObjectPattern();
2751
+ target = this.parseObjectPattern(assign);
2750
2752
  } else if (this.checkPunctuator("[")) {
2751
- target = this.parseArrayPattern();
2753
+ target = this.parseArrayPattern(assign);
2754
+ } else if (assign) {
2755
+ return this.parseAssignPatternLeaf();
2752
2756
  } else {
2753
2757
  const nameTok = this.expectIdentifier();
2754
2758
  let attributes;
@@ -2770,10 +2774,25 @@ var Parser = class {
2770
2774
  }
2771
2775
  return target;
2772
2776
  }
2773
- parseObjectPattern() {
2774
- 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 };
2775
2791
  }
2776
- parseObjectPatternInner() {
2792
+ parseObjectPattern(assign = false) {
2793
+ return this.inBrackets(() => this.parseObjectPatternInner(assign));
2794
+ }
2795
+ parseObjectPatternInner(assign) {
2777
2796
  const start = this.current();
2778
2797
  this.expectPunctuator("{");
2779
2798
  const properties = [];
@@ -2781,7 +2800,7 @@ var Parser = class {
2781
2800
  while (!this.checkPunctuator("}")) {
2782
2801
  if (this.checkOperator("...")) {
2783
2802
  this.advance();
2784
- rest = this.parseBindingTarget(false);
2803
+ rest = this.parseBindingTarget(false, assign);
2785
2804
  break;
2786
2805
  }
2787
2806
  const propStart = this.current();
@@ -2794,17 +2813,17 @@ var Parser = class {
2794
2813
  key = this.parseExpression();
2795
2814
  this.expectPunctuator("]");
2796
2815
  this.expectPunctuator(":");
2797
- value = this.parseBindingTarget(false);
2816
+ value = this.parseBindingTarget(false, assign);
2798
2817
  } else if (this.checkType("Literal") && this.current().kind === "string") {
2799
2818
  const t = this.advance();
2800
2819
  key = { type: "StringLiteral", value: t.value, raw: t.raw, ...spanFrom(t, t) };
2801
2820
  this.expectPunctuator(":");
2802
- value = this.parseBindingTarget(false);
2821
+ value = this.parseBindingTarget(false, assign);
2803
2822
  } else {
2804
2823
  const nameTok = this.expectIdentifier();
2805
2824
  key = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
2806
2825
  if (this.matchPunctuator(":")) {
2807
- value = this.parseBindingTarget(false);
2826
+ value = this.parseBindingTarget(false, assign);
2808
2827
  } else {
2809
2828
  shorthand = true;
2810
2829
  value = { type: "IdentifierPattern", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
@@ -2827,10 +2846,10 @@ var Parser = class {
2827
2846
  this.expectPunctuator("}");
2828
2847
  return { type: "ObjectPattern", properties, rest, ...spanFrom(start, this.previous()) };
2829
2848
  }
2830
- parseArrayPattern() {
2831
- return this.inBrackets(() => this.parseArrayPatternInner());
2849
+ parseArrayPattern(assign = false) {
2850
+ return this.inBrackets(() => this.parseArrayPatternInner(assign));
2832
2851
  }
2833
- parseArrayPatternInner() {
2852
+ parseArrayPatternInner(assign) {
2834
2853
  const start = this.current();
2835
2854
  this.expectPunctuator("[");
2836
2855
  const elements = [];
@@ -2838,7 +2857,7 @@ var Parser = class {
2838
2857
  while (!this.checkPunctuator("]")) {
2839
2858
  if (this.checkOperator("...")) {
2840
2859
  this.advance();
2841
- rest = this.parseBindingTarget(false);
2860
+ rest = this.parseBindingTarget(false, assign);
2842
2861
  break;
2843
2862
  }
2844
2863
  if (this.checkPunctuator(",")) {
@@ -2847,7 +2866,7 @@ var Parser = class {
2847
2866
  continue;
2848
2867
  }
2849
2868
  const elStart = this.current();
2850
- const value = this.parseBindingTarget(false);
2869
+ const value = this.parseBindingTarget(false, assign);
2851
2870
  let def;
2852
2871
  if (this.matchOperator("=")) def = this.parseExpression();
2853
2872
  elements.push({ type: "ArrayPatternElement", value, default: def, ...spanFrom(elStart, this.previous()) });
@@ -3984,6 +4003,10 @@ var Analyzer = class {
3984
4003
  this.checkConstAssign(id, t);
3985
4004
  return;
3986
4005
  }
4006
+ case "MemberExpression":
4007
+ case "IndexExpression":
4008
+ this.visitExpression(t, scope);
4009
+ return;
3987
4010
  case "ObjectPattern":
3988
4011
  for (const p of t.properties) {
3989
4012
  if (p.computed) this.visitExpression(p.key, scope);
@@ -4019,7 +4042,9 @@ var Analyzer = class {
4019
4042
  switch (stmt.type) {
4020
4043
  case "VariableDeclaration": {
4021
4044
  if (stmt.init) this.visitExpression(stmt.init, scope);
4022
- this.visitType(stmt.name.typeAnnotation, scope);
4045
+ if (stmt.name.type !== "MemberExpression" && stmt.name.type !== "IndexExpression") {
4046
+ this.visitType(stmt.name.typeAnnotation, scope);
4047
+ }
4023
4048
  this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
4024
4049
  return;
4025
4050
  }
@@ -6436,6 +6461,14 @@ var TypeAnalyzer = class {
6436
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}'`);
6437
6462
  }
6438
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
+ }));
6439
6472
  for (const member of stmt.members) {
6440
6473
  if (member.type === "ClassConstructor") continue;
6441
6474
  if (CLASS_RESERVED.has(member.name.name)) {
@@ -7942,7 +7975,7 @@ var TypeAnalyzer = class {
7942
7975
  /** Synthesize a type from a destructuring pattern used without an
7943
7976
  * annotation (`function f({ a, b = 1 })`). */
7944
7977
  patternToType(target, env) {
7945
- 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;
7946
7979
  if (target.type === "ObjectPattern") {
7947
7980
  const entries = [];
7948
7981
  for (const p of target.properties) {
@@ -8348,6 +8381,7 @@ var TypeAnalyzer = class {
8348
8381
  * can actually be. An annotation, a pattern or a default still wins. */
8349
8382
  paramsFromSignatures(func, signatures) {
8350
8383
  if (!signatures?.length) return;
8384
+ for (const signature of signatures) this.checkOverload(func, signature);
8351
8385
  const resolved = signatures.map((sig) => this.signatureToFnType(sig));
8352
8386
  func.params.forEach((param, i) => {
8353
8387
  if (param.typeAnnotation || param.pattern || param.default) return;
@@ -8361,6 +8395,64 @@ var TypeAnalyzer = class {
8361
8395
  if (candidates.length) this.contextualParams.set(param, union(candidates));
8362
8396
  });
8363
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
+ }
8364
8456
  signatureToFnType(sig) {
8365
8457
  const names = sig.generics.map((g) => g.name);
8366
8458
  const record = (type) => {
@@ -8785,6 +8877,12 @@ var TypeAnalyzer = class {
8785
8877
  }
8786
8878
  return;
8787
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;
8788
8886
  case "ObjectPattern": {
8789
8887
  for (const p of target.properties) {
8790
8888
  const key = !p.computed && p.key.type === "Identifier" ? p.key.name : !p.computed && p.key.type === "StringLiteral" ? p.key.value : void 0;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tilua/parser",
3
- "version": "2.3.0",
3
+ "version": "2.4.0",
4
4
  "description": "tilua parser for roblox",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",