@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.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
|
}
|
|
@@ -4565,14 +4608,17 @@ function classArguments(t, name) {
|
|
|
4565
4608
|
if (t.kind === "object") return t.class?.typeArguments?.get(name);
|
|
4566
4609
|
return void 0;
|
|
4567
4610
|
}
|
|
4568
|
-
function unify(param, arg, vars, out) {
|
|
4611
|
+
function unify(param, arg, vars, out, how = "union") {
|
|
4569
4612
|
if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
|
|
4570
|
-
unify(param.constraint, arg, vars, out);
|
|
4613
|
+
unify(param.constraint, arg, vars, out, how);
|
|
4571
4614
|
return;
|
|
4572
4615
|
}
|
|
4573
4616
|
if (param.kind === "typeParam" && vars.has(param.name)) {
|
|
4574
4617
|
const prev = out.get(param.name);
|
|
4575
|
-
out.set(param.name,
|
|
4618
|
+
if (!prev) out.set(param.name, arg);
|
|
4619
|
+
else if (isAssignable(arg, prev)) {
|
|
4620
|
+
} else if (isAssignable(prev, arg)) out.set(param.name, arg);
|
|
4621
|
+
else if (how === "union") out.set(param.name, union([prev, arg]));
|
|
4576
4622
|
return;
|
|
4577
4623
|
}
|
|
4578
4624
|
if (arg.kind === "any" || arg.kind === "never") return;
|
|
@@ -5340,7 +5386,7 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
5340
5386
|
params: declaration.typeParams.map((g) => g.name)
|
|
5341
5387
|
});
|
|
5342
5388
|
}
|
|
5343
|
-
} else
|
|
5389
|
+
} else exportPattern(declaration.name);
|
|
5344
5390
|
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
5345
5391
|
const name = stmt.alias.name.name;
|
|
5346
5392
|
const type = types.aliases.get(name);
|
|
@@ -5579,12 +5625,10 @@ function assignedFields(block) {
|
|
|
5579
5625
|
const names = /* @__PURE__ */ new Set();
|
|
5580
5626
|
walkNodes(block, (node) => {
|
|
5581
5627
|
const record = node;
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
names.add(member.property.name);
|
|
5587
|
-
}
|
|
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);
|
|
5588
5632
|
}
|
|
5589
5633
|
});
|
|
5590
5634
|
return names;
|
|
@@ -6060,12 +6104,10 @@ var TypeAnalyzer = class {
|
|
|
6060
6104
|
classDisplayNames = /* @__PURE__ */ new WeakMap();
|
|
6061
6105
|
/** `const Name = class ... end` — the name the class will be known by. */
|
|
6062
6106
|
nameClassExpressions(stmt) {
|
|
6063
|
-
|
|
6064
|
-
|
|
6065
|
-
|
|
6066
|
-
|
|
6067
|
-
}
|
|
6068
|
-
});
|
|
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
|
+
}
|
|
6069
6111
|
}
|
|
6070
6112
|
classTypeParams(stmt) {
|
|
6071
6113
|
return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
|
|
@@ -7348,35 +7390,30 @@ var TypeAnalyzer = class {
|
|
|
7348
7390
|
switch (stmt.type) {
|
|
7349
7391
|
case "VariableDeclaration": {
|
|
7350
7392
|
this.nameClassExpressions(stmt);
|
|
7351
|
-
|
|
7352
|
-
|
|
7353
|
-
|
|
7354
|
-
|
|
7355
|
-
}
|
|
7356
|
-
const
|
|
7357
|
-
this.
|
|
7358
|
-
|
|
7359
|
-
|
|
7360
|
-
|
|
7361
|
-
|
|
7362
|
-
|
|
7363
|
-
|
|
7364
|
-
|
|
7365
|
-
|
|
7366
|
-
message: `Type '${briefType(inferred)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(inferred, declared)
|
|
7367
|
-
});
|
|
7368
|
-
} else if (declared.kind !== "any") {
|
|
7369
|
-
this.reportExcessProperties(source, declared);
|
|
7370
|
-
}
|
|
7371
|
-
}
|
|
7372
|
-
const mode = this.initIsAsConst(source) ? "asconst" : !isFreshLiteralExpr(source) ? "keep" : stmt.kind === "const" ? "const" : "widen";
|
|
7373
|
-
this.bindPattern(target, inferred, env, mode);
|
|
7374
|
-
if (stmt.kind === "const") {
|
|
7375
|
-
this.correlateDestructuring(target, inferred, env);
|
|
7376
|
-
this.correlateIndexed(target, source, env);
|
|
7377
|
-
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);
|
|
7378
7408
|
}
|
|
7379
|
-
}
|
|
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
|
+
}
|
|
7380
7417
|
return;
|
|
7381
7418
|
}
|
|
7382
7419
|
case "ClassDeclaration": {
|
|
@@ -7433,48 +7470,41 @@ var TypeAnalyzer = class {
|
|
|
7433
7470
|
return;
|
|
7434
7471
|
}
|
|
7435
7472
|
case "AssignmentStatement": {
|
|
7436
|
-
stmt.
|
|
7437
|
-
|
|
7438
|
-
|
|
7439
|
-
|
|
7440
|
-
|
|
7441
|
-
|
|
7442
|
-
|
|
7443
|
-
|
|
7444
|
-
|
|
7445
|
-
|
|
7446
|
-
|
|
7447
|
-
|
|
7448
|
-
|
|
7449
|
-
|
|
7450
|
-
|
|
7451
|
-
|
|
7452
|
-
|
|
7453
|
-
|
|
7454
|
-
|
|
7455
|
-
|
|
7456
|
-
|
|
7457
|
-
const declared = this.bindingType.get(id);
|
|
7458
|
-
if (this.emitDiagnostics && !isAssignable(next, declared) && declared.kind !== "any") {
|
|
7459
|
-
this.diagnostics.push({
|
|
7460
|
-
node: stmt,
|
|
7461
|
-
message: `Type '${briefType(next)}' is not assignable to '${briefType(declared)}'` + this.explainMismatch(next, declared)
|
|
7462
|
-
});
|
|
7463
|
-
}
|
|
7464
|
-
this.setBinding(env, id, narrowTo(declared, next));
|
|
7465
|
-
} else {
|
|
7466
|
-
this.setBinding(env, id, next);
|
|
7467
|
-
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
|
+
});
|
|
7468
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]));
|
|
7469
7499
|
}
|
|
7470
|
-
} else if (target.type === "MemberExpression" || target.type === "IndexExpression") {
|
|
7471
|
-
this.infer(target, env);
|
|
7472
|
-
this.checkReadonlyAssign(target, env);
|
|
7473
|
-
this.assignToRef(target, isFreshLiteralExpr(source) ? widen(vt) : vt, env);
|
|
7474
|
-
} else if (target.type === "ObjectPattern" || target.type === "ArrayPattern") {
|
|
7475
|
-
this.reassignPattern(target, vt, env);
|
|
7476
7500
|
}
|
|
7477
|
-
})
|
|
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
|
+
}
|
|
7478
7508
|
return;
|
|
7479
7509
|
}
|
|
7480
7510
|
case "CompoundAssignmentStatement":
|
|
@@ -7638,55 +7668,6 @@ var TypeAnalyzer = class {
|
|
|
7638
7668
|
return;
|
|
7639
7669
|
}
|
|
7640
7670
|
}
|
|
7641
|
-
/** Flatten an expression list into the values it actually produces.
|
|
7642
|
-
*
|
|
7643
|
-
* Lua's adjustment rule: every expression but the last contributes one
|
|
7644
|
-
* value, and the last contributes all of its values if it is a call or
|
|
7645
|
-
* `...`. That is what makes `local ok, err = pcall(f)` work, and it is
|
|
7646
|
-
* why `local x = f()` takes only the first value.
|
|
7647
|
-
*
|
|
7648
|
-
* `sources` maps each produced value back to the expression it came from
|
|
7649
|
-
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
7650
|
-
* caller can still do contextual typing against the written expression. */
|
|
7651
|
-
valueList(exprs, env) {
|
|
7652
|
-
const types = [];
|
|
7653
|
-
const sources = [];
|
|
7654
|
-
exprs.forEach((e, i) => {
|
|
7655
|
-
const t = this.infer(e, env);
|
|
7656
|
-
const last = i === exprs.length - 1;
|
|
7657
|
-
if (e.type === "SpreadElement") {
|
|
7658
|
-
const held = this.typeOf.get(e.argument);
|
|
7659
|
-
const expanded = held && this.expand(held);
|
|
7660
|
-
if (expanded?.kind === "tuple") {
|
|
7661
|
-
for (const element of expanded.elements) {
|
|
7662
|
-
types.push(element);
|
|
7663
|
-
sources.push(e);
|
|
7664
|
-
}
|
|
7665
|
-
return;
|
|
7666
|
-
}
|
|
7667
|
-
types.push(t);
|
|
7668
|
-
sources.push(e);
|
|
7669
|
-
return;
|
|
7670
|
-
}
|
|
7671
|
-
void last;
|
|
7672
|
-
types.push(t);
|
|
7673
|
-
sources.push(e);
|
|
7674
|
-
});
|
|
7675
|
-
return { types, sources };
|
|
7676
|
-
}
|
|
7677
|
-
/** `const a, b = x, y` pairs names with values, one each. A call is one
|
|
7678
|
-
* value — several come back as an array — so `const a, b = f()` is a
|
|
7679
|
-
* name short of a value, and says how to take them apart. */
|
|
7680
|
-
checkValueCount(node, names, values, types) {
|
|
7681
|
-
if (!this.emitDiagnostics || !values.length || types.length >= names) return;
|
|
7682
|
-
const last = values[values.length - 1];
|
|
7683
|
-
const lastType = types[types.length - 1] && this.expand(types[types.length - 1]);
|
|
7684
|
-
const call = last.type === "CallExpression" || last.type === "MethodCallExpression";
|
|
7685
|
-
this.diagnostics.push({
|
|
7686
|
-
node,
|
|
7687
|
-
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`
|
|
7688
|
-
});
|
|
7689
|
-
}
|
|
7690
7671
|
/** Run `visit` with a fresh place to collect `break` states, and return
|
|
7691
7672
|
* what it collected. */
|
|
7692
7673
|
withBreakScope(visit) {
|
|
@@ -7757,8 +7738,10 @@ var TypeAnalyzer = class {
|
|
|
7757
7738
|
// --------------------------------------------------------
|
|
7758
7739
|
// Functions
|
|
7759
7740
|
// --------------------------------------------------------
|
|
7760
|
-
|
|
7761
|
-
|
|
7741
|
+
/** `returns`, when given, collects the type of each `return` the walk
|
|
7742
|
+
* reads — see `inferFunctionBody`. */
|
|
7743
|
+
visitFunctionBody(func, outerEnv, returns) {
|
|
7744
|
+
this.withTypeParams(func.generics, () => this.visitFunctionBodyInner(func, outerEnv, returns));
|
|
7762
7745
|
}
|
|
7763
7746
|
/** A parameter's type: annotation, else a shape synthesized from a
|
|
7764
7747
|
* destructuring pattern, else inferred from its default, else `any`. */
|
|
@@ -7807,6 +7790,53 @@ var TypeAnalyzer = class {
|
|
|
7807
7790
|
* it, as in TypeScript: `signal:Connect(function(player) ... end)` knows
|
|
7808
7791
|
* `player` from `Connect`'s callback type. Anything else is inferred as
|
|
7809
7792
|
* usual. */
|
|
7793
|
+
/** The arguments of a call, in two passes where one is needed.
|
|
7794
|
+
*
|
|
7795
|
+
* A function written at the call site with a parameter nothing annotates
|
|
7796
|
+
* reads that parameter from the signature — and where the signature says
|
|
7797
|
+
* `(v: T) => U`, `T` is only known once the other arguments have been
|
|
7798
|
+
* read. So such an argument is read once quietly, to get a shape to pick
|
|
7799
|
+
* a signature with, and then again with `T` in hand: `map(xs, v => v + 1)`
|
|
7800
|
+
* has its `v` a number by then, and `U` follows from what the body
|
|
7801
|
+
* answers. Everything else is read once, as it always was. */
|
|
7802
|
+
inferArguments(written, fns, selfOf, extra, env) {
|
|
7803
|
+
const expected = this.expectedArguments(written, fns, selfOf);
|
|
7804
|
+
written.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
7805
|
+
const waits = written.map((a, i) => this.waitsOnInference(a, expected[i]));
|
|
7806
|
+
if (!waits.some(Boolean)) return written.map((a) => this.infer(a, env));
|
|
7807
|
+
const argTypes = written.map((a, i) => waits[i] ? this.quietly(() => this.infer(a, env)) : this.infer(a, env));
|
|
7808
|
+
const picked = this.pickOverload(fns, argTypes, (f) => extra(f, argTypes), this.spreadOf(written, selfOf(fns[0])));
|
|
7809
|
+
const subst = picked?.typeParams?.length ? this.inferTypeArgs(picked, extra(picked, argTypes)) : void 0;
|
|
7810
|
+
written.forEach((a, i) => {
|
|
7811
|
+
if (!waits[i]) return;
|
|
7812
|
+
if (picked && subst) {
|
|
7813
|
+
const self = selfOf(picked);
|
|
7814
|
+
const declared = i + self < picked.params.length ? picked.params[i + self].type : picked.varargs;
|
|
7815
|
+
if (declared) this.applyContext(a, this.reduceType(substitute(declared, subst)));
|
|
7816
|
+
}
|
|
7817
|
+
argTypes[i] = this.infer(a, env);
|
|
7818
|
+
});
|
|
7819
|
+
return argTypes;
|
|
7820
|
+
}
|
|
7821
|
+
/** Is this argument a function whose own type waits on the call's? */
|
|
7822
|
+
waitsOnInference(arg, expected) {
|
|
7823
|
+
if (!expected || !containsTypeParam(expected)) return false;
|
|
7824
|
+
let e = arg;
|
|
7825
|
+
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
7826
|
+
if (e.type !== "FunctionExpression") return false;
|
|
7827
|
+
return e.func.params.some((p) => !p.typeAnnotation && !p.default && !p.pattern && p.name !== "self" && p.name !== "this");
|
|
7828
|
+
}
|
|
7829
|
+
/** Read something without saying anything about it: a first pass whose
|
|
7830
|
+
* only purpose is a shape to go on. */
|
|
7831
|
+
quietly(read) {
|
|
7832
|
+
const was = this.emitDiagnostics;
|
|
7833
|
+
this.emitDiagnostics = false;
|
|
7834
|
+
try {
|
|
7835
|
+
return read();
|
|
7836
|
+
} finally {
|
|
7837
|
+
this.emitDiagnostics = was;
|
|
7838
|
+
}
|
|
7839
|
+
}
|
|
7810
7840
|
applyContext(expr, expected) {
|
|
7811
7841
|
let e = expr;
|
|
7812
7842
|
while (e.type === "ParenthesizedExpression") e = e.expression;
|
|
@@ -7955,7 +7985,7 @@ var TypeAnalyzer = class {
|
|
|
7955
7985
|
this.contextualReturns.pop();
|
|
7956
7986
|
}
|
|
7957
7987
|
}
|
|
7958
|
-
visitFunctionBodyInner(func, outerEnv) {
|
|
7988
|
+
visitFunctionBodyInner(func, outerEnv, returns) {
|
|
7959
7989
|
const env = forkEnv(outerEnv);
|
|
7960
7990
|
for (const p of func.params) {
|
|
7961
7991
|
if (p.pattern) {
|
|
@@ -7972,7 +8002,7 @@ var TypeAnalyzer = class {
|
|
|
7972
8002
|
if (p.typeAnnotation) this.annotated.add(id);
|
|
7973
8003
|
}
|
|
7974
8004
|
}
|
|
7975
|
-
this.withVarargs(func, () => this.collectReturns(
|
|
8005
|
+
this.withVarargs(func, () => this.collectReturns(returns, () => {
|
|
7976
8006
|
this.visitBlock(func.body, env);
|
|
7977
8007
|
this.checkReturnsAtAll(func, this.declaredReturns[this.declaredReturns.length - 1]);
|
|
7978
8008
|
}));
|
|
@@ -8026,7 +8056,7 @@ var TypeAnalyzer = class {
|
|
|
8026
8056
|
if (arg === void 0) return;
|
|
8027
8057
|
const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
|
|
8028
8058
|
const preserve = keepsLiterals(param) || param.kind !== "typeParam" && containsTypeParam(param);
|
|
8029
|
-
unify(p.type, preserve ? arg : widen(arg), vars, subst);
|
|
8059
|
+
unify(p.type, preserve ? arg : widen(arg), vars, subst, "first");
|
|
8030
8060
|
});
|
|
8031
8061
|
if (f.varargs && f.restIsWhole) {
|
|
8032
8062
|
const rest = argTypes.slice(f.params.length).map((a) => a === void 0 ? unknownType : widen(a));
|
|
@@ -8377,7 +8407,12 @@ var TypeAnalyzer = class {
|
|
|
8377
8407
|
asserts: node.asserts
|
|
8378
8408
|
};
|
|
8379
8409
|
}
|
|
8380
|
-
|
|
8410
|
+
/** `visited`, when given, is what the real walk of the body just
|
|
8411
|
+
* collected from its `return`s: a function expression is walked before
|
|
8412
|
+
* its type is asked for, so there is nothing left to guess, and walking
|
|
8413
|
+
* the body a second time only to read them made every callback nested in
|
|
8414
|
+
* another cost twice its parent's. */
|
|
8415
|
+
inferFunctionBody(func, env, visited) {
|
|
8381
8416
|
const names = func.generics.map((g) => g.name);
|
|
8382
8417
|
return this.withTypeParams(func.generics, () => {
|
|
8383
8418
|
const params = func.params.flatMap((p) => {
|
|
@@ -8406,6 +8441,8 @@ var TypeAnalyzer = class {
|
|
|
8406
8441
|
returns = this.resolveType(func.returnType);
|
|
8407
8442
|
} else if (func.predicate) {
|
|
8408
8443
|
returns = booleanType;
|
|
8444
|
+
} else if (visited) {
|
|
8445
|
+
returns = visited.length ? union([...visited]) : this.withVarargs(func, () => this.silently(() => this.inferReturnType(func.body, bodyEnv)));
|
|
8409
8446
|
} else {
|
|
8410
8447
|
const collected = [];
|
|
8411
8448
|
returns = this.withVarargs(func, () => this.silently(() => {
|
|
@@ -9278,10 +9315,12 @@ var TypeAnalyzer = class {
|
|
|
9278
9315
|
return this.inferArray(expr, env, false);
|
|
9279
9316
|
case "TableExpression":
|
|
9280
9317
|
return this.inferObject(expr, env, false);
|
|
9281
|
-
case "FunctionExpression":
|
|
9318
|
+
case "FunctionExpression": {
|
|
9282
9319
|
this.checkParamOrder(expr.func.params, expr);
|
|
9283
|
-
|
|
9284
|
-
|
|
9320
|
+
const returned = [];
|
|
9321
|
+
this.visitFunctionBody(expr.func, env, returned);
|
|
9322
|
+
return this.inferFunctionBody(expr.func, env, returned);
|
|
9323
|
+
}
|
|
9285
9324
|
case "ParenthesizedExpression":
|
|
9286
9325
|
return this.infer(expr.expression, env);
|
|
9287
9326
|
case "TypeAssertionExpression": {
|
|
@@ -9485,9 +9524,7 @@ var TypeAnalyzer = class {
|
|
|
9485
9524
|
const united = this.unionSignatures(callee);
|
|
9486
9525
|
const fns = united ?? this.overloadsOf(callee);
|
|
9487
9526
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9488
|
-
const
|
|
9489
|
-
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
9490
|
-
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
9527
|
+
const argTypes = this.inferArguments(expr.arguments, fns, () => 0, (_, args) => args, env);
|
|
9491
9528
|
if (fns.length) {
|
|
9492
9529
|
this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
|
|
9493
9530
|
const spread = this.spreadOf(expr.arguments);
|
|
@@ -9552,9 +9589,13 @@ var TypeAnalyzer = class {
|
|
|
9552
9589
|
const united = this.unionSignatures(method);
|
|
9553
9590
|
const fns = united ?? this.overloadsOf(method);
|
|
9554
9591
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
9555
|
-
const
|
|
9556
|
-
|
|
9557
|
-
|
|
9592
|
+
const argTypes = this.inferArguments(
|
|
9593
|
+
expr.arguments,
|
|
9594
|
+
fns,
|
|
9595
|
+
(f) => this.takesSelf(f) ? 1 : 0,
|
|
9596
|
+
(f, args) => this.takesSelf(f) ? [objType, ...args] : args,
|
|
9597
|
+
env
|
|
9598
|
+
);
|
|
9558
9599
|
if (fns.length) {
|
|
9559
9600
|
const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
|
|
9560
9601
|
const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
|
|
@@ -9610,7 +9651,35 @@ var TypeAnalyzer = class {
|
|
|
9610
9651
|
}
|
|
9611
9652
|
const read = this.expand(type);
|
|
9612
9653
|
if (read.kind === "unknown" && read.declared) this.reportUnknownAccess(object);
|
|
9613
|
-
|
|
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
|
+
}
|
|
9614
9683
|
}
|
|
9615
9684
|
/** Objects already reported as possibly nil: a loop body is visited more
|
|
9616
9685
|
* than once. */
|
|
@@ -10532,13 +10601,12 @@ var TypeAnalyzer = class {
|
|
|
10532
10601
|
computingAhead = /* @__PURE__ */ new Set();
|
|
10533
10602
|
declaredAhead(id) {
|
|
10534
10603
|
this.aheadDeclarations ??= this.indexAheadDeclarations();
|
|
10535
|
-
const
|
|
10536
|
-
if (!
|
|
10604
|
+
const statement = this.aheadDeclarations.get(id);
|
|
10605
|
+
if (!statement || this.computingAhead.has(id)) return void 0;
|
|
10537
10606
|
this.computingAhead.add(id);
|
|
10538
10607
|
const wasEmitting = this.emitDiagnostics;
|
|
10539
10608
|
this.emitDiagnostics = false;
|
|
10540
10609
|
try {
|
|
10541
|
-
const { statement, index } = found;
|
|
10542
10610
|
let type;
|
|
10543
10611
|
if (statement.type === "DeclareStatement") {
|
|
10544
10612
|
type = this.resolveType(statement.valueType);
|
|
@@ -10547,11 +10615,11 @@ var TypeAnalyzer = class {
|
|
|
10547
10615
|
} else if (statement.type === "ClassDeclaration") {
|
|
10548
10616
|
type = this.classValueType(statement);
|
|
10549
10617
|
} else if (statement.type === "VariableDeclaration") {
|
|
10550
|
-
const target = statement.
|
|
10618
|
+
const target = statement.name;
|
|
10551
10619
|
if (target.type === "IdentifierPattern" && target.typeAnnotation) {
|
|
10552
10620
|
type = this.resolveType(target.typeAnnotation);
|
|
10553
|
-
} else if (statement.init
|
|
10554
|
-
const value = this.infer(statement.init
|
|
10621
|
+
} else if (statement.init) {
|
|
10622
|
+
const value = this.infer(statement.init, /* @__PURE__ */ new Map());
|
|
10555
10623
|
type = statement.kind === "const" ? value : widen(value);
|
|
10556
10624
|
}
|
|
10557
10625
|
}
|
|
@@ -10569,11 +10637,10 @@ var TypeAnalyzer = class {
|
|
|
10569
10637
|
for (const statement of this.program.body.statements) {
|
|
10570
10638
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
10571
10639
|
if (declaration.type !== "VariableDeclaration") continue;
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10575
|
-
|
|
10576
|
-
});
|
|
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);
|
|
10577
10644
|
}
|
|
10578
10645
|
const visit = (node) => {
|
|
10579
10646
|
if (!node || typeof node !== "object") return;
|
|
@@ -10584,11 +10651,11 @@ var TypeAnalyzer = class {
|
|
|
10584
10651
|
const record = node;
|
|
10585
10652
|
if (record.type === "ClassDeclaration" && record.name) {
|
|
10586
10653
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10587
|
-
if (id !== void 0) out.set(id,
|
|
10654
|
+
if (id !== void 0) out.set(id, node);
|
|
10588
10655
|
}
|
|
10589
10656
|
if (record.type === "FunctionDeclaration" && record.name) {
|
|
10590
10657
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
10591
|
-
if (id !== void 0) out.set(id,
|
|
10658
|
+
if (id !== void 0) out.set(id, node);
|
|
10592
10659
|
}
|
|
10593
10660
|
for (const [key, value] of Object.entries(node)) {
|
|
10594
10661
|
if (key !== "line" && key !== "column" && value && typeof value === "object") visit(value);
|
|
@@ -10597,7 +10664,7 @@ var TypeAnalyzer = class {
|
|
|
10597
10664
|
visit(this.program.body);
|
|
10598
10665
|
for (const [name, statement] of this.deferredDeclares) {
|
|
10599
10666
|
const id = this.scopes.globalsByName.get(name);
|
|
10600
|
-
if (id !== void 0) out.set(id,
|
|
10667
|
+
if (id !== void 0) out.set(id, statement);
|
|
10601
10668
|
}
|
|
10602
10669
|
return out;
|
|
10603
10670
|
}
|