luaut-parser 2.0.0 → 2.1.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
@@ -852,6 +852,9 @@ var Parser = class {
852
852
  }
853
853
  if (t.type === "Identifier" && t.value === "declare") {
854
854
  const p1 = this.peek(1);
855
+ if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
856
+ return this.parseDeclareClassStatement();
857
+ }
855
858
  if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
856
859
  return this.parseDeclareStatement();
857
860
  }
@@ -893,6 +896,36 @@ var Parser = class {
893
896
  const valueType = this.parseType();
894
897
  return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
895
898
  }
899
+ /** A declared type's name. It may be qualified once — `Enum.Material` —
900
+ * which is how a definitions file names types under a namespace, and how
901
+ * they are then written (`const m: Enum.Material`). */
902
+ parseTypeName() {
903
+ const first = this.expectIdentifier();
904
+ if (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
905
+ this.advance();
906
+ const second = this.expectIdentifier();
907
+ return { type: "Identifier", name: `${first.value}.${second.value}`, ...spanFrom(first, second) };
908
+ }
909
+ return tokenIdentifier(first);
910
+ }
911
+ // `declare class Name extends Base { member: T, ... }`
912
+ parseDeclareClassStatement() {
913
+ const start = this.current();
914
+ this.advance();
915
+ this.advance();
916
+ const name = this.parseTypeName();
917
+ let superclass;
918
+ if (this.checkIdentifierValue("extends")) {
919
+ this.advance();
920
+ const base = this.parseType();
921
+ if (base.type !== "TypeReference") this.error("A class can only extend another class, written by name");
922
+ superclass = base;
923
+ }
924
+ if (!this.checkPunctuator("{")) this.error("Expected '{' to start the class body");
925
+ const body = this.parseTableType();
926
+ if (body.type !== "TableTypeNode") this.error("A class body lists members ('name: T'), not a mapped type");
927
+ return { type: "DeclareClassStatement", name, superclass, body, ...spanFrom(start, this.previous()) };
928
+ }
896
929
  // `import { a, b as c } from '...'` / `import Default from '...'` /
897
930
  // `import Default, { a } from '...'`. Compiled away entirely by the
898
931
  // bundler — never survives into emitted Luau.
