@tilua/parser 2.0.0 → 2.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 +16 -3
- package/dist/index.cjs +363 -61
- package/dist/index.d.cts +19 -2
- package/dist/index.d.ts +19 -2
- package/dist/index.js +363 -61
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -2398,6 +2398,21 @@ var Parser = class {
|
|
|
2398
2398
|
base = { type: "MemberExpression", object: base, property: prop, optional: true, ...spanFrom(base, prop) };
|
|
2399
2399
|
continue;
|
|
2400
2400
|
}
|
|
2401
|
+
if (punct === "." && this.punctuatorAt(2, "[")) {
|
|
2402
|
+
this.advance();
|
|
2403
|
+
this.advance();
|
|
2404
|
+
this.expectPunctuator("[");
|
|
2405
|
+
const index = this.inBrackets(() => this.parseExpression());
|
|
2406
|
+
this.expectPunctuator("]");
|
|
2407
|
+
base = {
|
|
2408
|
+
type: "IndexExpression",
|
|
2409
|
+
object: base,
|
|
2410
|
+
index,
|
|
2411
|
+
optional: true,
|
|
2412
|
+
...spanFrom(base, this.previous())
|
|
2413
|
+
};
|
|
2414
|
+
continue;
|
|
2415
|
+
}
|
|
2401
2416
|
if (punct === "." && this.punctuatorAt(2, "(")) {
|
|
2402
2417
|
this.advance();
|
|
2403
2418
|
this.advance();
|
|
@@ -3151,8 +3166,12 @@ var Parser = class {
|
|
|
3151
3166
|
* `name?: T` for an optional property or parameter. */
|
|
3152
3167
|
parseSuffixType() {
|
|
3153
3168
|
if (this.checkIdentifierValue("readonly")) {
|
|
3169
|
+
const start = this.current();
|
|
3154
3170
|
this.advance();
|
|
3155
|
-
|
|
3171
|
+
const t2 = this.parseSuffixType();
|
|
3172
|
+
if (t2.type === "ArrayTypeNode" || t2.type === "TupleTypeNode") return { ...t2, isReadonly: true };
|
|
3173
|
+
this.problemAt(start, "'readonly' belongs on an array or a tuple: a property is marked where it is written");
|
|
3174
|
+
return t2;
|
|
3156
3175
|
}
|
|
3157
3176
|
let t = this.parsePrimaryType();
|
|
3158
3177
|
while (true) {
|
|
@@ -3703,31 +3722,34 @@ var Analyzer = class {
|
|
|
3703
3722
|
* can call it, but a call straight in the block before the declaration
|
|
3704
3723
|
* finds nothing there yet. At a module's top level the whole function is
|
|
3705
3724
|
* hoisted, and this does not apply. */
|
|
3706
|
-
checkUseBeforeDefine(identifier, id) {
|
|
3725
|
+
checkUseBeforeDefine(identifier, id, at = this.functionDepth) {
|
|
3707
3726
|
const binding = this.bindings.get(id);
|
|
3708
|
-
if (
|
|
3727
|
+
if (this.typeQueryDepth > 0) return;
|
|
3709
3728
|
const declaration = binding.declarationNode;
|
|
3710
|
-
|
|
3711
|
-
|
|
3729
|
+
if (!declaration) return;
|
|
3730
|
+
const hoisted = binding.declaredBy === "function" || binding.declaredBy === "class";
|
|
3731
|
+
const depth = hoisted ? this.hoisted.get(declaration) : this.declaredDepth.get(id);
|
|
3732
|
+
const readable = hoisted || binding.kind === "local";
|
|
3733
|
+
if (!readable || depth === void 0 || depth !== at) return;
|
|
3712
3734
|
const before = identifier.line.start < declaration.line.start || identifier.line.start === declaration.line.start && identifier.column.start < declaration.column.start;
|
|
3713
3735
|
if (!before) return;
|
|
3714
3736
|
this.diagnostics.push({
|
|
3715
3737
|
node: identifier,
|
|
3716
|
-
message: `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run`,
|
|
3738
|
+
message: hoisted ? `'${binding.name}' is used before its definition: inside a function, a function declared further down is only there once its declaration has run` : `'${binding.name}' is used before its declaration, and holds nothing until that line has run`,
|
|
3717
3739
|
kind: "use-before-define"
|
|
3718
3740
|
});
|
|
3719
3741
|
}
|
|
3720
3742
|
noteDeferred(identifier, scope, id, assignment) {
|
|
3721
3743
|
if (this.functionDepth === 0 && this.typeQueryDepth === 0) return;
|
|
3722
3744
|
if (this.bindings.get(id).kind !== "global") return;
|
|
3723
|
-
this.deferredGlobals.push({ node: identifier, scope, assignment });
|
|
3745
|
+
this.deferredGlobals.push({ node: identifier, scope, assignment, depth: this.functionDepth });
|
|
3724
3746
|
}
|
|
3725
3747
|
/** Point each deferred read of a global at the declaration of that name
|
|
3726
3748
|
* that turned up later — in the module, or in any block around the code
|
|
3727
3749
|
* that reads it. Such code runs after the declaration has: a closure
|
|
3728
3750
|
* written inside a value reads the name the value is bound to. */
|
|
3729
3751
|
resolveForwardReferences() {
|
|
3730
|
-
for (const { node, scope, assignment } of this.deferredGlobals) {
|
|
3752
|
+
for (const { node, scope, assignment, depth } of this.deferredGlobals) {
|
|
3731
3753
|
const localId = this.lookup(scope, node.name);
|
|
3732
3754
|
const globalId = this.bindingOf.get(node);
|
|
3733
3755
|
if (localId === void 0 || globalId === void 0 || localId === globalId) continue;
|
|
@@ -3742,7 +3764,10 @@ var Analyzer = class {
|
|
|
3742
3764
|
this.bindingOf.set(node, localId);
|
|
3743
3765
|
this.bindings.get(localId).references.push(node);
|
|
3744
3766
|
if (assignment) this.checkConstAssign(localId, node);
|
|
3745
|
-
else if (this.typeQueryDepth === 0)
|
|
3767
|
+
else if (this.typeQueryDepth === 0) {
|
|
3768
|
+
this.checkTypeOnly(localId, node);
|
|
3769
|
+
this.checkUseBeforeDefine(node, localId, depth);
|
|
3770
|
+
}
|
|
3746
3771
|
}
|
|
3747
3772
|
}
|
|
3748
3773
|
/** Every read of a global nothing declares. A global assigned somewhere
|
|
@@ -3773,9 +3798,13 @@ var Analyzer = class {
|
|
|
3773
3798
|
}
|
|
3774
3799
|
const id = this.nextId++;
|
|
3775
3800
|
this.bindings.set(id, { id, name, kind, declarationNode: node, references: [], isConst, declaredBy });
|
|
3801
|
+
this.declaredDepth.set(id, this.functionDepth);
|
|
3776
3802
|
scope.declarations.set(name, id);
|
|
3777
3803
|
return id;
|
|
3778
3804
|
}
|
|
3805
|
+
/** Which function body each name was declared in. A read of a `const` from
|
|
3806
|
+
* the same body, but above it, is a read of nothing. */
|
|
3807
|
+
declaredDepth = /* @__PURE__ */ new Map();
|
|
3779
3808
|
resolve(scope, name) {
|
|
3780
3809
|
for (let s = scope; s; s = s.parent) {
|
|
3781
3810
|
const id = s.declarations.get(name);
|
|
@@ -4428,9 +4457,9 @@ function substitute(t, subst) {
|
|
|
4428
4457
|
case "typeParam":
|
|
4429
4458
|
return subst.get(t.name) ?? t;
|
|
4430
4459
|
case "array":
|
|
4431
|
-
return arrayOf(substitute(t.element, subst));
|
|
4460
|
+
return { ...arrayOf(substitute(t.element, subst)), readonly: t.readonly };
|
|
4432
4461
|
case "tuple":
|
|
4433
|
-
return tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst));
|
|
4462
|
+
return { ...tuple(t.elements.map((e) => substitute(e, subst)), t.rest && substitute(t.rest, subst)), readonly: t.readonly };
|
|
4434
4463
|
case "object": {
|
|
4435
4464
|
const entries = [];
|
|
4436
4465
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
|
|
@@ -4536,14 +4565,17 @@ function classArguments(t, name) {
|
|
|
4536
4565
|
if (t.kind === "object") return t.class?.typeArguments?.get(name);
|
|
4537
4566
|
return void 0;
|
|
4538
4567
|
}
|
|
4539
|
-
function unify(param, arg, vars, out) {
|
|
4568
|
+
function unify(param, arg, vars, out, how = "union") {
|
|
4540
4569
|
if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
|
|
4541
|
-
unify(param.constraint, arg, vars, out);
|
|
4570
|
+
unify(param.constraint, arg, vars, out, how);
|
|
4542
4571
|
return;
|
|
4543
4572
|
}
|
|
4544
4573
|
if (param.kind === "typeParam" && vars.has(param.name)) {
|
|
4545
4574
|
const prev = out.get(param.name);
|
|
4546
|
-
out.set(param.name,
|
|
4575
|
+
if (!prev) out.set(param.name, arg);
|
|
4576
|
+
else if (isAssignable(arg, prev)) {
|
|
4577
|
+
} else if (isAssignable(prev, arg)) out.set(param.name, arg);
|
|
4578
|
+
else if (how === "union") out.set(param.name, union([prev, arg]));
|
|
4547
4579
|
return;
|
|
4548
4580
|
}
|
|
4549
4581
|
if (arg.kind === "any" || arg.kind === "never") return;
|
|
@@ -4595,6 +4627,20 @@ function unify(param, arg, vars, out) {
|
|
|
4595
4627
|
return;
|
|
4596
4628
|
}
|
|
4597
4629
|
}
|
|
4630
|
+
function memberKey(t) {
|
|
4631
|
+
switch (t.kind) {
|
|
4632
|
+
case "literal":
|
|
4633
|
+
return `l${t.base}:${String(t.value)}`;
|
|
4634
|
+
case "primitive":
|
|
4635
|
+
return `p${t.name}`;
|
|
4636
|
+
case "any":
|
|
4637
|
+
case "unknown":
|
|
4638
|
+
case "never":
|
|
4639
|
+
return t.kind;
|
|
4640
|
+
default:
|
|
4641
|
+
return formatType(t);
|
|
4642
|
+
}
|
|
4643
|
+
}
|
|
4598
4644
|
function union(types) {
|
|
4599
4645
|
const flat = [];
|
|
4600
4646
|
for (const t of types) {
|
|
@@ -4606,7 +4652,7 @@ function union(types) {
|
|
|
4606
4652
|
const seen = /* @__PURE__ */ new Map();
|
|
4607
4653
|
for (const t of flat) {
|
|
4608
4654
|
if (t.kind === "never") continue;
|
|
4609
|
-
const key =
|
|
4655
|
+
const key = memberKey(t);
|
|
4610
4656
|
if (!seen.has(key)) seen.set(key, t);
|
|
4611
4657
|
}
|
|
4612
4658
|
let members = reduceSubtypes([...seen.values()]);
|
|
@@ -4635,7 +4681,7 @@ function intersection(types) {
|
|
|
4635
4681
|
const seen = /* @__PURE__ */ new Map();
|
|
4636
4682
|
for (const t of flat) {
|
|
4637
4683
|
if (t.kind === "unknown") continue;
|
|
4638
|
-
seen.set(
|
|
4684
|
+
seen.set(memberKey(t), t);
|
|
4639
4685
|
}
|
|
4640
4686
|
const members = [...seen.values()];
|
|
4641
4687
|
if (members.length === 0) return unknownType;
|
|
@@ -4662,9 +4708,9 @@ function widen(t) {
|
|
|
4662
4708
|
case "literal":
|
|
4663
4709
|
return primitive(t.base);
|
|
4664
4710
|
case "array":
|
|
4665
|
-
return arrayOf(widen(t.element));
|
|
4711
|
+
return { ...arrayOf(widen(t.element)), readonly: t.readonly };
|
|
4666
4712
|
case "tuple":
|
|
4667
|
-
return tuple(t.elements.map(widen), t.rest && widen(t.rest));
|
|
4713
|
+
return { ...tuple(t.elements.map(widen), t.rest && widen(t.rest)), readonly: t.readonly };
|
|
4668
4714
|
case "object": {
|
|
4669
4715
|
if (t.frozen || t.class) return t;
|
|
4670
4716
|
const entries = [];
|
|
@@ -4762,6 +4808,7 @@ function isAssignableInner(a, b) {
|
|
|
4762
4808
|
return b.kind === "primitive" && b.name === "string";
|
|
4763
4809
|
}
|
|
4764
4810
|
if (a.kind === "primitive") return b.kind === "primitive" && b.name === a.name;
|
|
4811
|
+
if ((a.kind === "array" || a.kind === "tuple") && a.readonly && (b.kind === "array" || b.kind === "tuple") && !b.readonly) return false;
|
|
4765
4812
|
if (a.kind === "array") {
|
|
4766
4813
|
if (b.kind === "array") return isAssignable(a.element, b.element);
|
|
4767
4814
|
if (b.kind === "tuple") return b.elements.length === 0 && !!b.rest && isAssignable(a.element, b.rest);
|
|
@@ -5107,10 +5154,10 @@ function formatTypeUncached(t) {
|
|
|
5107
5154
|
case "literal":
|
|
5108
5155
|
return t.base === "string" ? JSON.stringify(t.value) : String(t.value);
|
|
5109
5156
|
case "array":
|
|
5110
|
-
return `${formatAtom(t.element)}[]`;
|
|
5157
|
+
return `${t.readonly ? "readonly " : ""}${formatAtom(t.element)}[]`;
|
|
5111
5158
|
case "tuple": {
|
|
5112
5159
|
const inner = [...t.elements.map(formatType), ...t.rest ? [`...${formatAtom(t.rest)}[]`] : []];
|
|
5113
|
-
return
|
|
5160
|
+
return `${t.readonly ? "readonly " : ""}[${inner.join(", ")}]`;
|
|
5114
5161
|
}
|
|
5115
5162
|
case "object": {
|
|
5116
5163
|
if (t.name) {
|
|
@@ -5633,7 +5680,7 @@ function expressionLabel(e, depth = 0) {
|
|
|
5633
5680
|
case "IndexExpression": {
|
|
5634
5681
|
const o = expressionLabel(e.object, depth + 1);
|
|
5635
5682
|
const i = e.index.type === "StringLiteral" ? JSON.stringify(e.index.value) : e.index.type === "NumberLiteral" ? e.index.raw : e.index.type === "Identifier" ? e.index.name : "...";
|
|
5636
|
-
return o === void 0 ? void 0 : `${o}[${i}]`;
|
|
5683
|
+
return o === void 0 ? void 0 : `${o}${e.optional ? "?." : ""}[${i}]`;
|
|
5637
5684
|
}
|
|
5638
5685
|
case "ParenthesizedExpression": {
|
|
5639
5686
|
const inner = expressionLabel(e.expression, depth + 1);
|
|
@@ -5684,7 +5731,8 @@ var TypeAnalyzer = class {
|
|
|
5684
5731
|
/** Put on every ref to one of `aliasDefs`, so a module that imports the
|
|
5685
5732
|
* type expands the ref here rather than by its name there. */
|
|
5686
5733
|
origin = { expand: (t) => this.expand(t) };
|
|
5687
|
-
/** See `resolveClass`.
|
|
5734
|
+
/** See `resolveClass`. Shared with other analyses over the same libraries
|
|
5735
|
+
* when none of their names is redefined here; see `shareableClasses`. */
|
|
5688
5736
|
classTypes = /* @__PURE__ */ new WeakMap();
|
|
5689
5737
|
/** See `instanceType` — one instance type per `class ... end`. */
|
|
5690
5738
|
instanceTypes = /* @__PURE__ */ new WeakMap();
|
|
@@ -5730,6 +5778,11 @@ var TypeAnalyzer = class {
|
|
|
5730
5778
|
/** Recursion guard for `preVisitBody`. */
|
|
5731
5779
|
preVisitDepth = 0;
|
|
5732
5780
|
run() {
|
|
5781
|
+
const shared = shareableClasses(this.options.libs ?? [], this.program);
|
|
5782
|
+
if (shared) {
|
|
5783
|
+
this.classTypes = shared.classTypes;
|
|
5784
|
+
this.classMembers = shared.classMembers;
|
|
5785
|
+
}
|
|
5733
5786
|
this.registerAliasDefs(preludeProgram().body, true);
|
|
5734
5787
|
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
|
|
5735
5788
|
this.registerAliasDefs(this.program.body);
|
|
@@ -6895,14 +6948,17 @@ var TypeAnalyzer = class {
|
|
|
6895
6948
|
return literal(node.value);
|
|
6896
6949
|
case "TypeLiteralNumber":
|
|
6897
6950
|
return literal(node.value);
|
|
6898
|
-
case "ArrayTypeNode":
|
|
6899
|
-
|
|
6951
|
+
case "ArrayTypeNode": {
|
|
6952
|
+
const made = arrayOf(this.resolveType(node.element));
|
|
6953
|
+
return node.isReadonly ? { ...made, readonly: true } : made;
|
|
6954
|
+
}
|
|
6900
6955
|
case "TupleTypeNode": {
|
|
6901
6956
|
const rest = node.rest && this.expand(this.resolveType(node.rest));
|
|
6902
|
-
|
|
6957
|
+
const made = tuple(
|
|
6903
6958
|
node.elements.map((e) => this.resolveType(e)),
|
|
6904
6959
|
rest ? rest.kind === "array" ? rest.element : rest.kind === "any" ? anyType : unknownType : void 0
|
|
6905
6960
|
);
|
|
6961
|
+
return node.isReadonly && made.kind === "tuple" ? { ...made, readonly: true } : made;
|
|
6906
6962
|
}
|
|
6907
6963
|
case "UnionTypeNode":
|
|
6908
6964
|
return union(node.types.map((t) => this.resolveType(t)));
|
|
@@ -7704,8 +7760,10 @@ var TypeAnalyzer = class {
|
|
|
7704
7760
|
// --------------------------------------------------------
|
|
7705
7761
|
// Functions
|
|
7706
7762
|
// --------------------------------------------------------
|
|
7707
|
-
|
|
7708
|
-
|
|
7763
|
+
/** `returns`, when given, collects the type of each `return` the walk
|
|
7764
|
+
* reads — see `inferFunctionBody`. */
|
|
7765
|
+
visitFunctionBody(func, outerEnv, returns) {
|
|
7766
|
+
this.withTypeParams(func.generics, () => this.visitFunctionBodyInner(func, outerEnv, returns));
|
|
7709
7767
|
}
|
|
7710
7768
|
/** A parameter's type: annotation, else a shape synthesized from a
|
|
7711
7769
|
* destructuring pattern, else inferred from its default, else `any`. */
|
|
@@ -7722,8 +7780,19 @@ var TypeAnalyzer = class {
|
|
|
7722
7780
|
if (p.rest) return arrayOf(unknownType);
|
|
7723
7781
|
if (p.pattern) return this.patternToType(p.pattern, env);
|
|
7724
7782
|
if (p.default) return widen(this.infer(p.default, env));
|
|
7725
|
-
|
|
7783
|
+
const contextual = this.contextualParams.get(p);
|
|
7784
|
+
if (contextual) return contextual;
|
|
7785
|
+
if (this.emitDiagnostics && !receiver && p.name && p.line && !this.untypedParamReported.has(p)) {
|
|
7786
|
+
this.untypedParamReported.add(p);
|
|
7787
|
+
this.diagnostics.push({
|
|
7788
|
+
node: p,
|
|
7789
|
+
message: `Parameter '${p.name}' has no type, so it is 'any': give it one, or a default to read it from`
|
|
7790
|
+
});
|
|
7791
|
+
}
|
|
7792
|
+
return anyType;
|
|
7726
7793
|
}
|
|
7794
|
+
/** Parameters already spoken about: a body is visited more than once. */
|
|
7795
|
+
untypedParamReported = /* @__PURE__ */ new WeakSet();
|
|
7727
7796
|
/** What a function expression's unannotated parameters are, from where
|
|
7728
7797
|
* it is written — see `applyContext`. */
|
|
7729
7798
|
contextualParams = /* @__PURE__ */ new WeakMap();
|
|
@@ -7743,6 +7812,53 @@ var TypeAnalyzer = class {
|
|
|
7743
7812
|
* it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
|
|
7744
7813
|
* `player` from `Connect`'s callback type. Anything else is inferred as
|
|
7745
7814
|
* usual. */
|
|
7815
|
+
/** The arguments of a call, in two passes where one is needed.
|
|
7816
|
+
*
|
|
7817
|
+
* A function written at the call site with a parameter nothing annotates
|
|
7818
|
+
* reads that parameter from the signature — and where the signature says
|
|
7819
|
+
* `(v: T) => U`, `T` is only known once the other arguments have been
|
|
7820
|
+
* read. So such an argument is read once quietly, to get a shape to pick
|
|
7821
|
+
* a signature with, and then again with `T` in hand: `map(xs, v => v + 1)`
|
|
7822
|
+
* has its `v` a number by then, and `U` follows from what the body
|
|
7823
|
+
* answers. Everything else is read once, as it always was. */
|
|
7824
|
+
inferArguments(written, fns, selfOf, extra, env) {
|
|
7825
|
+
const expected = this.expectedArguments(written, fns, selfOf);
|
|
7826
|
+
written.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
7827
|
+
const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
|
|
7828
|
+
if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
|
|
7829
|
+
const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
|
|
7830
|
+
const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
|
|
7831
|
+
const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
|
|
7832
|
+
written.forEach((a, i) => {
|
|
7833
|
+
if (!waits[i]) return;
|
|
7834
|
+
if (picked && subst) {
|
|
7835
|
+
const self = selfOf(picked);
|
|
7836
|
+
const declared = i + self < picked.params.length ? picked.params[i + self].type : picked.varargs;
|
|
7837
|
+
if (declared) this.applyContext(a, this.reduceType(substitute(declared, subst)));
|
|
7838
|
+
}
|
|
7839
|
+
argTypes[i] = this.infer(a, env);
|
|
7840
|
+
});
|
|
7841
|
+
return argTypes;
|
|
7842
|
+
}
|
|
7843
|
+
/** Is this argument a function whose own type waits on the call's? */
|
|
7844
|
+
waitsOnInference(arg, expected) {
|
|
7845
|
+
if (!expected || !containsTypeParam(expected)) return false;
|
|
7846
|
+
let e = arg;
|
|
7847
|
+
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
7848
|
+
if (e.type !== "FunctionExpression") return false;
|
|
7849
|
+
return e.func.params.some((p) => !p.typeAnnotation && !p.default && !p.pattern && p.name !== "self" && p.name !== "this");
|
|
7850
|
+
}
|
|
7851
|
+
/** Read something without saying anything about it: a first pass whose
|
|
7852
|
+
* only purpose is a shape to go on. */
|
|
7853
|
+
quietly(read) {
|
|
7854
|
+
const was = this.emitDiagnostics;
|
|
7855
|
+
this.emitDiagnostics = false;
|
|
7856
|
+
try {
|
|
7857
|
+
return read();
|
|
7858
|
+
} finally {
|
|
7859
|
+
this.emitDiagnostics = was;
|
|
7860
|
+
}
|
|
7861
|
+
}
|
|
7746
7862
|
applyContext(expr, expected) {
|
|
7747
7863
|
let e = expr;
|
|
7748
7864
|
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
@@ -7891,7 +8007,7 @@ var TypeAnalyzer = class {
|
|
|
7891
8007
|
this.contextualReturns.pop();
|
|
7892
8008
|
}
|
|
7893
8009
|
}
|
|
7894
|
-
visitFunctionBodyInner(func, outerEnv) {
|
|
8010
|
+
visitFunctionBodyInner(func, outerEnv, returns) {
|
|
7895
8011
|
const env = forkEnv(outerEnv);
|
|
7896
8012
|
for (const p of func.params) {
|
|
7897
8013
|
if (p.pattern) {
|
|
@@ -7908,7 +8024,7 @@ var TypeAnalyzer = class {
|
|
|
7908
8024
|
if (p.typeAnnotation) this.annotated.add(id);
|
|
7909
8025
|
}
|
|
7910
8026
|
}
|
|
7911
|
-
this.withVarargs(func, () => this.collectReturns(
|
|
8027
|
+
this.withVarargs(func, () => this.collectReturns(returns, () => {
|
|
7912
8028
|
this.visitBlock(func.body, env);
|
|
7913
8029
|
this.checkReturnsAtAll(func, this.declaredReturns[this.declaredReturns.length - 1]);
|
|
7914
8030
|
}));
|
|
@@ -7962,7 +8078,7 @@ var TypeAnalyzer = class {
|
|
|
7962
8078
|
if (arg === void 0) return;
|
|
7963
8079
|
const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
|
|
7964
8080
|
const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
|
|
7965
|
-
unify(p.type, preserve ? arg : widen(arg), vars, subst);
|
|
8081
|
+
unify(p.type, preserve ? arg : widen(arg), vars, subst, "first");
|
|
7966
8082
|
});
|
|
7967
8083
|
if (f.varargs && f.restIsWhole) {
|
|
7968
8084
|
const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
|
|
@@ -8018,6 +8134,11 @@ var TypeAnalyzer = class {
|
|
|
8018
8134
|
* this returns `undefined` and the ordinary pick stands. */
|
|
8019
8135
|
distributedReturn(fns, argTypes, picked, argsFor) {
|
|
8020
8136
|
if (fns.length < 2) return void 0;
|
|
8137
|
+
if (argTypes.some((t) => this.expand(t).kind === "any")) {
|
|
8138
|
+
const taken = fns.filter((f) => this.overloadAccepts(f, argsFor(f, argTypes)));
|
|
8139
|
+
if (taken.length < 2) return void 0;
|
|
8140
|
+
return union(taken.map((f) => this.callReturn(f, argsFor(f, argTypes))));
|
|
8141
|
+
}
|
|
8021
8142
|
const position = argTypes.findIndex((t) => this.expand(t).kind === "union");
|
|
8022
8143
|
if (position < 0) return void 0;
|
|
8023
8144
|
const members = this.expand(argTypes[position]).types;
|
|
@@ -8066,6 +8187,8 @@ var TypeAnalyzer = class {
|
|
|
8066
8187
|
* lone argument cannot be checked against without false errors. */
|
|
8067
8188
|
boundParams(f) {
|
|
8068
8189
|
if (!f.typeParams?.length) return f.params.map((p) => p.type);
|
|
8190
|
+
const known = this.boundParamsCache.get(f);
|
|
8191
|
+
if (known) return known;
|
|
8069
8192
|
const bounds = new Map(f.typeParams.map((name) => [name, anyType]));
|
|
8070
8193
|
const seen = /* @__PURE__ */ new WeakSet();
|
|
8071
8194
|
const walk = (value) => {
|
|
@@ -8083,8 +8206,11 @@ var TypeAnalyzer = class {
|
|
|
8083
8206
|
for (const child of Object.values(value)) walk(child);
|
|
8084
8207
|
};
|
|
8085
8208
|
for (const p of f.params) if (containsTypeParam(p.type)) walk(p.type);
|
|
8086
|
-
|
|
8209
|
+
const bound = f.params.map((p) => this.reduceType(substitute(p.type, bounds)));
|
|
8210
|
+
this.boundParamsCache.set(f, bound);
|
|
8211
|
+
return bound;
|
|
8087
8212
|
}
|
|
8213
|
+
boundParamsCache = /* @__PURE__ */ new WeakMap();
|
|
8088
8214
|
/** Record what each written argument is expected to be — see
|
|
8089
8215
|
* `TypeAnalysis.expectedTypeOf`. */
|
|
8090
8216
|
recordExpected(written, fns, selfOf, argsOf = () => []) {
|
|
@@ -8303,7 +8429,12 @@ var TypeAnalyzer = class {
|
|
|
8303
8429
|
asserts: node.asserts
|
|
8304
8430
|
};
|
|
8305
8431
|
}
|
|
8306
|
-
|
|
8432
|
+
/** `visited`, when given, is what the real walk of the body just
|
|
8433
|
+
* collected from its `return`s: a function expression is walked before
|
|
8434
|
+
* its type is asked for, so there is nothing left to guess, and walking
|
|
8435
|
+
* the body a second time only to read them made every callback nested in
|
|
8436
|
+
* another cost twice its parent's. */
|
|
8437
|
+
inferFunctionBody(func, env, visited) {
|
|
8307
8438
|
const names = func.generics.map((g) => g.name);
|
|
8308
8439
|
return this.withTypeParams(func.generics, () => {
|
|
8309
8440
|
const params = func.params.flatMap((p) => {
|
|
@@ -8332,6 +8463,8 @@ var TypeAnalyzer = class {
|
|
|
8332
8463
|
returns = this.resolveType(func.returnType);
|
|
8333
8464
|
} else if (func.predicate) {
|
|
8334
8465
|
returns = booleanType;
|
|
8466
|
+
} else if (visited) {
|
|
8467
|
+
returns = visited.length ? union([...visited]) : this.withVarargs(func, () => this.silently(() => this.inferReturnType(func.body, bodyEnv)));
|
|
8335
8468
|
} else {
|
|
8336
8469
|
const collected = [];
|
|
8337
8470
|
returns = this.withVarargs(func, () => this.silently(() => {
|
|
@@ -8548,7 +8681,7 @@ var TypeAnalyzer = class {
|
|
|
8548
8681
|
/** What one `for (const item in source)` hands over each time, and how it
|
|
8549
8682
|
* walks the source. `pairs`/`ipairs` are read from the table they are
|
|
8550
8683
|
* given, so a record's keys stay the literals they are. */
|
|
8551
|
-
iterationOf(source, sourceType) {
|
|
8684
|
+
iterationOf(source, sourceType, report = true) {
|
|
8552
8685
|
if (source.type === "CallExpression" && source.callee.type === "Identifier" && source.arguments[0]) {
|
|
8553
8686
|
const name = source.callee.name;
|
|
8554
8687
|
const src = this.expand(this.typeOf.get(source.arguments[0]) ?? unknownType);
|
|
@@ -8586,7 +8719,14 @@ var TypeAnalyzer = class {
|
|
|
8586
8719
|
if (t.kind === "object") {
|
|
8587
8720
|
return [t.indexer?.value ?? union([...t.properties.values()].map((p) => p.type)), values];
|
|
8588
8721
|
}
|
|
8589
|
-
if (t.kind
|
|
8722
|
+
if (t.kind === "union") {
|
|
8723
|
+
const each = t.types.map((member) => this.iterationOf(source, member, false));
|
|
8724
|
+
const [first] = each;
|
|
8725
|
+
if (first && each.every(([, form]) => form.walks === first[1].walks && form.viaIter === first[1].viaIter)) {
|
|
8726
|
+
return [union(each.map(([item]) => item)), first[1]];
|
|
8727
|
+
}
|
|
8728
|
+
}
|
|
8729
|
+
if (t.kind !== "never" && report && this.emitDiagnostics) {
|
|
8590
8730
|
this.diagnostics.push({
|
|
8591
8731
|
node: source,
|
|
8592
8732
|
message: `Cannot loop over '${formatType(sourceType)}': a loop walks an array, a table, an iterator function or an iteration such as 'pairs(t)'`
|
|
@@ -8955,7 +9095,14 @@ var TypeAnalyzer = class {
|
|
|
8955
9095
|
const names = keys.map(nameOf);
|
|
8956
9096
|
const others = keys.filter((_, i) => names[i] === void 0);
|
|
8957
9097
|
let message;
|
|
8958
|
-
|
|
9098
|
+
const listOnly = (t) => {
|
|
9099
|
+
const x = this.expand(t);
|
|
9100
|
+
if (x.kind === "union") return x.types.every(listOnly);
|
|
9101
|
+
return x.kind === "array" || x.kind === "tuple";
|
|
9102
|
+
};
|
|
9103
|
+
if (names.some((name) => name !== void 0) && listOnly(withoutNil(this.expand(object)))) {
|
|
9104
|
+
message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
|
|
9105
|
+
} else if (others.length && this.cannotIndex(object, others)) {
|
|
8959
9106
|
message = `Type '${briefType(index)}' cannot be used to index type '${shown}'`;
|
|
8960
9107
|
} else if (!others.length && names.every((name) => this.memberMissing(object, name))) {
|
|
8961
9108
|
message = keys.length === 1 ? `Property '${names[0]}' does not exist on type '${shown}'` : `None of ${briefType(index)} is a property of type '${shown}'`;
|
|
@@ -9190,10 +9337,12 @@ var TypeAnalyzer = class {
|
|
|
9190
9337
|
return this.inferArray(expr, env, false);
|
|
9191
9338
|
case "TableExpression":
|
|
9192
9339
|
return this.inferObject(expr, env, false);
|
|
9193
|
-
case "FunctionExpression":
|
|
9340
|
+
case "FunctionExpression": {
|
|
9194
9341
|
this.checkParamOrder(expr.func.params, expr);
|
|
9195
|
-
|
|
9196
|
-
|
|
9342
|
+
const returned = [];
|
|
9343
|
+
this.visitFunctionBody(expr.func, env, returned);
|
|
9344
|
+
return this.inferFunctionBody(expr.func, env, returned);
|
|
9345
|
+
}
|
|
9197
9346
|
case "ParenthesizedExpression":
|
|
9198
9347
|
return this.infer(expr.expression, env);
|
|
9199
9348
|
case "TypeAssertionExpression": {
|
|
@@ -9227,10 +9376,18 @@ var TypeAnalyzer = class {
|
|
|
9227
9376
|
switch (expr.operator) {
|
|
9228
9377
|
case "not":
|
|
9229
9378
|
return booleanType;
|
|
9230
|
-
case "-":
|
|
9231
|
-
|
|
9232
|
-
|
|
9233
|
-
|
|
9379
|
+
case "-": {
|
|
9380
|
+
const answered = this.operatorResult(expr, "-", arg, void 0);
|
|
9381
|
+
if (answered) return answered;
|
|
9382
|
+
this.checkOperands(expr, "-", arg);
|
|
9383
|
+
return this.arithmeticOn([arg]);
|
|
9384
|
+
}
|
|
9385
|
+
case "#": {
|
|
9386
|
+
const answered = this.operatorResult(expr, "#", arg, void 0);
|
|
9387
|
+
if (answered) return answered;
|
|
9388
|
+
this.checkOperands(expr, "#", arg);
|
|
9389
|
+
return numberType;
|
|
9390
|
+
}
|
|
9234
9391
|
}
|
|
9235
9392
|
return arg;
|
|
9236
9393
|
}
|
|
@@ -9259,16 +9416,21 @@ var TypeAnalyzer = class {
|
|
|
9259
9416
|
if (unwrapParens(expr.left).type === "StringLiteral") this.expectedTypeOf.set(unwrapParens(expr.left), r);
|
|
9260
9417
|
}
|
|
9261
9418
|
switch (op) {
|
|
9262
|
-
case "..":
|
|
9263
|
-
|
|
9419
|
+
case "..": {
|
|
9420
|
+
const answered = this.operatorResult(expr, op, l, r);
|
|
9421
|
+
if (answered) return answered;
|
|
9422
|
+
this.checkOperands(expr, op, l, r);
|
|
9423
|
+
return stringType;
|
|
9424
|
+
}
|
|
9264
9425
|
case "<":
|
|
9265
9426
|
case ">":
|
|
9266
9427
|
case "<=":
|
|
9267
9428
|
case ">=":
|
|
9268
|
-
this.checkComparison(expr, op, l, r);
|
|
9429
|
+
if (!this.checkComparison(expr, op, l, r)) this.checkOperands(expr, op, l, r);
|
|
9269
9430
|
return booleanType;
|
|
9270
9431
|
case "==":
|
|
9271
9432
|
case "~=":
|
|
9433
|
+
this.checkOverlap(expr, op, l, r);
|
|
9272
9434
|
return booleanType;
|
|
9273
9435
|
case "+":
|
|
9274
9436
|
case "-":
|
|
@@ -9276,8 +9438,12 @@ var TypeAnalyzer = class {
|
|
|
9276
9438
|
case "/":
|
|
9277
9439
|
case "//":
|
|
9278
9440
|
case "%":
|
|
9279
|
-
case "^":
|
|
9280
|
-
|
|
9441
|
+
case "^": {
|
|
9442
|
+
const answered = this.operatorResult(expr, op, l, r);
|
|
9443
|
+
if (answered) return answered;
|
|
9444
|
+
this.checkOperands(expr, op, l, r);
|
|
9445
|
+
return this.arithmeticOn([l, r]);
|
|
9446
|
+
}
|
|
9281
9447
|
}
|
|
9282
9448
|
return union([l, r]);
|
|
9283
9449
|
}
|
|
@@ -9380,9 +9546,7 @@ var TypeAnalyzer = class {
|
|
|
9380
9546
|
const united = this.unionSignatures(callee);
|
|
9381
9547
|
const fns = united ?? this.overloadsOf(callee);
|
|
9382
9548
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9383
|
-
const
|
|
9384
|
-
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
9385
|
-
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
9549
|
+
const argTypes = this.inferArguments(expr.arguments, fns, () => 0, (_, args) => args, env);
|
|
9386
9550
|
if (fns.length) {
|
|
9387
9551
|
this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
|
|
9388
9552
|
const spread = this.spreadOf(expr.arguments);
|
|
@@ -9447,9 +9611,13 @@ var TypeAnalyzer = class {
|
|
|
9447
9611
|
const united = this.unionSignatures(method);
|
|
9448
9612
|
const fns = united ?? this.overloadsOf(method);
|
|
9449
9613
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9450
|
-
const
|
|
9451
|
-
|
|
9452
|
-
|
|
9614
|
+
const argTypes = this.inferArguments(
|
|
9615
|
+
expr.arguments,
|
|
9616
|
+
fns,
|
|
9617
|
+
(f) => this.takesSelf(f) ? 1 : 0,
|
|
9618
|
+
(f, args) => this.takesSelf(f) ? [objType, ...args] : args,
|
|
9619
|
+
env
|
|
9620
|
+
);
|
|
9453
9621
|
if (fns.length) {
|
|
9454
9622
|
const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
|
|
9455
9623
|
const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
|
|
@@ -9525,6 +9693,14 @@ var TypeAnalyzer = class {
|
|
|
9525
9693
|
name = target.property.name;
|
|
9526
9694
|
object = target.object;
|
|
9527
9695
|
} else if (target.type === "IndexExpression") {
|
|
9696
|
+
const list = this.expand(this.infer(target.object, env));
|
|
9697
|
+
if ((list.kind === "array" || list.kind === "tuple") && list.readonly) {
|
|
9698
|
+
this.diagnostics.push({
|
|
9699
|
+
node: target,
|
|
9700
|
+
message: `Cannot assign to an element of '${formatType(list)}': it is read-only`
|
|
9701
|
+
});
|
|
9702
|
+
return;
|
|
9703
|
+
}
|
|
9528
9704
|
const index = this.expand(this.infer(target.index, env));
|
|
9529
9705
|
if (index.kind !== "literal" || typeof index.value !== "string") return;
|
|
9530
9706
|
name = index.value;
|
|
@@ -9697,9 +9873,27 @@ var TypeAnalyzer = class {
|
|
|
9697
9873
|
if (s.indexer) indexer = mergeIndexer(indexer, s.indexer);
|
|
9698
9874
|
}
|
|
9699
9875
|
}
|
|
9876
|
+
this.checkDuplicateKey(expr, field, entries);
|
|
9700
9877
|
}
|
|
9701
9878
|
return objectType(entries, indexer, asConst || void 0);
|
|
9702
9879
|
}
|
|
9880
|
+
/** `{ a: 1, a: 2 }` — the second wins and the first was written for
|
|
9881
|
+
* nothing, which is a typo far more often than a decision. A spread is
|
|
9882
|
+
* not one: overriding what it brought is exactly what it is for. */
|
|
9883
|
+
checkDuplicateKey(expr, field, entries) {
|
|
9884
|
+
if (!this.emitDiagnostics || this.duplicateKeyReported.has(field)) return;
|
|
9885
|
+
const written = field.type === "TableFieldNamed" ? field.key.type === "Identifier" ? field.key.name : field.key.value : field.type === "TableFieldShorthand" ? field.name.name : void 0;
|
|
9886
|
+
if (written === void 0) return;
|
|
9887
|
+
const spreadBefore = expr.fields.slice(0, expr.fields.indexOf(field)).some((f) => f.type === "TableFieldSpread");
|
|
9888
|
+
if (spreadBefore) return;
|
|
9889
|
+
if (entries.filter(([k]) => k === written).length < 2) return;
|
|
9890
|
+
this.duplicateKeyReported.add(field);
|
|
9891
|
+
this.diagnostics.push({
|
|
9892
|
+
node: field.type === "TableFieldNamed" ? field.key : field.name,
|
|
9893
|
+
message: `'${written}' is given twice in this table; only the last one is kept`
|
|
9894
|
+
});
|
|
9895
|
+
}
|
|
9896
|
+
duplicateKeyReported = /* @__PURE__ */ new WeakSet();
|
|
9703
9897
|
/** A value inferred `as const`, widened back wherever `context` does not
|
|
9704
9898
|
* ask for a literal: `satisfies`' result type. A property keeps `"circle"`
|
|
9705
9899
|
* when the contract's property admits string literals, and becomes
|
|
@@ -10192,27 +10386,84 @@ var TypeAnalyzer = class {
|
|
|
10192
10386
|
arithmeticOn(operands) {
|
|
10193
10387
|
return operands.some((t) => this.expand(t).kind === "any") ? anyType : numberType;
|
|
10194
10388
|
}
|
|
10389
|
+
/** `a == b` where nothing `a` can hold is anything `b` can: the answer is
|
|
10390
|
+
* always the same, so the comparison was not the one meant. Only types
|
|
10391
|
+
* that say what they hold take part — `any`, `unknown` and a type
|
|
10392
|
+
* parameter say nothing, and a table compares by identity, so two of them
|
|
10393
|
+
* may well be the same table. */
|
|
10394
|
+
checkOverlap(node, op, l, r) {
|
|
10395
|
+
if (!this.emitDiagnostics) return;
|
|
10396
|
+
const left = this.expand(l);
|
|
10397
|
+
const right = this.expand(r);
|
|
10398
|
+
const judged = (t) => {
|
|
10399
|
+
const parts = t.kind === "union" ? t.types.map((m) => this.expand(m)) : [t];
|
|
10400
|
+
return parts.every((p) => p.kind === "literal" || p.kind === "primitive");
|
|
10401
|
+
};
|
|
10402
|
+
const isNil = (t) => t.kind === "primitive" && t.name === "nil";
|
|
10403
|
+
if (isNil(left) || isNil(right)) return;
|
|
10404
|
+
if (!judged(left) || !judged(right)) return;
|
|
10405
|
+
if (isAssignable(left, right) || isAssignable(right, left)) return;
|
|
10406
|
+
this.diagnostics.push({
|
|
10407
|
+
node,
|
|
10408
|
+
message: `This comparison is always ${op === "==" ? "false" : "true"}: '${formatType(l)}' and '${formatType(r)}' have no value in common`
|
|
10409
|
+
});
|
|
10410
|
+
}
|
|
10411
|
+
/** What an operator takes, where no metamethod answered for it. Luau
|
|
10412
|
+
* raises on the rest — `n * 2` with `n` nil is an error at the line it
|
|
10413
|
+
* runs, and a nil `n` is exactly what `number | nil` says may happen — so
|
|
10414
|
+
* it is said here instead. `any` passes: nothing is known about it. */
|
|
10415
|
+
checkOperands(node, op, left, right) {
|
|
10416
|
+
if (!this.emitDiagnostics) return;
|
|
10417
|
+
const fits = (t) => {
|
|
10418
|
+
const x = this.expand(t);
|
|
10419
|
+
if (x.kind === "any" || x.kind === "never") return true;
|
|
10420
|
+
switch (op) {
|
|
10421
|
+
// `..` joins strings, and Luau writes a number into one.
|
|
10422
|
+
case "..":
|
|
10423
|
+
return isAssignable(x, stringType) || isAssignable(x, numberType);
|
|
10424
|
+
// `#` counts a string's bytes or a table's entries.
|
|
10425
|
+
case "#":
|
|
10426
|
+
return x.kind === "array" || x.kind === "tuple" || x.kind === "object" || isAssignable(x, stringType);
|
|
10427
|
+
default:
|
|
10428
|
+
return isAssignable(x, numberType);
|
|
10429
|
+
}
|
|
10430
|
+
};
|
|
10431
|
+
if (op === "<" || op === ">" || op === "<=" || op === ">=") {
|
|
10432
|
+
const both = (t) => this.expand(t);
|
|
10433
|
+
const [l, r] = [both(left), both(right)];
|
|
10434
|
+
const loose = (t) => t.kind === "any" || t.kind === "never";
|
|
10435
|
+
const alike = (as) => isAssignable(l, as) && isAssignable(r, as);
|
|
10436
|
+
if (loose(l) || loose(r) || alike(numberType) || alike(stringType)) return;
|
|
10437
|
+
} else if (fits(left) && (right === void 0 || fits(right))) {
|
|
10438
|
+
return;
|
|
10439
|
+
}
|
|
10440
|
+
this.diagnostics.push({
|
|
10441
|
+
node,
|
|
10442
|
+
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)}'`
|
|
10443
|
+
});
|
|
10444
|
+
}
|
|
10195
10445
|
/** `a < b` between class instances: Luau asks `__lt` (`__le` for `<=`),
|
|
10196
10446
|
* with the operands swapped for `>` and `>=`, and an instance without
|
|
10197
10447
|
* one cannot be compared at all. */
|
|
10198
10448
|
checkComparison(node, op, l, r) {
|
|
10199
|
-
if (!this.emitDiagnostics) return;
|
|
10449
|
+
if (!this.emitDiagnostics) return false;
|
|
10200
10450
|
const left = this.expand(l);
|
|
10201
10451
|
const right = this.expand(r);
|
|
10202
10452
|
const instance = (t) => t.kind === "object" && t.class !== void 0;
|
|
10203
|
-
if (!instance(left) && !instance(right)) return;
|
|
10453
|
+
if (!instance(left) && !instance(right)) return false;
|
|
10204
10454
|
const name = op === "<" || op === ">" ? "__lt" : "__le";
|
|
10205
10455
|
const operands = op === ">" || op === ">=" ? [right, left] : [left, right];
|
|
10206
10456
|
for (const receiver of operands) {
|
|
10207
10457
|
const method = receiver.kind === "object" ? receiver.properties.get(name) : void 0;
|
|
10208
10458
|
if (!method) continue;
|
|
10209
|
-
if (this.pickOverload(this.overloadsOf(method.type), operands)) return;
|
|
10459
|
+
if (this.pickOverload(this.overloadsOf(method.type), operands)) return true;
|
|
10210
10460
|
break;
|
|
10211
10461
|
}
|
|
10212
10462
|
this.diagnostics.push({
|
|
10213
10463
|
node,
|
|
10214
10464
|
message: `Operator '${op}' cannot be applied to types '${formatType(l)}' and '${formatType(r)}'`
|
|
10215
10465
|
});
|
|
10466
|
+
return true;
|
|
10216
10467
|
}
|
|
10217
10468
|
/** What an operator on a value with metamethods gives: `a + b` calls
|
|
10218
10469
|
* `__add` on `a`, or failing that on `b` with the operands swapped — the
|
|
@@ -10499,7 +10750,56 @@ var TypeAnalyzer = class {
|
|
|
10499
10750
|
return this.bindingByDecl.get(node) ?? this.bindingByPos.get(posKey(name, node.line.start, node.column.start));
|
|
10500
10751
|
}
|
|
10501
10752
|
};
|
|
10502
|
-
|
|
10753
|
+
var libraryNames = /* @__PURE__ */ new WeakMap();
|
|
10754
|
+
function namesDeclaredIn(program) {
|
|
10755
|
+
const known = libraryNames.get(program);
|
|
10756
|
+
if (known) return known;
|
|
10757
|
+
const names = /* @__PURE__ */ new Set();
|
|
10758
|
+
for (const stmt of program.body.statements) {
|
|
10759
|
+
const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
|
|
10760
|
+
if (declaration.type === "TypeAliasStatement") names.add(declaration.name.name);
|
|
10761
|
+
else if (declaration.type === "ExportTypeAliasStatement") names.add(declaration.alias.name.name);
|
|
10762
|
+
else if (declaration.type === "DeclareClassStatement") names.add(declaration.name.name);
|
|
10763
|
+
else if (declaration.type === "ClassDeclaration" && declaration.name) names.add(declaration.name.name);
|
|
10764
|
+
else if (stmt.type === "ImportStatement") {
|
|
10765
|
+
for (const s of stmt.specifiers) names.add(s.local.name);
|
|
10766
|
+
if (stmt.defaultImport) names.add(stmt.defaultImport.name);
|
|
10767
|
+
if (stmt.namespaceImport) names.add(stmt.namespaceImport.name);
|
|
10768
|
+
}
|
|
10769
|
+
}
|
|
10770
|
+
libraryNames.set(program, names);
|
|
10771
|
+
return names;
|
|
10772
|
+
}
|
|
10773
|
+
var sharedClassesByLib = /* @__PURE__ */ new WeakMap();
|
|
10774
|
+
function shareableClasses(libs, program) {
|
|
10775
|
+
if (!libs.length) return void 0;
|
|
10776
|
+
const mine = namesDeclaredIn(program);
|
|
10777
|
+
if (mine.size) {
|
|
10778
|
+
for (const lib of libs) {
|
|
10779
|
+
for (const name of namesDeclaredIn(lib)) if (mine.has(name)) return void 0;
|
|
10780
|
+
}
|
|
10781
|
+
}
|
|
10782
|
+
const existing = sharedClassesByLib.get(libs[0]);
|
|
10783
|
+
if (existing) {
|
|
10784
|
+
const same = existing.libs.length === libs.length && existing.libs.every((lib, i) => lib === libs[i]);
|
|
10785
|
+
if (same) return existing;
|
|
10786
|
+
return void 0;
|
|
10787
|
+
}
|
|
10788
|
+
const store = { libs: [...libs], classTypes: /* @__PURE__ */ new WeakMap(), classMembers: /* @__PURE__ */ new WeakMap() };
|
|
10789
|
+
sharedClassesByLib.set(libs[0], store);
|
|
10790
|
+
return store;
|
|
10791
|
+
}
|
|
10792
|
+
var referencedNamesOf = /* @__PURE__ */ new WeakMap();
|
|
10793
|
+
var typeQueryIn = /* @__PURE__ */ new WeakMap();
|
|
10794
|
+
function referencedTypeNames(node, out) {
|
|
10795
|
+
if (out === void 0 && node && typeof node === "object") {
|
|
10796
|
+
const known = referencedNamesOf.get(node);
|
|
10797
|
+
if (known) return known;
|
|
10798
|
+
const found = referencedTypeNames(node, []);
|
|
10799
|
+
referencedNamesOf.set(node, found);
|
|
10800
|
+
return found;
|
|
10801
|
+
}
|
|
10802
|
+
out = out ?? [];
|
|
10503
10803
|
if (!node || typeof node !== "object") return out;
|
|
10504
10804
|
if (Array.isArray(node)) {
|
|
10505
10805
|
for (const item of node) referencedTypeNames(item, out);
|
|
@@ -10516,9 +10816,11 @@ function referencedTypeNames(node, out = []) {
|
|
|
10516
10816
|
}
|
|
10517
10817
|
function containsTypeQuery(node) {
|
|
10518
10818
|
if (!node || typeof node !== "object") return false;
|
|
10519
|
-
|
|
10520
|
-
if (
|
|
10521
|
-
|
|
10819
|
+
const known = typeQueryIn.get(node);
|
|
10820
|
+
if (known !== void 0) return known;
|
|
10821
|
+
const found = Array.isArray(node) ? node.some(containsTypeQuery) : node.type === "TypeofTypeNode" || Object.values(node).some(containsTypeQuery);
|
|
10822
|
+
typeQueryIn.set(node, found);
|
|
10823
|
+
return found;
|
|
10522
10824
|
}
|
|
10523
10825
|
var STRING_INTRINSICS = /* @__PURE__ */ new Set(["Uppercase", "Lowercase", "Capitalize", "Uncapitalize"]);
|
|
10524
10826
|
function briefType(t) {
|