luaut-parser 1.0.0 → 1.2.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
@@ -541,6 +541,17 @@ function spanFrom(start, end) {
541
541
  column: { start: start.column.start, end: end.column.end }
542
542
  };
543
543
  }
544
+ function tokenIdentifier(t) {
545
+ return { type: "Identifier", name: t.value, ...spanFrom(t, t) };
546
+ }
547
+ function nameIdentifier(name, at) {
548
+ return {
549
+ type: "Identifier",
550
+ name,
551
+ line: { start: at.line.start, end: at.line.start },
552
+ column: { start: at.column.start, end: at.column.start + name.length }
553
+ };
554
+ }
544
555
  var BINARY_PRECEDENCE = {
545
556
  "or": 1,
546
557
  "and": 2,
@@ -859,9 +870,11 @@ var Parser = class {
859
870
  const params = head.params.map((p) => ({
860
871
  type: "FunctionTypeParameter",
861
872
  name: p.name || void 0,
873
+ id: p.name ? nameIdentifier(p.name, p) : void 0,
862
874
  optional: p.optional,
863
- typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], ...spanFrom(start, start) },
864
- ...spanFrom(start, this.previous())
875
+ typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
876
+ line: p.line,
877
+ column: p.column
865
878
  }));
866
879
  const valueType2 = {
867
880
  type: "FunctionTypeNode",
@@ -873,12 +886,12 @@ var Parser = class {
873
886
  predicate: head.predicate,
874
887
  ...spanFrom(start, this.previous())
875
888
  };
876
- return { type: "DeclareStatement", name: nameTok2.value, valueType: valueType2, ...spanFrom(start, this.previous()) };
889
+ return { type: "DeclareStatement", name: nameTok2.value, id: tokenIdentifier(nameTok2), valueType: valueType2, ...spanFrom(start, this.previous()) };
877
890
  }
878
891
  const nameTok = this.expectIdentifier();
879
892
  this.expectPunctuator(":");
880
893
  const valueType = this.parseType();
881
- return { type: "DeclareStatement", name: nameTok.value, valueType, ...spanFrom(start, this.previous()) };
894
+ return { type: "DeclareStatement", name: nameTok.value, id: tokenIdentifier(nameTok), valueType, ...spanFrom(start, this.previous()) };
882
895
  }
883
896
  // `import { a, b as c } from '...'` / `import Default from '...'` /
884
897
  // `import Default, { a } from '...'`. Compiled away entirely by the
@@ -937,6 +950,21 @@ var Parser = class {
937
950
  }
938
951
  return { type: "ImportSpecifier", imported, local, ...spanFrom(imported, local) };
939
952
  }
953
+ /** `from "<path>"`: consumes `from` and the module string. */
954
+ parseModuleSource() {
955
+ this.advance();
956
+ const sourceTok = this.current();
957
+ if (sourceTok.type !== "Literal" || sourceTok.kind !== "string") {
958
+ this.error("Expected string literal module path after 'from'");
959
+ }
960
+ this.advance();
961
+ return {
962
+ type: "StringLiteral",
963
+ value: sourceTok.value,
964
+ raw: sourceTok.raw,
965
+ ...spanFrom(sourceTok, sourceTok)
966
+ };
967
+ }
940
968
  // `export const ...` / `export let ...` / `export const function ...` /
941
969
  // `export type ...` / `export default <expr>`
942
970
  parseExportStatement() {
@@ -955,7 +983,30 @@ var Parser = class {
955
983
  const declaration = this.parseVariableDeclaration();
956
984
  return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
957
985
  }
958
- this.error("Expected 'const', 'let', 'type', or 'default' after 'export'");
986
+ if (this.checkPunctuator("{")) {
987
+ this.advance();
988
+ const specifiers = [];
989
+ while (!this.checkPunctuator("}")) {
990
+ const local = this.parseIdentifier();
991
+ let exported = local;
992
+ if (this.checkKeyword("as")) {
993
+ this.advance();
994
+ exported = this.parseIdentifier();
995
+ }
996
+ specifiers.push({ type: "ExportSpecifier", local, exported, ...spanFrom(local, exported) });
997
+ if (!this.matchPunctuator(",")) break;
998
+ }
999
+ this.expectPunctuator("}");
1000
+ const source = this.checkKeyword("from") ? this.parseModuleSource() : void 0;
1001
+ return { type: "ExportNamedStatement", specifiers, source, ...spanFrom(start, this.previous()) };
1002
+ }
1003
+ if (this.checkOperator("*")) {
1004
+ this.advance();
1005
+ if (!this.checkKeyword("from")) this.error("Expected 'from' after 'export *'");
1006
+ const source = this.parseModuleSource();
1007
+ return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
1008
+ }
1009
+ this.error("Expected 'const', 'let', 'type', 'default', '{' or '*' after 'export'");
959
1010
  }
960
1011
  // `const x = ...` / `let x, y = ...` / `const function f() ... end`.
961
1012
  // luaut has no `local` — `const` bindings are immutable, `let` mutable.
@@ -1989,8 +2040,9 @@ var Parser = class {
1989
2040
  }
1990
2041
  if (this.checkIdentifierValue("infer") && this.peek(1).type === "Identifier") {
1991
2042
  this.advance();
1992
- const name = this.expectIdentifier().value;
1993
- return { type: "InferTypeNode", name, ...spanFrom(t, this.previous()) };
2043
+ const nameTok = this.expectIdentifier();
2044
+ const name = nameTok.value;
2045
+ return { type: "InferTypeNode", name, id: tokenIdentifier(nameTok), ...spanFrom(t, this.previous()) };
1994
2046
  }
