@tilua/parser 2.1.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 +274 -207
- package/dist/index.d.cts +22 -12
- package/dist/index.d.ts +22 -12
- package/dist/index.js +274 -207
- package/package.json +1 -1
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
|
}
|
|
@@ -4670,14 +4713,17 @@ function classArguments(t, name) {
|
|
|
4670
4713
|
if (t.kind === "object") return t.class?.typeArguments?.get(name);
|
|
4671
4714
|
return void 0;
|
|
4672
4715
|
}
|
|
4673
|
-
function unify(param, arg, vars, out) {
|
|
4716
|
+
function unify(param, arg, vars, out, how = "union") {
|
|
4674
4717
|
if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
|
|
4675
|
-
unify(param.constraint, arg, vars, out);
|
|
4718
|
+
unify(param.constraint, arg, vars, out, how);
|
|
4676
4719
|
return;
|
|
4677
4720
|
}
|
|
4678
4721
|
if (param.kind === "typeParam" && vars.has(param.name)) {
|
|
4679
4722
|
const prev = out.get(param.name);
|
|
4680
|
-
out.set(param.name,
|
|
4723
|
+
if (!prev) out.set(param.name, arg);
|
|
4724
|
+
else if (isAssignable(arg, prev)) {
|
|
4725
|
+
} else if (isAssignable(prev, arg)) out.set(param.name, arg);
|
|
4726
|
+
else if (how === "union") out.set(param.name, union([prev, arg]));
|
|
4681
4727
|
return;
|
|
4682
4728
|
}
|
|
4683
4729
|
if (arg.kind === "any" || arg.kind === "never") return;
|
|
@@ -5445,7 +5491,7 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5445
5491
|
params: declaration.typeParams.map((g) => g.name)
|
|
5446
5492
|
});
|
|
5447
5493
|
}
|
|
5448
|
-
} else
|
|
5494
|
+
} else exportPattern(declaration.name);
|
|
5449
5495
|
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
5450
5496
|
const name = stmt.alias.name.name;
|
|
5451
5497
|
const type = types.aliases.get(name);
|
|
@@ -5684,12 +5730,10 @@ function assignedFields(block) {
|
|
|
5684
5730
|
const names = /* @__PURE__ */ new Set();
|
|
5685
5731
|
walkNodes(block, (node) => {
|
|
5686
5732
|
const record = node;
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
names.add(member.property.name);
|
|
5692
|
-
}
|
|
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);
|
|
5693
5737
|
}
|
|
5694
5738
|
});
|
|
5695
5739
|
return names;
|
|
@@ -6165,12 +6209,10 @@ var TypeAnalyzer = class {
|
|
|
6165
6209
|
classDisplayNames = /* @__PURE__ */ new WeakMap();
|
|
6166
6210
|
/** `const Name = class ... end` — the name the class will be known by. */
|
|
6167
6211
|
nameClassExpressions(stmt) {
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
6172
|
-
}
|
|
6173
|
-
});
|
|
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
|
+
}
|
|
6174
6216
|
}
|
|
6175
6217
|
classTypeParams(stmt) {
|
|
6176
6218
|
return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
|
|
@@ -7453,35 +7495,30 @@ var TypeAnalyzer = class {
|
|
|
7453
7495
|
switch (stmt.type) {
|
|
7454
7496
|
case "VariableDeclaration": {
|
|
7455
7497
|
this.nameClassExpressions(stmt);
|
|
7456
|
-
|
|
7457
|
-
|
|
7458
|
-
|
|
7459
|
-
|
|
7460
|
-
}
|
|
7461
|
-
const
|
|
7462
|
-
this.
|
|
7463
|
-
|
|
7464
|
-
|
|
7465
|
-
|
|
7466
|
-
|
|
7467
|
-
|
|
7468
|
-
|
|
7469
|
-
|
|
7470
|
-
|
|
7471
|
-
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7472
|
-
});
|
|
7473
|
-
} else if (declared.kind !== "any") {
|
|
7474
|
-
this.reportExcessProperties(source, declared);
|
|
7475
|
-
}
|
|
7476
|
-
}
|
|
7477
|
-
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7478
|
-
this.bindPattern(target, inferred, env, mode);
|
|
7479
|
-
if (stmt.kind === "const") {
|
|
7480
|
-
this.correlateDestructuring(target, inferred, env);
|
|
7481
|
-
this.correlateIndexed(target, source, env);
|
|
7482
|
-
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);
|
|
7483
7513
|
}
|
|
7484
|
-
}
|
|
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
|
+
}
|
|
7485
7522
|
return;
|
|
7486
7523
|
}
|
|
7487
7524
|
case "ClassDeclaration": {
|
|
@@ -7538,48 +7575,41 @@ var TypeAnalyzer = class {
|
|
|
7538
7575
|
return;
|
|
7539
7576
|
}
|
|
7540
7577
|
case "AssignmentStatement": {
|
|
7541
|
-
stmt.
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
|
|
7547
|
-
|
|
7548
|
-
|
|
7549
|
-
|
|
7550
|
-
|
|
7551
|
-
|
|
7552
|
-
|
|
7553
|
-
|
|
7554
|
-
|
|
7555
|
-
|
|
7556
|
-
|
|
7557
|
-
|
|
7558
|
-
|
|
7559
|
-
|
|
7560
|
-
|
|
7561
|
-
|
|
7562
|
-
const declared = this.bindingType.get(id);
|
|
7563
|
-
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7564
|
-
this.diagnostics.push({
|
|
7565
|
-
node: stmt,
|
|
7566
|
-
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7567
|
-
});
|
|
7568
|
-
}
|
|
7569
|
-
this.setBinding(env, id, narrowTo(declared, next));
|
|
7570
|
-
} else {
|
|
7571
|
-
this.setBinding(env, id, next);
|
|
7572
|
-
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
|
+
});
|
|
7573
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]));
|
|
7574
7604
|
}
|
|
7575
|
-
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7576
|
-
this.infer(target, env);
|
|
7577
|
-
this.checkReadonlyAssign(target, env);
|
|
7578
|
-
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7579
|
-
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7580
|
-
this.reassignPattern(target, vt, env);
|
|
7581
7605
|
}
|
|
7582
|
-
})
|
|
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
|
+
}
|
|
7583
7613
|
return;
|
|
7584
7614
|
}
|
|
7585
7615
|
case "CompoundAssignmentStatement":
|
|
@@ -7743,55 +7773,6 @@ var TypeAnalyzer = class {
|
|
|
7743
7773
|
return;
|
|
7744
7774
|
}
|
|
7745
7775
|
}
|
|
7746
|
-
/** Flatten an expression list into the values it actually produces.
|
|
7747
|
-
*
|
|
7748
|
-
* Lua's adjustment rule: every expression but the last contributes one
|
|
7749
|
-
* value, and the last contributes all of its values if it is a call or
|
|
7750
|
-
* `...`. That is what makes `local ok, err = pcall(f)` work, and it is
|
|
7751
|
-
* why `local x = f()` takes only the first value.
|
|
7752
|
-
*
|
|
7753
|
-
* `sources` maps each produced value back to the expression it came from
|
|
7754
|
-
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7755
|
-
* caller can still do contextual typing against the written expression. */
|
|
7756
|
-
valueList(exprs, env) {
|
|
7757
|
-
const types = [];
|
|
7758
|
-
const sources = [];
|
|
7759
|
-
exprs.forEach((e, i) => {
|
|
7760
|
-
const t = this.infer(e, env);
|
|
7761
|
-
const last = i === exprs.length - 1;
|
|
7762
|
-
if (e.type === "SpreadElement") {
|
|
7763
|
-
const held = this.typeOf.get(e.argument);
|
|
7764
|
-
const expanded = held && this.expand(held);
|
|
7765
|
-
if (expanded?.kind === "tuple") {
|
|
7766
|
-
for (const element of expanded.elements) {
|
|
7767
|
-
types.push(element);
|
|
7768
|
-
sources.push(e);
|
|
7769
|
-
}
|
|
7770
|
-
return;
|
|
7771
|
-
}
|
|
7772
|
-
types.push(t);
|
|
7773
|
-
sources.push(e);
|
|
7774
|
-
return;
|
|
7775
|
-
}
|
|
7776
|
-
void last;
|
|
7777
|
-
types.push(t);
|
|
7778
|
-
sources.push(e);
|
|
7779
|
-
});
|
|
7780
|
-
return { types, sources };
|
|
7781
|
-
}
|
|
7782
|
-
/** `const a, b = x, y` pairs names with values, one each. A call is one
|
|
7783
|
-
* value — several come back as an array — so `const a, b = f()` is a
|
|
7784
|
-
* name short of a value, and says how to take them apart. */
|
|
7785
|
-
checkValueCount(node, names, values, types) {
|
|
7786
|
-
if (!this.emitDiagnostics || !values.length || types.length >= names) return;
|
|
7787
|
-
const last = values[values.length - 1];
|
|
7788
|
-
const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
|
|
7789
|
-
const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
|
|
7790
|
-
this.diagnostics.push({
|
|
7791
|
-
node,
|
|
7792
|
-
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`
|
|
7793
|
-
});
|
|
7794
|
-
}
|
|
7795
7776
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7796
7777
|
* what it collected. */
|
|
7797
7778
|
withBreakScope(visit) {
|
|
@@ -7862,8 +7843,10 @@ var TypeAnalyzer = class {
|
|
|
7862
7843
|
// --------------------------------------------------------
|
|
7863
7844
|
// Functions
|
|
7864
7845
|
// --------------------------------------------------------
|
|
7865
|
-
|
|
7866
|
-
|
|
7846
|
+
/** `returns`, when given, collects the type of each `return` the walk
|
|
7847
|
+
* reads — see `inferFunctionBody`. */
|
|
7848
|
+
visitFunctionBody(func, outerEnv, returns) {
|
|
7849
|
+
this.withTypeParams(func.generics, () => this.visitFunctionBodyInner(func, outerEnv, returns));
|
|
7867
7850
|
}
|
|
7868
7851
|
/** A parameter's type: annotation, else a shape synthesized from a
|
|
7869
7852
|
* destructuring pattern, else inferred from its default, else `any`. */
|
|
@@ -7912,6 +7895,53 @@ var TypeAnalyzer = class {
|
|
|
7912
7895
|
* it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
|
|
7913
7896
|
* `player` from `Connect`'s callback type. Anything else is inferred as
|
|
7914
7897
|
* usual. */
|
|
7898
|
+
/** The arguments of a call, in two passes where one is needed.
|
|
7899
|
+
*
|
|
7900
|
+
* A function written at the call site with a parameter nothing annotates
|
|
7901
|
+
* reads that parameter from the signature — and where the signature says
|
|
7902
|
+
* `(v: T) => U`, `T` is only known once the other arguments have been
|
|
7903
|
+
* read. So such an argument is read once quietly, to get a shape to pick
|
|
7904
|
+
* a signature with, and then again with `T` in hand: `map(xs, v => v + 1)`
|
|
7905
|
+
* has its `v` a number by then, and `U` follows from what the body
|
|
7906
|
+
* answers. Everything else is read once, as it always was. */
|
|
7907
|
+
inferArguments(written, fns, selfOf, extra, env) {
|
|
7908
|
+
const expected = this.expectedArguments(written, fns, selfOf);
|
|
7909
|
+
written.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
7910
|
+
const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
|
|
7911
|
+
if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
|
|
7912
|
+
const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
|
|
7913
|
+
const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
|
|
7914
|
+
const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
|
|
7915
|
+
written.forEach((a, i) => {
|
|
7916
|
+
if (!waits[i]) return;
|
|
7917
|
+
if (picked && subst) {
|
|
7918
|
+
const self = selfOf(picked);
|
|
7919
|
+
const declared = i + self < picked.params.length ? picked.params[i + self].type : picked.varargs;
|
|
7920
|
+
if (declared) this.applyContext(a, this.reduceType(substitute(declared, subst)));
|
|
7921
|
+
}
|
|
7922
|
+
argTypes[i] = this.infer(a, env);
|
|
7923
|
+
});
|
|
7924
|
+
return argTypes;
|
|
7925
|
+
}
|
|
7926
|
+
/** Is this argument a function whose own type waits on the call's? */
|
|
7927
|
+
waitsOnInference(arg, expected) {
|
|
7928
|
+
if (!expected || !containsTypeParam(expected)) return false;
|
|
7929
|
+
let e = arg;
|
|
7930
|
+
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
7931
|
+
if (e.type !== "FunctionExpression") return false;
|
|
7932
|
+
return e.func.params.some((p) => !p.typeAnnotation && !p.default && !p.pattern && p.name !== "self" && p.name !== "this");
|
|
7933
|
+
}
|
|
7934
|
+
/** Read something without saying anything about it: a first pass whose
|
|
7935
|
+
* only purpose is a shape to go on. */
|
|
7936
|
+
quietly(read) {
|
|
7937
|
+
const was = this.emitDiagnostics;
|
|
7938
|
+
this.emitDiagnostics = false;
|
|
7939
|
+
try {
|
|
7940
|
+
return read();
|
|
7941
|
+
} finally {
|
|
7942
|
+
this.emitDiagnostics = was;
|
|
7943
|
+
}
|
|
7944
|
+
}
|
|
7915
7945
|
applyContext(expr, expected) {
|
|
7916
7946
|
let e = expr;
|
|
7917
7947
|
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
@@ -8060,7 +8090,7 @@ var TypeAnalyzer = class {
|
|
|
8060
8090
|
this.contextualReturns.pop();
|
|
8061
8091
|
}
|
|
8062
8092
|
}
|
|
8063
|
-
visitFunctionBodyInner(func, outerEnv) {
|
|
8093
|
+
visitFunctionBodyInner(func, outerEnv, returns) {
|
|
8064
8094
|
const env = forkEnv(outerEnv);
|
|
8065
8095
|
for (const p of func.params) {
|
|
8066
8096
|
if (p.pattern) {
|
|
@@ -8077,7 +8107,7 @@ var TypeAnalyzer = class {
|
|
|
8077
8107
|
if (p.typeAnnotation) this.annotated.add(id);
|
|
8078
8108
|
}
|
|
8079
8109
|
}
|
|
8080
|
-
this.withVarargs(func, () => this.collectReturns(
|
|
8110
|
+
this.withVarargs(func, () => this.collectReturns(returns, () => {
|
|
8081
8111
|
this.visitBlock(func.body, env);
|
|
8082
8112
|
this.checkReturnsAtAll(func, this.declaredReturns[this.declaredReturns.length - 1]);
|
|
8083
8113
|
}));
|
|
@@ -8131,7 +8161,7 @@ var TypeAnalyzer = class {
|
|
|
8131
8161
|
if (arg === void 0) return;
|
|
8132
8162
|
const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
|
|
8133
8163
|
const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
|
|
8134
|
-
unify(p.type, preserve ? arg : widen(arg), vars, subst);
|
|
8164
|
+
unify(p.type, preserve ? arg : widen(arg), vars, subst, "first");
|
|
8135
8165
|
});
|
|
8136
8166
|
if (f.varargs && f.restIsWhole) {
|
|
8137
8167
|
const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
|
|
@@ -8482,7 +8512,12 @@ var TypeAnalyzer = class {
|
|
|
8482
8512
|
asserts: node.asserts
|
|
8483
8513
|
};
|
|
8484
8514
|
}
|
|
8485
|
-
|
|
8515
|
+
/** `visited`, when given, is what the real walk of the body just
|
|
8516
|
+
* collected from its `return`s: a function expression is walked before
|
|
8517
|
+
* its type is asked for, so there is nothing left to guess, and walking
|
|
8518
|
+
* the body a second time only to read them made every callback nested in
|
|
8519
|
+
* another cost twice its parent's. */
|
|
8520
|
+
inferFunctionBody(func, env, visited) {
|
|
8486
8521
|
const names = func.generics.map((g) => g.name);
|
|
8487
8522
|
return this.withTypeParams(func.generics, () => {
|
|
8488
8523
|
const params = func.params.flatMap((p) => {
|
|
@@ -8511,6 +8546,8 @@ var TypeAnalyzer = class {
|
|
|
8511
8546
|
returns = this.resolveType(func.returnType);
|
|
8512
8547
|
} else if (func.predicate) {
|
|
8513
8548
|
returns = booleanType;
|
|
8549
|
+
} else if (visited) {
|
|
8550
|
+
returns = visited.length ? union([...visited]) : this.withVarargs(func, () => this.silently(() => this.inferReturnType(func.body, bodyEnv)));
|
|
8514
8551
|
} else {
|
|
8515
8552
|
const collected = [];
|
|
8516
8553
|
returns = this.withVarargs(func, () => this.silently(() => {
|
|
@@ -9383,10 +9420,12 @@ var TypeAnalyzer = class {
|
|
|
9383
9420
|
return this.inferArray(expr, env, false);
|
|
9384
9421
|
case "TableExpression":
|
|
9385
9422
|
return this.inferObject(expr, env, false);
|
|
9386
|
-
case "FunctionExpression":
|
|
9423
|
+
case "FunctionExpression": {
|
|
9387
9424
|
this.checkParamOrder(expr.func.params, expr);
|
|
9388
|
-
|
|
9389
|
-
|
|
9425
|
+
const returned = [];
|
|
9426
|
+
this.visitFunctionBody(expr.func, env, returned);
|
|
9427
|
+
return this.inferFunctionBody(expr.func, env, returned);
|
|
9428
|
+
}
|
|
9390
9429
|
case "ParenthesizedExpression":
|
|
9391
9430
|
return this.infer(expr.expression, env);
|
|
9392
9431
|
case "TypeAssertionExpression": {
|
|
@@ -9590,9 +9629,7 @@ var TypeAnalyzer = class {
|
|
|
9590
9629
|
const united = this.unionSignatures(callee);
|
|
9591
9630
|
const fns = united ?? this.overloadsOf(callee);
|
|
9592
9631
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9593
|
-
const
|
|
9594
|
-
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
9595
|
-
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
9632
|
+
const argTypes = this.inferArguments(expr.arguments, fns, () => 0, (_, args) => args, env);
|
|
9596
9633
|
if (fns.length) {
|
|
9597
9634
|
this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
|
|
9598
9635
|
const spread = this.spreadOf(expr.arguments);
|
|
@@ -9657,9 +9694,13 @@ var TypeAnalyzer = class {
|
|
|
9657
9694
|
const united = this.unionSignatures(method);
|
|
9658
9695
|
const fns = united ?? this.overloadsOf(method);
|
|
9659
9696
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9660
|
-
const
|
|
9661
|
-
|
|
9662
|
-
|
|
9697
|
+
const argTypes = this.inferArguments(
|
|
9698
|
+
expr.arguments,
|
|
9699
|
+
fns,
|
|
9700
|
+
(f) => this.takesSelf(f) ? 1 : 0,
|
|
9701
|
+
(f, args) => this.takesSelf(f) ? [objType, ...args] : args,
|
|
9702
|
+
env
|
|
9703
|
+
);
|
|
9663
9704
|
if (fns.length) {
|
|
9664
9705
|
const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
|
|
9665
9706
|
const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
|
|
@@ -9715,7 +9756,35 @@ var TypeAnalyzer = class {
|
|
|
9715
9756
|
}
|
|
9716
9757
|
const read = this.expand(type);
|
|
9717
9758
|
if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
|
|
9718
|
-
|
|
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
|
+
}
|
|
9719
9788
|
}
|
|
9720
9789
|
/** Objects already reported as possibly nil: a loop body is visited more
|
|
9721
9790
|
* than once. */
|
|
@@ -10637,13 +10706,12 @@ var TypeAnalyzer = class {
|
|
|
10637
10706
|
computingAhead = /* @__PURE__ */ new Set();
|
|
10638
10707
|
declaredAhead(id) {
|
|
10639
10708
|
this.aheadDeclarations ??= this.indexAheadDeclarations();
|
|
10640
|
-
const
|
|
10641
|
-
if (!
|
|
10709
|
+
const statement = this.aheadDeclarations.get(id);
|
|
10710
|
+
if (!statement || this.computingAhead.has(id)) return void 0;
|
|
10642
10711
|
this.computingAhead.add(id);
|
|
10643
10712
|
const wasEmitting = this.emitDiagnostics;
|
|
10644
10713
|
this.emitDiagnostics = false;
|
|
10645
10714
|
try {
|
|
10646
|
-
const { statement, index } = found;
|
|
10647
10715
|
let type;
|
|
10648
10716
|
if (statement.type === "DeclareStatement") {
|
|
10649
10717
|
type = this.resolveType(statement.valueType);
|
|
@@ -10652,11 +10720,11 @@ var TypeAnalyzer = class {
|
|
|
10652
10720
|
} else if (statement.type === "ClassDeclaration") {
|
|
10653
10721
|
type = this.classValueType(statement);
|
|
10654
10722
|
} else if (statement.type === "VariableDeclaration") {
|
|
10655
|
-
const target = statement.
|
|
10723
|
+
const target = statement.name;
|
|
10656
10724
|
if (target.type === "IdentifierPattern" && target.typeAnnotation) {
|
|
10657
10725
|
type = this.resolveType(target.typeAnnotation);
|
|
10658
|
-
} else if (statement.init
|
|
10659
|
-
const value = this.infer(statement.init
|
|
10726
|
+
} else if (statement.init) {
|
|
10727
|
+
const value = this.infer(statement.init, /* @__PURE__ */ new Map());
|
|
10660
10728
|
type = statement.kind === "const" ? value : widen(value);
|
|
10661
10729
|
}
|
|
10662
10730
|
}
|
|
@@ -10674,11 +10742,10 @@ var TypeAnalyzer = class {
|
|
|
10674
10742
|
for (const statement of this.program.body.statements) {
|
|
10675
10743
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
10676
10744
|
if (declaration.type !== "VariableDeclaration") continue;
|
|
10677
|
-
|
|
10678
|
-
|
|
10679
|
-
|
|
10680
|
-
|
|
10681
|
-
});
|
|
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);
|
|
10682
10749
|
}
|
|
10683
10750
|
const visit = (node) => {
|
|
10684
10751
|
if (!node || typeof node !== "object") return;
|
|
@@ -10689,11 +10756,11 @@ var TypeAnalyzer = class {
|
|
|
10689
10756
|
const record = node;
|
|
10690
10757
|
if (record.type === "ClassDeclaration" && record.name) {
|
|
10691
10758
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10692
|
-
if (id !== void 0) out.set(id,
|
|
10759
|
+
if (id !== void 0) out.set(id, node);
|
|
10693
10760
|
}
|
|
10694
10761
|
if (record.type === "FunctionDeclaration" && record.name) {
|
|
10695
10762
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10696
|
-
if (id !== void 0) out.set(id,
|
|
10763
|
+
if (id !== void 0) out.set(id, node);
|
|
10697
10764
|
}
|
|
10698
10765
|
for (const [key, value] of Object.entries(node)) {
|
|
10699
10766
|
if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
|
|
@@ -10702,7 +10769,7 @@ var TypeAnalyzer = class {
|
|
|
10702
10769
|
visit(this.program.body);
|
|
10703
10770
|
for (const [name, statement] of this.deferredDeclares) {
|
|
10704
10771
|
const id = this.scopes.globalsByName.get(name);
|
|
10705
|
-
if (id !== void 0) out.set(id,
|
|
10772
|
+
if (id !== void 0) out.set(id, statement);
|
|
10706
10773
|
}
|
|
10707
10774
|
return out;
|
|
10708
10775
|
}
|