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.cjs CHANGED
@@ -45,6 +45,7 @@ __export(index_exports, {
45
45
  getBinding: () => getBinding,
46
46
  intersection: () => intersection,
47
47
  isAssignable: () => isAssignable,
48
+ isClassType: () => isClassType,
48
49
  isGlobal: () => isGlobal,
49
50
  isPossiblyFalsy: () => isPossiblyFalsy,
50
51
  isPossiblyTruthy: () => isPossiblyTruthy,
@@ -944,6 +945,9 @@ var Parser = class {
944
945
  }
945
946
  if (t.type === "Identifier" && t.value === "declare") {
946
947
  const p1 = this.peek(1);
948
+ if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
949
+ return this.parseDeclareClassStatement();
950
+ }
947
951
  if (p1.type === "Identifier" || p1.type === "Keyword" && p1.value === "function") {
948
952
  return this.parseDeclareStatement();
949
953
  }
@@ -985,6 +989,36 @@ var Parser = class {
985
989
  const valueType = this.parseType();
986
990
  return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
987
991
  }
992
+ /** A declared type's name. It may be qualified once — `Enum.Material` —
993
+ * which is how a definitions file names types under a namespace, and how
994
+ * they are then written (`const m: Enum.Material`). */
995
+ parseTypeName() {
996
+ const first = this.expectIdentifier();
997
+ if (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
998
+ this.advance();
999
+ const second = this.expectIdentifier();
1000
+ return { type: "Identifier", name: `${first.value}.${second.value}`, ...spanFrom(first, second) };
1001
+ }
1002
+ return tokenIdentifier(first);
1003
+ }
1004
+ // `declare class Name extends Base { member: T, ... }`
1005
+ parseDeclareClassStatement() {
1006
+ const start = this.current();
1007
+ this.advance();
1008
+ this.advance();
1009
+ const name = this.parseTypeName();
1010
+ let superclass;
1011
+ if (this.checkIdentifierValue("extends")) {
1012
+ this.advance();
1013
+ const base = this.parseType();
1014
+ if (base.type !== "TypeReference") this.error("A class can only extend another class, written by name");
1015
+ superclass = base;
1016
+ }
1017
+ if (!this.checkPunctuator("{")) this.error("Expected '{' to start the class body");
1018
+ const body = this.parseTableType();
1019
+ if (body.type !== "TableTypeNode") this.error("A class body lists members ('name: T'), not a mapped type");
1020
+ return { type: "DeclareClassStatement", name, superclass, body, ...spanFrom(start, this.previous()) };
1021
+ }
988
1022
  // `import { a, b as c } from '...'` / `import Default from '...'` /
989
1023
  // `import Default, { a } from '...'`. Compiled away entirely by the
990
1024
  // bundler — never survives into emitted Luau.
@@ -1315,8 +1349,7 @@ var Parser = class {
1315
1349
  parseTypeAliasStatement() {
1316
1350
  const start = this.current();
1317
1351
  this.advance();
1318
- const nameTok = this.expectIdentifier();
1319
- const name = { type: "Identifier", name: nameTok.value, ...spanFrom(nameTok, nameTok) };
1352
+ const name = this.parseTypeName();
1320
1353
  let generics = [];
1321
1354
  if (this.checkOperator("<")) {
1322
1355
  generics = this.parseGenericTypeParameterList();
@@ -2818,6 +2851,9 @@ var Analyzer = class {
2818
2851
  case "DeclareStatement":
2819
2852
  this.visitType(stmt.valueType, scope);
2820
2853
  return;
2854
+ case "DeclareClassStatement":
2855
+ this.visitType(stmt.body, scope);
2856
+ return;
2821
2857
  case "TypeAliasStatement":
2822
2858
  case "ExportTypeAliasStatement":
2823
2859
  this.visitType(stmt.definition, scope);
@@ -2991,6 +3027,9 @@ function analyzeScopes(program, options = {}) {
2991
3027
  }
2992
3028
 
2993
3029
  // src/ast/typeModel.ts
3030
+ function isClassType(t) {
3031
+ return t.kind === "object" && t.class !== void 0;
3032
+ }
2994
3033
  function typeParam(name, constraint, isConst) {
2995
3034
  return { kind: "typeParam", name, constraint, isConst };
2996
3035
  }
@@ -3050,10 +3089,16 @@ function substitute(t, subst) {
3050
3089
  }
3051
3090
  case "function": {
3052
3091
  const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
3092
+ let params = t.params.map((p) => ({ ...p, type: substitute(p.type, inner) }));
3093
+ let varargs = t.varargs && substitute(t.varargs, inner);
3094
+ if (varargs?.kind === "tuple" && varargs.isPack) {
3095
+ params = [...params, ...varargs.elements.map((type) => ({ type }))];
3096
+ varargs = void 0;
3097
+ }
3053
3098
  return {
3054
3099
  kind: "function",
3055
- params: t.params.map((p) => ({ ...p, type: substitute(p.type, inner) })),
3056
- varargs: t.varargs && substitute(t.varargs, inner),
3100
+ params,
3101
+ varargs,
3057
3102
  returns: substitute(t.returns, inner),
3058
3103
  typeParams: t.typeParams,
3059
3104
  predicate: t.predicate && {
@@ -3133,7 +3178,8 @@ function unify(param, arg, vars, out) {
3133
3178
  }
3134
3179
  return;
3135
3180
  case "object":
3136
- if (arg.kind === "object") {
3181
+ if (param.class) return;
3182
+ if (arg.kind === "object" && !arg.class) {
3137
3183
  for (const [k, pv] of param.properties) {
3138
3184
  const av = arg.properties.get(k);
3139
3185
  if (av) unify(pv.type, av.type, vars, out);
@@ -3216,7 +3262,7 @@ function widen(t) {
3216
3262
  case "tuple":
3217
3263
  return tuple(t.elements.map(widen), t.isPack);
3218
3264
  case "object": {
3219
- if (t.frozen) return t;
3265
+ if (t.frozen || t.class) return t;
3220
3266
  const entries = [];
3221
3267
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: widen(v.type) }]);
3222
3268
  const w = objectType(entries, t.indexer && { key: t.indexer.key, value: widen(t.indexer.value) });
@@ -3243,6 +3289,10 @@ function isAssignable(rawA, rawB) {
3243
3289
  if (expandAlias) {
3244
3290
  if (a.kind === "genericRef" && b.kind !== "genericRef") a = expandAlias(a);
3245
3291
  else if (b.kind === "genericRef" && a.kind !== "genericRef") b = expandAlias(b);
3292
+ else if (a.kind === "genericRef" && b.kind === "genericRef" && a.name !== b.name) {
3293
+ a = expandAlias(a);
3294
+ b = expandAlias(b);
3295
+ }
3246
3296
  if (a === b) return true;
3247
3297
  }
3248
3298
  for (let i = 0; i < comparing.length; i += 2) {
@@ -3298,6 +3348,8 @@ function isAssignableInner(a, b) {
3298
3348
  }
3299
3349
  if (a.kind === "object") {
3300
3350
  if (b.kind !== "object") return false;
3351
+ if (b.class) return a.class !== void 0 && a.class.ancestors.includes(b.class.name);
3352
+ if (a.class && (b.indexer || b.properties.size === 0)) return false;
3301
3353
  for (const [name, bp] of b.properties) {
3302
3354
  const ap = a.properties.get(name);
3303
3355
  if (!ap) {
@@ -3440,6 +3492,7 @@ function containsFreeTypeParam(t, seen, bound) {
3440
3492
  case "intersection":
3441
3493
  return t.types.some((m) => containsTypeParam(m, seen, bound));
3442
3494
  case "object":
3495
+ if (t.class) return false;
3443
3496
  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));
3444
3497
  case "function": {
3445
3498
  const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
@@ -3638,7 +3691,7 @@ function mergeObjectMembers(types) {
3638
3691
  return expanded !== void 0 && expanded !== t && collect(expanded);
3639
3692
  }
3640
3693
  if (t.kind === "intersection") return t.types.every(collect);
3641
- if (t.kind === "object") {
3694
+ if (t.kind === "object" && !t.class) {
3642
3695
  objects.push(t);
3643
3696
  return true;
3644
3697
  }
@@ -3850,6 +3903,62 @@ function keepsLiterals(paramType) {
3850
3903
  const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
3851
3904
  return members.some((m) => m.kind === "literal");
3852
3905
  }
3906
+ var AliasMap = class extends Map {
3907
+ pending = /* @__PURE__ */ new Map();
3908
+ defer(name, resolve5) {
3909
+ super.delete(name);
3910
+ this.pending.set(name, resolve5);
3911
+ }
3912
+ get(name) {
3913
+ const resolved = super.get(name);
3914
+ if (resolved !== void 0) return resolved;
3915
+ const resolve5 = this.pending.get(name);
3916
+ if (!resolve5) return void 0;
3917
+ this.pending.delete(name);
3918
+ const type = resolve5();
3919
+ super.set(name, type);
3920
+ return type;
3921
+ }
3922
+ has(name) {
3923
+ return super.has(name) || (this.pending?.has(name) ?? false);
3924
+ }
3925
+ set(name, type) {
3926
+ this.pending?.delete(name);
3927
+ return super.set(name, type);
3928
+ }
3929
+ delete(name) {
3930
+ const deferred = this.pending?.delete(name) ?? false;
3931
+ return super.delete(name) || deferred;
3932
+ }
3933
+ get size() {
3934
+ return super.size + (this.pending?.size ?? 0);
3935
+ }
3936
+ keys() {
3937
+ return [...super.keys(), ...this.pending?.keys() ?? []][Symbol.iterator]();
3938
+ }
3939
+ entries() {
3940
+ return [...this.keys()].map((name) => [name, this.get(name)])[Symbol.iterator]();
3941
+ }
3942
+ values() {
3943
+ return [...this.keys()].map((name) => this.get(name))[Symbol.iterator]();
3944
+ }
3945
+ forEach(callback, thisArg) {
3946
+ for (const [name, type] of this.entries()) callback.call(thisArg, type, name, this);
3947
+ }
3948
+ [Symbol.iterator]() {
3949
+ return this.entries();
3950
+ }
3951
+ };
3952
+ var METAMETHODS = {
3953
+ "+": "__add",
3954
+ "-": "__sub",
3955
+ "*": "__mul",
3956
+ "/": "__div",
3957
+ "//": "__idiv",
3958
+ "%": "__mod",
3959
+ "^": "__pow",
3960
+ "..": "__concat"
3961
+ };
3853
3962
  function posKey(name, line, column) {
3854
3963
  return `${name}@${line}:${column}`;
3855
3964
  }
@@ -3870,9 +3979,12 @@ var TypeAnalyzer = class {
3870
3979
  expectedTypeOf = /* @__PURE__ */ new Map();
3871
3980
  /** Public: each alias resolved once (generic aliases keep their params as
3872
3981
  * `typeParam` nodes in the body). */
3873
- aliases = /* @__PURE__ */ new Map();
3982
+ aliases = new AliasMap();
3874
3983
  /** Uninstantiated alias definitions, for `Name<Args>` instantiation. */
3875
3984
  aliasDefs = /* @__PURE__ */ new Map();
3985
+ /** See `resolveClass`. */
3986
+ classTypes = /* @__PURE__ */ new WeakMap();
3987
+ classMembers = /* @__PURE__ */ new WeakMap();
3876
3988
  /** Generic parameters currently in lexical scope (alias body / generic fn),
3877
3989
  * with their `extends` constraints resolved. */
3878
3990
  typeParamScope = [];
@@ -3982,24 +4094,123 @@ var TypeAnalyzer = class {
3982
4094
  for (const stmt of block.statements) {
3983
4095
  const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
3984
4096
  if (alias) this.aliasDefs.set(alias.name.name, { params: alias.generics, node: alias.definition });
4097
+ if (stmt.type === "DeclareClassStatement") {
4098
+ this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
4099
+ }
3985
4100
  }
3986
4101
  }
3987
- /** Seed global types from `declare` statements. Repeating a name builds an
3988
- * *overload set* (an intersection, in declaration order) rather than
3989
- * replacing which is how `typeof` gets one signature per result string. */
4102
+ /** A non-generic definition's type. */
4103
+ resolveDef(def) {
4104
+ return def.class ? this.classType(def.class) : this.resolveType(def.node);
4105
+ }
4106
+ /** One type per class declaration, so every mention of a class is the same
4107
+ * object — its own members included, which refer back to it. */
4108
+ classType(stmt) {
4109
+ return this.classTypes.get(stmt) ?? this.resolveClass(stmt);
4110
+ }
4111
+ /** A class's members are resolved the first time anyone asks for
4112
+ * `properties` — its own from its body, the inherited ones from its
4113
+ * superclass.
4114
+ *
4115
+ * Both have to wait. A definitions file for a whole engine declares
4116
+ * thousands of classes that all refer to one another; resolving each body
4117
+ * as soon as the class is named would resolve every class on every
4118
+ * analysis, when a script touches a handful. And classes refer to one
4119
+ * another constantly — `Object.IsA` mentions a map of every class, each
4120
+ * of which extends `Object` — so while one class resolves, one it extends
4121
+ * may itself be half-resolved; copying its members then would miss some
4122
+ * for good. */
4123
+ resolveClass(stmt) {
4124
+ const name = stmt.name.name;
4125
+ const { ancestors, cyclic } = this.classChain(stmt);
4126
+ const superclass = !cyclic && ancestors.length > 1 ? this.aliasDefs.get(ancestors[1])?.class : void 0;
4127
+ let own;
4128
+ let resolvingOwn = false;
4129
+ const ownMembers = () => {
4130
+ if (own || resolvingOwn) return own;
4131
+ resolvingOwn = true;
4132
+ try {
4133
+ own = this.resolveType(stmt.body);
4134
+ } finally {
4135
+ resolvingOwn = false;
4136
+ }
4137
+ return own;
4138
+ };
4139
+ let complete;
4140
+ const members = () => {
4141
+ if (complete) return complete;
4142
+ const mine = ownMembers();
4143
+ if (!mine) return void 0;
4144
+ const base = superclass ? this.classMembers.get(this.classType(superclass))?.() : void 0;
4145
+ if (superclass && !base) return void 0;
4146
+ return complete = {
4147
+ properties: new Map([...base?.properties ?? [], ...mine.properties]),
4148
+ indexer: mine.indexer ?? base?.indexer
4149
+ };
4150
+ };
4151
+ const type = { kind: "object", name, class: { name, superclass: superclass?.name.name, ancestors } };
4152
+ Object.defineProperties(type, {
4153
+ properties: { enumerable: true, get: () => members()?.properties ?? own?.properties ?? /* @__PURE__ */ new Map() },
4154
+ indexer: { enumerable: true, get: () => members()?.indexer ?? own?.indexer }
4155
+ });
4156
+ this.classTypes.set(stmt, type);
4157
+ this.classMembers.set(type, members);
4158
+ if (this.program.body.statements.includes(stmt)) ownMembers();
4159
+ return type;
4160
+ }
4161
+ /** `extends` must name a class, and the chain must end. */
4162
+ checkClass(stmt) {
4163
+ if (!stmt.superclass || !this.emitDiagnostics) return;
4164
+ const base = stmt.superclass.base;
4165
+ if (!this.aliasDefs.get(base)?.class) {
4166
+ const known = this.aliasDefs.has(base) || this.importedTypes.has(base);
4167
+ this.diagnostics.push({
4168
+ node: stmt.superclass,
4169
+ message: known ? `'${base}' is not a class; a class can only extend another class` : `Cannot find class '${base}'`
4170
+ });
4171
+ } else if (this.classChain(stmt).cyclic) {
4172
+ this.diagnostics.push({ node: stmt.superclass, message: `'${stmt.name.name}' cannot extend itself` });
4173
+ }
4174
+ }
4175
+ /** The class and the classes it extends, nearest first, read from the
4176
+ * declarations — no type has to be resolved to know them. The walk stops
4177
+ * at a superclass that is not a class. */
4178
+ classChain(stmt) {
4179
+ const ancestors = [stmt.name.name];
4180
+ for (let cls = stmt; cls?.superclass; ) {
4181
+ const base = cls.superclass.base;
4182
+ if (ancestors.includes(base)) return { ancestors, cyclic: true };
4183
+ cls = this.aliasDefs.get(base)?.class;
4184
+ if (!cls) break;
4185
+ ancestors.push(base);
4186
+ }
4187
+ return { ancestors, cyclic: false };
4188
+ }
4189
+ /** Seed global types from `declare` statements. Repeating a function name
4190
+ * builds an *overload set* (an intersection, in declaration order) rather
4191
+ * than replacing — which is how `typeof` gets one signature per result
4192
+ * string. Any other value is simply redeclared: a sourcemap's
4193
+ * `declare script: <this file's instance>` replaces the library's
4194
+ * `declare script: LuaSourceContainer`. */
3990
4195
  harvestDeclares(block) {
3991
4196
  for (const stmt of block.statements) {
3992
4197
  if (stmt.type !== "DeclareStatement") continue;
3993
4198
  const t = this.resolveType(stmt.valueType);
3994
4199
  const prev = this.libGlobalTypes.get(stmt.name);
3995
- this.libGlobalTypes.set(stmt.name, prev ? intersection([prev, t]) : t);
4200
+ const overload = prev && stmt.valueType.type === "FunctionTypeNode" && (prev.kind === "function" || prev.kind === "intersection");
4201
+ this.libGlobalTypes.set(stmt.name, overload ? intersection([prev, t]) : t);
3996
4202
  }
3997
4203
  }
3998
4204
  resolveAllAliases() {
3999
4205
  for (const [name, def] of this.aliasDefs) {
4206
+ if (def.class && !this.program.body.statements.includes(def.class)) {
4207
+ const cls = def.class;
4208
+ this.aliases.defer(name, () => this.classType(cls));
4209
+ continue;
4210
+ }
4000
4211
  if (containsTypeQuery(def.node)) continue;
4001
4212
  this.withTypeParams(def.params, () => {
4002
- this.aliases.set(name, this.resolveType(def.node));
4213
+ this.aliases.set(name, this.resolveDef(def));
4003
4214
  });
4004
4215
  }
4005
4216
  }
@@ -4009,7 +4220,7 @@ var TypeAnalyzer = class {
4009
4220
  for (const [name, def] of this.aliasDefs) {
4010
4221
  if (this.aliases.has(name)) continue;
4011
4222
  this.withTypeParams(def.params, () => {
4012
- this.aliases.set(name, this.resolveType(def.node));
4223
+ this.aliases.set(name, this.resolveDef(def));
4013
4224
  });
4014
4225
  }
4015
4226
  return this.aliases;
@@ -4037,10 +4248,7 @@ var TypeAnalyzer = class {
4037
4248
  /** Instantiate a generic alias: `Box<number>` -> `{ value: number }`. */
4038
4249
  instantiateAlias(def, args) {
4039
4250
  if (this.instantiationDepth > 20) return unknownType;
4040
- const subst = /* @__PURE__ */ new Map();
4041
- def.params.forEach((p, i) => {
4042
- subst.set(p.name, args[i] ?? (p.default ? this.resolveType(p.default) : unknownType));
4043
- });
4251
+ const subst = this.bindTypeArguments(def.params, args);
4044
4252
  this.instantiationDepth++;
4045
4253
  try {
4046
4254
  const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
@@ -4049,6 +4257,24 @@ var TypeAnalyzer = class {
4049
4257
  this.instantiationDepth--;
4050
4258
  }
4051
4259
  }
4260
+ /** Pair written type arguments with the parameters they instantiate. A
4261
+ * pack parameter (`T...`) takes every argument from its position on, as
4262
+ * one pack: `Signal<Instance, string>` binds `T` to `(Instance, string)`,
4263
+ * and `Signal<()>` to the empty pack. Left out, a parameter takes its
4264
+ * default (`T... = ...any` is `any`), or `unknown`. */
4265
+ bindTypeArguments(params, args) {
4266
+ const subst = /* @__PURE__ */ new Map();
4267
+ params.forEach((p, i) => {
4268
+ let arg = args[i];
4269
+ if (p.isPack && i < args.length) {
4270
+ const rest = args.slice(i);
4271
+ const single = rest.length === 1 ? rest[0] : void 0;
4272
+ arg = single && (single.kind === "tuple" && single.isPack || single.kind === "typeParam" || single.kind === "any") ? single : tuple([...rest], true);
4273
+ }
4274
+ subst.set(p.name, arg ?? (p.default ? this.resolveType(p.default) : unknownType));
4275
+ });
4276
+ return subst;
4277
+ }
4052
4278
  // --------------------------------------------------------
4053
4279
  // TypeNode -> Type
4054
4280
  // --------------------------------------------------------
@@ -4110,6 +4336,12 @@ var TypeAnalyzer = class {
4110
4336
  }
4111
4337
  const lib = this.options.libTypes?.[node.base];
4112
4338
  if (lib) return lib;
4339
+ } else if (this.aliasDefs.has(name)) {
4340
+ return this.expand({
4341
+ kind: "genericRef",
4342
+ name,
4343
+ typeArguments: node.typeArguments.map((a) => this.resolveType(a))
4344
+ });
4113
4345
  }
4114
4346
  return {
4115
4347
  kind: "genericRef",
@@ -4236,6 +4468,7 @@ var TypeAnalyzer = class {
4236
4468
  return this.resolveType(node.typeAnnotation);
4237
4469
  case "TypePackNode": {
4238
4470
  if (node.types.length === 1 && !node.hasVarargs) return this.resolveType(node.types[0]);
4471
+ if (!node.types.length && node.varargType) return this.resolveType(node.varargType);
4239
4472
  return tuple(node.types.map((t) => this.resolveType(t)), true);
4240
4473
  }
4241
4474
  }
@@ -4259,7 +4492,7 @@ var TypeAnalyzer = class {
4259
4492
  this.reduceDepth++;
4260
4493
  try {
4261
4494
  const result = this.reduceTypeInner(t);
4262
- this.reduceCache.set(t, result);
4495
+ if (result.kind !== "keyof") this.reduceCache.set(t, result);
4263
4496
  return result;
4264
4497
  } finally {
4265
4498
  this.reduceDepth--;
@@ -4271,6 +4504,7 @@ var TypeAnalyzer = class {
4271
4504
  case "keyof": {
4272
4505
  const target = this.reduceType(t.target);
4273
4506
  if (containsTypeParam(target)) return { kind: "keyof", target };
4507
+ if (target.kind === "genericRef" && this.resolvingAliases.has(target.name)) return t;
4274
4508
  return this.keysOf(target);
4275
4509
  }
4276
4510
  case "indexedAccess": {
@@ -4312,6 +4546,7 @@ var TypeAnalyzer = class {
4312
4546
  t.predicate
4313
4547
  );
4314
4548
  case "object": {
4549
+ if (t.class) return t;
4315
4550
  const entries = [];
4316
4551
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.reduceType(v.type) }]);
4317
4552
  const reduced = objectType(entries, t.indexer && {
@@ -4451,6 +4686,7 @@ var TypeAnalyzer = class {
4451
4686
  t.typeParams
4452
4687
  );
4453
4688
  case "object": {
4689
+ if (t.class) return t;
4454
4690
  const entries = [];
4455
4691
  for (const [k, v] of t.properties) entries.push([k, { ...v, type: this.stripInfer(v.type, bindings) }]);
4456
4692
  return objectType(entries, t.indexer && {
@@ -4504,6 +4740,11 @@ var TypeAnalyzer = class {
4504
4740
  visitStatement(stmt, env) {
4505
4741
  switch (stmt.type) {
4506
4742
  case "VariableDeclaration": {
4743
+ stmt.names.forEach((target, i) => {
4744
+ if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
4745
+ this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
4746
+ }
4747
+ });
4507
4748
  const { types: valueTypes, sources } = this.valueList(stmt.init, env);
4508
4749
  stmt.names.forEach((target, i) => {
4509
4750
  const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
@@ -4564,6 +4805,16 @@ var TypeAnalyzer = class {
4564
4805
  return;
4565
4806
  }
4566
4807
  case "AssignmentStatement": {
4808
+ stmt.targets.forEach((target, i) => {
4809
+ const value = stmt.values[i];
4810
+ if (!value) return;
4811
+ if (target.type === "MemberExpression" || target.type === "IndexExpression") {
4812
+ this.applyContext(value, this.infer(target, env));
4813
+ } else if (target.type === "Identifier") {
4814
+ const id = this.bindingIdOf(target);
4815
+ if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
4816
+ }
4817
+ });
4567
4818
  const { types: valueTypes, sources } = this.valueList(stmt.values, env);
4568
4819
  stmt.targets.forEach((target, i) => {
4569
4820
  const vt = valueTypes[i] ?? unknownType;
@@ -4708,6 +4959,9 @@ var TypeAnalyzer = class {
4708
4959
  case "BreakStatement":
4709
4960
  this.breakStates[this.breakStates.length - 1]?.push(forkEnv(env));
4710
4961
  return;
4962
+ case "DeclareClassStatement":
4963
+ this.checkClass(stmt);
4964
+ return;
4711
4965
  case "ContinueStatement":
4712
4966
  case "TypeAliasStatement":
4713
4967
  case "ExportTypeAliasStatement":
@@ -4829,7 +5083,46 @@ var TypeAnalyzer = class {
4829
5083
  }
4830
5084
  if (p.pattern) return this.patternToType(p.pattern, env);
4831
5085
  if (p.default) return widen(this.infer(p.default, env));
4832
- return anyType;
5086
+ return this.contextualParams.get(p) ?? anyType;
5087
+ }
5088
+ /** What a function expression's unannotated parameters are, from where
5089
+ * it is written — see `applyContext`. */
5090
+ contextualParams = /* @__PURE__ */ new WeakMap();
5091
+ /** `expected` is the type the surroundings want for `expr`. A function
5092
+ * expression written there takes its unannotated parameters' types from
5093
+ * it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
5094
+ * `player` from `Connect`'s callback type. Anything else is inferred as
5095
+ * usual. */
5096
+ applyContext(expr, expected) {
5097
+ let e = expr;
5098
+ while (e.type === "ParenthesizedExpression") e = e.expression;
5099
+ if (e.type !== "FunctionExpression" || !expected) return;
5100
+ const members = expected.kind === "union" ? expected.types : [expected];
5101
+ const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
5102
+ if (!signatures.length) return;
5103
+ e.func.params.forEach((p, k) => {
5104
+ if (p.typeAnnotation || p.pattern || p.default) return;
5105
+ const candidates = [];
5106
+ for (const signature of signatures) {
5107
+ const t2 = signature.params[k]?.type ?? signature.varargs;
5108
+ if (t2) candidates.push(t2);
5109
+ }
5110
+ if (!candidates.length) return;
5111
+ const t = union(candidates);
5112
+ this.contextualParams.set(p, containsTypeParam(t) ? anyType : t);
5113
+ });
5114
+ }
5115
+ /** The parameter type each written argument lands on, across `fns`. */
5116
+ expectedArguments(written, fns, selfOf) {
5117
+ return written.map((_, j) => {
5118
+ const candidates = [];
5119
+ for (const f of fns) {
5120
+ const i = j + selfOf(f);
5121
+ const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
5122
+ if (param) candidates.push(param);
5123
+ }
5124
+ return candidates.length ? union(candidates) : void 0;
5125
+ });
4833
5126
  }
4834
5127
  /** Synthesize a type from a destructuring pattern used without an
4835
5128
  * annotation (`function f({ a, b = 1 })`). */
@@ -4881,7 +5174,8 @@ var TypeAnalyzer = class {
4881
5174
  f.params.forEach((p, i) => {
4882
5175
  const arg = argTypes[i];
4883
5176
  if (arg === void 0) return;
4884
- unify(p.type, keepsLiterals(p.type) ? arg : widen(arg), vars, subst);
5177
+ const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
5178
+ unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
4885
5179
  });
4886
5180
  for (const name of f.typeParams ?? []) if (!subst.has(name)) subst.set(name, unknownType);
4887
5181
  return subst;
@@ -4942,12 +5236,13 @@ var TypeAnalyzer = class {
4942
5236
  return;
4943
5237
  }
4944
5238
  const t = value;
5239
+ if (t.kind === "object" && t.class) return;
4945
5240
  if (t.kind === "typeParam" && typeof t.name === "string" && bounds.has(t.name) && t.constraint && !containsTypeParam(t.constraint)) {
4946
5241
  bounds.set(t.name, this.reduceType(t.constraint));
4947
5242
  }
4948
5243
  for (const child of Object.values(value)) walk(child);
4949
5244
  };
4950
- for (const p of f.params) walk(p.type);
5245
+ for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
4951
5246
  return f.params.map((p) => substitute(p.type, bounds));
4952
5247
  }
4953
5248
  /** Record what each written argument is expected to be — see
@@ -5286,7 +5581,7 @@ var TypeAnalyzer = class {
5286
5581
  this.expandCache.set(key, t);
5287
5582
  this.resolvingAliases.add(t.name);
5288
5583
  try {
5289
- const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.resolveType(def.node);
5584
+ const r = def.params.length ? this.instantiateAlias(def, t.typeArguments) : this.resolveDef(def);
5290
5585
  const named = def.params.length === 0 && (r.kind === "object" || r.kind === "intersection") && !r.name ? { ...r, name: t.name } : r;
5291
5586
  this.expandCache.set(key, named);
5292
5587
  return named;
@@ -5409,9 +5704,9 @@ var TypeAnalyzer = class {
5409
5704
  case "not":
5410
5705
  return booleanType;
5411
5706
  case "-":
5412
- return numberType;
5707
+ return this.operatorResult(expr, "-", arg, void 0) ?? numberType;
5413
5708
  case "#":
5414
- return numberType;
5709
+ return this.operatorResult(expr, "#", arg, void 0) ?? numberType;
5415
5710
  }
5416
5711
  return arg;
5417
5712
  }
@@ -5433,7 +5728,7 @@ var TypeAnalyzer = class {
5433
5728
  const r = this.infer(expr.right, env);
5434
5729
  switch (op) {
5435
5730
  case "..":
5436
- return stringType;
5731
+ return this.operatorResult(expr, op, l, r) ?? stringType;
5437
5732
  case "==":
5438
5733
  case "~=":
5439
5734
  case "<":
@@ -5448,7 +5743,7 @@ var TypeAnalyzer = class {
5448
5743
  case "//":
5449
5744
  case "%":
5450
5745
  case "^":
5451
- return numberType;
5746
+ return this.operatorResult(expr, op, l, r) ?? numberType;
5452
5747
  }
5453
5748
  return union([l, r]);
5454
5749
  }
@@ -5467,8 +5762,10 @@ var TypeAnalyzer = class {
5467
5762
  }
5468
5763
  case "CallExpression": {
5469
5764
  const callee = this.infer(expr.callee, env);
5470
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
5471
5765
  const fns = this.overloadsOf(callee);
5766
+ const expected = this.expectedArguments(expr.arguments, fns, () => 0);
5767
+ expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
5768
+ const argTypes = expr.arguments.map((a) => this.infer(a, env));
5472
5769
  if (fns.length) {
5473
5770
  this.recordExpected(expr.arguments, fns, () => 0);
5474
5771
  const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
@@ -5483,8 +5780,10 @@ var TypeAnalyzer = class {
5483
5780
  }
5484
5781
  case "MethodCallExpression": {
5485
5782
  const objType = this.infer(expr.object, env);
5486
- const argTypes = expr.arguments.map((a) => this.infer(a, env));
5487
5783
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
5784
+ const expected = this.expectedArguments(expr.arguments, fns, (f) => this.takesSelf(f) ? 1 : 0);
5785
+ expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
5786
+ const argTypes = expr.arguments.map((a) => this.infer(a, env));
5488
5787
  if (fns.length) {
5489
5788
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
5490
5789
  const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
@@ -5876,6 +6175,35 @@ var TypeAnalyzer = class {
5876
6175
  this.selfType = saved;
5877
6176
  }
5878
6177
  }
6178
+ /** What an operator on a value with metamethods gives: `a + b` calls
6179
+ * `__add` on `a`, or failing that on `b` with the operands swapped — the
6180
+ * order Luau tries them in. That is how `Vector3 + Vector3`, `CFrame *
6181
+ * Vector3` and `2 * vector` get their types from the declarations.
6182
+ * `undefined` when neither operand declares the metamethod; an operand
6183
+ * that declares it but accepts neither argument is reported. */
6184
+ operatorResult(node, op, left, right) {
6185
+ const name = right === void 0 ? op === "-" ? "__unm" : "__len" : METAMETHODS[op];
6186
+ if (!name) return void 0;
6187
+ const candidates = right === void 0 ? [[left, void 0]] : [[left, right], [right, left]];
6188
+ let declared;
6189
+ for (const [receiver, other] of candidates) {
6190
+ const t = this.expand(receiver);
6191
+ const method = t.kind === "object" ? t.properties.get(name) : void 0;
6192
+ if (!method) continue;
6193
+ declared ??= receiver;
6194
+ const args = other === void 0 ? [receiver] : [receiver, other];
6195
+ const picked = this.pickOverload(this.overloadsOf(method.type), args);
6196
+ if (picked) return this.callReturn(picked, args);
6197
+ }
6198
+ if (declared && this.emitDiagnostics) {
6199
+ this.diagnostics.push({
6200
+ node,
6201
+ 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)}'`
6202
+ });
6203
+ return anyType;
6204
+ }
6205
+ return void 0;
6206
+ }
5879
6207
  /** Does this signature take the receiver as its first parameter?
5880
6208
  *
5881
6209
  * Luau's `:` is sugar both ways: `function T:m(a)` declares
@@ -6249,7 +6577,6 @@ function sourceMapTypes(text, path, options) {
6249
6577
  const lines = [];
6250
6578
  const aliasOfFile = /* @__PURE__ */ new Map();
6251
6579
  const used = /* @__PURE__ */ new Set();
6252
- const canOmit = options.classes.has("Omit");
6253
6580
  const aliasOfNode = /* @__PURE__ */ new Map();
6254
6581
  const aliasFor = (segments) => {
6255
6582
  const base = `SourceMap_${segments.map((s) => s.replace(/[^A-Za-z0-9_]/g, "_")).join("_")}`;
@@ -6265,7 +6592,7 @@ function sourceMapTypes(text, path, options) {
6265
6592
  const className = IDENTIFIER.test(node.className) && options.classes.has(node.className) ? node.className : "Instance";
6266
6593
  const taken = options.membersOf?.(className) ?? INSTANCE_MEMBERS;
6267
6594
  const members = [];
6268
- if (parent && canOmit) members.push(`Parent: ${parent}`);
6595
+ if (parent) members.push(`Parent: ${parent}`);
6269
6596
  const named = /* @__PURE__ */ new Set();
6270
6597
  for (const child of node.children ?? []) {
6271
6598
  if (!isNode(child)) continue;
@@ -6274,8 +6601,7 @@ function sourceMapTypes(text, path, options) {
6274
6601
  named.add(child.name);
6275
6602
  members.push(`${child.name}: ${childAlias}`);
6276
6603
  }
6277
- const base = parent && canOmit ? `Omit<${className}, "Parent">` : className;
6278
- lines.push(`type ${alias} = ${members.length ? `${base} & { ${members.join(", ")} }` : base}`);
6604
+ lines.push(`declare class ${alias} extends ${className} { ${members.join(", ")} }`);
6279
6605
  return alias;
6280
6606
  };
6281
6607
  const rootAlias = visit(root, [root.name], void 0);
@@ -6353,6 +6679,7 @@ var index_default = luautparser;
6353
6679
  getBinding,
6354
6680
  intersection,
6355
6681
  isAssignable,
6682
+ isClassType,
6356
6683
  isGlobal,
6357
6684
  isPossiblyFalsy,
6358
6685
  isPossiblyTruthy,