1995
2047
  if (t.type === "Operator" && t.value === "<") {
1996
2048
  const generics = this.parseGenericTypeParameterList();
@@ -2027,6 +2079,16 @@ var Parser = class {
2027
2079
  this.expectPunctuator(")");
2028
2080
  return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
2029
2081
  }
2082
+ if (t.type === "Identifier" && t.value === "typeof" && this.peek(1).type === "Identifier") {
2083
+ this.advance();
2084
+ let expression = this.parseIdentifier();
2085
+ while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
2086
+ this.advance();
2087
+ const property = this.parseIdentifier();
2088
+ expression = { type: "MemberExpression", object: expression, property, ...spanFrom(expression, property) };
2089
+ }
2090
+ return { type: "TypeofTypeNode", expression, ...spanFrom(t, this.previous()) };
2091
+ }
2030
2092
  if (t.type === "Literal" && t.kind === "string") {
2031
2093
  this.advance();
2032
2094
  return { type: "TypeLiteralString", value: t.value, ...spanFrom(t, t) };
@@ -2089,17 +2151,21 @@ var Parser = class {
2089
2151
  let name;
2090
2152
  let optional2 = false;
2091
2153
  const named = this.checkType("Identifier") && this.peek(1).type === "Punctuator" && (this.peek(1).value === ":" || this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":");
2154
+ let nameTok;
2092
2155
  if (named) {
2093
- name = this.expectIdentifier().value;
2156
+ const tok = this.expectIdentifier();
2157
+ nameTok = tok;
2158
+ name = tok.value;
2094
2159
  optional2 = this.matchPunctuator("?");
2095
2160
  this.advance();
2096
2161
  }
2097
- const paramStart = this.current();
2162
+ const paramStart = nameTok ?? this.current();
2098
2163
  const typeAnnotation = this.parseType();
2099
2164
  params.push({
2100
2165
  type: "FunctionTypeParameter",
2101
2166
  name,
2102
2167
  typeAnnotation,
2168
+ id: nameTok && tokenIdentifier(nameTok),
2103
2169
  optional: optional2 || void 0,
2104
2170
  ...spanFrom(paramStart, this.previous())
2105
2171
  });
@@ -2161,7 +2227,8 @@ var Parser = class {
2161
2227
  readonly = false;
2162
2228
  }
2163
2229
  this.expectPunctuator("[");
2164
- const parameter = this.expectIdentifier().value;
2230
+ const parameterTok = this.expectIdentifier();
2231
+ const parameter = parameterTok.value;
2165
2232
  this.advance();
2166
2233
  const constraint = this.parseType();
2167
2234
  let nameType;
@@ -2185,6 +2252,7 @@ var Parser = class {
2185
2252
  return {
2186
2253
  type: "MappedTypeNode",
2187
2254
  parameter,
2255
+ parameterId: tokenIdentifier(parameterTok),
2188
2256
  constraint,
2189
2257
  nameType,
2190
2258
  template,
@@ -2211,30 +2279,43 @@ var Parser = class {
2211
2279
  this.expectPunctuator("{");
2212
2280
  const properties = [];
2213
2281
  while (!this.checkPunctuator("}")) {
2282
+ const propStart = this.current();
2214
2283
  if (this.checkPunctuator("[")) {
2215
2284
  this.advance();
2216
2285
  const keyType = this.parseType();
2217
2286
  this.expectPunctuator("]");
2218
2287
  this.expectPunctuator(":");
2219
2288
  const valueType = this.parseType();
2220
- properties.push({ type: "TableTypeIndexer", keyType, valueType });
2289
+ properties.push({ type: "TableTypeIndexer", keyType, valueType, ...spanFrom(propStart, this.previous()) });
2221
2290
  } else if (this.checkIdentifierValue("readonly") && this.peek(1).type === "Identifier") {
2222
2291
  this.advance();
2223
- const name = this.expectIdentifier().value;
2292
+ const keyTok = this.expectIdentifier();
2293
+ const name = keyTok.value;
2224
2294
  const optional2 = this.matchPunctuator("?");
2225
2295
  this.expectPunctuator(":");
2226
2296
  const valueType = this.parseType();
2227
- properties.push({ type: "TableTypeProperty", name, valueType, optional: optional2, readonly: true });
2297
+ properties.push({
2298
+ type: "TableTypeProperty",
2299
+ name,
2300
+ key: tokenIdentifier(keyTok),
2301
+ valueType,
2302
+ optional: optional2,
2303
+ readonly: true,
2304
+ ...spanFrom(propStart, this.previous())
2305
+ });
2228
2306
  } else if (this.checkType("Identifier") && (this.peek(1).type === "Punctuator" && this.peek(1).value === ":" || this.peek(1).type === "Punctuator" && this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":")) {
2229
- const name = this.expectIdentifier().value;
2307
+ const keyTok = this.expectIdentifier();
2308
+ const name = keyTok.value;
2230
2309
  const optional2 = this.matchPunctuator("?");
2231
2310
  this.expectPunctuator(":");
2232
2311
  const valueType = this.parseType();
2233
2312
  properties.push({
2234
2313
  type: "TableTypeProperty",
2235
2314
  name,
2315
+ key: tokenIdentifier(keyTok),
2236
2316
  valueType,
2237
- optional: optional2
2317
+ optional: optional2,
2318
+ ...spanFrom(propStart, this.previous())
2238
2319
  });
2239
2320
  } else {
2240
2321
  this.error("Expected object type property ('name: T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples");
@@ -2296,6 +2377,7 @@ var Parser = class {
2296
2377
  list.push({
2297
2378
  type: "GenericTypeParameter",
2298
2379
  name: nameTok.value,
2380
+ id: tokenIdentifier(nameTok),
2299
2381
  isPack,
2300
2382
  isConst: isConst || void 0,
2301
2383
  constraint,
@@ -2425,6 +2507,15 @@ var Analyzer = class {
2425
2507
  }
2426
2508
  return this.getOrCreateGlobalBinding(name);
2427
2509
  }
2510
+ /** Like `resolve`, but never creates a global: `undefined` when no
2511
+ * enclosing scope declares the name. */
2512
+ lookup(scope, name) {
2513
+ for (let s = scope; s; s = s.parent) {
2514
+ const id = s.declarations.get(name);
2515
+ if (id !== void 0) return id;
2516
+ }
2517
+ return void 0;
2518
+ }
2428
2519
  getOrCreateGlobalBinding(name) {
2429
2520
  const existing = this.globalScope.declarations.get(name);
2430
2521
  if (existing !== void 0) return existing;
@@ -2540,12 +2631,14 @@ var Analyzer = class {
2540
2631
  switch (stmt.type) {
2541
2632
  case "VariableDeclaration": {
2542
2633
  for (const init of stmt.init) this.visitExpression(init, scope);
2634
+ for (const name of stmt.names) this.visitType(name.typeAnnotation, scope);
2543
2635
  const isConst = stmt.kind === "const";
2544
2636
  for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
2545
2637
  return;
2546
2638
  }
2547
2639
  case "FunctionDeclaration": {
2548
2640
  this.declare(scope, stmt.name.name, "local", stmt.name, stmt.kind === "const");
2641
+ for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
2549
2642
  this.visitFunctionBody(stmt.func, scope);
2550
2643
  return;
2551
2644
  }
@@ -2555,6 +2648,7 @@ var Analyzer = class {
2555
2648
  } else {
2556
2649
  this.reference(scope, stmt.target.base);
2557
2650
  }
2651
+ for (const signature of stmt.signatures ?? []) this.visitSignature(signature, scope);
2558
2652
  this.visitFunctionBody(stmt.func, scope, stmt.isMethod);
2559
2653
  return;
2560
2654
  }
@@ -2628,10 +2722,13 @@ var Analyzer = class {
2628
2722
  case "BreakStatement":
2629
2723
  case "ContinueStatement":
2630
2724
  case "ErrorStatement":
2725
+ return;
2631
2726
  case "DeclareStatement":
2727
+ this.visitType(stmt.valueType, scope);
2632
2728
  return;
2633
2729
  case "TypeAliasStatement":
2634
2730
  case "ExportTypeAliasStatement":
2731
+ this.visitType(stmt.definition, scope);
2635
2732
  return;
2636
2733
  case "ImportStatement": {
2637
2734
  if (stmt.defaultImport) {
@@ -2648,6 +2745,18 @@ var Analyzer = class {
2648
2745
  case "ExportDefaultStatement":
2649
2746
  this.visitExpression(stmt.declaration, scope);
2650
2747
  return;
2748
+ case "ExportNamedStatement":
2749
+ if (!stmt.source) {
2750
+ for (const specifier of stmt.specifiers) {
2751
+ const id = this.lookup(scope, specifier.local.name);
2752
+ if (id === void 0) continue;
2753
+ this.bindingOf.set(specifier.local, id);
2754
+ this.bindings.get(id).references.push(specifier.local);
2755
+ }
2756
+ }
2757
+ return;
2758
+ case "ExportAllStatement":
2759
+ return;
2651
2760
  }
2652
2761
  }
2653
2762
  // ---------------- functions ----------------
@@ -2655,6 +2764,7 @@ var Analyzer = class {
2655
2764
  const fnScope = childScope(outerScope);
2656
2765
  func.params.forEach((param, i) => {
2657
2766
  const kind = isMethod && i === 0 ? "self" : "param";
2767
+ this.visitType(param.typeAnnotation, fnScope);
2658
2768
  if (param.default) this.visitExpression(param.default, fnScope);
2659
2769
  if (param.pattern) {
2660
2770
  this.declarePattern(fnScope, param.pattern, kind, fnScope);
@@ -2662,8 +2772,37 @@ var Analyzer = class {
2662
2772
  this.declare(fnScope, param.name, kind, param);
2663
2773
  }
2664
2774
  });
2775
+ this.visitType(func.varargTypeAnnotation, fnScope);
2776
+ this.visitType(func.returnType, fnScope);
2665
2777
  this.visitBlock(func.body, fnScope);
2666
2778
  }
2779
+ /** An overload signature: no body and no bindings, but its types can hold
2780
+ * a `typeof x`. */
2781
+ visitSignature(signature, scope) {
2782
+ for (const param of signature.params) this.visitType(param.typeAnnotation, scope);
2783
+ this.visitType(signature.returnType, scope);
2784
+ }
2785
+ /** Resolve the value references inside a type. Only `typeof x` has any —
2786
+ * everything else in a type names types, which live in their own
2787
+ * namespace and are not this pass's business. */
2788
+ visitType(node, scope) {
2789
+ if (!node) return;
2790
+ const walk = (value) => {
2791
+ if (!value || typeof value !== "object") return;
2792
+ if (Array.isArray(value)) {
2793
+ for (const item of value) walk(item);
2794
+ return;
2795
+ }
2796
+ if (value.type === "TypeofTypeNode") {
2797
+ this.visitExpression(value.expression, scope);
2798
+ return;
2799
+ }
2800
+ for (const key of Object.keys(value)) {
2801
+ if (key !== "line" && key !== "column") walk(value[key]);
2802
+ }
2803
+ };
2804
+ walk(node);
2805
+ }
2667
2806
  // ---------------- expressions ----------------
2668
2807
  visitExpression(expr, scope) {
2669
2808
  switch (expr.type) {
@@ -2722,6 +2861,11 @@ var Analyzer = class {
2722
2861
  return;
2723
2862
  case "TypeAssertionExpression":
2724
2863
  this.visitExpression(expr.expression, scope);
2864
+ this.visitType(expr.typeAnnotation, scope);
2865
+ return;
2866
+ case "SatisfiesExpression":
2867
+ this.visitExpression(expr.expression, scope);
2868
+ this.visitType(expr.typeAnnotation, scope);
2725
2869
  return;
2726
2870
  case "IfElseExpression":
2727
2871
  for (const clause of expr.clauses) {
@@ -3392,6 +3536,94 @@ function formatKey(k) {
3392
3536
  function analyzeTypes(program, scopes, options = {}) {
3393
3537
  return new TypeAnalyzer(program, scopes, options).run();
3394
3538
  }
3539
+ function moduleExports(program, scopes, types, resolveModule) {
3540
+ const byDeclaration = /* @__PURE__ */ new Map();
3541
+ for (const binding of scopes.bindings.values()) {
3542
+ if (binding.declarationNode) byDeclaration.set(binding.declarationNode, binding.id);
3543
+ }
3544
+ const values = /* @__PURE__ */ new Map();
3545
+ const exportedTypes = /* @__PURE__ */ new Map();
3546
+ let defaultType;
3547
+ const stars = [];
3548
+ const setValue = (name, type) => {
3549
+ if (name === "default") defaultType = type;
3550
+ else values.set(name, type);
3551
+ };
3552
+ const reexport = (from, name, as) => {
3553
+ if (!from || from.partial) {
3554
+ setValue(as, anyType);
3555
+ return;
3556
+ }
3557
+ if (name === "default") {
3558
+ if (from.default) setValue(as, from.default);
3559
+ return;
3560
+ }
3561
+ const value = from.values.get(name);
3562
+ if (value) setValue(as, value);
3563
+ const type = from.types.get(name);
3564
+ if (type) exportedTypes.set(as, type);
3565
+ };
3566
+ const aliasParams = (name) => {
3567
+ for (const s of program.body.statements) {
3568
+ const alias = s.type === "TypeAliasStatement" ? s : s.type === "ExportTypeAliasStatement" ? s.alias : void 0;
3569
+ if (alias?.name.name === name) return alias.generics.map((g) => g.name);
3570
+ }
3571
+ return [];
3572
+ };
3573
+ const exportName = (declaration, name) => {
3574
+ const id = byDeclaration.get(declaration);
3575
+ values.set(name, (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType);
3576
+ };
3577
+ const exportPattern = (target) => {
3578
+ switch (target.type) {
3579
+ case "IdentifierPattern":
3580
+ exportName(target, target.name);
3581
+ return;
3582
+ case "ObjectPattern":
3583
+ for (const p of target.properties) exportPattern(p.value);
3584
+ if (target.rest) exportPattern(target.rest);
3585
+ return;
3586
+ case "ArrayPattern":
3587
+ for (const el of target.elements) if (el) exportPattern(el.value);
3588
+ if (target.rest) exportPattern(target.rest);
3589
+ return;
3590
+ }
3591
+ };
3592
+ for (const stmt of program.body.statements) {
3593
+ if (stmt.type === "ExportStatement") {
3594
+ const declaration = stmt.declaration;
3595
+ if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
3596
+ else for (const target of declaration.names) exportPattern(target);
3597
+ } else if (stmt.type === "ExportTypeAliasStatement") {
3598
+ const name = stmt.alias.name.name;
3599
+ const type = types.aliases.get(name);
3600
+ if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
3601
+ } else if (stmt.type === "ExportDefaultStatement") {
3602
+ defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
3603
+ } else if (stmt.type === "ExportNamedStatement") {
3604
+ if (stmt.source) {
3605
+ const from = resolveModule?.(stmt.source.value);
3606
+ for (const s of stmt.specifiers) reexport(from, s.local.name, s.exported.name);
3607
+ } else {
3608
+ for (const s of stmt.specifiers) {
3609
+ const id = scopes.bindingOf.get(s.local);
3610
+ if (id !== void 0) setValue(s.exported.name, types.bindingType.get(id) ?? anyType);
3611
+ const alias = types.aliases.get(s.local.name);
3612
+ if (alias) exportedTypes.set(s.exported.name, { type: alias, params: aliasParams(s.local.name) });
3613
+ }
3614
+ }
3615
+ } else if (stmt.type === "ExportAllStatement") {
3616
+ stars.push(stmt.source.value);
3617
+ }
3618
+ }
3619
+ for (const specifier of stars) {
3620
+ const from = resolveModule?.(specifier);
3621
+ if (!from || from.partial) continue;
3622
+ for (const [name, type] of from.values) if (!values.has(name)) values.set(name, type);
3623
+ for (const [name, type] of from.types) if (!exportedTypes.has(name)) exportedTypes.set(name, type);
3624
+ }
3625
+ return { values, types: exportedTypes, default: defaultType };
3626
+ }
3395
3627
  function bindKey(id) {
3396
3628
  return `$${id}`;
3397
3629
  }
@@ -3505,6 +3737,8 @@ var TypeAnalyzer = class {
3505
3737
  typeOf = /* @__PURE__ */ new Map();
3506
3738
  bindingType = /* @__PURE__ */ new Map();
3507
3739
  narrowedTypeOf = /* @__PURE__ */ new Map();
3740
+ typeOfTypeNode = /* @__PURE__ */ new Map();
3741
+ expectedTypeOf = /* @__PURE__ */ new Map();
3508
3742
  /** Public: each alias resolved once (generic aliases keep their params as
3509
3743
  * `typeParam` nodes in the body). */
3510
3744
  aliases = /* @__PURE__ */ new Map();
@@ -3539,6 +3773,10 @@ var TypeAnalyzer = class {
3539
3773
  * (`type Tree = { children: Tree[] }`); it resolves to a nominal ref. */
3540
3774
  resolvingAliases = /* @__PURE__ */ new Set();
3541
3775
  diagnostics = [];
3776
+ /** `import`ed type names, from `resolveModule`. */
3777
+ importedTypes = /* @__PURE__ */ new Map();
3778
+ /** `resolveModule` results, one lookup per module path. */
3779
+ resolvedModules = /* @__PURE__ */ new Map();
3542
3780
  emitDiagnostics;
3543
3781
  /** Recursion guard for `preVisitBody`. */
3544
3782
  preVisitDepth = 0;
@@ -3547,6 +3785,7 @@ var TypeAnalyzer = class {
3547
3785
  this.registerAliasDefs(this.program.body);
3548
3786
  for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
3549
3787
  this.harvestDeclares(this.program.body);
3788
+ this.registerImportedTypes();
3550
3789
  this.resolveAllAliases();
3551
3790
  this.indexDeclarations();
3552
3791
  for (const [name, id] of this.scopes.globalsByName) {
@@ -3564,13 +3803,52 @@ var TypeAnalyzer = class {
3564
3803
  typeOf: this.typeOf,
3565
3804
  bindingType: this.bindingType,
3566
3805
  narrowedTypeOf: this.narrowedTypeOf,
3567
- aliases: this.aliases,
3806
+ typeOfTypeNode: this.typeOfTypeNode,
3807
+ expectedTypeOf: this.expectedTypeOf,
3808
+ aliases: this.resolveDeferredAliases(),
3568
3809
  diagnostics: this.diagnostics
3569
3810
  };
3570
3811
  }
3571
3812
  // --------------------------------------------------------
3572
3813
  // Aliases
3573
3814
  // --------------------------------------------------------
3815
+ moduleFor(specifier) {
3816
+ if (!this.resolvedModules.has(specifier)) {
3817
+ this.resolvedModules.set(specifier, this.options.resolveModule?.(specifier));
3818
+ }
3819
+ return this.resolvedModules.get(specifier);
3820
+ }
3821
+ /** `export ... from "./x"`: the module must exist, and so must each name. */
3822
+ checkReexport(source, names) {
3823
+ if (!this.options.resolveModule || !this.emitDiagnostics) return;
3824
+ const exports = this.moduleFor(source.value);
3825
+ if (!exports) {
3826
+ this.diagnostics.push({ node: source, message: `Cannot find module '${source.value}'` });
3827
+ return;
3828
+ }
3829
+ if (exports.partial) return;
3830
+ for (const name of names) {
3831
+ const found = name.name === "default" ? exports.default !== void 0 : exports.values.has(name.name) || exports.types.has(name.name);
3832
+ if (!found) {
3833
+ this.diagnostics.push({ node: name, message: `Module '${source.value}' has no exported member '${name.name}'` });
3834
+ }
3835
+ }
3836
+ }
3837
+ registerImportedTypes() {
3838
+ if (!this.options.resolveModule) return;
3839
+ for (const stmt of this.program.body.statements) {
3840
+ if (stmt.type !== "ImportStatement") continue;
3841
+ const exports = this.moduleFor(stmt.source.value);
3842
+ if (!exports) continue;
3843
+ for (const s of stmt.specifiers) {
3844
+ const exported = exports.types.get(s.imported.name);
3845
+ if (exported) {
3846
+ this.importedTypes.set(s.local.name, exported);
3847
+ this.aliases.set(s.local.name, exported.type);
3848
+ }
3849
+ }
3850
+ }
3851
+ }
3574
3852
  registerAliasDefs(block) {
3575
3853
  for (const stmt of block.statements) {
3576
3854
  const alias = stmt.type === "TypeAliasStatement" ? stmt : stmt.type === "ExportTypeAliasStatement" ? stmt.alias : void 0;
@@ -3590,11 +3868,23 @@ var TypeAnalyzer = class {
3590
3868
  }
3591
3869
  resolveAllAliases() {
3592
3870
  for (const [name, def] of this.aliasDefs) {
3871
+ if (containsTypeQuery(def.node)) continue;
3593
3872
  this.withTypeParams(def.params, () => {
3594
3873
  this.aliases.set(name, this.resolveType(def.node));
3595
3874
  });
3596
3875
  }
3597
3876
  }
3877
+ /** The aliases `resolveAllAliases` left for later, now that every binding
3878
+ * has its type. */
3879
+ resolveDeferredAliases() {
3880
+ for (const [name, def] of this.aliasDefs) {
3881
+ if (this.aliases.has(name)) continue;
3882
+ this.withTypeParams(def.params, () => {
3883
+ this.aliases.set(name, this.resolveType(def.node));
3884
+ });
3885
+ }
3886
+ return this.aliases;
3887
+ }
3598
3888
  withTypeParams(params, fn2) {
3599
3889
  const start = this.typeParamScope.length;
3600
3890
  for (const p of params) this.typeParamScope.push({ name: p.name, isConst: p.isConst });
@@ -3634,6 +3924,11 @@ var TypeAnalyzer = class {
3634
3924
  // TypeNode -> Type
3635
3925
  // --------------------------------------------------------
3636
3926
  resolveType(node) {
3927
+ const type = this.resolveTypeNode(node);
3928
+ if (this.instantiationDepth === 0) this.typeOfTypeNode.set(node, type);
3929
+ return type;
3930
+ }
3931
+ resolveTypeNode(node) {
3637
3932
  switch (node.type) {
3638
3933
  case "TypeReference": {
3639
3934
  const name = node.namespace ? `${node.namespace}.${node.base}` : node.base;
@@ -3674,6 +3969,16 @@ var TypeAnalyzer = class {
3674
3969
  typeArguments: node.typeArguments.map((a) => this.resolveType(a))
3675
3970
  });
3676
3971
  }
3972
+ const imported = this.importedTypes.get(node.base);
3973
+ if (imported) {
3974
+ if (!imported.params.length) return imported.type;
3975
+ const subst = /* @__PURE__ */ new Map();
3976
+ imported.params.forEach((name2, i) => {
3977
+ const arg = node.typeArguments[i];
3978
+ subst.set(name2, arg ? this.resolveType(arg) : unknownType);
3979
+ });
3980
+ return this.reduceType(substitute(imported.type, subst));
3981
+ }
3677
3982
  const lib = this.options.libTypes?.[node.base];
3678
3983
  if (lib) return lib;
3679
3984
  }
@@ -4226,11 +4531,49 @@ var TypeAnalyzer = class {
4226
4531
  case "ExportDefaultStatement":
4227
4532
  this.infer(stmt.declaration, env);
4228
4533
  return;
4534
+ case "ExportNamedStatement": {
4535
+ if (stmt.source) {
4536
+ this.checkReexport(stmt.source, stmt.specifiers.map((s) => s.local));
4537
+ return;
4538
+ }
4539
+ for (const s of stmt.specifiers) {
4540
+ if (this.bindingIdOf(s.local) !== void 0) {
4541
+ this.infer(s.local, env);
4542
+ } else if (!this.aliasDefs.has(s.local.name) && !this.importedTypes.has(s.local.name)) {
4543
+ if (this.emitDiagnostics) {
4544
+ this.diagnostics.push({ node: s.local, message: `Cannot find name '${s.local.name}' to export` });
4545
+ }
4546
+ }
4547
+ }
4548
+ return;
4549
+ }
4550
+ case "ExportAllStatement":
4551
+ this.checkReexport(stmt.source, []);
4552
+ return;
4229
4553
  case "ImportStatement": {
4230
- const ids = [];
4231
- if (stmt.defaultImport) ids.push(this.bindingIdOf(stmt.defaultImport));
4232
- for (const s of stmt.specifiers) ids.push(this.bindingIdOf(s.local));
4233
- for (const id of ids) if (id !== void 0) this.bindingType.set(id, anyType);
4554
+ const resolving = this.options.resolveModule !== void 0;
4555
+ const exports = resolving ? this.moduleFor(stmt.source.value) : void 0;
4556
+ const specifier = stmt.source.value;
4557
+ const report = (node, message) => {
4558
+ if (this.emitDiagnostics) this.diagnostics.push({ node, message });
4559
+ };
4560
+ if (resolving && !exports) report(stmt.source, `Cannot find module '${specifier}'`);
4561
+ const usable = exports && !exports.partial ? exports : void 0;
4562
+ if (stmt.defaultImport) {
4563
+ if (usable && usable.default === void 0) {
4564
+ report(stmt.defaultImport, `Module '${specifier}' has no default export`);
4565
+ }
4566
+ const id = this.bindingIdByName(stmt.defaultImport.name, stmt.defaultImport);
4567
+ if (id !== void 0) this.bindingType.set(id, usable?.default ?? anyType);
4568
+ }
4569
+ for (const s of stmt.specifiers) {
4570
+ const value = usable?.values.get(s.imported.name);
4571
+ if (usable && !value && !usable.types.has(s.imported.name)) {
4572
+ report(s.imported, `Module '${specifier}' has no exported member '${s.imported.name}'`);
4573
+ }
4574
+ const id = this.bindingIdByName(s.local.name, s.local);
4575
+ if (id !== void 0) this.bindingType.set(id, value ?? anyType);
4576
+ }
4234
4577
  return;
4235
4578
  }
4236
4579
  case "BreakStatement":
@@ -4443,16 +4786,76 @@ var TypeAnalyzer = class {
4443
4786
  return void 0;
4444
4787
  }
4445
4788
  /** Can this signature be called with these argument types? The signature's
4446
- * own generic parameters act as wildcards they are what the call would
4447
- * infer, so they must not make the match fail. */
4789
+ * own type parameters stand for what the call would infer, so each is
4790
+ * checked only against its constraint `<K extends keyof Services>`
4791
+ * accepts `"Players"` but not `""`. */
4448
4792
  overloadAccepts(f, argTypes) {
4449
4793
  if (!f.varargs && argTypes.length > f.params.length) return false;
4450
- const wildcards = new Map((f.typeParams ?? []).map((n) => [n, anyType]));
4794
+ const params = this.boundParams(f);
4451
4795
  return f.params.every((p, i) => {
4452
4796
  if (argTypes[i] === void 0) return p.optional === true;
4453
- return isAssignable(argTypes[i], substitute(p.type, wildcards));
4797
+ return isAssignable(argTypes[i], params[i]);
4798
+ });
4799
+ }
4800
+ /** A signature's parameter types as a call site sees them before inference:
4801
+ * each type parameter replaced by its constraint, or by `any` when it has
4802
+ * none — or when the constraint mentions another type parameter, which a
4803
+ * lone argument cannot be checked against without false errors. */
4804
+ boundParams(f) {
4805
+ if (!f.typeParams?.length) return f.params.map((p) => p.type);
4806
+ const bounds = new Map(f.typeParams.map((name) => [name, anyType]));
4807
+ const seen = /* @__PURE__ */ new WeakSet();
4808
+ const walk = (value) => {
4809
+ if (!value || typeof value !== "object" || seen.has(value)) return;
4810
+ seen.add(value);
4811
+ if (value instanceof Map) {
4812
+ value.forEach(walk);
4813
+ return;
4814
+ }
4815
+ const t = value;
4816
+ if (t.kind === "typeParam" && typeof t.name === "string" && bounds.has(t.name) && t.constraint && !containsTypeParam(t.constraint)) {
4817
+ bounds.set(t.name, this.reduceType(t.constraint));
4818
+ }
4819
+ for (const child of Object.values(value)) walk(child);
4820
+ };
4821
+ for (const p of f.params) walk(p.type);
4822
+ return f.params.map((p) => substitute(p.type, bounds));
4823
+ }
4824
+ /** Record what each written argument is expected to be — see
4825
+ * `TypeAnalysis.expectedTypeOf`. */
4826
+ recordExpected(written, fns, selfOf) {
4827
+ written.forEach((arg, j) => {
4828
+ const candidates = [];
4829
+ for (const f of fns) {
4830
+ const i = j + selfOf(f);
4831
+ const param = i < f.params.length ? this.boundParams(f)[i] : f.varargs;
4832
+ if (param) candidates.push(param);
4833
+ }
4834
+ if (candidates.length) this.expectedTypeOf.set(arg, union(candidates));
4454
4835
  });
4455
4836
  }
4837
+ /** No signature accepts the call, and the argument count is not the
4838
+ * problem: say which argument is wrong, the way TypeScript does. */
4839
+ reportArguments(call, written, fns, argsFor, selfOf) {
4840
+ if (!this.emitDiagnostics) return;
4841
+ if (fns.length > 1) {
4842
+ this.diagnostics.push({ node: call, message: "No overload matches this call" });
4843
+ return;
4844
+ }
4845
+ const f = fns[0];
4846
+ const args = argsFor(f);
4847
+ const params = this.boundParams(f);
4848
+ const self = selfOf(f);
4849
+ for (let i = 0; i < f.params.length; i++) {
4850
+ const arg = args[i];
4851
+ if (arg === void 0 || isAssignable(arg, params[i])) continue;
4852
+ this.diagnostics.push({
4853
+ node: written[i - self] ?? call,
4854
+ message: `Argument of type '${formatType(arg)}' is not assignable to parameter of type '${briefType(params[i])}'`
4855
+ });
4856
+ return;
4857
+ }
4858
+ }
4456
4859
  /** A required parameter may not follow an optional one — otherwise the
4457
4860
  * optional one could never actually be omitted. Same rule as TypeScript,
4458
4861
  * and it applies to a default (`a = 1`) as much as to a `?`. */
@@ -4482,22 +4885,25 @@ var TypeAnalyzer = class {
4482
4885
  return { min, max: f.varargs ? void 0 : f.params.length };
4483
4886
  }
4484
4887
  /** Report a call that passes too few or too many arguments. Only fires
4485
- * when *no* overload accepts the call, so an overload set still reports
4486
- * once, against its first signature. */
4888
+ * when *no* overload accepts the count, so an overload set still reports
4889
+ * once, against its first signature. Returns whether the count fits, so
4890
+ * an argument's type is only complained about when its count is right. */
4487
4891
  checkArity(node, fns, argCount, selfArgs) {
4488
- if (!this.emitDiagnostics || !fns.length) return;
4892
+ if (!fns.length) return true;
4489
4893
  const fits = fns.some((f) => {
4490
4894
  const { min: min2, max: max2 } = this.arityOf(f);
4491
4895
  const n = argCount + selfArgs;
4492
4896
  return n >= min2 && (max2 === void 0 || n <= max2);
4493
4897
  });
4494
- if (fits) return;
4898
+ if (fits) return true;
4899
+ if (!this.emitDiagnostics) return false;
4495
4900
  const { min, max } = this.arityOf(fns[0]);
4496
4901
  const need = max === void 0 ? `at least ${min - selfArgs}` : min === max ? `${min - selfArgs}` : `${min - selfArgs}-${max - selfArgs}`;
4497
4902
  this.diagnostics.push({
4498
4903
  node,
4499
4904
  message: `Expected ${need} argument${need === "1" ? "" : "s"}, got ${argCount}`
4500
4905
  });
4906
+ return false;
4501
4907
  }
4502
4908
  signatureToFnType(sig) {
4503
4909
  const names = sig.generics.map((g) => g.name);
@@ -4533,11 +4939,18 @@ var TypeAnalyzer = class {
4533
4939
  inferFunctionBody(func, env) {
4534
4940
  const names = func.generics.map((g) => g.name);
4535
4941
  return this.withTypeParams(func.generics, () => {
4536
- const params = func.params.map((p) => ({
4537
- name: p.pattern ? void 0 : p.name,
4538
- type: this.paramType(p, env),
4539
- optional: p.optional || p.default !== void 0
4540
- }));
4942
+ const params = func.params.map((p) => {
4943
+ const type = this.paramType(p, env);
4944
+ if (!p.pattern) {
4945
+ const id = this.bindingIdByName(p.name, p);
4946
+ if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
4947
+ }
4948
+ return {
4949
+ name: p.pattern ? void 0 : p.name,
4950
+ type,
4951
+ optional: p.optional || p.default !== void 0
4952
+ };
4953
+ });
4541
4954
  const bodyEnv = forkEnv(env);
4542
4955
  for (const p of func.params) {
4543
4956
  if (p.pattern) this.bindPattern(p.pattern, this.paramType(p, bodyEnv), bodyEnv, "widen");
@@ -4928,11 +5341,13 @@ var TypeAnalyzer = class {
4928
5341
  const argTypes = expr.arguments.map((a) => this.infer(a, env));
4929
5342
  const fns = this.overloadsOf(callee);
4930
5343
  if (fns.length) {
4931
- this.checkArity(expr, fns, argTypes.length, 0);
5344
+ this.recordExpected(expr.arguments, fns, () => 0);
5345
+ const arityFits = this.checkArity(expr, fns, argTypes.length, 0);
4932
5346
  const picked = this.pickOverload(fns, argTypes);
4933
5347
  if (picked) {
4934
5348
  return this.callReturn(picked, this.constArgs(picked, expr.arguments, argTypes, env));
4935
5349
  }
5350
+ if (arityFits) this.reportArguments(expr, expr.arguments, fns, () => argTypes, () => 0);
4936
5351
  return union(fns.map((f) => this.callReturn(f, argTypes)));
4937
5352
  }
4938
5353
  return callee.kind === "any" ? anyType : unknownType;
@@ -4943,13 +5358,16 @@ var TypeAnalyzer = class {
4943
5358
  const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
4944
5359
  if (fns.length) {
4945
5360
  const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
4946
- this.checkArity(expr, fns, argTypes.length, this.takesSelf(fns[0]) ? 1 : 0);
5361
+ const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
5362
+ this.recordExpected(expr.arguments, fns, selfOf);
5363
+ const arityFits = this.checkArity(expr, fns, argTypes.length, this.takesSelf(fns[0]) ? 1 : 0);
4947
5364
  const picked = this.pickOverload(fns, argTypes, withSelf);
4948
5365
  if (picked) {
4949
5366
  const self = this.takesSelf(picked) ? 1 : 0;
4950
5367
  const written = this.constArgs(picked, expr.arguments, argTypes, env, self);
4951
5368
  return this.callReturn(picked, this.takesSelf(picked) ? [objType, ...written] : written);
4952
5369
  }
5370
+ if (arityFits) this.reportArguments(expr, expr.arguments, fns, withSelf, selfOf);
4953
5371
  return union(fns.map((f) => this.callReturn(f, withSelf(f))));
4954
5372
  }
4955
5373
  return objType.kind === "any" ? anyType : unknownType;
@@ -5391,6 +5809,19 @@ var TypeAnalyzer = class {
5391
5809
  return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
5392
5810
  }
5393
5811
  };
5812
+ function containsTypeQuery(node) {
5813
+ if (!node || typeof node !== "object") return false;
5814
+ if (Array.isArray(node)) return node.some(containsTypeQuery);
5815
+ if (node.type === "TypeofTypeNode") return true;
5816
+ return Object.values(node).some(containsTypeQuery);
5817
+ }
5818
+ function briefType(t) {
5819
+ if (t.kind === "union" && t.types.length > 8) {
5820
+ const shown = t.types.slice(0, 6).map(formatType).join(" | ");
5821
+ return `${shown} | ... ${t.types.length - 6} more`;
5822
+ }
5823
+ return formatType(t);
5824
+ }
5394
5825
 
5395
5826
  // src/lib/luau.ts
5396
5827
  import { readFileSync } from "fs";
@@ -5456,6 +5887,7 @@ export {
5456
5887
  luauLib,
5457
5888
  luautparser,
5458
5889
  matchInfer,
5890
+ moduleExports,
5459
5891
  narrowExclude,
5460
5892
  narrowFalsy,
5461
5893
  narrowTo,