@tilua/parser 2.2.0 → 2.3.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 +11 -3
- package/dist/index.cjs +193 -189
- package/dist/index.d.cts +12 -10
- package/dist/index.d.ts +12 -10
- package/dist/index.js +193 -189
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -593,9 +593,17 @@ An array is a table, at runtime as much as in the types: `return [a, b]` is
|
|
|
593
593
|
a Roblox method such as `FindPartOnRay` — the type library puts a function of
|
|
594
594
|
its own in place of that global or method, which answers one value, as its
|
|
595
595
|
type says. A tuple may end in a rest,
|
|
596
|
-
`[string, ...number[]]`: that many, then any number more.
|
|
597
|
-
|
|
598
|
-
and
|
|
596
|
+
`[string, ...number[]]`: that many, then any number more.
|
|
597
|
+
|
|
598
|
+
A declaration and an assignment take one value, and a call is one value
|
|
599
|
+
wherever it is written — an argument, a `return`. Luau's `local a, b = 1, 2`
|
|
600
|
+
is an error that says to write `const [a, b] = [1, 2]`. Where a Luau function
|
|
601
|
+
answers several values and nothing gathers them, ask for all of them by making
|
|
602
|
+
the call an array's last element:
|
|
603
|
+
|
|
604
|
+
```luau
|
|
605
|
+
const [a, b] = [require(module)] -- { require(module) }: every value
|
|
606
|
+
```
|
|
599
607
|
|
|
600
608
|
**A loop takes one value too** — `for (const item in source)`, and several are
|
|
601
609
|
an array the pattern takes apart:
|
package/dist/index.cjs
CHANGED
|
@@ -969,10 +969,10 @@ var Parser = class {
|
|
|
969
969
|
expressionOr(stop) {
|
|
970
970
|
return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
|
|
971
971
|
}
|
|
972
|
-
/** A comma-separated list of values
|
|
973
|
-
*
|
|
974
|
-
*
|
|
975
|
-
*
|
|
972
|
+
/** A comma-separated list of values where one belongs — a `return`'s, a
|
|
973
|
+
* declaration's, an assignment's — read whole, so that writing several
|
|
974
|
+
* is reported once and the rest still means something. A spread belongs
|
|
975
|
+
* in a call's arguments or an array, and is reported here too. */
|
|
976
976
|
expressionListOr(stop) {
|
|
977
977
|
const until = () => stop() || this.checkPunctuator(",");
|
|
978
978
|
const item = () => {
|
|
@@ -1564,7 +1564,7 @@ var Parser = class {
|
|
|
1564
1564
|
}
|
|
1565
1565
|
this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
|
|
1566
1566
|
}
|
|
1567
|
-
// `const x = ...` / `let
|
|
1567
|
+
// `const x = ...` / `let [a, b] = ...`.
|
|
1568
1568
|
// tilua has no `local` — `const` bindings are immutable, `let` mutable.
|
|
1569
1569
|
/** `kind` reads the leading word as that keyword (recovery's `local`). */
|
|
1570
1570
|
parseVariableDeclaration(as) {
|
|
@@ -1578,14 +1578,34 @@ var Parser = class {
|
|
|
1578
1578
|
while (this.matchPunctuator(",")) {
|
|
1579
1579
|
names.push(this.parseBindingTarget(true));
|
|
1580
1580
|
}
|
|
1581
|
-
let
|
|
1581
|
+
let values = [];
|
|
1582
1582
|
if (this.matchOperator("=")) {
|
|
1583
|
-
|
|
1583
|
+
values = this.expressionListOr(() => false);
|
|
1584
1584
|
} else if (kind === "const") {
|
|
1585
1585
|
if (!this.recover) this.error("'const' declaration requires an initializer");
|
|
1586
1586
|
this.softError("'const' declaration requires an initializer");
|
|
1587
1587
|
}
|
|
1588
|
-
|
|
1588
|
+
let name = names[0];
|
|
1589
|
+
let init = values[0];
|
|
1590
|
+
if (names.length > 1 || values.length > 1) {
|
|
1591
|
+
this.problemAt(start, `A declaration takes one value: take several from an array, '${kind} [a, b] = [x, y]'`);
|
|
1592
|
+
if (names.length > 1) name = this.namesAsPattern(names);
|
|
1593
|
+
if (values.length > 1) init = this.valuesAsArray(values);
|
|
1594
|
+
}
|
|
1595
|
+
return { type: "VariableDeclaration", kind, name, init, ...spanFrom(start, this.previous()) };
|
|
1596
|
+
}
|
|
1597
|
+
/** `a, b` written where one name goes, read as `[a, b]`. */
|
|
1598
|
+
namesAsPattern(names) {
|
|
1599
|
+
const elements = names.map((value) => ({
|
|
1600
|
+
type: "ArrayPatternElement",
|
|
1601
|
+
value,
|
|
1602
|
+
...spanFrom(value, value)
|
|
1603
|
+
}));
|
|
1604
|
+
return { type: "ArrayPattern", elements, ...spanFrom(names[0], names[names.length - 1]) };
|
|
1605
|
+
}
|
|
1606
|
+
/** `x, y` written where one value goes, read as `[x, y]`. */
|
|
1607
|
+
valuesAsArray(values) {
|
|
1608
|
+
return { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
|
|
1589
1609
|
}
|
|
1590
1610
|
parseIfStatement() {
|
|
1591
1611
|
const start = this.current();
|
|
@@ -2066,7 +2086,7 @@ var Parser = class {
|
|
|
2066
2086
|
argument = values[0];
|
|
2067
2087
|
if (values.length > 1) {
|
|
2068
2088
|
this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
|
|
2069
|
-
argument =
|
|
2089
|
+
argument = this.valuesAsArray(values);
|
|
2070
2090
|
}
|
|
2071
2091
|
}
|
|
2072
2092
|
return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
|
|
@@ -2092,28 +2112,15 @@ var Parser = class {
|
|
|
2092
2112
|
parseExpressionStatement() {
|
|
2093
2113
|
const start = this.current();
|
|
2094
2114
|
if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
|
|
2095
|
-
const
|
|
2096
|
-
|
|
2097
|
-
];
|
|
2098
|
-
while (this.matchPunctuator(",")) {
|
|
2099
|
-
targets.push(this.parseAssignTarget());
|
|
2100
|
-
}
|
|
2101
|
-
this.expectOperator("=");
|
|
2102
|
-
const values = this.expressionListOr(() => false);
|
|
2103
|
-
return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
|
|
2115
|
+
const target = this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern();
|
|
2116
|
+
return this.parseAssignmentRest(start, target);
|
|
2104
2117
|
}
|
|
2105
2118
|
const statementStart = this.cursor;
|
|
2106
2119
|
const errorsAtStart = this.errors.length;
|
|
2107
2120
|
const first = this.parsePrefixExpression();
|
|
2108
2121
|
if (this.checkOperator("=") || this.checkPunctuator(",")) {
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
targets.push(this.parseAssignTarget());
|
|
2112
|
-
}
|
|
2113
|
-
for (const target of targets) this.rejectOptionalTarget(target);
|
|
2114
|
-
this.expectOperator("=");
|
|
2115
|
-
const values = this.expressionListOr(() => false);
|
|
2116
|
-
return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
|
|
2122
|
+
this.rejectOptionalTarget(first);
|
|
2123
|
+
return this.parseAssignmentRest(start, first);
|
|
2117
2124
|
}
|
|
2118
2125
|
const t = this.current();
|
|
2119
2126
|
if (t.type === "Operator" && COMPOUND_ASSIGN_OPS.has(t.value)) {
|
|
@@ -2139,6 +2146,21 @@ var Parser = class {
|
|
|
2139
2146
|
}
|
|
2140
2147
|
return { type: "ExpressionStatement", expression, ...spanFrom(start, this.previous()) };
|
|
2141
2148
|
}
|
|
2149
|
+
/** From after an assignment's target: `= value`. Luau's `a, b = x, y`
|
|
2150
|
+
* is reported, and read as its first target taking its first value. */
|
|
2151
|
+
parseAssignmentRest(start, target) {
|
|
2152
|
+
let several = false;
|
|
2153
|
+
while (this.matchPunctuator(",")) {
|
|
2154
|
+
this.parseAssignTarget();
|
|
2155
|
+
several = true;
|
|
2156
|
+
}
|
|
2157
|
+
this.expectOperator("=");
|
|
2158
|
+
const values = this.expressionListOr(() => false);
|
|
2159
|
+
if (several || values.length > 1) {
|
|
2160
|
+
this.problemAt(start, "An assignment takes one value: take several from an array, '[a, b] = [x, y]'");
|
|
2161
|
+
}
|
|
2162
|
+
return { type: "AssignmentStatement", target, value: values[0], ...spanFrom(start, this.previous()) };
|
|
2163
|
+
}
|
|
2142
2164
|
// ============================================================
|
|
2143
2165
|
// Expressions
|
|
2144
2166
|
// ============================================================
|
|
@@ -2622,8 +2644,8 @@ var Parser = class {
|
|
|
2622
2644
|
startsSuperUse() {
|
|
2623
2645
|
return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
|
|
2624
2646
|
}
|
|
2625
|
-
/** After `...`, is there something to spread?
|
|
2626
|
-
*
|
|
2647
|
+
/** After `...`, is there something to spread? tilua has no bare `...`:
|
|
2648
|
+
* a varying number of arguments is a rest parameter, `...args: T[]`. */
|
|
2627
2649
|
startsSpread() {
|
|
2628
2650
|
const next = this.peek(1);
|
|
2629
2651
|
if (next.type === "Punctuator") {
|
|
@@ -2642,8 +2664,8 @@ var Parser = class {
|
|
|
2642
2664
|
const token = this.peek(ahead);
|
|
2643
2665
|
return token.type === "Punctuator" && token.value === value;
|
|
2644
2666
|
}
|
|
2645
|
-
/** An assignment target after the first
|
|
2646
|
-
*
|
|
2667
|
+
/** An assignment target after the first — Luau's `a, b = ...`, read only
|
|
2668
|
+
* to be reported: a prefix expression or a destructuring pattern. */
|
|
2647
2669
|
parseAssignTarget() {
|
|
2648
2670
|
if (this.checkPunctuator("{")) return this.parseObjectPattern();
|
|
2649
2671
|
if (this.checkPunctuator("[")) return this.parseArrayPattern();
|
|
@@ -3134,7 +3156,7 @@ var Parser = class {
|
|
|
3134
3156
|
};
|
|
3135
3157
|
})() : this.parseFunctionHead();
|
|
3136
3158
|
this.expectPunctuator("=>");
|
|
3137
|
-
const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.
|
|
3159
|
+
const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.parseArrowExpressionBody();
|
|
3138
3160
|
const func = {
|
|
3139
3161
|
type: "FunctionBody",
|
|
3140
3162
|
generics: head.generics,
|
|
@@ -3147,6 +3169,29 @@ var Parser = class {
|
|
|
3147
3169
|
};
|
|
3148
3170
|
return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
|
|
3149
3171
|
}
|
|
3172
|
+
/** `() => value` returns the value. `() => a = 1` and `() => n += 1` are
|
|
3173
|
+
* an assignment, which is a statement: the body is that
|
|
3174
|
+
* statement, and the function returns nothing. */
|
|
3175
|
+
parseArrowExpressionBody() {
|
|
3176
|
+
const start = this.cursor;
|
|
3177
|
+
const errors = this.errors.length;
|
|
3178
|
+
const expression = this.parseExpression(0);
|
|
3179
|
+
const t = this.current();
|
|
3180
|
+
const assigns = t.type === "Operator" && (t.value === "=" || COMPOUND_ASSIGN_OPS.has(t.value));
|
|
3181
|
+
if (!assigns) return this.returnOf(expression);
|
|
3182
|
+
this.cursor = start;
|
|
3183
|
+
this.errors.length = errors;
|
|
3184
|
+
const first = this.current();
|
|
3185
|
+
const target = this.parsePrefixExpression();
|
|
3186
|
+
this.rejectOptionalTarget(target);
|
|
3187
|
+
const opToken = this.current();
|
|
3188
|
+
const op = opToken.value;
|
|
3189
|
+
if (opToken.type !== "Operator" || op !== "=" && !COMPOUND_ASSIGN_OPS.has(op)) this.error("Expected '='");
|
|
3190
|
+
this.advance();
|
|
3191
|
+
const value = this.expressionOr(() => false);
|
|
3192
|
+
const statement = op === "=" ? { type: "AssignmentStatement", target, value, ...spanFrom(first, this.previous()) } : { type: "CompoundAssignmentStatement", operator: op, target, value, ...spanFrom(first, this.previous()) };
|
|
3193
|
+
return { type: "Block", statements: [statement], ...spanFrom(statement, statement) };
|
|
3194
|
+
}
|
|
3150
3195
|
/** A one-expression body: the value is what the function returns. */
|
|
3151
3196
|
returnOf(expression) {
|
|
3152
3197
|
const statement = {
|
|
@@ -4078,10 +4123,9 @@ var Analyzer = class {
|
|
|
4078
4123
|
visitStatement(stmt, scope) {
|
|
4079
4124
|
switch (stmt.type) {
|
|
4080
4125
|
case "VariableDeclaration": {
|
|
4081
|
-
|
|
4082
|
-
|
|
4083
|
-
|
|
4084
|
-
for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
|
|
4126
|
+
if (stmt.init) this.visitExpression(stmt.init, scope);
|
|
4127
|
+
this.visitType(stmt.name.typeAnnotation, scope);
|
|
4128
|
+
this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
|
|
4085
4129
|
return;
|
|
4086
4130
|
}
|
|
4087
4131
|
case "FunctionDeclaration": {
|
|
@@ -4115,16 +4159,15 @@ var Analyzer = class {
|
|
|
4115
4159
|
return;
|
|
4116
4160
|
}
|
|
4117
4161
|
case "AssignmentStatement": {
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
4122
|
-
|
|
4123
|
-
|
|
4124
|
-
|
|
4125
|
-
|
|
4126
|
-
|
|
4127
|
-
}
|
|
4162
|
+
this.visitExpression(stmt.value, scope);
|
|
4163
|
+
const target = stmt.target;
|
|
4164
|
+
if (target.type === "Identifier") {
|
|
4165
|
+
this.referenceAsAssignmentTarget(scope, target);
|
|
4166
|
+
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
4167
|
+
this.assignPattern(scope, target);
|
|
4168
|
+
} else {
|
|
4169
|
+
this.visitExpression(target, scope);
|
|
4170
|
+
this.checkModuleWrite(target);
|
|
4128
4171
|
}
|
|
4129
4172
|
return;
|
|
4130
4173
|
}
|
|
@@ -5448,7 +5491,7 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5448
5491
|
params: declaration.typeParams.map((g) => g.name)
|
|
5449
5492
|
});
|
|
5450
5493
|
}
|
|
5451
|
-
} else
|
|
5494
|
+
} else exportPattern(declaration.name);
|
|
5452
5495
|
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
5453
5496
|
const name = stmt.alias.name.name;
|
|
5454
5497
|
const type = types.aliases.get(name);
|
|
@@ -5687,12 +5730,10 @@ function assignedFields(block) {
|
|
|
5687
5730
|
const names = /* @__PURE__ */ new Set();
|
|
5688
5731
|
walkNodes(block, (node) => {
|
|
5689
5732
|
const record = node;
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
names.add(member.property.name);
|
|
5695
|
-
}
|
|
5733
|
+
if (record.type !== "AssignmentStatement" && record.type !== "CompoundAssignmentStatement") return;
|
|
5734
|
+
const member = record.target;
|
|
5735
|
+
if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
|
|
5736
|
+
names.add(member.property.name);
|
|
5696
5737
|
}
|
|
5697
5738
|
});
|
|
5698
5739
|
return names;
|
|
@@ -6168,12 +6209,10 @@ var TypeAnalyzer = class {
|
|
|
6168
6209
|
classDisplayNames = /* @__PURE__ */ new WeakMap();
|
|
6169
6210
|
/** `const Name = class ... end` — the name the class will be known by. */
|
|
6170
6211
|
nameClassExpressions(stmt) {
|
|
6171
|
-
|
|
6172
|
-
|
|
6173
|
-
|
|
6174
|
-
|
|
6175
|
-
}
|
|
6176
|
-
});
|
|
6212
|
+
const value = stmt.init;
|
|
6213
|
+
if (stmt.name.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
|
|
6214
|
+
this.classDisplayNames.set(value, stmt.name.name);
|
|
6215
|
+
}
|
|
6177
6216
|
}
|
|
6178
6217
|
classTypeParams(stmt) {
|
|
6179
6218
|
return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
|
|
@@ -7456,35 +7495,30 @@ var TypeAnalyzer = class {
|
|
|
7456
7495
|
switch (stmt.type) {
|
|
7457
7496
|
case "VariableDeclaration": {
|
|
7458
7497
|
this.nameClassExpressions(stmt);
|
|
7459
|
-
|
|
7460
|
-
|
|
7461
|
-
|
|
7462
|
-
|
|
7463
|
-
}
|
|
7464
|
-
const
|
|
7465
|
-
this.
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7475
|
-
});
|
|
7476
|
-
} else if (declared.kind !== "any") {
|
|
7477
|
-
this.reportExcessProperties(source, declared);
|
|
7478
|
-
}
|
|
7479
|
-
}
|
|
7480
|
-
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7481
|
-
this.bindPattern(target, inferred, env, mode);
|
|
7482
|
-
if (stmt.kind === "const") {
|
|
7483
|
-
this.correlateDestructuring(target, inferred, env);
|
|
7484
|
-
this.correlateIndexed(target, source, env);
|
|
7485
|
-
this.aliasReference(target, source);
|
|
7498
|
+
const target = stmt.name;
|
|
7499
|
+
const source = stmt.init;
|
|
7500
|
+
if (target.type === "IdentifierPattern" && target.typeAnnotation && source) {
|
|
7501
|
+
this.applyContext(source, this.resolveType(target.typeAnnotation));
|
|
7502
|
+
}
|
|
7503
|
+
const inferred = source ? this.infer(source, env) : nilType;
|
|
7504
|
+
if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
|
|
7505
|
+
const declared = this.resolveType(target.typeAnnotation);
|
|
7506
|
+
if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
|
|
7507
|
+
this.diagnostics.push({
|
|
7508
|
+
node: stmt,
|
|
7509
|
+
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7510
|
+
});
|
|
7511
|
+
} else if (declared.kind !== "any") {
|
|
7512
|
+
this.reportExcessProperties(source, declared);
|
|
7486
7513
|
}
|
|
7487
|
-
}
|
|
7514
|
+
}
|
|
7515
|
+
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7516
|
+
this.bindPattern(target, inferred, env, mode);
|
|
7517
|
+
if (stmt.kind === "const") {
|
|
7518
|
+
this.correlateDestructuring(target, inferred, env);
|
|
7519
|
+
this.correlateIndexed(target, source, env);
|
|
7520
|
+
this.aliasReference(target, source);
|
|
7521
|
+
}
|
|
7488
7522
|
return;
|
|
7489
7523
|
}
|
|
7490
7524
|
case "ClassDeclaration": {
|
|
@@ -7541,48 +7575,41 @@ var TypeAnalyzer = class {
|
|
|
7541
7575
|
return;
|
|
7542
7576
|
}
|
|
7543
7577
|
case "AssignmentStatement": {
|
|
7544
|
-
stmt.
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
|
|
7563
|
-
|
|
7564
|
-
|
|
7565
|
-
const declared = this.bindingType.get(id);
|
|
7566
|
-
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7567
|
-
this.diagnostics.push({
|
|
7568
|
-
node: stmt,
|
|
7569
|
-
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7570
|
-
});
|
|
7571
|
-
}
|
|
7572
|
-
this.setBinding(env, id, narrowTo(declared, next));
|
|
7573
|
-
} else {
|
|
7574
|
-
this.setBinding(env, id, next);
|
|
7575
|
-
this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
|
|
7578
|
+
const target = stmt.target;
|
|
7579
|
+
const source = stmt.value;
|
|
7580
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7581
|
+
this.applyContext(source, this.infer(target, env));
|
|
7582
|
+
} else if (target.type === "Identifier") {
|
|
7583
|
+
const id = this.bindingIdOf(target);
|
|
7584
|
+
if (id !== void 0 && this.annotated.has(id)) this.applyContext(source, this.bindingType.get(id));
|
|
7585
|
+
}
|
|
7586
|
+
const vt = this.infer(source, env);
|
|
7587
|
+
if (target.type === "Identifier") {
|
|
7588
|
+
const id = this.bindingIdOf(target);
|
|
7589
|
+
if (id !== void 0) {
|
|
7590
|
+
this.uncorrelate(id);
|
|
7591
|
+
const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
|
|
7592
|
+
if (this.annotated.has(id)) {
|
|
7593
|
+
const declared = this.bindingType.get(id);
|
|
7594
|
+
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7595
|
+
this.diagnostics.push({
|
|
7596
|
+
node: stmt,
|
|
7597
|
+
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7598
|
+
});
|
|
7576
7599
|
}
|
|
7600
|
+
this.setBinding(env, id, narrowTo(declared, next));
|
|
7601
|
+
} else {
|
|
7602
|
+
this.setBinding(env, id, next);
|
|
7603
|
+
this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
|
|
7577
7604
|
}
|
|
7578
|
-
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7579
|
-
this.infer(target, env);
|
|
7580
|
-
this.checkReadonlyAssign(target, env);
|
|
7581
|
-
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7582
|
-
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7583
|
-
this.reassignPattern(target, vt, env);
|
|
7584
7605
|
}
|
|
7585
|
-
})
|
|
7606
|
+
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7607
|
+
this.infer(target, env);
|
|
7608
|
+
this.checkReadonlyAssign(target, env);
|
|
7609
|
+
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7610
|
+
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7611
|
+
this.reassignPattern(target, vt, env);
|
|
7612
|
+
}
|
|
7586
7613
|
return;
|
|
7587
7614
|
}
|
|
7588
7615
|
case "CompoundAssignmentStatement":
|
|
@@ -7746,55 +7773,6 @@ var TypeAnalyzer = class {
|
|
|
7746
7773
|
return;
|
|
7747
7774
|
}
|
|
7748
7775
|
}
|
|
7749
|
-
/** Flatten an expression list into the values it actually produces.
|
|
7750
|
-
*
|
|
7751
|
-
* Lua's adjustment rule: every expression but the last contributes one
|
|
7752
|
-
* value, and the last contributes all of its values if it is a call or
|
|
7753
|
-
* `...`. That is what makes `local ok, err = pcall(f)` work, and it is
|
|
7754
|
-
* why `local x = f()` takes only the first value.
|
|
7755
|
-
*
|
|
7756
|
-
* `sources` maps each produced value back to the expression it came from
|
|
7757
|
-
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7758
|
-
* caller can still do contextual typing against the written expression. */
|
|
7759
|
-
valueList(exprs, env) {
|
|
7760
|
-
const types = [];
|
|
7761
|
-
const sources = [];
|
|
7762
|
-
exprs.forEach((e, i) => {
|
|
7763
|
-
const t = this.infer(e, env);
|
|
7764
|
-
const last = i === exprs.length - 1;
|
|
7765
|
-
if (e.type === "SpreadElement") {
|
|
7766
|
-
const held = this.typeOf.get(e.argument);
|
|
7767
|
-
const expanded = held && this.expand(held);
|
|
7768
|
-
if (expanded?.kind === "tuple") {
|
|
7769
|
-
for (const element of expanded.elements) {
|
|
7770
|
-
types.push(element);
|
|
7771
|
-
sources.push(e);
|
|
7772
|
-
}
|
|
7773
|
-
return;
|
|
7774
|
-
}
|
|
7775
|
-
types.push(t);
|
|
7776
|
-
sources.push(e);
|
|
7777
|
-
return;
|
|
7778
|
-
}
|
|
7779
|
-
void last;
|
|
7780
|
-
types.push(t);
|
|
7781
|
-
sources.push(e);
|
|
7782
|
-
});
|
|
7783
|
-
return { types, sources };
|
|
7784
|
-
}
|
|
7785
|
-
/** `const a, b = x, y` pairs names with values, one each. A call is one
|
|
7786
|
-
* value — several come back as an array — so `const a, b = f()` is a
|
|
7787
|
-
* name short of a value, and says how to take them apart. */
|
|
7788
|
-
checkValueCount(node, names, values, types) {
|
|
7789
|
-
if (!this.emitDiagnostics || !values.length || types.length >= names) return;
|
|
7790
|
-
const last = values[values.length - 1];
|
|
7791
|
-
const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
|
|
7792
|
-
const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
|
|
7793
|
-
this.diagnostics.push({
|
|
7794
|
-
node,
|
|
7795
|
-
message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
|
|
7796
|
-
});
|
|
7797
|
-
}
|
|
7798
7776
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7799
7777
|
* what it collected. */
|
|
7800
7778
|
withBreakScope(visit) {
|
|
@@ -9778,7 +9756,35 @@ var TypeAnalyzer = class {
|
|
|
9778
9756
|
}
|
|
9779
9757
|
const read = this.expand(type);
|
|
9780
9758
|
if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
|
|
9781
|
-
|
|
9759
|
+
const optionalNil = link.optional === true && this.mayBeNil(inChain ?? full);
|
|
9760
|
+
return { type, shortCircuits: inChain !== void 0 || optionalNil };
|
|
9761
|
+
}
|
|
9762
|
+
/** Whether a value of `raw` can be nil — `includesNil`, and also the types
|
|
9763
|
+
* that do not say: `any`, `unknown`, an unconstrained type parameter. */
|
|
9764
|
+
mayBeNil(raw) {
|
|
9765
|
+
const t = this.expand(raw);
|
|
9766
|
+
switch (t.kind) {
|
|
9767
|
+
case "primitive":
|
|
9768
|
+
return t.name === "nil";
|
|
9769
|
+
case "any":
|
|
9770
|
+
case "unknown":
|
|
9771
|
+
return true;
|
|
9772
|
+
case "typeParam":
|
|
9773
|
+
return t.constraint === void 0 || this.mayBeNil(t.constraint);
|
|
9774
|
+
case "union":
|
|
9775
|
+
return t.types.some((m) => this.mayBeNil(m));
|
|
9776
|
+
case "never":
|
|
9777
|
+
case "literal":
|
|
9778
|
+
case "array":
|
|
9779
|
+
case "tuple":
|
|
9780
|
+
case "object":
|
|
9781
|
+
case "function":
|
|
9782
|
+
case "intersection":
|
|
9783
|
+
case "templateLiteral":
|
|
9784
|
+
return false;
|
|
9785
|
+
default:
|
|
9786
|
+
return true;
|
|
9787
|
+
}
|
|
9782
9788
|
}
|
|
9783
9789
|
/** Objects already reported as possibly nil: a loop body is visited more
|
|
9784
9790
|
* than once. */
|
|
@@ -10700,13 +10706,12 @@ var TypeAnalyzer = class {
|
|
|
10700
10706
|
computingAhead = /* @__PURE__ */ new Set();
|
|
10701
10707
|
declaredAhead(id) {
|
|
10702
10708
|
this.aheadDeclarations ??= this.indexAheadDeclarations();
|
|
10703
|
-
const
|
|
10704
|
-
if (!
|
|
10709
|
+
const statement = this.aheadDeclarations.get(id);
|
|
10710
|
+
if (!statement || this.computingAhead.has(id)) return void 0;
|
|
10705
10711
|
this.computingAhead.add(id);
|
|
10706
10712
|
const wasEmitting = this.emitDiagnostics;
|
|
10707
10713
|
this.emitDiagnostics = false;
|
|
10708
10714
|
try {
|
|
10709
|
-
const { statement, index } = found;
|
|
10710
10715
|
let type;
|
|
10711
10716
|
if (statement.type === "DeclareStatement") {
|
|
10712
10717
|
type = this.resolveType(statement.valueType);
|
|
@@ -10715,11 +10720,11 @@ var TypeAnalyzer = class {
|
|
|
10715
10720
|
} else if (statement.type === "ClassDeclaration") {
|
|
10716
10721
|
type = this.classValueType(statement);
|
|
10717
10722
|
} else if (statement.type === "VariableDeclaration") {
|
|
10718
|
-
const target = statement.
|
|
10723
|
+
const target = statement.name;
|
|
10719
10724
|
if (target.type === "IdentifierPattern" && target.typeAnnotation) {
|
|
10720
10725
|
type = this.resolveType(target.typeAnnotation);
|
|
10721
|
-
} else if (statement.init
|
|
10722
|
-
const value = this.infer(statement.init
|
|
10726
|
+
} else if (statement.init) {
|
|
10727
|
+
const value = this.infer(statement.init, /* @__PURE__ */ new Map());
|
|
10723
10728
|
type = statement.kind === "const" ? value : widen(value);
|
|
10724
10729
|
}
|
|
10725
10730
|
}
|
|
@@ -10737,11 +10742,10 @@ var TypeAnalyzer = class {
|
|
|
10737
10742
|
for (const statement of this.program.body.statements) {
|
|
10738
10743
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
10739
10744
|
if (declaration.type !== "VariableDeclaration") continue;
|
|
10740
|
-
|
|
10741
|
-
|
|
10742
|
-
|
|
10743
|
-
|
|
10744
|
-
});
|
|
10745
|
+
const target = declaration.name;
|
|
10746
|
+
if (target.type !== "IdentifierPattern") continue;
|
|
10747
|
+
const id = this.bindingIdByName(target.name, target);
|
|
10748
|
+
if (id !== void 0) out.set(id, declaration);
|
|
10745
10749
|
}
|
|
10746
10750
|
const visit = (node) => {
|
|
10747
10751
|
if (!node || typeof node !== "object") return;
|
|
@@ -10752,11 +10756,11 @@ var TypeAnalyzer = class {
|
|
|
10752
10756
|
const record = node;
|
|
10753
10757
|
if (record.type === "ClassDeclaration" && record.name) {
|
|
10754
10758
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10755
|
-
if (id !== void 0) out.set(id,
|
|
10759
|
+
if (id !== void 0) out.set(id, node);
|
|
10756
10760
|
}
|
|
10757
10761
|
if (record.type === "FunctionDeclaration" && record.name) {
|
|
10758
10762
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10759
|
-
if (id !== void 0) out.set(id,
|
|
10763
|
+
if (id !== void 0) out.set(id, node);
|
|
10760
10764
|
}
|
|
10761
10765
|
for (const [key, value] of Object.entries(node)) {
|
|
10762
10766
|
if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
|
|
@@ -10765,7 +10769,7 @@ var TypeAnalyzer = class {
|
|
|
10765
10769
|
visit(this.program.body);
|
|
10766
10770
|
for (const [name, statement] of this.deferredDeclares) {
|
|
10767
10771
|
const id = this.scopes.globalsByName.get(name);
|
|
10768
|
-
if (id !== void 0) out.set(id,
|
|
10772
|
+
if (id !== void 0) out.set(id, statement);
|
|
10769
10773
|
}
|
|
10770
10774
|
return out;
|
|
10771
10775
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -242,14 +242,17 @@ interface DeclareClassStatement extends BaseNode {
|
|
|
242
242
|
interface ErrorStatement extends BaseNode {
|
|
243
243
|
type: "ErrorStatement";
|
|
244
244
|
}
|
|
245
|
-
/** `const x = 1` / `let
|
|
246
|
-
* (Luau's `local` is gone).
|
|
247
|
-
*
|
|
245
|
+
/** `const x = 1` / `let [a, b] = pair` — the only variable-binding form in
|
|
246
|
+
* tilua (Luau's `local` is gone). One name, or one pattern, and one value:
|
|
247
|
+
* several are taken from an array with a destructuring. `const` bindings are
|
|
248
|
+
* immutable and infer literal types (`const n = 1` → `1`); `let` bindings
|
|
249
|
+
* are mutable and widen. */
|
|
248
250
|
interface VariableDeclaration extends BaseNode {
|
|
249
251
|
type: "VariableDeclaration";
|
|
250
252
|
kind: "const" | "let";
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
name: BindingTarget;
|
|
254
|
+
/** Absent only on `let x`. */
|
|
255
|
+
init?: Expression;
|
|
253
256
|
}
|
|
254
257
|
type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern;
|
|
255
258
|
interface IdentifierPattern extends BaseNode {
|
|
@@ -448,10 +451,11 @@ interface ClassConstructor extends BaseNode {
|
|
|
448
451
|
interface SuperExpression extends BaseNode {
|
|
449
452
|
type: "SuperExpression";
|
|
450
453
|
}
|
|
454
|
+
/** `x = v` / `[a, b] = [b, a]` — one target, one value. */
|
|
451
455
|
interface AssignmentStatement extends BaseNode {
|
|
452
456
|
type: "AssignmentStatement";
|
|
453
|
-
|
|
454
|
-
|
|
457
|
+
target: Expression | ObjectPattern | ArrayPattern;
|
|
458
|
+
value: Expression;
|
|
455
459
|
}
|
|
456
460
|
interface CompoundAssignmentStatement extends BaseNode {
|
|
457
461
|
type: "CompoundAssignmentStatement";
|
|
@@ -672,9 +676,7 @@ interface ArrayExpression extends BaseNode {
|
|
|
672
676
|
* only produces one in those two places.
|
|
673
677
|
*
|
|
674
678
|
* Lua spreads with `table.unpack`, which only yields every value when it is
|
|
675
|
-
* written last; anywhere else the compiler builds the whole list first.
|
|
676
|
-
* `...` is unaffected — that is the vararg pack, and `f(...)` passes it on
|
|
677
|
-
* as it always did. */
|
|
679
|
+
* written last; anywhere else the compiler builds the whole list first. */
|
|
678
680
|
interface SpreadElement extends BaseNode {
|
|
679
681
|
type: "SpreadElement";
|
|
680
682
|
argument: Expression;
|
package/dist/index.d.ts
CHANGED
|
@@ -242,14 +242,17 @@ interface DeclareClassStatement extends BaseNode {
|
|
|
242
242
|
interface ErrorStatement extends BaseNode {
|
|
243
243
|
type: "ErrorStatement";
|
|
244
244
|
}
|
|
245
|
-
/** `const x = 1` / `let
|
|
246
|
-
* (Luau's `local` is gone).
|
|
247
|
-
*
|
|
245
|
+
/** `const x = 1` / `let [a, b] = pair` — the only variable-binding form in
|
|
246
|
+
* tilua (Luau's `local` is gone). One name, or one pattern, and one value:
|
|
247
|
+
* several are taken from an array with a destructuring. `const` bindings are
|
|
248
|
+
* immutable and infer literal types (`const n = 1` → `1`); `let` bindings
|
|
249
|
+
* are mutable and widen. */
|
|
248
250
|
interface VariableDeclaration extends BaseNode {
|
|
249
251
|
type: "VariableDeclaration";
|
|
250
252
|
kind: "const" | "let";
|
|
251
|
-
|
|
252
|
-
|
|
253
|
+
name: BindingTarget;
|
|
254
|
+
/** Absent only on `let x`. */
|
|
255
|
+
init?: Expression;
|
|
253
256
|
}
|
|
254
257
|
type BindingTarget = IdentifierPattern | ObjectPattern | ArrayPattern;
|
|
255
258
|
interface IdentifierPattern extends BaseNode {
|
|
@@ -448,10 +451,11 @@ interface ClassConstructor extends BaseNode {
|
|
|
448
451
|
interface SuperExpression extends BaseNode {
|
|
449
452
|
type: "SuperExpression";
|
|
450
453
|
}
|
|
454
|
+
/** `x = v` / `[a, b] = [b, a]` — one target, one value. */
|
|
451
455
|
interface AssignmentStatement extends BaseNode {
|
|
452
456
|
type: "AssignmentStatement";
|
|
453
|
-
|
|
454
|
-
|
|
457
|
+
target: Expression | ObjectPattern | ArrayPattern;
|
|
458
|
+
value: Expression;
|
|
455
459
|
}
|
|
456
460
|
interface CompoundAssignmentStatement extends BaseNode {
|
|
457
461
|
type: "CompoundAssignmentStatement";
|
|
@@ -672,9 +676,7 @@ interface ArrayExpression extends BaseNode {
|
|
|
672
676
|
* only produces one in those two places.
|
|
673
677
|
*
|
|
674
678
|
* Lua spreads with `table.unpack`, which only yields every value when it is
|
|
675
|
-
* written last; anywhere else the compiler builds the whole list first.
|
|
676
|
-
* `...` is unaffected — that is the vararg pack, and `f(...)` passes it on
|
|
677
|
-
* as it always did. */
|
|
679
|
+
* written last; anywhere else the compiler builds the whole list first. */
|
|
678
680
|
interface SpreadElement extends BaseNode {
|
|
679
681
|
type: "SpreadElement";
|
|
680
682
|
argument: Expression;
|
package/dist/index.js
CHANGED
|
@@ -864,10 +864,10 @@ var Parser = class {
|
|
|
864
864
|
expressionOr(stop) {
|
|
865
865
|
return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
|
|
866
866
|
}
|
|
867
|
-
/** A comma-separated list of values
|
|
868
|
-
*
|
|
869
|
-
*
|
|
870
|
-
*
|
|
867
|
+
/** A comma-separated list of values where one belongs — a `return`'s, a
|
|
868
|
+
* declaration's, an assignment's — read whole, so that writing several
|
|
869
|
+
* is reported once and the rest still means something. A spread belongs
|
|
870
|
+
* in a call's arguments or an array, and is reported here too. */
|
|
871
871
|
expressionListOr(stop) {
|
|
872
872
|
const until = () => stop() || this.checkPunctuator(",");
|
|
873
873
|
const item = () => {
|
|
@@ -1459,7 +1459,7 @@ var Parser = class {
|
|
|
1459
1459
|
}
|
|
1460
1460
|
this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
|
|
1461
1461
|
}
|
|
1462
|
-
// `const x = ...` / `let
|
|
1462
|
+
// `const x = ...` / `let [a, b] = ...`.
|
|
1463
1463
|
// tilua has no `local` — `const` bindings are immutable, `let` mutable.
|
|
1464
1464
|
/** `kind` reads the leading word as that keyword (recovery's `local`). */
|
|
1465
1465
|
parseVariableDeclaration(as) {
|
|
@@ -1473,14 +1473,34 @@ var Parser = class {
|
|
|
1473
1473
|
while (this.matchPunctuator(",")) {
|
|
1474
1474
|
names.push(this.parseBindingTarget(true));
|
|
1475
1475
|
}
|
|
1476
|
-
let
|
|
1476
|
+
let values = [];
|
|
1477
1477
|
if (this.matchOperator("=")) {
|
|
1478
|
-
|
|
1478
|
+
values = this.expressionListOr(() => false);
|
|
1479
1479
|
} else if (kind === "const") {
|
|
1480
1480
|
if (!this.recover) this.error("'const' declaration requires an initializer");
|
|
1481
1481
|
this.softError("'const' declaration requires an initializer");
|
|
1482
1482
|
}
|
|
1483
|
-
|
|
1483
|
+
let name = names[0];
|
|
1484
|
+
let init = values[0];
|
|
1485
|
+
if (names.length > 1 || values.length > 1) {
|
|
1486
|
+
this.problemAt(start, `A declaration takes one value: take several from an array, '${kind} [a, b] = [x, y]'`);
|
|
1487
|
+
if (names.length > 1) name = this.namesAsPattern(names);
|
|
1488
|
+
if (values.length > 1) init = this.valuesAsArray(values);
|
|
1489
|
+
}
|
|
1490
|
+
return { type: "VariableDeclaration", kind, name, init, ...spanFrom(start, this.previous()) };
|
|
1491
|
+
}
|
|
1492
|
+
/** `a, b` written where one name goes, read as `[a, b]`. */
|
|
1493
|
+
namesAsPattern(names) {
|
|
1494
|
+
const elements = names.map((value) => ({
|
|
1495
|
+
type: "ArrayPatternElement",
|
|
1496
|
+
value,
|
|
1497
|
+
...spanFrom(value, value)
|
|
1498
|
+
}));
|
|
1499
|
+
return { type: "ArrayPattern", elements, ...spanFrom(names[0], names[names.length - 1]) };
|
|
1500
|
+
}
|
|
1501
|
+
/** `x, y` written where one value goes, read as `[x, y]`. */
|
|
1502
|
+
valuesAsArray(values) {
|
|
1503
|
+
return { type: "ArrayExpression", elements: values, ...spanFrom(values[0], values[values.length - 1]) };
|
|
1484
1504
|
}
|
|
1485
1505
|
parseIfStatement() {
|
|
1486
1506
|
const start = this.current();
|
|
@@ -1961,7 +1981,7 @@ var Parser = class {
|
|
|
1961
1981
|
argument = values[0];
|
|
1962
1982
|
if (values.length > 1) {
|
|
1963
1983
|
this.problemAt(start, "A function returns one value: return several as an array, 'return [a, b]'");
|
|
1964
|
-
argument =
|
|
1984
|
+
argument = this.valuesAsArray(values);
|
|
1965
1985
|
}
|
|
1966
1986
|
}
|
|
1967
1987
|
return { type: "ReturnStatement", argument, ...spanFrom(start, this.previous()) };
|
|
@@ -1987,28 +2007,15 @@ var Parser = class {
|
|
|
1987
2007
|
parseExpressionStatement() {
|
|
1988
2008
|
const start = this.current();
|
|
1989
2009
|
if (this.checkPunctuator("{") || this.checkPunctuator("[")) {
|
|
1990
|
-
const
|
|
1991
|
-
|
|
1992
|
-
];
|
|
1993
|
-
while (this.matchPunctuator(",")) {
|
|
1994
|
-
targets.push(this.parseAssignTarget());
|
|
1995
|
-
}
|
|
1996
|
-
this.expectOperator("=");
|
|
1997
|
-
const values = this.expressionListOr(() => false);
|
|
1998
|
-
return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
|
|
2010
|
+
const target = this.checkPunctuator("{") ? this.parseObjectPattern() : this.parseArrayPattern();
|
|
2011
|
+
return this.parseAssignmentRest(start, target);
|
|
1999
2012
|
}
|
|
2000
2013
|
const statementStart = this.cursor;
|
|
2001
2014
|
const errorsAtStart = this.errors.length;
|
|
2002
2015
|
const first = this.parsePrefixExpression();
|
|
2003
2016
|
if (this.checkOperator("=") || this.checkPunctuator(",")) {
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
targets.push(this.parseAssignTarget());
|
|
2007
|
-
}
|
|
2008
|
-
for (const target of targets) this.rejectOptionalTarget(target);
|
|
2009
|
-
this.expectOperator("=");
|
|
2010
|
-
const values = this.expressionListOr(() => false);
|
|
2011
|
-
return { type: "AssignmentStatement", targets, values, ...spanFrom(start, this.previous()) };
|
|
2017
|
+
this.rejectOptionalTarget(first);
|
|
2018
|
+
return this.parseAssignmentRest(start, first);
|
|
2012
2019
|
}
|
|
2013
2020
|
const t = this.current();
|
|
2014
2021
|
if (t.type === "Operator" && COMPOUND_ASSIGN_OPS.has(t.value)) {
|
|
@@ -2034,6 +2041,21 @@ var Parser = class {
|
|
|
2034
2041
|
}
|
|
2035
2042
|
return { type: "ExpressionStatement", expression, ...spanFrom(start, this.previous()) };
|
|
2036
2043
|
}
|
|
2044
|
+
/** From after an assignment's target: `= value`. Luau's `a, b = x, y`
|
|
2045
|
+
* is reported, and read as its first target taking its first value. */
|
|
2046
|
+
parseAssignmentRest(start, target) {
|
|
2047
|
+
let several = false;
|
|
2048
|
+
while (this.matchPunctuator(",")) {
|
|
2049
|
+
this.parseAssignTarget();
|
|
2050
|
+
several = true;
|
|
2051
|
+
}
|
|
2052
|
+
this.expectOperator("=");
|
|
2053
|
+
const values = this.expressionListOr(() => false);
|
|
2054
|
+
if (several || values.length > 1) {
|
|
2055
|
+
this.problemAt(start, "An assignment takes one value: take several from an array, '[a, b] = [x, y]'");
|
|
2056
|
+
}
|
|
2057
|
+
return { type: "AssignmentStatement", target, value: values[0], ...spanFrom(start, this.previous()) };
|
|
2058
|
+
}
|
|
2037
2059
|
// ============================================================
|
|
2038
2060
|
// Expressions
|
|
2039
2061
|
// ============================================================
|
|
@@ -2517,8 +2539,8 @@ var Parser = class {
|
|
|
2517
2539
|
startsSuperUse() {
|
|
2518
2540
|
return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
|
|
2519
2541
|
}
|
|
2520
|
-
/** After `...`, is there something to spread?
|
|
2521
|
-
*
|
|
2542
|
+
/** After `...`, is there something to spread? tilua has no bare `...`:
|
|
2543
|
+
* a varying number of arguments is a rest parameter, `...args: T[]`. */
|
|
2522
2544
|
startsSpread() {
|
|
2523
2545
|
const next = this.peek(1);
|
|
2524
2546
|
if (next.type === "Punctuator") {
|
|
@@ -2537,8 +2559,8 @@ var Parser = class {
|
|
|
2537
2559
|
const token = this.peek(ahead);
|
|
2538
2560
|
return token.type === "Punctuator" && token.value === value;
|
|
2539
2561
|
}
|
|
2540
|
-
/** An assignment target after the first
|
|
2541
|
-
*
|
|
2562
|
+
/** An assignment target after the first — Luau's `a, b = ...`, read only
|
|
2563
|
+
* to be reported: a prefix expression or a destructuring pattern. */
|
|
2542
2564
|
parseAssignTarget() {
|
|
2543
2565
|
if (this.checkPunctuator("{")) return this.parseObjectPattern();
|
|
2544
2566
|
if (this.checkPunctuator("[")) return this.parseArrayPattern();
|
|
@@ -3029,7 +3051,7 @@ var Parser = class {
|
|
|
3029
3051
|
};
|
|
3030
3052
|
})() : this.parseFunctionHead();
|
|
3031
3053
|
this.expectPunctuator("=>");
|
|
3032
|
-
const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.
|
|
3054
|
+
const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.parseArrowExpressionBody();
|
|
3033
3055
|
const func = {
|
|
3034
3056
|
type: "FunctionBody",
|
|
3035
3057
|
generics: head.generics,
|
|
@@ -3042,6 +3064,29 @@ var Parser = class {
|
|
|
3042
3064
|
};
|
|
3043
3065
|
return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
|
|
3044
3066
|
}
|
|
3067
|
+
/** `() => value` returns the value. `() => a = 1` and `() => n += 1` are
|
|
3068
|
+
* an assignment, which is a statement: the body is that
|
|
3069
|
+
* statement, and the function returns nothing. */
|
|
3070
|
+
parseArrowExpressionBody() {
|
|
3071
|
+
const start = this.cursor;
|
|
3072
|
+
const errors = this.errors.length;
|
|
3073
|
+
const expression = this.parseExpression(0);
|
|
3074
|
+
const t = this.current();
|
|
3075
|
+
const assigns = t.type === "Operator" && (t.value === "=" || COMPOUND_ASSIGN_OPS.has(t.value));
|
|
3076
|
+
if (!assigns) return this.returnOf(expression);
|
|
3077
|
+
this.cursor = start;
|
|
3078
|
+
this.errors.length = errors;
|
|
3079
|
+
const first = this.current();
|
|
3080
|
+
const target = this.parsePrefixExpression();
|
|
3081
|
+
this.rejectOptionalTarget(target);
|
|
3082
|
+
const opToken = this.current();
|
|
3083
|
+
const op = opToken.value;
|
|
3084
|
+
if (opToken.type !== "Operator" || op !== "=" && !COMPOUND_ASSIGN_OPS.has(op)) this.error("Expected '='");
|
|
3085
|
+
this.advance();
|
|
3086
|
+
const value = this.expressionOr(() => false);
|
|
3087
|
+
const statement = op === "=" ? { type: "AssignmentStatement", target, value, ...spanFrom(first, this.previous()) } : { type: "CompoundAssignmentStatement", operator: op, target, value, ...spanFrom(first, this.previous()) };
|
|
3088
|
+
return { type: "Block", statements: [statement], ...spanFrom(statement, statement) };
|
|
3089
|
+
}
|
|
3045
3090
|
/** A one-expression body: the value is what the function returns. */
|
|
3046
3091
|
returnOf(expression) {
|
|
3047
3092
|
const statement = {
|
|
@@ -3973,10 +4018,9 @@ var Analyzer = class {
|
|
|
3973
4018
|
visitStatement(stmt, scope) {
|
|
3974
4019
|
switch (stmt.type) {
|
|
3975
4020
|
case "VariableDeclaration": {
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
for (const name of stmt.names) this.declarePattern(scope, name, "local", scope, isConst);
|
|
4021
|
+
if (stmt.init) this.visitExpression(stmt.init, scope);
|
|
4022
|
+
this.visitType(stmt.name.typeAnnotation, scope);
|
|
4023
|
+
this.declarePattern(scope, stmt.name, "local", scope, stmt.kind === "const");
|
|
3980
4024
|
return;
|
|
3981
4025
|
}
|
|
3982
4026
|
case "FunctionDeclaration": {
|
|
@@ -4010,16 +4054,15 @@ var Analyzer = class {
|
|
|
4010
4054
|
return;
|
|
4011
4055
|
}
|
|
4012
4056
|
case "AssignmentStatement": {
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
}
|
|
4057
|
+
this.visitExpression(stmt.value, scope);
|
|
4058
|
+
const target = stmt.target;
|
|
4059
|
+
if (target.type === "Identifier") {
|
|
4060
|
+
this.referenceAsAssignmentTarget(scope, target);
|
|
4061
|
+
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
4062
|
+
this.assignPattern(scope, target);
|
|
4063
|
+
} else {
|
|
4064
|
+
this.visitExpression(target, scope);
|
|
4065
|
+
this.checkModuleWrite(target);
|
|
4023
4066
|
}
|
|
4024
4067
|
return;
|
|
4025
4068
|
}
|
|
@@ -5343,7 +5386,7 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5343
5386
|
params: declaration.typeParams.map((g) => g.name)
|
|
5344
5387
|
});
|
|
5345
5388
|
}
|
|
5346
|
-
} else
|
|
5389
|
+
} else exportPattern(declaration.name);
|
|
5347
5390
|
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
5348
5391
|
const name = stmt.alias.name.name;
|
|
5349
5392
|
const type = types.aliases.get(name);
|
|
@@ -5582,12 +5625,10 @@ function assignedFields(block) {
|
|
|
5582
5625
|
const names = /* @__PURE__ */ new Set();
|
|
5583
5626
|
walkNodes(block, (node) => {
|
|
5584
5627
|
const record = node;
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
names.add(member.property.name);
|
|
5590
|
-
}
|
|
5628
|
+
if (record.type !== "AssignmentStatement" && record.type !== "CompoundAssignmentStatement") return;
|
|
5629
|
+
const member = record.target;
|
|
5630
|
+
if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
|
|
5631
|
+
names.add(member.property.name);
|
|
5591
5632
|
}
|
|
5592
5633
|
});
|
|
5593
5634
|
return names;
|
|
@@ -6063,12 +6104,10 @@ var TypeAnalyzer = class {
|
|
|
6063
6104
|
classDisplayNames = /* @__PURE__ */ new WeakMap();
|
|
6064
6105
|
/** `const Name = class ... end` — the name the class will be known by. */
|
|
6065
6106
|
nameClassExpressions(stmt) {
|
|
6066
|
-
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
}
|
|
6071
|
-
});
|
|
6107
|
+
const value = stmt.init;
|
|
6108
|
+
if (stmt.name.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
|
|
6109
|
+
this.classDisplayNames.set(value, stmt.name.name);
|
|
6110
|
+
}
|
|
6072
6111
|
}
|
|
6073
6112
|
classTypeParams(stmt) {
|
|
6074
6113
|
return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
|
|
@@ -7351,35 +7390,30 @@ var TypeAnalyzer = class {
|
|
|
7351
7390
|
switch (stmt.type) {
|
|
7352
7391
|
case "VariableDeclaration": {
|
|
7353
7392
|
this.nameClassExpressions(stmt);
|
|
7354
|
-
|
|
7355
|
-
|
|
7356
|
-
|
|
7357
|
-
|
|
7358
|
-
}
|
|
7359
|
-
const
|
|
7360
|
-
this.
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
|
|
7367
|
-
|
|
7368
|
-
|
|
7369
|
-
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7370
|
-
});
|
|
7371
|
-
} else if (declared.kind !== "any") {
|
|
7372
|
-
this.reportExcessProperties(source, declared);
|
|
7373
|
-
}
|
|
7374
|
-
}
|
|
7375
|
-
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7376
|
-
this.bindPattern(target, inferred, env, mode);
|
|
7377
|
-
if (stmt.kind === "const") {
|
|
7378
|
-
this.correlateDestructuring(target, inferred, env);
|
|
7379
|
-
this.correlateIndexed(target, source, env);
|
|
7380
|
-
this.aliasReference(target, source);
|
|
7393
|
+
const target = stmt.name;
|
|
7394
|
+
const source = stmt.init;
|
|
7395
|
+
if (target.type === "IdentifierPattern" && target.typeAnnotation && source) {
|
|
7396
|
+
this.applyContext(source, this.resolveType(target.typeAnnotation));
|
|
7397
|
+
}
|
|
7398
|
+
const inferred = source ? this.infer(source, env) : nilType;
|
|
7399
|
+
if (this.emitDiagnostics && target.type === "IdentifierPattern" && target.typeAnnotation && source) {
|
|
7400
|
+
const declared = this.resolveType(target.typeAnnotation);
|
|
7401
|
+
if (declared.kind !== "any" && !this.namesNothing(declared) && !this.fitsAnnotation(source, declared, inferred, env)) {
|
|
7402
|
+
this.diagnostics.push({
|
|
7403
|
+
node: stmt,
|
|
7404
|
+
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7405
|
+
});
|
|
7406
|
+
} else if (declared.kind !== "any") {
|
|
7407
|
+
this.reportExcessProperties(source, declared);
|
|
7381
7408
|
}
|
|
7382
|
-
}
|
|
7409
|
+
}
|
|
7410
|
+
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7411
|
+
this.bindPattern(target, inferred, env, mode);
|
|
7412
|
+
if (stmt.kind === "const") {
|
|
7413
|
+
this.correlateDestructuring(target, inferred, env);
|
|
7414
|
+
this.correlateIndexed(target, source, env);
|
|
7415
|
+
this.aliasReference(target, source);
|
|
7416
|
+
}
|
|
7383
7417
|
return;
|
|
7384
7418
|
}
|
|
7385
7419
|
case "ClassDeclaration": {
|
|
@@ -7436,48 +7470,41 @@ var TypeAnalyzer = class {
|
|
|
7436
7470
|
return;
|
|
7437
7471
|
}
|
|
7438
7472
|
case "AssignmentStatement": {
|
|
7439
|
-
stmt.
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
const declared = this.bindingType.get(id);
|
|
7461
|
-
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7462
|
-
this.diagnostics.push({
|
|
7463
|
-
node: stmt,
|
|
7464
|
-
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7465
|
-
});
|
|
7466
|
-
}
|
|
7467
|
-
this.setBinding(env, id, narrowTo(declared, next));
|
|
7468
|
-
} else {
|
|
7469
|
-
this.setBinding(env, id, next);
|
|
7470
|
-
this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
|
|
7473
|
+
const target = stmt.target;
|
|
7474
|
+
const source = stmt.value;
|
|
7475
|
+
if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7476
|
+
this.applyContext(source, this.infer(target, env));
|
|
7477
|
+
} else if (target.type === "Identifier") {
|
|
7478
|
+
const id = this.bindingIdOf(target);
|
|
7479
|
+
if (id !== void 0 && this.annotated.has(id)) this.applyContext(source, this.bindingType.get(id));
|
|
7480
|
+
}
|
|
7481
|
+
const vt = this.infer(source, env);
|
|
7482
|
+
if (target.type === "Identifier") {
|
|
7483
|
+
const id = this.bindingIdOf(target);
|
|
7484
|
+
if (id !== void 0) {
|
|
7485
|
+
this.uncorrelate(id);
|
|
7486
|
+
const next = isFreshLiteralExpr(source) ? widen(vt) : vt;
|
|
7487
|
+
if (this.annotated.has(id)) {
|
|
7488
|
+
const declared = this.bindingType.get(id);
|
|
7489
|
+
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7490
|
+
this.diagnostics.push({
|
|
7491
|
+
node: stmt,
|
|
7492
|
+
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7493
|
+
});
|
|
7471
7494
|
}
|
|
7495
|
+
this.setBinding(env, id, narrowTo(declared, next));
|
|
7496
|
+
} else {
|
|
7497
|
+
this.setBinding(env, id, next);
|
|
7498
|
+
this.bindingType.set(id, union([this.bindingType.get(id) ?? next, next]));
|
|
7472
7499
|
}
|
|
7473
|
-
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7474
|
-
this.infer(target, env);
|
|
7475
|
-
this.checkReadonlyAssign(target, env);
|
|
7476
|
-
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7477
|
-
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7478
|
-
this.reassignPattern(target, vt, env);
|
|
7479
7500
|
}
|
|
7480
|
-
})
|
|
7501
|
+
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7502
|
+
this.infer(target, env);
|
|
7503
|
+
this.checkReadonlyAssign(target, env);
|
|
7504
|
+
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7505
|
+
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7506
|
+
this.reassignPattern(target, vt, env);
|
|
7507
|
+
}
|
|
7481
7508
|
return;
|
|
7482
7509
|
}
|
|
7483
7510
|
case "CompoundAssignmentStatement":
|
|
@@ -7641,55 +7668,6 @@ var TypeAnalyzer = class {
|
|
|
7641
7668
|
return;
|
|
7642
7669
|
}
|
|
7643
7670
|
}
|
|
7644
|
-
/** Flatten an expression list into the values it actually produces.
|
|
7645
|
-
*
|
|
7646
|
-
* Lua's adjustment rule: every expression but the last contributes one
|
|
7647
|
-
* value, and the last contributes all of its values if it is a call or
|
|
7648
|
-
* `...`. That is what makes `local ok, err = pcall(f)` work, and it is
|
|
7649
|
-
* why `local x = f()` takes only the first value.
|
|
7650
|
-
*
|
|
7651
|
-
* `sources` maps each produced value back to the expression it came from
|
|
7652
|
-
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7653
|
-
* caller can still do contextual typing against the written expression. */
|
|
7654
|
-
valueList(exprs, env) {
|
|
7655
|
-
const types = [];
|
|
7656
|
-
const sources = [];
|
|
7657
|
-
exprs.forEach((e, i) => {
|
|
7658
|
-
const t = this.infer(e, env);
|
|
7659
|
-
const last = i === exprs.length - 1;
|
|
7660
|
-
if (e.type === "SpreadElement") {
|
|
7661
|
-
const held = this.typeOf.get(e.argument);
|
|
7662
|
-
const expanded = held && this.expand(held);
|
|
7663
|
-
if (expanded?.kind === "tuple") {
|
|
7664
|
-
for (const element of expanded.elements) {
|
|
7665
|
-
types.push(element);
|
|
7666
|
-
sources.push(e);
|
|
7667
|
-
}
|
|
7668
|
-
return;
|
|
7669
|
-
}
|
|
7670
|
-
types.push(t);
|
|
7671
|
-
sources.push(e);
|
|
7672
|
-
return;
|
|
7673
|
-
}
|
|
7674
|
-
void last;
|
|
7675
|
-
types.push(t);
|
|
7676
|
-
sources.push(e);
|
|
7677
|
-
});
|
|
7678
|
-
return { types, sources };
|
|
7679
|
-
}
|
|
7680
|
-
/** `const a, b = x, y` pairs names with values, one each. A call is one
|
|
7681
|
-
* value — several come back as an array — so `const a, b = f()` is a
|
|
7682
|
-
* name short of a value, and says how to take them apart. */
|
|
7683
|
-
checkValueCount(node, names, values, types) {
|
|
7684
|
-
if (!this.emitDiagnostics || !values.length || types.length >= names) return;
|
|
7685
|
-
const last = values[values.length - 1];
|
|
7686
|
-
const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
|
|
7687
|
-
const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
|
|
7688
|
-
this.diagnostics.push({
|
|
7689
|
-
node,
|
|
7690
|
-
message: call && (lastType?.kind === "tuple" || lastType?.kind === "array") ? `A call is one value, here an array: take its parts with '[${Array.from({ length: names }, (_, i) => String.fromCharCode(97 + i)).join(", ")}] = ...'` : `${names} names, but ${types.length} ${types.length === 1 ? "value" : "values"}: each name needs one`
|
|
7691
|
-
});
|
|
7692
|
-
}
|
|
7693
7671
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7694
7672
|
* what it collected. */
|
|
7695
7673
|
withBreakScope(visit) {
|
|
@@ -9673,7 +9651,35 @@ var TypeAnalyzer = class {
|
|
|
9673
9651
|
}
|
|
9674
9652
|
const read = this.expand(type);
|
|
9675
9653
|
if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
|
|
9676
|
-
|
|
9654
|
+
const optionalNil = link.optional === true && this.mayBeNil(inChain ?? full);
|
|
9655
|
+
return { type, shortCircuits: inChain !== void 0 || optionalNil };
|
|
9656
|
+
}
|
|
9657
|
+
/** Whether a value of `raw` can be nil — `includesNil`, and also the types
|
|
9658
|
+
* that do not say: `any`, `unknown`, an unconstrained type parameter. */
|
|
9659
|
+
mayBeNil(raw) {
|
|
9660
|
+
const t = this.expand(raw);
|
|
9661
|
+
switch (t.kind) {
|
|
9662
|
+
case "primitive":
|
|
9663
|
+
return t.name === "nil";
|
|
9664
|
+
case "any":
|
|
9665
|
+
case "unknown":
|
|
9666
|
+
return true;
|
|
9667
|
+
case "typeParam":
|
|
9668
|
+
return t.constraint === void 0 || this.mayBeNil(t.constraint);
|
|
9669
|
+
case "union":
|
|
9670
|
+
return t.types.some((m) => this.mayBeNil(m));
|
|
9671
|
+
case "never":
|
|
9672
|
+
case "literal":
|
|
9673
|
+
case "array":
|
|
9674
|
+
case "tuple":
|
|
9675
|
+
case "object":
|
|
9676
|
+
case "function":
|
|
9677
|
+
case "intersection":
|
|
9678
|
+
case "templateLiteral":
|
|
9679
|
+
return false;
|
|
9680
|
+
default:
|
|
9681
|
+
return true;
|
|
9682
|
+
}
|
|
9677
9683
|
}
|
|
9678
9684
|
/** Objects already reported as possibly nil: a loop body is visited more
|
|
9679
9685
|
* than once. */
|
|
@@ -10595,13 +10601,12 @@ var TypeAnalyzer = class {
|
|
|
10595
10601
|
computingAhead = /* @__PURE__ */ new Set();
|
|
10596
10602
|
declaredAhead(id) {
|
|
10597
10603
|
this.aheadDeclarations ??= this.indexAheadDeclarations();
|
|
10598
|
-
const
|
|
10599
|
-
if (!
|
|
10604
|
+
const statement = this.aheadDeclarations.get(id);
|
|
10605
|
+
if (!statement || this.computingAhead.has(id)) return void 0;
|
|
10600
10606
|
this.computingAhead.add(id);
|
|
10601
10607
|
const wasEmitting = this.emitDiagnostics;
|
|
10602
10608
|
this.emitDiagnostics = false;
|
|
10603
10609
|
try {
|
|
10604
|
-
const { statement, index } = found;
|
|
10605
10610
|
let type;
|
|
10606
10611
|
if (statement.type === "DeclareStatement") {
|
|
10607
10612
|
type = this.resolveType(statement.valueType);
|
|
@@ -10610,11 +10615,11 @@ var TypeAnalyzer = class {
|
|
|
10610
10615
|
} else if (statement.type === "ClassDeclaration") {
|
|
10611
10616
|
type = this.classValueType(statement);
|
|
10612
10617
|
} else if (statement.type === "VariableDeclaration") {
|
|
10613
|
-
const target = statement.
|
|
10618
|
+
const target = statement.name;
|
|
10614
10619
|
if (target.type === "IdentifierPattern" && target.typeAnnotation) {
|
|
10615
10620
|
type = this.resolveType(target.typeAnnotation);
|
|
10616
|
-
} else if (statement.init
|
|
10617
|
-
const value = this.infer(statement.init
|
|
10621
|
+
} else if (statement.init) {
|
|
10622
|
+
const value = this.infer(statement.init, /* @__PURE__ */ new Map());
|
|
10618
10623
|
type = statement.kind === "const" ? value : widen(value);
|
|
10619
10624
|
}
|
|
10620
10625
|
}
|
|
@@ -10632,11 +10637,10 @@ var TypeAnalyzer = class {
|
|
|
10632
10637
|
for (const statement of this.program.body.statements) {
|
|
10633
10638
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
10634
10639
|
if (declaration.type !== "VariableDeclaration") continue;
|
|
10635
|
-
|
|
10636
|
-
|
|
10637
|
-
|
|
10638
|
-
|
|
10639
|
-
});
|
|
10640
|
+
const target = declaration.name;
|
|
10641
|
+
if (target.type !== "IdentifierPattern") continue;
|
|
10642
|
+
const id = this.bindingIdByName(target.name, target);
|
|
10643
|
+
if (id !== void 0) out.set(id, declaration);
|
|
10640
10644
|
}
|
|
10641
10645
|
const visit = (node) => {
|
|
10642
10646
|
if (!node || typeof node !== "object") return;
|
|
@@ -10647,11 +10651,11 @@ var TypeAnalyzer = class {
|
|
|
10647
10651
|
const record = node;
|
|
10648
10652
|
if (record.type === "ClassDeclaration" && record.name) {
|
|
10649
10653
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10650
|
-
if (id !== void 0) out.set(id,
|
|
10654
|
+
if (id !== void 0) out.set(id, node);
|
|
10651
10655
|
}
|
|
10652
10656
|
if (record.type === "FunctionDeclaration" && record.name) {
|
|
10653
10657
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10654
|
-
if (id !== void 0) out.set(id,
|
|
10658
|
+
if (id !== void 0) out.set(id, node);
|
|
10655
10659
|
}
|
|
10656
10660
|
for (const [key, value] of Object.entries(node)) {
|
|
10657
10661
|
if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
|
|
@@ -10660,7 +10664,7 @@ var TypeAnalyzer = class {
|
|
|
10660
10664
|
visit(this.program.body);
|
|
10661
10665
|
for (const [name, statement] of this.deferredDeclares) {
|
|
10662
10666
|
const id = this.scopes.globalsByName.get(name);
|
|
10663
|
-
if (id !== void 0) out.set(id,
|
|
10667
|
+
if (id !== void 0) out.set(id, statement);
|
|
10664
10668
|
}
|
|
10665
10669
|
return out;
|
|
10666
10670
|
}
|