@@ -1223,8 +1256,7 @@ var Parser = class {
1223
1256
  parseTypeAliasStatement() {
1224
1257
  const start = this.current();
1225
1258
  this.advance();
1226
- const nameTok = this.expectIdentifier();
1227
- const name = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
1259
+ const name = this.parseTypeName();
1228
1260
  let generics = [];
1229
1261
  if (this.checkOperator("<")) {
1230
1262
  generics = this.parseGenericTypeParameterList();
@@ -2726,6 +2758,9 @@ var Analyzer = class {
2726
2758
  case "DeclareStatement":
2727
2759
  this.visitType(stmt.valueType, scope);
2728
2760
  return;
2761
+ case "DeclareClassStatement":
2762
+ this.visitType(stmt.body, scope);
2763
+ return;
2729
2764
  case "TypeAliasStatement":
2730
2765
  case "ExportTypeAliasStatement":
2731
2766
  this.visitType(stmt.definition, scope);
@@ -2899,6 +2934,9 @@ function analyzeScopes(program, options = {}) {
2899
2934
  }
2900
2935
 
2901
2936
  // src/ast/typeModel.ts
2937
+ function isClassType(t) {
2938
+ return t.kind === "object" && t.class !== void 0;
2939
+ }
2902
2940
  function typeParam(name, constraint, isConst) {
2903
2941
  return { kind: "typeParam", name, constraint, isConst };
2904
2942
  }
@@ -2958,10 +2996,16 @@ function substitute(t, subst) {
2958
2996
  }
2959
2997
  case "function": {
2960
2998
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
2999
+ let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
3000
+ let varargs = t.varargs && substitute(t.varargs, inner);
3001
+ if (varargs?.kind === "tuple" && varargs.isPack) {
3002
+ params = [...params, ...varargs.elements.map((type) => ({ type }))];
3003
+ varargs = void 0;
3004
+ }
2961
3005
  return {
2962
3006
  kind: "function",
2963
- params: t.params.map((p) => ({ ...p, type: substitute(p.type, inner) })),
2964
- varargs: t.varargs && substitute(t.varargs, inner),
3007
+ params,
3008
+ varargs,
2965
3009
  returns: substitute(t.returns, inner),
2966
3010
  typeParams: t.typeParams,
2967
3011
  predicate: t.predicate && {
@@ -3041,7 +3085,8 @@ function unify(param, arg, vars, out) {
3041
3085
  }
3042
3086
  return;
3043
3087
  case "object":
3044
- if (arg.kind === "object") {
3088
+ if (param.class) return;
3089
+ if (arg.kind === "object" && !arg.class) {
3045
3090
  for (const [k, pv] of param.properties) {
3046
3091
  const av = arg.properties.get(k);
3047
3092
  if (av) unify(pv.type, av.type, vars, out);
@@ -3124,7 +3169,7 @@ function widen(t) {
3124
3169
  case "tuple":
3125
3170
  return tuple(t.elements.map(widen), t.isPack);
3126
3171
  case "object": {
3127
- if (t.frozen) return t;
3172
+ if (t.frozen || t.class) return t;
3128
3173
  const entries = [];
3129
3174
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: widen(v.type) }]);
3130
3175
  const w = objectType(entries, t.indexer && { key: t.indexer.key, value: widen(t.indexer.value) });
@@ -3151,6 +3196,10 @@ function isAssignable(rawA, rawB) {
3151
3196
  if (expandAlias) {
3152
3197
  if (a.kind === "genericRef" && b.kind !== "genericRef") a = expandAlias(a);
3153
3198
  else if (b.kind === "genericRef" && a.kind !== "genericRef") b = expandAlias(b);
3199
+ else if (a.kind === "genericRef" && b.kind === "genericRef" && a.name !== b.name) {
3200
+ a = expandAlias(a);
3201
+ b = expandAlias(b);
3202
+ }
3154
3203
  if (a === b) return true;
3155
3204
  }
3156
3205
  for (let i = 0; i < comparing.length; i += 2) {
@@ -3206,6 +3255,8 @@ function isAssignableInner(a, b) {
3206
3255
  }
3207
3256
  if (a.kind === "object") {
3208
3257
  if (b.kind !== "object") return false;
3258
+ if (b.class) return a.class !== void 0 && a.class.ancestors.includes(b.class.name);
3259
+ if (a.class && (b.indexer || b.properties.size === 0)) return false;
3209
3260
  for (const [name, bp] of b.properties) {
3210
3261
  const ap = a.properties.get(name);
3211
3262
  if (!ap) {
@@ -3348,6 +3399,7 @@ function containsFreeTypeParam(t, seen, bound) {
3348
3399
  case "intersection":
3349
3400
  return t.types.some((m) => containsTypeParam(m, seen, bound));
3350
3401
  case "object":
3402
+ if (t.class) return false;
3351
3403
  return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
3352
3404
  case "function": {
3353
3405
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
@@ -3546,7 +3598,7 @@ function mergeObjectMembers(types) {
3546
3598
  return expanded !== void 0 && expanded !== t && collect(expanded);
3547
3599
  }
3548
3600
  if (t.kind === "intersection") return t.types.every(collect);
3549
- if (t.kind === "object") {
3601
+ if (t.kind === "object" && !t.class) {
3550
3602
  objects.push(t);
3551
3603
  return true;
3552
3604
  }
@@ -3758,6 +3810,62 @@ function keepsLiterals(paramType) {
3758
3810
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
3759
3811
  return members.some((m) => m.kind === "literal");
3760
3812
  }
3813
+ var AliasMap = class extends Map {
3814
+ pending = /* @__PURE__ */ new Map();
3815
+ defer(name, resolve5) {
3816
+ super.delete(name);
3817
+ this.pending.set(name, resolve5);
3818
+ }
3819
+ get(name) {
3820
+ const resolved = super.get(name);
3821
+ if (resolved !== void 0) return resolved;
3822
+ const resolve5 = this.pending.get(name);
3823
+ if (!resolve5) return void 0;
3824
+ this.pending.delete(name);
3825
+ const type = resolve5();
3826
+ super.set(name, type);
3827
+ return type;
3828
+ }
3829
+ has(name) {
3830
+ return super.has(name) || (this.pending?.has(name) ?? false);
3831
+ }
3832
+ set(name, type) {
3833
+ this.pending?.delete(name);
3834
+ return super.set(name, type);
3835
+ }
3836
+ delete(name) {
3837
+ const deferred = this.pending?.delete(name) ?? false;
3838
+ return super.delete(name) || deferred;
3839
+ }
3840
+ get size() {
3841
+ return super.size + (this.pending?.size ?? 0);
3842
+ }
3843
+ keys() {
3844
+ return [...super.keys(), ...this.pending?.keys() ?? []][Symbol.iterator]();
3845
+ }
3846
+ entries() {
3847
+ return [...this.keys()].map((name) => [name, this.get(name)])[Symbol.iterator]();
3848
+ }
3849
+ values() {
3850
+ return [...this.keys()].map((name) => this.get(name))[Symbol.iterator]();
3851
+ }
3852
+ forEach(callback, thisArg) {
3853
+ for (const [name, type] of this.entries()) callback.call(thisArg, type, name, this);
3854
+ }
3855
+ [Symbol.iterator]() {
3856
+ return this.entries();
3857
+ }
3858
+ };
3859
+ var METAMETHODS = {
3860
+ "+": "__add",
3861
+ "-": "__sub",
3862
+ "*": "__mul",
3863
+ "/": "__div",
3864
+ "//": "__idiv",
3865
+ "%": "__mod",
3866
+ "^": "__pow",
3867
+ "..": "__concat"
3868
+ };
3761
3869
  function posKey(name, line, column) {
3762
3870
  return `${name}@${line}:${column}`;
3763
3871
  }
@@ -3778,9 +3886,12 @@ var TypeAnalyzer = class {
3778
3886
  expectedTypeOf = /* @__PURE__ */ new Map();
3779
3887
  /** Public: each alias resolved once (generic aliases keep their params as
3780
3888
  * `typeParam` nodes in the body). */
3781
- aliases = /* @__PURE__ */ new Map();
3889
+ aliases = new AliasMap();
3782
3890
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
3783
3891
  aliasDefs = /* @__PURE__ */ new Map();
3892
+ /** See `resolveClass`. */
3893
+ classTypes = /* @__PURE__ */ new WeakMap();
3894
+ classMembers = /* @__PURE__ */ new WeakMap();
3784
3895
  /** Generic parameters currently in lexical scope (alias body / generic fn),
3785
3896
  * with their `extends` constraints resolved. */
3786
3897
  typeParamScope = [];
@@ -3890,24 +4001,123 @@ var TypeAnalyzer = class {
3890
4001
  for (const stmt of block.statements) {
3891
4002
  const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
3892
4003
  if (alias) this.aliasDefs.set(alias.name.name, { params: alias.generics, node: alias.definition });
4004
+ if (stmt.type === "DeclareClassStatement") {
4005
+ this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
4006
+ }
3893
4007
  }
3894
4008
  }
3895
- /** Seed global types from `declare` statements. Repeating a name builds an
3896
- * *overload set* (an intersection, in declaration order) rather than
3897
- * replacing which is how `typeof` gets one signature per result string. */
4009
+ /** A non-generic definition's type. */
4010
+ resolveDef(def) {
4011
+ return def.class ? this.classType(def.class) : this.resolveType(def.node);
4012
+ }
4013
+ /** One type per class declaration, so every mention of a class is the same
4014
+ * object — its own members included, which refer back to it. */
4015
+ classType(stmt) {
4016
+ return this.classTypes.get(stmt) ?? this.resolveClass(stmt);
4017
+ }
4018
+ /** A class's members are resolved the first time anyone asks for
4019
+ * `properties` — its own from its body, the inherited ones from its
4020
+ * superclass.
4021
+ *
4022
+ * Both have to wait. A definitions file for a whole engine declares
4023
+ * thousands of classes that all refer to one another; resolving each body
4024
+ * as soon as the class is named would resolve every class on every
4025
+ * analysis, when a script touches a handful. And classes refer to one
4026
+ * another constantly — `Object.IsA` mentions a map of every class, each
4027
+ * of which extends `Object` — so while one class resolves, one it extends
4028
+ * may itself be half-resolved; copying its members then would miss some
4029
+ * for good. */
4030
+ resolveClass(stmt) {
4031
+ const name = stmt.name.name;
4032
+ const { ancestors, cyclic } = this.classChain(stmt);
4033
+ const superclass = !cyclic && ancestors.length > 1 ? this.aliasDefs.get(ancestors[1])?.class : void 0;
4034
+ let own;
4035
+ let resolvingOwn = false;
4036
+ const ownMembers = () => {
4037
+ if (own || resolvingOwn) return own;
4038
+ resolvingOwn = true;
4039
+ try {
4040
+ own = this.resolveType(stmt.body);
4041
+ } finally {
4042
+ resolvingOwn = false;
4043
+ }
4044
+ return own;
4045
+ };
4046
+ let complete;
4047
+ const members = () => {
4048
+ if (complete) return complete;
4049
+ const mine = ownMembers();
4050
+ if (!mine) return void 0;
4051
+ const base = superclass ? this.classMembers.get(this.classType(superclass))?.() : void 0;
4052
+ if (superclass && !base) return void 0;
4053
+ return complete = {
4054
+ properties: new Map([...base?.properties ?? [], ...mine.properties]),
4055
+ indexer: mine.indexer ?? base?.indexer
4056
+ };
4057
+ };
4058
+ const type = { kind: "object", name, class: { name, superclass: superclass?.name.name, ancestors } };
4059
+ Object.defineProperties(type, {
4060
+ properties: { enumerable: true, get: () => members()?.properties ?? own?.properties ?? /* @__PURE__ */ new Map() },
4061
+ indexer: { enumerable: true, get: () => members()?.indexer ?? own?.indexer }
4062
+ });
4063
+ this.classTypes.set(stmt, type);
4064
+ this.classMembers.set(type, members);
4065
+ if (this.program.body.statements.includes(stmt)) ownMembers();
4066
+ return type;
4067
+ }
4068
+ /** `extends` must name a class, and the chain must end. */
4069
+ checkClass(stmt) {
4070
+ if (!stmt.superclass || !this.emitDiagnostics) return;
4071
+ const base = stmt.superclass.base;
4072
+ if (!this.aliasDefs.get(base)?.class) {
4073
+ const known = this.aliasDefs.has(base) || this.importedTypes.has(base);
4074
+ this.diagnostics.push({
4075
+ node: stmt.superclass,
4076
+ message: known ? `'${base}' is not a class; a class can only extend another class` : `Cannot find class '${base}'`
4077
+ });
4078
+ } else if (this.classChain(stmt).cyclic) {
4079
+ this.diagnostics.push({ node: stmt.superclass, message: `'${stmt.name.name}' cannot extend itself` });
4080
+ }
4081
+ }
4082
+ /** The class and the classes it extends, nearest first, read from the
4083
+ * declarations — no type has to be resolved to know them. The walk stops
4084
+ * at a superclass that is not a class. */
4085
+ classChain(stmt) {
4086
+ const ancestors = [stmt.name.name];
4087
+ for (let cls = stmt; cls?.superclass; ) {
4088
+ const base = cls.superclass.base;
4089
+ if (ancestors.includes(base)) return { ancestors, cyclic: true };
4090
+ cls = this.aliasDefs.get(base)?.class;
4091
+ if (!cls) break;
4092
+ ancestors.push(base);
4093
+ }
4094
+ return { ancestors, cyclic: false };
4095
+ }
4096
+ /** Seed global types from `declare` statements. Repeating a function name
4097
+ * builds an *overload set* (an intersection, in declaration order) rather
4098
+ * than replacing — which is how `typeof` gets one signature per result
4099
+ * string. Any other value is simply redeclared: a sourcemap's
4100
+ * `declare script: <this file's instance>` replaces the library's
4101
+ * `declare script: LuaSourceContainer`. */
3898
4102
  harvestDeclares(block) {
3899
4103
  for (const stmt of block.statements) {
3900
4104
  if (stmt.type !== "DeclareStatement") continue;
3901
4105
  const t = this.resolveType(stmt.valueType);
3902
4106
  const prev = this.libGlobalTypes.get(stmt.name);
3903
- this.libGlobalTypes.set(stmt.name, prev ? intersection([prev, t]) : t);
4107
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
4108
+ this.libGlobalTypes.set(stmt.name, overload ? intersection([prev, t]) : t);
3904
4109
  }
3905
4110
  }
3906
4111
  resolveAllAliases() {
3907
4112
  for (const [name, def] of this.aliasDefs) {
4113
+ if (def.class && !this.program.body.statements.includes(def.class)) {
4114
+ const cls = def.class;
4115
+ this.aliases.defer(name, () => this.classType(cls));
4116
+ continue;
4117
+ }
3908
4118
  if (containsTypeQuery(def.node)) continue;
3909
4119
  this.withTypeParams(def.params, () => {
3910
- this.aliases.set(name, this.resolveType(def.node));
4120
+ this.aliases.set(name, this.resolveDef(def));
3911
4121
  });
3912
4122
  }
3913
4123
  }
@@ -3917,7 +4127,7 @@ var TypeAnalyzer = class {
3917
4127
  for (const [name, def] of this.aliasDefs) {
3918
4128
  if (this.aliases.has(name)) continue;
3919
4129
  this.withTypeParams(def.params, () => {
3920
- this.aliases.set(name, this.resolveType(def.node));
4130
+ this.aliases.set(name, this.resolveDef(def));
3921
4131
  });
3922
4132
  }
3923
4133
  return this.aliases;
@@ -3945,10 +4155,7 @@ var TypeAnalyzer = class {
3945
4155
  /** Instantiate a generic alias: `Box<number>` -> `{ value: number }`. */
3946
4156
  instantiateAlias(def, args) {
3947
4157
  if (this.instantiationDepth > 20) return unknownType;
3948
- const subst = /* @__PURE__ */ new Map();
3949
- def.params.forEach((p, i) => {
3950
- subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
3951
- });
4158
+ const subst = this.bindTypeArguments(def.params, args);
3952
4159
  this.instantiationDepth++;
3953
4160
  try {
3954
4161
  const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
@@ -3957,6 +4164,24 @@ var TypeAnalyzer = class {
3957
4164
  this.instantiationDepth--;
3958
4165
  }
3959
4166
  }
4167
+ /** Pair written type arguments with the parameters they instantiate. A
4168
+ * pack parameter (`T...`) takes every argument from its position on, as
4169
+ * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
4170
+ * and `Signal<()>` to the empty pack. Left out, a parameter takes its
4171
+ * default (`T... = ...any` is `any`), or `unknown`. */
4172
+ bindTypeArguments(params, args) {
4173
+ const subst = /* @__PURE__ */ new Map();
4174
+ params.forEach((p, i) => {
4175
+ let arg = args[i];
4176
+ if (p.isPack && i < args.length) {
4177
+ const rest = args.slice(i);
4178
+ const single = rest.length === 1 ? rest[0] : void 0;
4179
+ arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
4180
+ }
4181
+ subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
4182
+ });
4183
+ return subst;
4184
+ }
3960
4185
  // --------------------------------------------------------
3961
4186
  // TypeNode -> Type
3962
4187
  // --------------------------------------------------------
@@ -4018,6 +4243,12 @@ var TypeAnalyzer = class {
4018
4243
  }
4019
4244
  const lib = this.options.libTypes?.[node.base];
4020
4245
  if (lib) return lib;
4246
+ } else if (this.aliasDefs.has(name)) {
4247
+ return this.expand({
4248
+ kind: "genericRef",
4249
+ name,
4250
+ typeArguments: node.typeArguments.map((a) => this.resolveType(a))
4251
+ });
4021
4252
  }
4022
4253
  return {
4023
4254
  kind: "genericRef",
@@ -4144,6 +4375,7 @@ var TypeAnalyzer = class {
4144
4375
  return this.resolveType(node.typeAnnotation);
4145
4376
  case "TypePackNode": {
4146
4377
  if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
4378
+ if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
4147
4379
  return tuple(node.types.map((t) => this.resolveType(t)), true);
4148
4380
  }
4149
4381
  }
@@ -4167,7 +4399,7 @@ var TypeAnalyzer = class {
4167
4399
  this.reduceDepth++;
4168
4400
  try {
4169
4401
  const result = this.reduceTypeInner(t);
4170
- this.reduceCache.set(t, result);
4402
+ if (result.kind !== "keyof") this.reduceCache.set(t, result);
4171
4403
  return result;
4172
4404
  } finally {
4173
4405
  this.reduceDepth--;
@@ -4179,6 +4411,7 @@ var TypeAnalyzer = class {
4179
4411
  case "keyof": {
4180
4412
  const target = this.reduceType(t.target);
4181
4413
  if (containsTypeParam(target)) return { kind: "keyof", target };
4414
+ if (target.kind === "genericRef" && this.resolvingAliases.has(target.name)) return t;
4182
4415
  return this.keysOf(target);
4183
4416
  }
4184
4417
  case "indexedAccess": {
@@ -4220,6 +4453,7 @@ var TypeAnalyzer = class {
4220
4453
  t.predicate
4221
4454
  );
4222
4455
  case "object": {
4456
+ if (t.class) return t;
4223
4457
  const entries = [];
4224
4458
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.reduceType(v.type) }]);
4225
4459
  const reduced = objectType(entries, t.indexer && {
@@ -4359,6 +4593,7 @@ var TypeAnalyzer = class {
4359
4593
  t.typeParams
4360
4594
  );
4361
4595
  case "object": {
4596
+ if (t.class) return t;
4362
4597
  const entries = [];
4363
4598
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.stripInfer(v.type, bindings) }]);
4364
4599
  return objectType(entries, t.indexer && {
@@ -4412,6 +4647,11 @@ var TypeAnalyzer = class {
4412
4647
  visitStatement(stmt, env) {
4413
4648
  switch (stmt.type) {
4414
4649
  case "VariableDeclaration": {
4650
+ stmt.names.forEach((target, i) => {
4651
+ if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
4652
+ this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
4653
+ }
4654
+ });
4415
4655
  const { types: valueTypes, sources } = this.valueList(stmt.init, env);
4416
4656
  stmt.names.forEach((target, i) => {
4417
4657
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
@@ -4472,6 +4712,16 @@ var TypeAnalyzer = class {
4472
4712
  return;
4473
4713
  }
4474
4714
  case "AssignmentStatement": {
4715
+ stmt.targets.forEach((target, i) => {
4716
+ const value = stmt.values[i];
4717
+ if (!value) return;
4718
+ if (target.type === "MemberExpression" || target.type === "IndexExpression") {
4719
+ this.applyContext(value, this.infer(target, env));
4720
+ } else if (target.type === "Identifier") {
4721
+ const id = this.bindingIdOf(target);
4722
+ if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
4723
+ }
4724
+ });
4475
4725
  const { types: valueTypes, sources } = this.valueList(stmt.values, env);
4476
4726
  stmt.targets.forEach((target, i) => {
4477
4727
  const vt = valueTypes[i] ?? unknownType;
@@ -4616,6 +4866,9 @@ var TypeAnalyzer = class {
4616
4866
  case "BreakStatement":
4617
4867
  this.breakStates[this.breakStates.length - 1]?.push(forkEnv(env));
4618
4868
  return;
4869
+ case "DeclareClassStatement":
4870
+ this.checkClass(stmt);
4871
+ return;
4619
4872
  case "ContinueStatement":
4620
4873
  case "TypeAliasStatement":
4621
4874
  case "ExportTypeAliasStatement":
@@ -4737,7 +4990,46 @@ var TypeAnalyzer = class {
4737
4990
  }
4738
4991
  if (p.pattern) return this.patternToType(p.pattern, env);
4739
4992
  if (p.default) return widen(this.infer(p.default, env));
4740
- return anyType;
4993
+ return this.contextualParams.get(p) ?? anyType;
4994
+ }
4995
+ /** What a function expression's unannotated parameters are, from where
4996
+ * it is written — see `applyContext`. */
4997
+ contextualParams = /* @__PURE__ */ new WeakMap();
4998
+ /** `expected` is the type the surroundings want for `expr`. A function
4999
+ * expression written there takes its unannotated parameters' types from
5000
+ * it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
5001
+ * `player` from `Connect`'s callback type. Anything else is inferred as
5002
+ * usual. */
5003
+ applyContext(expr, expected) {
5004
+ let e = expr;
5005
+ while (e.type === "ParenthesizedExpression") e = e.expression;
5006
+ if (e.type !== "FunctionExpression" || !expected) return;
5007
+ const members = expected.kind === "union" ? expected.types : [expected];
5008
+ const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
5009
+ if (!signatures.length) return;
5010
+ e.func.params.forEach((p, k) => {
5011
+ if (p.typeAnnotation || p.pattern || p.default) return;
5012
+ const candidates = [];
5013
+ for (const signature of signatures) {
5014
+ const t2 = signature.params[k]?.type ?? signature.varargs;
5015
+ if (t2) candidates.push(t2);
5016
+ }
5017
+ if (!candidates.length) return;
5018
+ const t = union(candidates);
5019
+ this.contextualParams.set(p, containsTypeParam(t) ? anyType : t);
5020
+ });
5021
+ }
5022
+ /** The parameter type each written argument lands on, across `fns`. */
5023
+ expectedArguments(written, fns, selfOf) {
5024
+ return written.map((_, j) => {
5025
+ const candidates = [];
5026
+ for (const f of fns) {
5027
+ const i = j + selfOf(f);
5028
+ const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
5029
+ if (param) candidates.push(param);
5030
+ }
5031
+ return candidates.length ? union(candidates) : void 0;
5032
+ });
4741
5033
  }
4742
5034
  /** Synthesize a type from a destructuring pattern used without an
4743
5035
  * annotation (`function f({ a, b = 1 })`). */
@@ -4789,7 +5081,8 @@ var TypeAnalyzer = class {
4789
5081
  f.params.forEach((p, i) => {
4790
5082
  const arg = argTypes[i];
4791
5083
  if (arg === void 0) return;
4792
- unify(p.type, keepsLiterals(p.type) ? arg : widen(arg), vars, subst);
5084
+ const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
5085
+ unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
4793
5086
  });
4794
5087
  for (const name of f.typeParams ?? []) if (!subst.has(name)) subst.set(name, unknownType);
4795
5088
  return subst;
@@ -4850,12 +5143,13 @@ var TypeAnalyzer = class {
4850
5143
  return;
4851
5144
  }
4852
5145
  const t = value;
5146
+ if (t.kind === "object" && t.class) return;
4853
5147
  if (t.kind === "typeParam" && typeof t.name === "string" && bounds.has(t.name) && t.constraint && !containsTypeParam(t.constraint)) {
4854
5148
  bounds.set(t.name, this.reduceType(t.constraint));
4855
5149
  }
4856
5150
  for (const child of Object.values(value)) walk(child);
4857
5151
  };
4858
- for (const p of f.params) walk(p.type);
5152
+ for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
4859
5153
  return f.params.map((p) => substitute(p.type, bounds));
4860
5154
  }
4861
5155
  /** Record what each written argument is expected to be — see
@@ -5194,7 +5488,7 @@ var TypeAnalyzer = class {
5194
5488
  this.expandCache.set(key, t);
5195
5489
  this.resolvingAliases.add(t.name);
5196
5490
  try {
5197
- const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.resolveType(def.node);
5491
+ const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.resolveDef(def);
5198
5492
  const named = def.params.length === 0 && (r.kind === "object" || r.kind === "intersection") && !r.name ? { ...r, name: t.name } : r;
5199
5493
  this.expandCache.set(key, named);
5200
5494
  return named;
@@ -5317,9 +5611,9 @@ var TypeAnalyzer = class {
5317
5611
  case "not":
5318
5612
  return booleanType;
5319
5613
  case "-":
5320
- return numberType;
5614
+ return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
5321
5615
  case "#":
5322
- return numberType;
5616
+ return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
5323
5617
  }
5324
5618
  return arg;
5325
5619
  }
@@ -5341,7 +5635,7 @@ var TypeAnalyzer = class {
5341
5635
  const r = this.infer(expr.right, env);
5342
5636
  switch (op) {
5343
5637
  case "..":
5344
- return stringType;
5638
+ return this.operatorResult(expr, op, l, r) ?? stringType;
5345
5639
  case "==":
5346
5640
  case "~=":
5347
5641
  case "<":
@@ -5356,7 +5650,7 @@ var TypeAnalyzer = class {
5356
5650
  case "//":
5357
5651
  case "%":
5358
5652
  case "^":
5359
- return numberType;
5653
+ return this.operatorResult(expr, op, l, r) ?? numberType;
5360
5654
  }
5361
5655
  return union([l, r]);
5362
5656
  }
@@ -5375,8 +5669,10 @@ var TypeAnalyzer = class {
5375
5669
  }
5376
5670
  case "CallExpression": {
5377
5671
  const callee = this.infer(expr.callee, env);
5378
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
5379
5672
  const fns = this.overloadsOf(callee);
5673
+ const expected = this.expectedArguments(expr.arguments, fns, () => 0);
5674
+ expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
5675
+ const argTypes = expr.arguments.map((a) => this.infer(a, env));
5380
5676
  if (fns.length) {
5381
5677
  this.recordExpected(expr.arguments, fns, () => 0);
5382
5678
  const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
@@ -5391,8 +5687,10 @@ var TypeAnalyzer = class {
5391
5687
  }
5392
5688
  case "MethodCallExpression": {
5393
5689
  const objType = this.infer(expr.object, env);
5394
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
5395
5690
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
5691
+ const expected = this.expectedArguments(expr.arguments, fns, (f) => this.takesSelf(f) ? 1 : 0);
5692
+ expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
5693
+ const argTypes = expr.arguments.map((a) => this.infer(a, env));
5396
5694
  if (fns.length) {
5397
5695
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
5398
5696
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
@@ -5784,6 +6082,35 @@ var TypeAnalyzer = class {
5784
6082
  this.selfType = saved;
5785
6083
  }
5786
6084
  }
6085
+ /** What an operator on a value with metamethods gives: `a + b` calls
6086
+ * `__add` on `a`, or failing that on `b` with the operands swapped — the
6087
+ * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
6088
+ * Vector3` and `2 * vector` get their types from the declarations.
6089
+ * `undefined` when neither operand declares the metamethod; an operand
6090
+ * that declares it but accepts neither argument is reported. */
6091
+ operatorResult(node, op, left, right) {
6092
+ const name = right === void 0 ? op === "-" ? "__unm" : "__len" : METAMETHODS[op];
6093
+ if (!name) return void 0;
6094
+ const candidates = right === void 0 ? [[left, void 0]] : [[left, right], [right, left]];
6095
+ let declared;
6096
+ for (const [receiver, other] of candidates) {
6097
+ const t = this.expand(receiver);
6098
+ const method = t.kind === "object" ? t.properties.get(name) : void 0;
6099
+ if (!method) continue;
6100
+ declared ??= receiver;
6101
+ const args = other === void 0 ? [receiver] : [receiver, other];
6102
+ const picked = this.pickOverload(this.overloadsOf(method.type), args);
6103
+ if (picked) return this.callReturn(picked, args);
6104
+ }
6105
+ if (declared && this.emitDiagnostics) {
6106
+ this.diagnostics.push({
6107
+ node,
6108
+ message: right === void 0 ? `Operator '${op}' cannot be applied to type '${formatType(left)}'` : `Operator '${op}' cannot be applied to types '${formatType(left)}' and '${formatType(right)}'`
6109
+ });
6110
+ return anyType;
6111
+ }
6112
+ return void 0;
6113
+ }
5787
6114
  /** Does this signature take the receiver as its first parameter?
5788
6115
  *
5789
6116
  * Luau's `:` is sugar both ways: `function T:m(a)` declares
@@ -6157,7 +6484,6 @@ function sourceMapTypes(text, path, options) {
6157
6484
  const lines = [];
6158
6485
  const aliasOfFile = /* @__PURE__ */ new Map();
6159
6486
  const used = /* @__PURE__ */ new Set();
6160
- const canOmit = options.classes.has("Omit");
6161
6487
  const aliasOfNode = /* @__PURE__ */ new Map();
6162
6488
  const aliasFor = (segments) => {
6163
6489
  const base = `SourceMap_${segments.map((s) => s.replace(/[^A-Za-z0-9_]/g, "_")).join("_")}`;
@@ -6173,7 +6499,7 @@ function sourceMapTypes(text, path, options) {
6173
6499
  const className = IDENTIFIER.test(node.className) && options.classes.has(node.className) ? node.className : "Instance";
6174
6500
  const taken = options.membersOf?.(className) ?? INSTANCE_MEMBERS;
6175
6501
  const members = [];
6176
- if (parent && canOmit) members.push(`Parent: ${parent}`);
6502
+ if (parent) members.push(`Parent: ${parent}`);
6177
6503
  const named = /* @__PURE__ */ new Set();
6178
6504
  for (const child of node.children ?? []) {
6179
6505
  if (!isNode(child)) continue;
@@ -6182,8 +6508,7 @@ function sourceMapTypes(text, path, options) {
6182
6508
  named.add(child.name);
6183
6509
  members.push(`${child.name}: ${childAlias}`);
6184
6510
  }
6185
- const base = parent && canOmit ? `Omit<${className}, "Parent">` : className;
6186
- lines.push(`type ${alias} = ${members.length ? `${base} & { ${members.join(", ")} }` : base}`);
6511
+ lines.push(`declare class ${alias} extends ${className} { ${members.join(", ")} }`);
6187
6512
  return alias;
6188
6513
  };
6189
6514
  const rootAlias = visit(root, [root.name], void 0);
@@ -6261,6 +6586,7 @@ export {
6261
6586
  getBinding,
6262
6587
  intersection,
6263
6588
  isAssignable,
6589
+ isClassType,
6264
6590
  isGlobal,
6265
6591
  isPossiblyFalsy,
6266
6592
  isPossiblyTruthy,