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/README.md +18 -4
- package/dist/index.cjs +468 -35
- package/dist/index.d.cts +79 -7
- package/dist/index.d.ts +79 -7
- package/dist/index.js +467 -35
- package/dist/luau.d.luaut +245 -244
- package/dist/roblox.d.luaut +503 -503
- package/package.json +40 -40
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: [],
|
|
864
|
-
|
|
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.
|
|
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
|
|
1993
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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({
|
|
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
|
|
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
|
-
|
|
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
|
|
4231
|
-
|
|
4232
|
-
|
|
4233
|
-
|
|
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
|
|
4447
|
-
*
|
|
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
|
|
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],
|
|
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
|
|
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 (!
|
|
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
|
-
|
|
4538
|
-
|
|
4539
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|