luaut-parser 4.0.0 → 5.0.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 +296 -20
- package/dist/index.cjs +1535 -121
- package/dist/index.d.cts +143 -20
- package/dist/index.d.ts +143 -20
- package/dist/index.js +1534 -121
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -70,6 +70,7 @@ var Punctuators = [
|
|
|
70
70
|
",",
|
|
71
71
|
".",
|
|
72
72
|
"?",
|
|
73
|
+
"=>",
|
|
73
74
|
"->",
|
|
74
75
|
"&",
|
|
75
76
|
"|",
|
|
@@ -594,6 +595,13 @@ function applyDirectives(directives, diagnostics, lineOf) {
|
|
|
594
595
|
var UNUSED_EXPECT_ERROR = "Unused '@luaut-expect-error' directive";
|
|
595
596
|
|
|
596
597
|
// src/ast/builders.ts
|
|
598
|
+
function bindThis(func, at) {
|
|
599
|
+
bindThisParam(func.params, at);
|
|
600
|
+
func.isMethod = true;
|
|
601
|
+
}
|
|
602
|
+
function bindThisParam(params, at) {
|
|
603
|
+
params.unshift({ type: "FunctionParameter", name: "this", ...spanFrom(at, at) });
|
|
604
|
+
}
|
|
597
605
|
var ParseError = class extends Error {
|
|
598
606
|
constructor(message, line, column) {
|
|
599
607
|
super(`${message} (${line}:${column})`);
|
|
@@ -687,10 +695,13 @@ var Parser = class {
|
|
|
687
695
|
indentation;
|
|
688
696
|
/** Populated in recovery mode. */
|
|
689
697
|
errors = [];
|
|
690
|
-
/** Recovery found a block without its `
|
|
698
|
+
/** Recovery found a block without its `}`. */
|
|
691
699
|
missingEnd = false;
|
|
692
700
|
/** The column of the first token on each line, for `indentation`. */
|
|
693
701
|
lineIndent;
|
|
702
|
+
/** Inside a class body, where `super` means the base class. Outside
|
|
703
|
+
* one it is an ordinary name, so existing code using it still reads. */
|
|
704
|
+
classDepth = 0;
|
|
694
705
|
constructor(tokens, options = {}) {
|
|
695
706
|
this.tokens = tokens;
|
|
696
707
|
this.recover = options.recover ?? false;
|
|
@@ -829,8 +840,12 @@ var Parser = class {
|
|
|
829
840
|
expressionOr(stop) {
|
|
830
841
|
return this.attempt(() => this.parseExpression(), stop, (start, from) => this.errorExpression(start, from));
|
|
831
842
|
}
|
|
843
|
+
/** A comma-separated list of values: a `return`'s, a declaration's, an
|
|
844
|
+
* assignment's. `...xs` spreads an array into it, as in a call's
|
|
845
|
+
* arguments; bare `...` is the vararg pack, as it always was. */
|
|
832
846
|
expressionListOr(stop) {
|
|
833
|
-
const
|
|
847
|
+
const until = () => stop() || this.checkPunctuator(",");
|
|
848
|
+
const item = () => this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(until) : this.expressionOr(until);
|
|
834
849
|
const list = [item()];
|
|
835
850
|
while (this.matchPunctuator(",")) list.push(item());
|
|
836
851
|
return list;
|
|
@@ -998,7 +1013,62 @@ var Parser = class {
|
|
|
998
1013
|
// Block / Statement
|
|
999
1014
|
// ============================================================
|
|
1000
1015
|
isBlockEnd() {
|
|
1001
|
-
return this.isAtEnd() || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
|
|
1016
|
+
return this.isAtEnd() || this.checkPunctuator("}") || this.checkKeyword("end") || this.checkKeyword("else") || this.checkKeyword("elseif") || this.checkKeyword("until");
|
|
1017
|
+
}
|
|
1018
|
+
// ============================================================
|
|
1019
|
+
// Bodies
|
|
1020
|
+
// ------------------------------------------------------------
|
|
1021
|
+
// luaut writes a block in braces — `if (ready) { ... }`, `function f() {
|
|
1022
|
+
// ... }`. The `end` spellings Lua uses are still read, so a file written
|
|
1023
|
+
// in them keeps working while it is being moved over.
|
|
1024
|
+
//
|
|
1025
|
+
// A condition is in parentheses because `f {}` is a call: without them
|
|
1026
|
+
// `if ready { ... }` would be a call of `ready` and then a block. With
|
|
1027
|
+
// them the form is decided by looking past the closing parenthesis, which
|
|
1028
|
+
// is why a parenthesized condition in the older spelling still reads.
|
|
1029
|
+
// ============================================================
|
|
1030
|
+
/** `{ ... }` — a block in braces. */
|
|
1031
|
+
parseBraceBlock() {
|
|
1032
|
+
const brace = this.advance();
|
|
1033
|
+
const body = this.parseBlock(brace);
|
|
1034
|
+
if (this.matchPunctuator("}")) return body;
|
|
1035
|
+
if (!this.recover) this.error(`Expected '}' to close the block opened on line ${brace.line.start}`);
|
|
1036
|
+
this.softError(`Expected '}' to close the block opened on line ${brace.line.start}`);
|
|
1037
|
+
this.missingEnd = true;
|
|
1038
|
+
return body;
|
|
1039
|
+
}
|
|
1040
|
+
/** The `{ ... }` a construct's body is written in. Half-written — the
|
|
1041
|
+
* `{` not typed yet — it is empty and says so, rather than throwing the
|
|
1042
|
+
* whole construct away: what has been written is what an editor answers
|
|
1043
|
+
* from. */
|
|
1044
|
+
parseBracedBody(what) {
|
|
1045
|
+
if (this.checkPunctuator("{")) return this.parseBraceBlock();
|
|
1046
|
+
const at = this.current();
|
|
1047
|
+
if (!this.recover) this.error(`Expected '{' to open the body of '${what}'`);
|
|
1048
|
+
this.softError(`Expected '{' to open the body of '${what}'`);
|
|
1049
|
+
return { type: "Block", statements: [], ...spanFrom(at, at) };
|
|
1050
|
+
}
|
|
1051
|
+
/** Does a `{` follow the parenthesized group starting here? That is what
|
|
1052
|
+
* tells `if (ready) { ... }` from `if (ready) then ... end`. */
|
|
1053
|
+
braceFollowsGroup() {
|
|
1054
|
+
if (!this.checkPunctuator("(")) return false;
|
|
1055
|
+
const closers = { "(": ")", "[": "]", "{": "}" };
|
|
1056
|
+
const stack = [];
|
|
1057
|
+
for (let i = 0; ; i++) {
|
|
1058
|
+
const token = this.peek(i);
|
|
1059
|
+
if (token.type === "EOF") return false;
|
|
1060
|
+
if (token.type === "Punctuator") {
|
|
1061
|
+
const value = String(token.value);
|
|
1062
|
+
if (closers[value]) stack.push(closers[value]);
|
|
1063
|
+
else if (value === stack[stack.length - 1]) {
|
|
1064
|
+
stack.pop();
|
|
1065
|
+
if (!stack.length) {
|
|
1066
|
+
const next = this.peek(i + 1);
|
|
1067
|
+
return next.type === "Punctuator" && next.value === "{";
|
|
1068
|
+
}
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1002
1072
|
}
|
|
1003
1073
|
/** `opener` is the token that began the block (`if`, `function`, ...), for
|
|
1004
1074
|
* indentation recovery. */
|
|
@@ -1110,6 +1180,9 @@ var Parser = class {
|
|
|
1110
1180
|
if (t.type === "Identifier" && t.value === "type" && this.peek(1).type === "Identifier") {
|
|
1111
1181
|
return this.parseTypeAliasStatement();
|
|
1112
1182
|
}
|
|
1183
|
+
if (t.type === "Identifier" && t.value === "class" && this.peek(1).type === "Identifier") {
|
|
1184
|
+
return this.parseClassDeclaration();
|
|
1185
|
+
}
|
|
1113
1186
|
if (t.type === "Identifier" && t.value === "declare") {
|
|
1114
1187
|
const p1 = this.peek(1);
|
|
1115
1188
|
if (p1.type === "Identifier" && p1.value === "class" && this.peek(2).type === "Identifier") {
|
|
@@ -1135,6 +1208,7 @@ var Parser = class {
|
|
|
1135
1208
|
name: p.name || void 0,
|
|
1136
1209
|
id: p.name ? nameIdentifier(p.name, p) : void 0,
|
|
1137
1210
|
optional: p.optional,
|
|
1211
|
+
rest: p.rest,
|
|
1138
1212
|
typeAnnotation: p.typeAnnotation ?? { type: "TypeReference", base: "any", typeArguments: [], line: p.line, column: p.column },
|
|
1139
1213
|
line: p.line,
|
|
1140
1214
|
column: p.column
|
|
@@ -1283,7 +1357,7 @@ var Parser = class {
|
|
|
1283
1357
|
this.advance();
|
|
1284
1358
|
if (this.checkIdentifierValue("default")) {
|
|
1285
1359
|
this.advance();
|
|
1286
|
-
const declaration = this.parseExpression(0);
|
|
1360
|
+
const declaration = this.checkIdentifierValue("class") && this.peek(1).type === "Identifier" && !this.punctuatorAt(2, ":") && !this.operatorAt(2, "=") && !this.punctuatorAt(2, "(") ? this.parseClassDeclaration() : this.parseExpression(0);
|
|
1287
1361
|
return { type: "ExportDefaultStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1288
1362
|
}
|
|
1289
1363
|
if (this.checkIdentifierValue("type") && this.peek(1).type === "Identifier") {
|
|
@@ -1294,6 +1368,10 @@ var Parser = class {
|
|
|
1294
1368
|
const declaration = this.parseVariableDeclaration();
|
|
1295
1369
|
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1296
1370
|
}
|
|
1371
|
+
if (this.checkIdentifierValue("class") && this.peek(1).type === "Identifier") {
|
|
1372
|
+
const declaration = this.parseClassDeclaration();
|
|
1373
|
+
return { type: "ExportStatement", declaration, ...spanFrom(start, this.previous()) };
|
|
1374
|
+
}
|
|
1297
1375
|
if (this.checkKeyword("function")) {
|
|
1298
1376
|
const declaration = this.parseFunctionStatement(true);
|
|
1299
1377
|
if (declaration.type !== "FunctionDeclaration") this.error("An exported function needs a plain name: 'export function name()'");
|
|
@@ -1322,7 +1400,7 @@ var Parser = class {
|
|
|
1322
1400
|
const source = this.parseModuleSource();
|
|
1323
1401
|
return { type: "ExportAllStatement", source, ...spanFrom(start, this.previous()) };
|
|
1324
1402
|
}
|
|
1325
|
-
this.error("Expected 'const', 'let', 'function', 'type', 'default', '{' or '*' after 'export'");
|
|
1403
|
+
this.error("Expected 'const', 'let', 'function', 'class', 'type', 'default', '{' or '*' after 'export'");
|
|
1326
1404
|
}
|
|
1327
1405
|
// `const x = ...` / `let x, y = ...`.
|
|
1328
1406
|
// luaut has no `local` — `const` bindings are immutable, `let` mutable.
|
|
@@ -1350,63 +1428,63 @@ var Parser = class {
|
|
|
1350
1428
|
parseIfStatement() {
|
|
1351
1429
|
const start = this.current();
|
|
1352
1430
|
this.expectKeyword("if");
|
|
1431
|
+
return this.parseBracedIf(start);
|
|
1432
|
+
}
|
|
1433
|
+
parseBracedIf(start) {
|
|
1353
1434
|
const clauses = [];
|
|
1354
|
-
const
|
|
1355
|
-
const cond = this.expressionOr(untilThen);
|
|
1356
|
-
this.expectKeywordSoft("then");
|
|
1357
|
-
const body = this.parseBlock(start);
|
|
1358
|
-
clauses.push({ type: "IfClause", condition: cond, body, ...spanFrom(cond, this.previous()) });
|
|
1359
|
-
while (this.checkKeyword("elseif")) {
|
|
1435
|
+
const clause = () => {
|
|
1360
1436
|
const clauseStart = this.current();
|
|
1361
|
-
this.
|
|
1362
|
-
const
|
|
1363
|
-
this.
|
|
1364
|
-
const
|
|
1365
|
-
clauses.push({ type: "IfClause", condition
|
|
1366
|
-
}
|
|
1437
|
+
this.expectPunctuator("(");
|
|
1438
|
+
const condition = this.expressionOr(() => this.checkPunctuator(")"));
|
|
1439
|
+
this.expectCloser(")");
|
|
1440
|
+
const body = this.parseBracedBody("if");
|
|
1441
|
+
clauses.push({ type: "IfClause", condition, body, ...spanFrom(clauseStart, this.previous()) });
|
|
1442
|
+
};
|
|
1443
|
+
clause();
|
|
1367
1444
|
let alternate;
|
|
1368
|
-
|
|
1369
|
-
|
|
1445
|
+
while (this.checkKeyword("elseif") || this.checkKeyword("else")) {
|
|
1446
|
+
const isElse = this.checkKeyword("else");
|
|
1447
|
+
this.advance();
|
|
1448
|
+
if (!isElse) {
|
|
1449
|
+
clause();
|
|
1450
|
+
continue;
|
|
1451
|
+
}
|
|
1452
|
+
alternate = this.parseBracedBody("else");
|
|
1453
|
+
break;
|
|
1370
1454
|
}
|
|
1371
|
-
this.expectEnd(start);
|
|
1372
1455
|
return { type: "IfStatement", clauses, alternate, ...spanFrom(start, this.previous()) };
|
|
1373
1456
|
}
|
|
1374
1457
|
parseWhileStatement() {
|
|
1375
1458
|
const start = this.current();
|
|
1376
1459
|
this.expectKeyword("while");
|
|
1377
|
-
|
|
1378
|
-
this.
|
|
1379
|
-
|
|
1380
|
-
this.
|
|
1460
|
+
this.expectPunctuator("(");
|
|
1461
|
+
const condition = this.expressionOr(() => this.checkPunctuator(")"));
|
|
1462
|
+
this.expectCloser(")");
|
|
1463
|
+
const body = this.parseBracedBody("while");
|
|
1381
1464
|
return { type: "WhileStatement", condition, body, ...spanFrom(start, this.previous()) };
|
|
1382
1465
|
}
|
|
1383
1466
|
parseRepeatStatement() {
|
|
1384
1467
|
const start = this.current();
|
|
1385
1468
|
this.expectKeyword("repeat");
|
|
1386
|
-
const body = this.
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
} else {
|
|
1392
|
-
this.softError(`Expected 'until' to close 'repeat' on line ${start.line.start}`);
|
|
1393
|
-
this.missingEnd = true;
|
|
1394
|
-
condition = this.errorExpression(this.current(), this.cursor);
|
|
1395
|
-
}
|
|
1469
|
+
const body = this.parseBracedBody("repeat");
|
|
1470
|
+
this.expectKeyword("until");
|
|
1471
|
+
this.expectPunctuator("(");
|
|
1472
|
+
const condition = this.expressionOr(() => this.checkPunctuator(")"));
|
|
1473
|
+
this.expectCloser(")");
|
|
1396
1474
|
return { type: "RepeatStatement", body, condition, ...spanFrom(start, this.previous()) };
|
|
1397
1475
|
}
|
|
1398
1476
|
parseDoStatement() {
|
|
1399
1477
|
const start = this.current();
|
|
1400
1478
|
this.expectKeyword("do");
|
|
1401
|
-
const body = this.
|
|
1402
|
-
this.expectEnd(start);
|
|
1479
|
+
const body = this.parseBracedBody("do");
|
|
1403
1480
|
return { type: "DoStatement", body, ...spanFrom(start, this.previous()) };
|
|
1404
1481
|
}
|
|
1405
1482
|
parseForStatement() {
|
|
1406
1483
|
const start = this.current();
|
|
1407
1484
|
this.expectKeyword("for");
|
|
1485
|
+
this.expectPunctuator("(");
|
|
1408
1486
|
const first = this.parseBindingTarget(true);
|
|
1409
|
-
const untilDo = () => this.
|
|
1487
|
+
const untilDo = () => this.checkPunctuator(")");
|
|
1410
1488
|
if (first.type === "IdentifierPattern" && this.matchOperator("=")) {
|
|
1411
1489
|
const from = this.expressionOr(() => untilDo() || this.checkPunctuator(","));
|
|
1412
1490
|
this.expectPunctuator(",");
|
|
@@ -1415,9 +1493,7 @@ var Parser = class {
|
|
|
1415
1493
|
if (this.matchPunctuator(",")) {
|
|
1416
1494
|
step = this.expressionOr(untilDo);
|
|
1417
1495
|
}
|
|
1418
|
-
this.
|
|
1419
|
-
const body2 = this.parseBlock(start);
|
|
1420
|
-
this.expectEnd(start);
|
|
1496
|
+
const body2 = this.parseForBody();
|
|
1421
1497
|
return {
|
|
1422
1498
|
type: "NumericForStatement",
|
|
1423
1499
|
variable: this.identifierPatternToTypedIdentifier(first),
|
|
@@ -1434,9 +1510,7 @@ var Parser = class {
|
|
|
1434
1510
|
}
|
|
1435
1511
|
this.expectKeyword("in");
|
|
1436
1512
|
const iterators = this.expressionListOr(untilDo);
|
|
1437
|
-
this.
|
|
1438
|
-
const body = this.parseBlock(start);
|
|
1439
|
-
this.expectEnd(start);
|
|
1513
|
+
const body = this.parseForBody();
|
|
1440
1514
|
return {
|
|
1441
1515
|
type: "GenericForStatement",
|
|
1442
1516
|
variables,
|
|
@@ -1445,6 +1519,10 @@ var Parser = class {
|
|
|
1445
1519
|
...spanFrom(start, this.previous())
|
|
1446
1520
|
};
|
|
1447
1521
|
}
|
|
1522
|
+
parseForBody() {
|
|
1523
|
+
this.expectCloser(")");
|
|
1524
|
+
return this.parseBracedBody("for");
|
|
1525
|
+
}
|
|
1448
1526
|
/** `function name() end` declares `name`; `function a.b() end` and
|
|
1449
1527
|
* `function T:m() end` define a member. */
|
|
1450
1528
|
/** `exported` — the `export` before this `function` has been consumed, so
|
|
@@ -1511,6 +1589,183 @@ var Parser = class {
|
|
|
1511
1589
|
if (!this.recover) throw error;
|
|
1512
1590
|
this.record(error);
|
|
1513
1591
|
}
|
|
1592
|
+
/** `class Name extends Base <members> end`.
|
|
1593
|
+
*
|
|
1594
|
+
* The body is a block like every other in luaut, closed by `end` — not a
|
|
1595
|
+
* brace-delimited list. Members are written the way the same thing is
|
|
1596
|
+
* written outside a class: a field like a field (`x: number`), a method
|
|
1597
|
+
* like a function (`function m() ... end`). */
|
|
1598
|
+
parseClassDeclaration() {
|
|
1599
|
+
const start = this.current();
|
|
1600
|
+
this.advance();
|
|
1601
|
+
const name = this.parseIdentifier();
|
|
1602
|
+
const typeParams = this.checkOperator("<") ? this.parseGenericTypeParameterList() : [];
|
|
1603
|
+
const { superclass, superArguments } = this.parseExtends();
|
|
1604
|
+
const members = this.parseClassBody(start);
|
|
1605
|
+
return {
|
|
1606
|
+
type: "ClassDeclaration",
|
|
1607
|
+
name,
|
|
1608
|
+
typeParams,
|
|
1609
|
+
superclass,
|
|
1610
|
+
superArguments,
|
|
1611
|
+
members,
|
|
1612
|
+
...spanFrom(start, this.previous())
|
|
1613
|
+
};
|
|
1614
|
+
}
|
|
1615
|
+
/** `class ... end` as a value. It may be named — the name is for the class
|
|
1616
|
+
* itself, not for the scope around it — and takes no type parameters,
|
|
1617
|
+
* since nothing could write the arguments. */
|
|
1618
|
+
parseClassExpression() {
|
|
1619
|
+
const start = this.current();
|
|
1620
|
+
this.advance();
|
|
1621
|
+
const name = this.checkType("Identifier") && !this.checkIdentifierValue("extends") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=") && !this.punctuatorAt(1, "(") ? this.parseIdentifier() : void 0;
|
|
1622
|
+
if (this.checkOperator("<")) {
|
|
1623
|
+
this.error("A class written as a value takes no type parameters: nothing could write the arguments");
|
|
1624
|
+
}
|
|
1625
|
+
const { superclass, superArguments } = this.parseExtends();
|
|
1626
|
+
const members = this.parseClassBody(start);
|
|
1627
|
+
return { type: "ClassExpression", name, superclass, superArguments, members, ...spanFrom(start, this.previous()) };
|
|
1628
|
+
}
|
|
1629
|
+
/** `extends Base` / `extends Box<number>`. */
|
|
1630
|
+
parseExtends() {
|
|
1631
|
+
if (!this.checkIdentifierValue("extends")) return {};
|
|
1632
|
+
this.advance();
|
|
1633
|
+
const superclass = this.parseIdentifier();
|
|
1634
|
+
let superArguments;
|
|
1635
|
+
if (this.checkOperator("<")) {
|
|
1636
|
+
const written = this.tryTypeArguments();
|
|
1637
|
+
if (written) superArguments = written;
|
|
1638
|
+
}
|
|
1639
|
+
return { superclass, superArguments };
|
|
1640
|
+
}
|
|
1641
|
+
parseClassBody(start) {
|
|
1642
|
+
void start;
|
|
1643
|
+
if (!this.checkPunctuator("{")) this.error("Expected '{' to open the class body");
|
|
1644
|
+
this.advance();
|
|
1645
|
+
const members = [];
|
|
1646
|
+
this.classDepth++;
|
|
1647
|
+
try {
|
|
1648
|
+
while (!this.checkPunctuator("}") && !this.isAtEnd()) {
|
|
1649
|
+
if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
|
|
1650
|
+
const member = this.parseClassMember();
|
|
1651
|
+
if (member) members.push(member);
|
|
1652
|
+
}
|
|
1653
|
+
} finally {
|
|
1654
|
+
this.classDepth--;
|
|
1655
|
+
}
|
|
1656
|
+
this.expectCloser("}");
|
|
1657
|
+
return members;
|
|
1658
|
+
}
|
|
1659
|
+
/** `<A, B>` in a type position that is not a call: the arguments a class
|
|
1660
|
+
* extends its base with. */
|
|
1661
|
+
tryTypeArguments() {
|
|
1662
|
+
const saved = this.cursor;
|
|
1663
|
+
try {
|
|
1664
|
+
this.expectOperator("<");
|
|
1665
|
+
const args = [this.parseType()];
|
|
1666
|
+
while (this.matchPunctuator(",")) args.push(this.parseType());
|
|
1667
|
+
this.expectOperator(">");
|
|
1668
|
+
return args;
|
|
1669
|
+
} catch (error) {
|
|
1670
|
+
if (error instanceof ParseError || error instanceof ParseRecover) {
|
|
1671
|
+
this.cursor = saved;
|
|
1672
|
+
return void 0;
|
|
1673
|
+
}
|
|
1674
|
+
throw error;
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
parseClassMember() {
|
|
1678
|
+
const start = this.current();
|
|
1679
|
+
const isStatic = this.checkIdentifierValue("static") && !this.punctuatorAt(1, ":") && !this.operatorAt(1, "=");
|
|
1680
|
+
if (isStatic) this.advance();
|
|
1681
|
+
if (this.checkKeyword("function")) {
|
|
1682
|
+
this.advance();
|
|
1683
|
+
const memberName = this.parseIdentifier();
|
|
1684
|
+
const signatures = [];
|
|
1685
|
+
let written = memberName;
|
|
1686
|
+
while (true) {
|
|
1687
|
+
const head = this.parseFunctionHead();
|
|
1688
|
+
if (this.isClassOverloadContinuation(memberName.name, isStatic)) {
|
|
1689
|
+
if (!isStatic) bindThisParam(head.params, start);
|
|
1690
|
+
signatures.push({ ...this.headToSignature(head), name: written });
|
|
1691
|
+
if (isStatic) this.advance();
|
|
1692
|
+
this.expectKeyword("function");
|
|
1693
|
+
written = this.parseIdentifier();
|
|
1694
|
+
continue;
|
|
1695
|
+
}
|
|
1696
|
+
const func = this.headToBody(head, start);
|
|
1697
|
+
if (!isStatic) bindThis(func, start);
|
|
1698
|
+
return {
|
|
1699
|
+
type: "ClassMethod",
|
|
1700
|
+
name: memberName,
|
|
1701
|
+
isStatic,
|
|
1702
|
+
func,
|
|
1703
|
+
signatures: signatures.length ? signatures : void 0,
|
|
1704
|
+
...spanFrom(start, this.previous())
|
|
1705
|
+
};
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
if (!isStatic && this.checkIdentifierValue("constructor") && this.punctuatorAt(1, "(")) {
|
|
1709
|
+
this.advance();
|
|
1710
|
+
const head = this.parseFunctionHead();
|
|
1711
|
+
if (head.returnType) this.problem("A constructor has no return type; it always builds the instance");
|
|
1712
|
+
const func = this.headToBody(head, start);
|
|
1713
|
+
bindThis(func, start);
|
|
1714
|
+
return { type: "ClassConstructor", func, ...spanFrom(start, this.previous()) };
|
|
1715
|
+
}
|
|
1716
|
+
if ((this.checkIdentifierValue("get") || this.checkIdentifierValue("set")) && this.peek(1).type === "Identifier" && this.punctuatorAt(2, "(")) {
|
|
1717
|
+
const kind = this.advance().value;
|
|
1718
|
+
const memberName = this.parseIdentifier();
|
|
1719
|
+
const head = this.parseFunctionHead();
|
|
1720
|
+
const func = this.headToBody(head, start);
|
|
1721
|
+
if (!isStatic) bindThis(func, start);
|
|
1722
|
+
const written = func.params.length - (isStatic ? 0 : 1);
|
|
1723
|
+
if (kind === "get" && written > 0) {
|
|
1724
|
+
this.problem("A getter takes no parameters");
|
|
1725
|
+
}
|
|
1726
|
+
if (kind === "set" && written !== 1) {
|
|
1727
|
+
this.problem("A setter takes exactly one parameter: the value being assigned");
|
|
1728
|
+
}
|
|
1729
|
+
return { type: "ClassAccessor", kind, name: memberName, isStatic, func, ...spanFrom(start, this.previous()) };
|
|
1730
|
+
}
|
|
1731
|
+
if (this.checkType("Identifier")) {
|
|
1732
|
+
const memberName = this.parseIdentifier();
|
|
1733
|
+
let typeAnnotation;
|
|
1734
|
+
if (this.matchPunctuator(":")) {
|
|
1735
|
+
typeAnnotation = this.typeOr(() => this.checkOperator("="));
|
|
1736
|
+
}
|
|
1737
|
+
let init;
|
|
1738
|
+
if (this.matchOperator("=")) init = this.expressionOr(() => false);
|
|
1739
|
+
if (!typeAnnotation && !init) {
|
|
1740
|
+
this.error("A class field needs a type ('name: T') or a value ('name = v')");
|
|
1741
|
+
}
|
|
1742
|
+
return { type: "ClassField", name: memberName, isStatic, typeAnnotation, init, ...spanFrom(start, this.previous()) };
|
|
1743
|
+
}
|
|
1744
|
+
if (this.recover) {
|
|
1745
|
+
this.softError("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
|
|
1746
|
+
this.advance();
|
|
1747
|
+
return void 0;
|
|
1748
|
+
}
|
|
1749
|
+
this.error("Expected a class member: a field, 'function', 'constructor', 'get' or 'set'");
|
|
1750
|
+
}
|
|
1751
|
+
/** Give a class method its `this`: a real first parameter, the way
|
|
1752
|
+
* `function T:m()` gets a real `self`. Every later pass — scopes, types,
|
|
1753
|
+
* arity, lowering — then sees an ordinary parameter and needs to know
|
|
1754
|
+
* nothing about classes. */
|
|
1755
|
+
/** After a bodyless head inside a class body, does another declaration of
|
|
1756
|
+
* the same member follow? Then the head was an overload signature. */
|
|
1757
|
+
isClassOverloadContinuation(name, isStatic) {
|
|
1758
|
+
const offset = isStatic ? 1 : 0;
|
|
1759
|
+
if (isStatic && !this.checkIdentifierValue("static")) return false;
|
|
1760
|
+
const keyword = this.peek(offset);
|
|
1761
|
+
if (!(keyword.type === "Keyword" && keyword.value === "function")) return false;
|
|
1762
|
+
const named = this.peek(offset + 1);
|
|
1763
|
+
return named.type === "Identifier" && named.value === name;
|
|
1764
|
+
}
|
|
1765
|
+
operatorAt(ahead, value) {
|
|
1766
|
+
const token = this.peek(ahead);
|
|
1767
|
+
return token.type === "Operator" && token.value === value;
|
|
1768
|
+
}
|
|
1514
1769
|
parseFunctionName() {
|
|
1515
1770
|
const start = this.current();
|
|
1516
1771
|
const base = this.parseIdentifier();
|
|
@@ -1604,7 +1859,7 @@ var Parser = class {
|
|
|
1604
1859
|
...spanFrom(start, this.previous())
|
|
1605
1860
|
};
|
|
1606
1861
|
}
|
|
1607
|
-
if (first.type === "CallExpression" || first.type === "MethodCallExpression") {
|
|
1862
|
+
if (first.type === "CallExpression" || first.type === "MethodCallExpression" || first.type === "NewExpression") {
|
|
1608
1863
|
return { type: "CallStatement", expression: first, ...spanFrom(start, this.previous()) };
|
|
1609
1864
|
}
|
|
1610
1865
|
this.error("Unexpected expression statement (expected assignment or call)");
|
|
@@ -1792,12 +2047,20 @@ var Parser = class {
|
|
|
1792
2047
|
if (t.type === "Keyword" && t.value === "if") {
|
|
1793
2048
|
return this.parseIfElseExpression();
|
|
1794
2049
|
}
|
|
2050
|
+
if (t.type === "Identifier" && t.value === "class") {
|
|
2051
|
+
return this.parseClassExpression();
|
|
2052
|
+
}
|
|
1795
2053
|
if (t.type === "Punctuator" && t.value === "{") {
|
|
1796
2054
|
return this.parseTableExpression();
|
|
1797
2055
|
}
|
|
1798
2056
|
if (t.type === "Punctuator" && t.value === "[") {
|
|
1799
2057
|
return this.parseArrayExpression();
|
|
1800
2058
|
}
|
|
2059
|
+
if (this.checkType("Identifier") && this.punctuatorAt(1, "=>")) return this.parseArrow();
|
|
2060
|
+
if (this.checkPunctuator("(") || this.checkOperator("<")) {
|
|
2061
|
+
const arrow = this.tryParse(() => this.parseArrow());
|
|
2062
|
+
if (arrow) return arrow;
|
|
2063
|
+
}
|
|
1801
2064
|
if (t.type === "Identifier" || t.type === "Punctuator" && t.value === "(") {
|
|
1802
2065
|
return this.parsePrefixExpression();
|
|
1803
2066
|
}
|
|
@@ -1811,7 +2074,7 @@ var Parser = class {
|
|
|
1811
2074
|
} else {
|
|
1812
2075
|
let expression;
|
|
1813
2076
|
try {
|
|
1814
|
-
expression = shiftSpans(parseExpressionFromSource(p.raw), p.line, p.column);
|
|
2077
|
+
expression = shiftSpans(parseExpressionFromSource(p.raw, this.classDepth > 0), p.line, p.column);
|
|
1815
2078
|
} catch (e) {
|
|
1816
2079
|
if (!this.recover || !(e instanceof ParseError || e instanceof LexError)) throw e;
|
|
1817
2080
|
const at = token;
|
|
@@ -1849,7 +2112,12 @@ var Parser = class {
|
|
|
1849
2112
|
parsePrefixExpression() {
|
|
1850
2113
|
const start = this.current();
|
|
1851
2114
|
let base;
|
|
1852
|
-
if (this.
|
|
2115
|
+
if (this.startsNew()) {
|
|
2116
|
+
base = this.parseNewExpression();
|
|
2117
|
+
} else if (this.classDepth > 0 && this.checkIdentifierValue("super") && this.startsSuperUse()) {
|
|
2118
|
+
this.advance();
|
|
2119
|
+
base = { type: "SuperExpression", ...spanFrom(start, start) };
|
|
2120
|
+
} else if (this.checkType("Identifier")) {
|
|
1853
2121
|
base = this.parseIdentifier();
|
|
1854
2122
|
} else if (this.matchPunctuator("(")) {
|
|
1855
2123
|
const inner = this.parseExpression();
|
|
@@ -1946,11 +2214,13 @@ var Parser = class {
|
|
|
1946
2214
|
}
|
|
1947
2215
|
}
|
|
1948
2216
|
if (this.startsCallArguments()) {
|
|
2217
|
+
const onNewLine = this.current().line.start > base.line.end;
|
|
1949
2218
|
const args = this.parseCallArguments();
|
|
1950
2219
|
base = {
|
|
1951
2220
|
type: "CallExpression",
|
|
1952
2221
|
callee: base,
|
|
1953
2222
|
arguments: args,
|
|
2223
|
+
argumentsOnNewLine: onNewLine || void 0,
|
|
1954
2224
|
...spanFrom(base, this.previous())
|
|
1955
2225
|
};
|
|
1956
2226
|
continue;
|
|
@@ -1959,6 +2229,52 @@ var Parser = class {
|
|
|
1959
2229
|
}
|
|
1960
2230
|
return base;
|
|
1961
2231
|
}
|
|
2232
|
+
/** `new` is a soft keyword: it starts a construction only when a name
|
|
2233
|
+
* follows it, so a function or field called `new` — `Instance.new(x)`,
|
|
2234
|
+
* and `Vec.new(1)` itself — is untouched. */
|
|
2235
|
+
startsNew() {
|
|
2236
|
+
return this.checkIdentifierValue("new") && this.peek(1).type === "Identifier";
|
|
2237
|
+
}
|
|
2238
|
+
/** `super` on its own means the base class, and only `super(...)` and
|
|
2239
|
+
* `super.member` say anything; anything else is a name that happens to
|
|
2240
|
+
* be spelled that way. */
|
|
2241
|
+
startsSuperUse() {
|
|
2242
|
+
return this.punctuatorAt(1, "(") || this.punctuatorAt(1, ".") && this.peek(2).type === "Identifier";
|
|
2243
|
+
}
|
|
2244
|
+
/** `new Name(args)` / `new Module.Name(args)`. The callee is a name, or a
|
|
2245
|
+
* name reached through a module — never an arbitrary expression, so the
|
|
2246
|
+
* arguments are unambiguously the constructor's. */
|
|
2247
|
+
parseNewExpression() {
|
|
2248
|
+
const start = this.current();
|
|
2249
|
+
this.advance();
|
|
2250
|
+
let callee = this.parseIdentifier();
|
|
2251
|
+
while (this.checkPunctuator(".") && this.peek(1).type === "Identifier") {
|
|
2252
|
+
this.advance();
|
|
2253
|
+
const property = this.parseIdentifier();
|
|
2254
|
+
callee = { type: "MemberExpression", object: callee, property, ...spanFrom(start, property) };
|
|
2255
|
+
}
|
|
2256
|
+
const typeArguments = this.tryCallTypeArguments();
|
|
2257
|
+
if (!this.checkPunctuator("(")) {
|
|
2258
|
+
this.error("Expected '(' after the class being constructed: 'new Name(...)'");
|
|
2259
|
+
}
|
|
2260
|
+
const args = this.parseCallArguments();
|
|
2261
|
+
return { type: "NewExpression", callee, arguments: args, typeArguments, ...spanFrom(start, this.previous()) };
|
|
2262
|
+
}
|
|
2263
|
+
/** After `...`, is there something to spread? Nothing following it means
|
|
2264
|
+
* the vararg pack, which is what `f(...)` has always passed on. */
|
|
2265
|
+
startsSpread() {
|
|
2266
|
+
const next = this.peek(1);
|
|
2267
|
+
if (next.type === "Punctuator") {
|
|
2268
|
+
const value = next.value;
|
|
2269
|
+
return value === "(" || value === "{" || value === "[";
|
|
2270
|
+
}
|
|
2271
|
+
return next.type === "Identifier" || next.type === "Literal" || next.type === "InterpolatedString";
|
|
2272
|
+
}
|
|
2273
|
+
parseSpreadArgument(stop) {
|
|
2274
|
+
const dots = this.advance();
|
|
2275
|
+
const argument = this.expressionOr(stop);
|
|
2276
|
+
return { type: "SpreadElement", argument, ...spanFrom(dots, argument) };
|
|
2277
|
+
}
|
|
1962
2278
|
/** Is the token `ahead` places on the punctuator `value`? */
|
|
1963
2279
|
punctuatorAt(ahead, value) {
|
|
1964
2280
|
const token = this.peek(ahead);
|
|
@@ -2005,7 +2321,7 @@ var Parser = class {
|
|
|
2005
2321
|
while (true) {
|
|
2006
2322
|
if (this.recover && this.onNewLine() && this.startsTableField() && !this.startsMethodCall(1)) break;
|
|
2007
2323
|
const before = this.cursor;
|
|
2008
|
-
const argument = this.expressionOr(stop);
|
|
2324
|
+
const argument = this.checkOperator("...") && this.startsSpread() ? this.parseSpreadArgument(stop) : this.expressionOr(stop);
|
|
2009
2325
|
if (argument.type !== "ErrorExpression" || this.cursor > before || list.length) list.push(argument);
|
|
2010
2326
|
if (this.matchPunctuator(",") && !this.checkPunctuator(")")) continue;
|
|
2011
2327
|
if (!this.recover || this.checkPunctuator(")")) break;
|
|
@@ -2108,8 +2424,12 @@ var Parser = class {
|
|
|
2108
2424
|
while (!this.checkPunctuator("]")) {
|
|
2109
2425
|
if (this.checkOperator("...")) {
|
|
2110
2426
|
const dots = this.advance();
|
|
2111
|
-
|
|
2112
|
-
|
|
2427
|
+
if (this.checkPunctuator("]") || this.checkPunctuator(",")) {
|
|
2428
|
+
elements.push({ type: "VarargExpression", ...spanFrom(dots, dots) });
|
|
2429
|
+
} else {
|
|
2430
|
+
const argument = this.expressionOr(stop);
|
|
2431
|
+
elements.push({ type: "SpreadElement", argument, ...spanFrom(dots, argument) });
|
|
2432
|
+
}
|
|
2113
2433
|
} else {
|
|
2114
2434
|
elements.push(this.expressionOr(stop));
|
|
2115
2435
|
}
|
|
@@ -2298,8 +2618,26 @@ var Parser = class {
|
|
|
2298
2618
|
if (!this.checkPunctuator(")")) {
|
|
2299
2619
|
while (true) {
|
|
2300
2620
|
if (this.checkOperator("...")) {
|
|
2301
|
-
this.advance();
|
|
2621
|
+
const dots = this.advance();
|
|
2302
2622
|
hasVarargs = true;
|
|
2623
|
+
if (this.checkType("Identifier")) {
|
|
2624
|
+
const nameTok = this.expectIdentifier();
|
|
2625
|
+
let typeAnnotation2;
|
|
2626
|
+
if (this.matchPunctuator(":")) {
|
|
2627
|
+
typeAnnotation2 = this.typeOr(() => this.checkPunctuator(")"));
|
|
2628
|
+
}
|
|
2629
|
+
params.push({
|
|
2630
|
+
type: "FunctionParameter",
|
|
2631
|
+
name: nameTok.value,
|
|
2632
|
+
typeAnnotation: typeAnnotation2,
|
|
2633
|
+
rest: true,
|
|
2634
|
+
...spanFrom(dots, this.previous())
|
|
2635
|
+
});
|
|
2636
|
+
if (this.checkPunctuator(",")) {
|
|
2637
|
+
this.problem("A rest parameter is the last one: nothing can follow '...'");
|
|
2638
|
+
}
|
|
2639
|
+
break;
|
|
2640
|
+
}
|
|
2303
2641
|
if (this.matchPunctuator(":")) {
|
|
2304
2642
|
varargTypeAnnotation = this.parseTypeOrTypePackReference();
|
|
2305
2643
|
}
|
|
@@ -2391,10 +2729,64 @@ var Parser = class {
|
|
|
2391
2729
|
}
|
|
2392
2730
|
return void 0;
|
|
2393
2731
|
}
|
|
2732
|
+
/** Run `parse`, and put the parser back where it was if it fails. Used
|
|
2733
|
+
* where two forms start alike and only their end tells them apart: `(a,
|
|
2734
|
+
* b) => a + b` and `(a + b)` both open with a `(`. */
|
|
2735
|
+
tryParse(parse2) {
|
|
2736
|
+
const cursor = this.cursor;
|
|
2737
|
+
const errors = this.errors.length;
|
|
2738
|
+
try {
|
|
2739
|
+
return parse2();
|
|
2740
|
+
} catch (error) {
|
|
2741
|
+
if (!(error instanceof ParseError || error instanceof ParseRecover)) throw error;
|
|
2742
|
+
this.cursor = cursor;
|
|
2743
|
+
this.errors.length = errors;
|
|
2744
|
+
return void 0;
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
/** `x => x * 2` — a function, written short. The body is an expression,
|
|
2748
|
+
* which is returned, or a block in braces, as in TypeScript. */
|
|
2749
|
+
parseArrow() {
|
|
2750
|
+
const start = this.current();
|
|
2751
|
+
const head = this.checkType("Identifier") ? (() => {
|
|
2752
|
+
const name = this.expectIdentifier();
|
|
2753
|
+
return {
|
|
2754
|
+
start,
|
|
2755
|
+
generics: [],
|
|
2756
|
+
params: [{ type: "FunctionParameter", name: name.value, ...spanFrom(name, name) }],
|
|
2757
|
+
hasVarargs: false,
|
|
2758
|
+
varargTypeAnnotation: void 0,
|
|
2759
|
+
returnType: void 0,
|
|
2760
|
+
predicate: void 0
|
|
2761
|
+
};
|
|
2762
|
+
})() : this.parseFunctionHead();
|
|
2763
|
+
this.expectPunctuator("=>");
|
|
2764
|
+
const body = this.checkPunctuator("{") ? this.parseBraceBlock() : this.returnOf(this.parseExpression(0));
|
|
2765
|
+
const func = {
|
|
2766
|
+
type: "FunctionBody",
|
|
2767
|
+
generics: head.generics,
|
|
2768
|
+
params: head.params,
|
|
2769
|
+
hasVarargs: head.hasVarargs,
|
|
2770
|
+
varargTypeAnnotation: head.varargTypeAnnotation,
|
|
2771
|
+
returnType: head.returnType,
|
|
2772
|
+
predicate: head.predicate,
|
|
2773
|
+
body,
|
|
2774
|
+
...spanFrom(start, this.previous())
|
|
2775
|
+
};
|
|
2776
|
+
return { type: "FunctionExpression", func, ...spanFrom(start, this.previous()) };
|
|
2777
|
+
}
|
|
2778
|
+
/** A one-expression body: the value is what the function returns. */
|
|
2779
|
+
returnOf(expression) {
|
|
2780
|
+
const statement = {
|
|
2781
|
+
type: "ReturnStatement",
|
|
2782
|
+
arguments: [expression],
|
|
2783
|
+
...spanFrom(expression, expression)
|
|
2784
|
+
};
|
|
2785
|
+
return { type: "Block", statements: [statement], ...spanFrom(expression, expression) };
|
|
2786
|
+
}
|
|
2394
2787
|
parseFunctionBody(opener) {
|
|
2395
2788
|
const head = this.parseFunctionHead();
|
|
2396
|
-
const body = this.
|
|
2397
|
-
this.expectEnd(opener);
|
|
2789
|
+
const body = this.parseStatementBody(opener);
|
|
2398
2790
|
return {
|
|
2399
2791
|
type: "FunctionBody",
|
|
2400
2792
|
generics: head.generics,
|
|
@@ -2419,9 +2811,13 @@ var Parser = class {
|
|
|
2419
2811
|
...spanFrom(head.start, this.previous())
|
|
2420
2812
|
};
|
|
2421
2813
|
}
|
|
2814
|
+
/** A function's statements. */
|
|
2815
|
+
parseStatementBody(opener) {
|
|
2816
|
+
void opener;
|
|
2817
|
+
return this.parseBracedBody("function");
|
|
2818
|
+
}
|
|
2422
2819
|
headToBody(head, opener) {
|
|
2423
|
-
const body = this.
|
|
2424
|
-
this.expectEnd(opener);
|
|
2820
|
+
const body = this.parseStatementBody(opener);
|
|
2425
2821
|
return {
|
|
2426
2822
|
type: "FunctionBody",
|
|
2427
2823
|
generics: head.generics,
|
|
@@ -2645,8 +3041,21 @@ var Parser = class {
|
|
|
2645
3041
|
if (!this.checkPunctuator(")")) {
|
|
2646
3042
|
while (true) {
|
|
2647
3043
|
if (this.checkOperator("...")) {
|
|
2648
|
-
this.advance();
|
|
3044
|
+
const dots = this.advance();
|
|
2649
3045
|
hasVarargs = true;
|
|
3046
|
+
if (this.checkType("Identifier") && this.punctuatorAt(1, ":")) {
|
|
3047
|
+
const nameTok2 = this.expectIdentifier();
|
|
3048
|
+
this.advance();
|
|
3049
|
+
params.push({
|
|
3050
|
+
type: "FunctionTypeParameter",
|
|
3051
|
+
name: nameTok2.value,
|
|
3052
|
+
id: tokenIdentifier(nameTok2),
|
|
3053
|
+
typeAnnotation: this.parseType(),
|
|
3054
|
+
rest: true,
|
|
3055
|
+
...spanFrom(dots, this.previous())
|
|
3056
|
+
});
|
|
3057
|
+
break;
|
|
3058
|
+
}
|
|
2650
3059
|
varargType = this.parseType();
|
|
2651
3060
|
break;
|
|
2652
3061
|
}
|
|
@@ -2684,7 +3093,7 @@ var Parser = class {
|
|
|
2684
3093
|
}
|
|
2685
3094
|
}
|
|
2686
3095
|
this.expectPunctuator(")");
|
|
2687
|
-
if (this.matchPunctuator("->")) {
|
|
3096
|
+
if (this.matchPunctuator("=>") || this.matchPunctuator("->")) {
|
|
2688
3097
|
const predicate = this.tryParseTypePredicate();
|
|
2689
3098
|
const returnType = predicate ? { type: "TypeReference", base: "boolean", typeArguments: [], ...spanFrom(start, this.previous()) } : this.parseTypeOrTypePackReference();
|
|
2690
3099
|
return {
|
|
@@ -2827,8 +3236,22 @@ var Parser = class {
|
|
|
2827
3236
|
optional: optional2,
|
|
2828
3237
|
...spanFrom(propStart, this.previous())
|
|
2829
3238
|
});
|
|
3239
|
+
} else if (this.checkType("Literal") && this.current().kind === "string" && this.peek(1).type === "Punctuator" && (this.peek(1).value === ":" || this.peek(1).value === "?" && this.peek(2).type === "Punctuator" && this.peek(2).value === ":")) {
|
|
3240
|
+
const keyTok = this.advance();
|
|
3241
|
+
const name = String(keyTok.value);
|
|
3242
|
+
const optional2 = this.matchPunctuator("?");
|
|
3243
|
+
this.expectPunctuator(":");
|
|
3244
|
+
const valueType = this.parseType();
|
|
3245
|
+
properties.push({
|
|
3246
|
+
type: "TableTypeProperty",
|
|
3247
|
+
name,
|
|
3248
|
+
key: tokenIdentifier(keyTok),
|
|
3249
|
+
valueType,
|
|
3250
|
+
optional: optional2,
|
|
3251
|
+
...spanFrom(propStart, this.previous())
|
|
3252
|
+
});
|
|
2830
3253
|
} else {
|
|
2831
|
-
this.error(
|
|
3254
|
+
this.error(`Expected object type property ('name: T', '"name": T' or '[K]: V'); use 'T[]' for arrays and '[T, U]' for tuples`);
|
|
2832
3255
|
}
|
|
2833
3256
|
if (this.matchPunctuator(",") || this.matchPunctuator(";")) continue;
|
|
2834
3257
|
break;
|
|
@@ -2914,9 +3337,10 @@ function parseTypeFromSource(raw) {
|
|
|
2914
3337
|
const parser = new Parser(tokenize(raw));
|
|
2915
3338
|
return parser.parseType();
|
|
2916
3339
|
}
|
|
2917
|
-
function parseExpressionFromSource(raw) {
|
|
3340
|
+
function parseExpressionFromSource(raw, inClass = false) {
|
|
2918
3341
|
const tokens = tokenize(raw);
|
|
2919
3342
|
const parser = new Parser(tokens);
|
|
3343
|
+
if (inClass) parser.classDepth = 1;
|
|
2920
3344
|
const expr = parser.parseExpression();
|
|
2921
3345
|
return expr;
|
|
2922
3346
|
}
|
|
@@ -3025,6 +3449,11 @@ var Analyzer = class {
|
|
|
3025
3449
|
hoistFunctions(block, scope) {
|
|
3026
3450
|
for (const statement of block.statements) {
|
|
3027
3451
|
const declaration = statement.type === "ExportStatement" ? statement.declaration : statement;
|
|
3452
|
+
if (declaration.type === "ClassDeclaration") {
|
|
3453
|
+
this.declare(scope, declaration.name.name, "local", declaration.name, true, "class");
|
|
3454
|
+
this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
|
|
3455
|
+
continue;
|
|
3456
|
+
}
|
|
3028
3457
|
if (declaration.type !== "FunctionDeclaration") continue;
|
|
3029
3458
|
this.declare(scope, declaration.name.name, "local", declaration.name, true, "function");
|
|
3030
3459
|
this.hoisted.set(declaration.name, scope === this.moduleScope ? -1 : this.functionDepth);
|
|
@@ -3037,7 +3466,7 @@ var Analyzer = class {
|
|
|
3037
3466
|
* hoisted, and this does not apply. */
|
|
3038
3467
|
checkUseBeforeDefine(identifier, id) {
|
|
3039
3468
|
const binding = this.bindings.get(id);
|
|
3040
|
-
if (binding.declaredBy !== "function" || this.typeQueryDepth > 0) return;
|
|
3469
|
+
if (binding.declaredBy !== "function" && binding.declaredBy !== "class" || this.typeQueryDepth > 0) return;
|
|
3041
3470
|
const declaration = binding.declarationNode;
|
|
3042
3471
|
const depth = declaration && this.hoisted.get(declaration);
|
|
3043
3472
|
if (depth === void 0 || depth !== this.functionDepth) return;
|
|
@@ -3292,6 +3721,11 @@ var Analyzer = class {
|
|
|
3292
3721
|
this.visitFunctionBody(stmt.func, scope);
|
|
3293
3722
|
return;
|
|
3294
3723
|
}
|
|
3724
|
+
case "ClassDeclaration": {
|
|
3725
|
+
if (!this.hoisted.has(stmt.name)) this.declare(scope, stmt.name.name, "local", stmt.name, true, "class");
|
|
3726
|
+
this.visitClassBody(stmt, scope);
|
|
3727
|
+
return;
|
|
3728
|
+
}
|
|
3295
3729
|
case "FunctionDeclarationStatement": {
|
|
3296
3730
|
if (stmt.target.path.length === 0 && !stmt.target.method) {
|
|
3297
3731
|
this.referenceAsAssignmentTarget(scope, stmt.target.base);
|
|
@@ -3411,7 +3845,8 @@ var Analyzer = class {
|
|
|
3411
3845
|
this.visitStatement(stmt.declaration, scope);
|
|
3412
3846
|
return;
|
|
3413
3847
|
case "ExportDefaultStatement":
|
|
3414
|
-
this.
|
|
3848
|
+
if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, scope);
|
|
3849
|
+
else this.visitExpression(stmt.declaration, scope);
|
|
3415
3850
|
return;
|
|
3416
3851
|
case "ExportNamedStatement":
|
|
3417
3852
|
if (!stmt.source) {
|
|
@@ -3459,6 +3894,33 @@ var Analyzer = class {
|
|
|
3459
3894
|
}
|
|
3460
3895
|
/** `<K extends typeof config>` — a constraint is a type like any other,
|
|
3461
3896
|
* and the `typeof` in it reads a value. */
|
|
3897
|
+
/** A class body. Its type parameters live in a scope of their own, and
|
|
3898
|
+
* every member is written inside it — so a method's annotations see `T`,
|
|
3899
|
+
* and everything else sees what the class declaration sees, itself
|
|
3900
|
+
* included. `this` is not declared here: the parser makes it a real first
|
|
3901
|
+
* parameter, so it arrives with the rest of them. */
|
|
3902
|
+
visitClassBody(node, outer) {
|
|
3903
|
+
const scope = node.typeParams?.length ? childScope(outer) : outer;
|
|
3904
|
+
this.visitGenerics(node.typeParams, scope);
|
|
3905
|
+
if (node.superclass) this.reference(outer, node.superclass);
|
|
3906
|
+
for (const argument of node.superArguments ?? []) this.visitType(argument, scope);
|
|
3907
|
+
for (const member of node.members) {
|
|
3908
|
+
switch (member.type) {
|
|
3909
|
+
case "ClassField":
|
|
3910
|
+
this.visitType(member.typeAnnotation, scope);
|
|
3911
|
+
if (member.init) this.visitExpression(member.init, scope);
|
|
3912
|
+
break;
|
|
3913
|
+
case "ClassMethod":
|
|
3914
|
+
for (const signature of member.signatures ?? []) this.visitSignature(signature, scope);
|
|
3915
|
+
this.visitFunctionBody(member.func, scope, member.func.isMethod);
|
|
3916
|
+
break;
|
|
3917
|
+
case "ClassAccessor":
|
|
3918
|
+
case "ClassConstructor":
|
|
3919
|
+
this.visitFunctionBody(member.func, scope, member.func.isMethod);
|
|
3920
|
+
break;
|
|
3921
|
+
}
|
|
3922
|
+
}
|
|
3923
|
+
}
|
|
3462
3924
|
visitGenerics(generics, scope) {
|
|
3463
3925
|
for (const generic of generics ?? []) {
|
|
3464
3926
|
this.visitType(generic.constraint, scope);
|
|
@@ -3541,6 +4003,22 @@ var Analyzer = class {
|
|
|
3541
4003
|
this.visitExpression(expr.callee, scope);
|
|
3542
4004
|
for (const arg of expr.arguments) this.visitExpression(arg, scope);
|
|
3543
4005
|
return;
|
|
4006
|
+
case "NewExpression":
|
|
4007
|
+
this.visitExpression(expr.callee, scope);
|
|
4008
|
+
for (const argument of expr.arguments) this.visitExpression(argument, scope);
|
|
4009
|
+
for (const argument of expr.typeArguments ?? []) this.visitType(argument, scope);
|
|
4010
|
+
return;
|
|
4011
|
+
case "SuperExpression":
|
|
4012
|
+
return;
|
|
4013
|
+
case "SpreadElement":
|
|
4014
|
+
this.visitExpression(expr.argument, scope);
|
|
4015
|
+
return;
|
|
4016
|
+
case "ClassExpression": {
|
|
4017
|
+
const inner = childScope(scope);
|
|
4018
|
+
if (expr.name) this.declare(inner, expr.name.name, "local", expr.name, true, "class");
|
|
4019
|
+
this.visitClassBody(expr, inner);
|
|
4020
|
+
return;
|
|
4021
|
+
}
|
|
3544
4022
|
case "MethodCallExpression":
|
|
3545
4023
|
this.visitExpression(expr.object, scope);
|
|
3546
4024
|
for (const arg of expr.arguments) this.visitExpression(arg, scope);
|
|
@@ -3622,6 +4100,14 @@ function preludeProgram() {
|
|
|
3622
4100
|
function isClassType(t) {
|
|
3623
4101
|
return t.kind === "object" && t.class !== void 0;
|
|
3624
4102
|
}
|
|
4103
|
+
function isClassAssignable(got, want) {
|
|
4104
|
+
if (!got || !got.ancestors.includes(want.name)) return false;
|
|
4105
|
+
const wanted = want.typeArguments?.get(want.name);
|
|
4106
|
+
if (!wanted?.length) return true;
|
|
4107
|
+
const given = got.typeArguments?.get(want.name);
|
|
4108
|
+
if (!given) return true;
|
|
4109
|
+
return wanted.every((w, i) => given[i] !== void 0 && isAssignable(given[i], w));
|
|
4110
|
+
}
|
|
3625
4111
|
function typeParam(name, constraint, isConst) {
|
|
3626
4112
|
return { kind: "typeParam", name, constraint, isConst };
|
|
3627
4113
|
}
|
|
@@ -3673,11 +4159,21 @@ function substitute(t, subst) {
|
|
|
3673
4159
|
case "object": {
|
|
3674
4160
|
const entries = [];
|
|
3675
4161
|
for (const [k, v] of t.properties) entries.push([k, { ...v, type: substitute(v.type, subst) }]);
|
|
3676
|
-
|
|
4162
|
+
const out = objectType(
|
|
3677
4163
|
entries,
|
|
3678
4164
|
t.indexer && { key: substitute(t.indexer.key, subst), value: substitute(t.indexer.value, subst) },
|
|
3679
4165
|
t.frozen
|
|
3680
4166
|
);
|
|
4167
|
+
if (t.name) out.name = t.name;
|
|
4168
|
+
if (t.class) {
|
|
4169
|
+
out.class = {
|
|
4170
|
+
...t.class,
|
|
4171
|
+
typeArguments: t.class.typeArguments && new Map(
|
|
4172
|
+
[...t.class.typeArguments].map(([name, args]) => [name, args.map((a) => substitute(a, subst))])
|
|
4173
|
+
)
|
|
4174
|
+
};
|
|
4175
|
+
}
|
|
4176
|
+
return out;
|
|
3681
4177
|
}
|
|
3682
4178
|
case "function": {
|
|
3683
4179
|
const inner = t.typeParams ? new Map([...subst].filter(([k]) => !t.typeParams.includes(k))) : subst;
|
|
@@ -3744,6 +4240,11 @@ function substitute(t, subst) {
|
|
|
3744
4240
|
return t;
|
|
3745
4241
|
}
|
|
3746
4242
|
}
|
|
4243
|
+
function classArguments(t, name) {
|
|
4244
|
+
if (t.kind === "genericRef") return t.name === name ? t.typeArguments : void 0;
|
|
4245
|
+
if (t.kind === "object") return t.class?.typeArguments?.get(name);
|
|
4246
|
+
return void 0;
|
|
4247
|
+
}
|
|
3747
4248
|
function unify(param, arg, vars, out) {
|
|
3748
4249
|
if (param.kind === "typeParam" && param.constraint && !vars.has(param.name)) {
|
|
3749
4250
|
unify(param.constraint, arg, vars, out);
|
|
@@ -3771,7 +4272,12 @@ function unify(param, arg, vars, out) {
|
|
|
3771
4272
|
}
|
|
3772
4273
|
return;
|
|
3773
4274
|
case "object":
|
|
3774
|
-
if (param.class)
|
|
4275
|
+
if (param.class) {
|
|
4276
|
+
const wanted = param.class.typeArguments?.get(param.class.name);
|
|
4277
|
+
const given = classArguments(arg, param.class.name);
|
|
4278
|
+
if (wanted && given) wanted.forEach((w, i) => given[i] && unify(w, given[i], vars, out));
|
|
4279
|
+
return;
|
|
4280
|
+
}
|
|
3775
4281
|
if (arg.kind === "object" && !arg.class) {
|
|
3776
4282
|
for (const [k, pv] of param.properties) {
|
|
3777
4283
|
const av = arg.properties.get(k);
|
|
@@ -3780,6 +4286,11 @@ function unify(param, arg, vars, out) {
|
|
|
3780
4286
|
if (param.indexer && arg.indexer) unify(param.indexer.value, arg.indexer.value, vars, out);
|
|
3781
4287
|
}
|
|
3782
4288
|
return;
|
|
4289
|
+
case "genericRef": {
|
|
4290
|
+
const given = classArguments(arg, param.name);
|
|
4291
|
+
if (given) param.typeArguments.forEach((p, i) => given[i] && unify(p, given[i], vars, out));
|
|
4292
|
+
return;
|
|
4293
|
+
}
|
|
3783
4294
|
case "union":
|
|
3784
4295
|
for (const m of param.types) unify(m, arg, vars, out);
|
|
3785
4296
|
return;
|
|
@@ -3874,6 +4385,10 @@ var expandAlias;
|
|
|
3874
4385
|
function setAliasExpander(fn2) {
|
|
3875
4386
|
expandAlias = fn2;
|
|
3876
4387
|
}
|
|
4388
|
+
var deferredBound;
|
|
4389
|
+
function setDeferredBound(fn2) {
|
|
4390
|
+
deferredBound = fn2;
|
|
4391
|
+
}
|
|
3877
4392
|
var comparing = [];
|
|
3878
4393
|
function isAssignable(rawA, rawB) {
|
|
3879
4394
|
let a = isNoValue(rawA) ? nilType : rawA;
|
|
@@ -3904,10 +4419,29 @@ function isAssignableInner(a, b) {
|
|
|
3904
4419
|
if (b.kind === "unknown") return true;
|
|
3905
4420
|
if (b.kind === "never") return false;
|
|
3906
4421
|
if (a.kind === "unknown") return false;
|
|
4422
|
+
if (a.kind === "difference" && b.kind === "difference") {
|
|
4423
|
+
return isAssignable(a.base, b.base) && isAssignable(b.excluded, a.excluded);
|
|
4424
|
+
}
|
|
3907
4425
|
if (b.kind === "difference") {
|
|
3908
4426
|
return isAssignable(a, b.base) && !overlaps(a, b.excluded);
|
|
3909
4427
|
}
|
|
3910
|
-
if (a.kind === "difference")
|
|
4428
|
+
if (a.kind === "difference") {
|
|
4429
|
+
if (b.kind === "union" && b.types.some((m) => isAssignable(a, m))) return true;
|
|
4430
|
+
return isAssignable(a.base, b);
|
|
4431
|
+
}
|
|
4432
|
+
if (a.kind === "conditional" || a.kind === "indexedAccess") {
|
|
4433
|
+
if ((b.kind === "conditional" || b.kind === "indexedAccess" || b.kind === "union") && equalTypes(a, b)) {
|
|
4434
|
+
return true;
|
|
4435
|
+
}
|
|
4436
|
+
if (b.kind === "union" && b.types.some((m) => equalTypes(a, m))) return true;
|
|
4437
|
+
const bound = deferredBound?.(a);
|
|
4438
|
+
if (bound && bound !== a) return isAssignable(bound, b);
|
|
4439
|
+
}
|
|
4440
|
+
if (a.kind === "typeParam") {
|
|
4441
|
+
if (b.kind === "typeParam" && a.name === b.name) return true;
|
|
4442
|
+
if (b.kind === "union" && b.types.some((m) => m.kind === "typeParam" && m.name === a.name)) return true;
|
|
4443
|
+
return a.constraint ? isAssignable(a.constraint, b) : false;
|
|
4444
|
+
}
|
|
3911
4445
|
if (a.kind === "union") return a.types.every((t) => isAssignable(t, b));
|
|
3912
4446
|
if (b.kind === "union") return b.types.some((t) => isAssignable(a, t));
|
|
3913
4447
|
if (b.kind === "intersection") return b.types.every((t) => isAssignable(a, t));
|
|
@@ -3941,7 +4475,7 @@ function isAssignableInner(a, b) {
|
|
|
3941
4475
|
}
|
|
3942
4476
|
if (a.kind === "object") {
|
|
3943
4477
|
if (b.kind !== "object") return false;
|
|
3944
|
-
if (b.class) return a.class
|
|
4478
|
+
if (b.class) return isClassAssignable(a.class, b.class);
|
|
3945
4479
|
if (a.class && (b.indexer || b.properties.size === 0)) return false;
|
|
3946
4480
|
for (const [name, bp] of b.properties) {
|
|
3947
4481
|
const ap = a.properties.get(name);
|
|
@@ -3950,7 +4484,7 @@ function isAssignableInner(a, b) {
|
|
|
3950
4484
|
if (a.indexer && isAssignable(a.indexer.value, bp.type)) continue;
|
|
3951
4485
|
return false;
|
|
3952
4486
|
}
|
|
3953
|
-
if (!isAssignable(ap.type, bp.type)) return false;
|
|
4487
|
+
if (!isAssignable(ap.type, bp.optional ? optional(bp.type) : bp.type)) return false;
|
|
3954
4488
|
}
|
|
3955
4489
|
if (b.indexer) {
|
|
3956
4490
|
for (const [name, ap] of a.properties) {
|
|
@@ -3971,10 +4505,6 @@ function isAssignableInner(a, b) {
|
|
|
3971
4505
|
}
|
|
3972
4506
|
return isAssignable(a.returns, b.returns);
|
|
3973
4507
|
}
|
|
3974
|
-
if (a.kind === "typeParam") {
|
|
3975
|
-
if (b.kind === "typeParam" && a.name === b.name) return true;
|
|
3976
|
-
return a.constraint ? isAssignable(a.constraint, b) : false;
|
|
3977
|
-
}
|
|
3978
4508
|
if (b.kind === "typeParam") return false;
|
|
3979
4509
|
if (a.kind === "genericRef" || b.kind === "genericRef") {
|
|
3980
4510
|
return a.kind === "genericRef" && b.kind === "genericRef" && a.name === b.name && a.typeArguments.length === b.typeArguments.length && a.typeArguments.every((x, i) => equalTypes(x, b.typeArguments[i]));
|
|
@@ -4094,7 +4624,9 @@ function containsFreeTypeParam(t, seen, bound) {
|
|
|
4094
4624
|
case "intersection":
|
|
4095
4625
|
return t.types.some((m) => containsTypeParam(m, seen, bound));
|
|
4096
4626
|
case "object":
|
|
4097
|
-
if (t.class)
|
|
4627
|
+
if (t.class) {
|
|
4628
|
+
return [...t.class.typeArguments?.values() ?? []].some((args) => args.some((a) => containsTypeParam(a, seen, bound)));
|
|
4629
|
+
}
|
|
4098
4630
|
return [...t.properties.values()].some((v) => containsTypeParam(v.type, seen, bound)) || !!t.indexer && (containsTypeParam(t.indexer.key, seen, bound) || containsTypeParam(t.indexer.value, seen, bound));
|
|
4099
4631
|
case "function": {
|
|
4100
4632
|
const inner = t.typeParams?.length ? /* @__PURE__ */ new Set([...bound, ...t.typeParams]) : bound;
|
|
@@ -4264,7 +4796,10 @@ function formatTypeUncached(t) {
|
|
|
4264
4796
|
return t.isPack ? `(${inner})` : `[${inner}]`;
|
|
4265
4797
|
}
|
|
4266
4798
|
case "object": {
|
|
4267
|
-
if (t.name)
|
|
4799
|
+
if (t.name) {
|
|
4800
|
+
const args = t.class?.typeArguments?.get(t.class.name);
|
|
4801
|
+
return args?.length ? `${t.name}<${args.map(formatType).join(", ")}>` : t.name;
|
|
4802
|
+
}
|
|
4268
4803
|
const props = [...t.properties.entries()].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0).map(([k, v]) => `${v.readonly ? "readonly " : ""}${formatKey(k)}${v.optional ? "?" : ""}: ${formatType(v.type)}`);
|
|
4269
4804
|
if (t.indexer) props.push(`[${formatType(t.indexer.key)}]: ${formatType(t.indexer.value)}`);
|
|
4270
4805
|
return props.length ? `{ ${props.join(", ")} }` : "{}";
|
|
@@ -4283,7 +4818,7 @@ function formatTypeUncached(t) {
|
|
|
4283
4818
|
}).join(", ")}>` : "";
|
|
4284
4819
|
const ps = t.params.map((p) => `${p.name ? p.name + ": " : ""}${formatType(p.type)}`);
|
|
4285
4820
|
if (t.varargs) ps.push(`...${formatType(t.varargs)}`);
|
|
4286
|
-
return `${gen}(${ps.join(", ")})
|
|
4821
|
+
return `${gen}(${ps.join(", ")}) => ${formatPredicate(t) ?? formatType(t.returns)}`;
|
|
4287
4822
|
}
|
|
4288
4823
|
case "typeParam":
|
|
4289
4824
|
return t.name;
|
|
@@ -4327,7 +4862,7 @@ function formatPredicate(t) {
|
|
|
4327
4862
|
}
|
|
4328
4863
|
function formatAtom(t) {
|
|
4329
4864
|
if (t.kind === "intersection" && t.name) return t.name;
|
|
4330
|
-
if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference") {
|
|
4865
|
+
if (t.kind === "union" || t.kind === "intersection" || t.kind === "function" || t.kind === "difference" || t.kind === "conditional") {
|
|
4331
4866
|
return `(${formatType(t)})`;
|
|
4332
4867
|
}
|
|
4333
4868
|
return formatType(t);
|
|
@@ -4431,13 +4966,33 @@ function moduleExports(program, scopes, types, resolveModule) {
|
|
|
4431
4966
|
if (stmt.type === "ExportStatement") {
|
|
4432
4967
|
const declaration = stmt.declaration;
|
|
4433
4968
|
if (declaration.type === "FunctionDeclaration") exportName(declaration.name, declaration.name.name);
|
|
4434
|
-
else
|
|
4969
|
+
else if (declaration.type === "ClassDeclaration") {
|
|
4970
|
+
exportName(declaration.name, declaration.name.name);
|
|
4971
|
+
const instance = types.aliases.get(declaration.name.name);
|
|
4972
|
+
if (instance) {
|
|
4973
|
+
exportedTypes.set(declaration.name.name, {
|
|
4974
|
+
type: instance,
|
|
4975
|
+
params: declaration.typeParams.map((g) => g.name)
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
} else for (const target of declaration.names) exportPattern(target);
|
|
4435
4979
|
} else if (stmt.type === "ExportTypeAliasStatement") {
|
|
4436
4980
|
const name = stmt.alias.name.name;
|
|
4437
4981
|
const type = types.aliases.get(name);
|
|
4438
4982
|
if (type) exportedTypes.set(name, { type, params: stmt.alias.generics.map((g) => g.name) });
|
|
4439
4983
|
} else if (stmt.type === "ExportDefaultStatement") {
|
|
4440
|
-
|
|
4984
|
+
if (stmt.declaration.type === "ClassDeclaration") {
|
|
4985
|
+
const id = byDeclaration.get(stmt.declaration.name);
|
|
4986
|
+
defaultType = (id !== void 0 ? types.bindingType.get(id) : void 0) ?? anyType;
|
|
4987
|
+
const instance = types.aliases.get(stmt.declaration.name.name);
|
|
4988
|
+
if (instance) {
|
|
4989
|
+
const exported = { type: instance, params: stmt.declaration.typeParams.map((g) => g.name) };
|
|
4990
|
+
exportedTypes.set(stmt.declaration.name.name, exported);
|
|
4991
|
+
exportedTypes.set("default", exported);
|
|
4992
|
+
}
|
|
4993
|
+
} else {
|
|
4994
|
+
defaultType = types.typeOf.get(stmt.declaration) ?? anyType;
|
|
4995
|
+
}
|
|
4441
4996
|
} else if (stmt.type === "ExportNamedStatement") {
|
|
4442
4997
|
if (stmt.source) {
|
|
4443
4998
|
const from = resolveModule?.(stmt.source.value);
|
|
@@ -4565,6 +5120,51 @@ function keepsLiterals(paramType) {
|
|
|
4565
5120
|
const members = paramType.constraint.kind === "union" ? paramType.constraint.types : [paramType.constraint];
|
|
4566
5121
|
return members.some((m) => m.kind === "literal");
|
|
4567
5122
|
}
|
|
5123
|
+
var CLASS_LINKS = /* @__PURE__ */ new Set(["new", "ClassObject", "ParentClass"]);
|
|
5124
|
+
var CLASS_RESERVED = /* @__PURE__ */ new Set([
|
|
5125
|
+
"new",
|
|
5126
|
+
"ClassObject",
|
|
5127
|
+
"ParentClass",
|
|
5128
|
+
"__init",
|
|
5129
|
+
"__index",
|
|
5130
|
+
"__newindex",
|
|
5131
|
+
"__getters",
|
|
5132
|
+
"__setters",
|
|
5133
|
+
"__dynamic"
|
|
5134
|
+
]);
|
|
5135
|
+
function callsSuper(block) {
|
|
5136
|
+
let found = false;
|
|
5137
|
+
walkNodes(block, (node) => {
|
|
5138
|
+
const record = node;
|
|
5139
|
+
if (record.type === "CallExpression" && record.callee?.type === "SuperExpression") found = true;
|
|
5140
|
+
});
|
|
5141
|
+
return found;
|
|
5142
|
+
}
|
|
5143
|
+
function assignedFields(block) {
|
|
5144
|
+
const names = /* @__PURE__ */ new Set();
|
|
5145
|
+
walkNodes(block, (node) => {
|
|
5146
|
+
const record = node;
|
|
5147
|
+
const targets = record.type === "AssignmentStatement" ? record.targets : record.type === "CompoundAssignmentStatement" ? [record.target] : void 0;
|
|
5148
|
+
for (const target of targets ?? []) {
|
|
5149
|
+
const member = target;
|
|
5150
|
+
if (member.type === "MemberExpression" && member.object?.type === "Identifier" && member.object.name === "this" && member.property?.name) {
|
|
5151
|
+
names.add(member.property.name);
|
|
5152
|
+
}
|
|
5153
|
+
}
|
|
5154
|
+
});
|
|
5155
|
+
return names;
|
|
5156
|
+
}
|
|
5157
|
+
function walkNodes(root, visit) {
|
|
5158
|
+
if (!root || typeof root !== "object") return;
|
|
5159
|
+
if (Array.isArray(root)) {
|
|
5160
|
+
for (const item of root) walkNodes(item, visit);
|
|
5161
|
+
return;
|
|
5162
|
+
}
|
|
5163
|
+
visit(root);
|
|
5164
|
+
for (const [key, value] of Object.entries(root)) {
|
|
5165
|
+
if (key !== "line" && key !== "column" && value && typeof value === "object") walkNodes(value, visit);
|
|
5166
|
+
}
|
|
5167
|
+
}
|
|
4568
5168
|
var AliasMap = class extends Map {
|
|
4569
5169
|
pending = /* @__PURE__ */ new Map();
|
|
4570
5170
|
defer(name, resolve5) {
|
|
@@ -4688,6 +5288,8 @@ var TypeAnalyzer = class {
|
|
|
4688
5288
|
aliasDefs = /* @__PURE__ */ new Map();
|
|
4689
5289
|
/** See `resolveClass`. */
|
|
4690
5290
|
classTypes = /* @__PURE__ */ new WeakMap();
|
|
5291
|
+
/** See `instanceType` — one instance type per `class ... end`. */
|
|
5292
|
+
instanceTypes = /* @__PURE__ */ new WeakMap();
|
|
4691
5293
|
classMembers = /* @__PURE__ */ new WeakMap();
|
|
4692
5294
|
/** Generic parameters currently in lexical scope (alias body / generic fn),
|
|
4693
5295
|
* with their `extends` constraints resolved. */
|
|
@@ -4729,6 +5331,7 @@ var TypeAnalyzer = class {
|
|
|
4729
5331
|
this.registerAliasDefs(preludeProgram().body, true);
|
|
4730
5332
|
for (const lib of this.options.libs ?? []) this.registerAliasDefs(lib.body, true);
|
|
4731
5333
|
this.registerAliasDefs(this.program.body);
|
|
5334
|
+
this.registerNestedClasses();
|
|
4732
5335
|
for (const lib of this.options.libs ?? []) this.harvestDeclares(lib.body);
|
|
4733
5336
|
this.registerImportedTypes();
|
|
4734
5337
|
this.resolveAllAliases();
|
|
@@ -4740,6 +5343,7 @@ var TypeAnalyzer = class {
|
|
|
4740
5343
|
this.bindingType.set(id, t);
|
|
4741
5344
|
}
|
|
4742
5345
|
setAliasExpander((t) => this.expand(t));
|
|
5346
|
+
setDeferredBound((t) => this.deferredBound(t));
|
|
4743
5347
|
try {
|
|
4744
5348
|
const env = /* @__PURE__ */ new Map();
|
|
4745
5349
|
this.visitBlock(this.program.body, env);
|
|
@@ -4747,6 +5351,7 @@ var TypeAnalyzer = class {
|
|
|
4747
5351
|
if (this.options.reportUnknownTypes) this.reportUnknownTypes();
|
|
4748
5352
|
} finally {
|
|
4749
5353
|
setAliasExpander(void 0);
|
|
5354
|
+
setDeferredBound(void 0);
|
|
4750
5355
|
}
|
|
4751
5356
|
return {
|
|
4752
5357
|
typeOf: this.typeOf,
|
|
@@ -4796,6 +5401,11 @@ var TypeAnalyzer = class {
|
|
|
4796
5401
|
this.aliases.set(qualified, exported.type);
|
|
4797
5402
|
}
|
|
4798
5403
|
}
|
|
5404
|
+
const asDefault = stmt.defaultImport && exports.types.get("default");
|
|
5405
|
+
if (stmt.defaultImport && asDefault) {
|
|
5406
|
+
this.importedTypes.set(stmt.defaultImport.name, asDefault);
|
|
5407
|
+
this.aliases.set(stmt.defaultImport.name, asDefault.type);
|
|
5408
|
+
}
|
|
4799
5409
|
for (const s of stmt.specifiers) {
|
|
4800
5410
|
const exported = exports.types.get(s.imported.name);
|
|
4801
5411
|
if (exported) {
|
|
@@ -4830,10 +5440,40 @@ var TypeAnalyzer = class {
|
|
|
4830
5440
|
if (stmt.type === "DeclareClassStatement") {
|
|
4831
5441
|
this.aliasDefs.set(stmt.name.name, { params: [], node: stmt.body, class: stmt });
|
|
4832
5442
|
}
|
|
5443
|
+
const declaration = stmt.type === "ExportStatement" || stmt.type === "ExportDefaultStatement" ? stmt.declaration : stmt;
|
|
5444
|
+
if (declaration.type === "ClassDeclaration") this.registerClass(declaration);
|
|
4833
5445
|
}
|
|
4834
5446
|
}
|
|
5447
|
+
/** A class written inside a function or a block names a type too — its
|
|
5448
|
+
* own instances', which its methods' `this` is annotated with. Type names
|
|
5449
|
+
* are one namespace here, so it is registered with the rest; only a
|
|
5450
|
+
* second class of the same name would notice. */
|
|
5451
|
+
registerNestedClasses() {
|
|
5452
|
+
walkNodes(this.program.body, (node) => {
|
|
5453
|
+
const record = node;
|
|
5454
|
+
if (record.type !== "ClassDeclaration") return;
|
|
5455
|
+
const declaration = node;
|
|
5456
|
+
if (this.aliasDefs.has(declaration.name.name)) return;
|
|
5457
|
+
this.registerClass(declaration);
|
|
5458
|
+
});
|
|
5459
|
+
}
|
|
5460
|
+
/** A class's name as a type. `node` is a placeholder: `resolveDef` and
|
|
5461
|
+
* `instantiateAlias` both go to the declaration itself. */
|
|
5462
|
+
registerClass(declaration) {
|
|
5463
|
+
this.aliasDefs.set(declaration.name.name, {
|
|
5464
|
+
params: declaration.typeParams,
|
|
5465
|
+
node: {
|
|
5466
|
+
type: "TableTypeNode",
|
|
5467
|
+
properties: [],
|
|
5468
|
+
line: declaration.line,
|
|
5469
|
+
column: declaration.column
|
|
5470
|
+
},
|
|
5471
|
+
runtimeClass: declaration
|
|
5472
|
+
});
|
|
5473
|
+
}
|
|
4835
5474
|
/** A non-generic definition's type. */
|
|
4836
5475
|
resolveDef(def) {
|
|
5476
|
+
if (def.runtimeClass) return this.instanceType(def.runtimeClass);
|
|
4837
5477
|
return def.class ? this.classType(def.class) : this.resolveType(def.node);
|
|
4838
5478
|
}
|
|
4839
5479
|
/** One type per class declaration, so every mention of a class is the same
|
|
@@ -4891,6 +5531,381 @@ var TypeAnalyzer = class {
|
|
|
4891
5531
|
if (this.program.body.statements.includes(stmt)) ownMembers();
|
|
4892
5532
|
return type;
|
|
4893
5533
|
}
|
|
5534
|
+
// ============================================================
|
|
5535
|
+
// `class ... end` — the runtime kind
|
|
5536
|
+
// ------------------------------------------------------------
|
|
5537
|
+
// A declaration says two things at once. Its *name as a type* is
|
|
5538
|
+
// the type of its instances, nominal the way `declare class` is:
|
|
5539
|
+
// only the class and the classes extending it produce one. Its
|
|
5540
|
+
// *name as a value* is the class table — the statics, the class
|
|
5541
|
+
// it extends (`ParentClass`), and the `new` that builds an
|
|
5542
|
+
// instance, which is an ordinary function and can be called as
|
|
5543
|
+
// one. An instance reaches its own class back through
|
|
5544
|
+
// `ClassObject`.
|
|
5545
|
+
//
|
|
5546
|
+
// Members are resolved into one shape, filled in two passes: the
|
|
5547
|
+
// fields first, then the functions. That order is what lets a
|
|
5548
|
+
// method body read `this.x` while the class it belongs to is
|
|
5549
|
+
// still being worked out.
|
|
5550
|
+
// ============================================================
|
|
5551
|
+
classShapes = /* @__PURE__ */ new WeakMap();
|
|
5552
|
+
classValues = /* @__PURE__ */ new WeakMap();
|
|
5553
|
+
/** Identity for a class written as a value, which has no name to be known
|
|
5554
|
+
* by: two of them are different types however alike they look. */
|
|
5555
|
+
classIdentities = /* @__PURE__ */ new WeakMap();
|
|
5556
|
+
classIdentityCount = 0;
|
|
5557
|
+
/** The class whose members are being read, so `super` knows its base. */
|
|
5558
|
+
currentClass;
|
|
5559
|
+
withClass(stmt, fn2) {
|
|
5560
|
+
const previous = this.currentClass;
|
|
5561
|
+
this.currentClass = stmt;
|
|
5562
|
+
try {
|
|
5563
|
+
return fn2();
|
|
5564
|
+
} finally {
|
|
5565
|
+
this.currentClass = previous;
|
|
5566
|
+
}
|
|
5567
|
+
}
|
|
5568
|
+
/** What the class is known by. A declaration is known by its name, the way
|
|
5569
|
+
* a `declare class` is — that is what makes it the same class across
|
|
5570
|
+
* modules. A class written as a value is known by where it is written. */
|
|
5571
|
+
classIdentity(stmt) {
|
|
5572
|
+
if (stmt.type === "ClassDeclaration") return stmt.name.name;
|
|
5573
|
+
let identity = this.classIdentities.get(stmt);
|
|
5574
|
+
if (!identity) {
|
|
5575
|
+
identity = `${stmt.name?.name ?? "class"}@${++this.classIdentityCount}`;
|
|
5576
|
+
this.classIdentities.set(stmt, identity);
|
|
5577
|
+
}
|
|
5578
|
+
return identity;
|
|
5579
|
+
}
|
|
5580
|
+
/** What it is *shown* as. A class written as a value has no name of its
|
|
5581
|
+
* own, so it borrows the one it is being bound to — `const Counter =
|
|
5582
|
+
* class ... end` reads as `Counter` everywhere. */
|
|
5583
|
+
className(stmt) {
|
|
5584
|
+
return stmt.name?.name ?? this.classDisplayNames.get(stmt) ?? "(class)";
|
|
5585
|
+
}
|
|
5586
|
+
classDisplayNames = /* @__PURE__ */ new WeakMap();
|
|
5587
|
+
/** `const Name = class ... end` — the name the class will be known by. */
|
|
5588
|
+
nameClassExpressions(stmt) {
|
|
5589
|
+
stmt.names.forEach((target, i) => {
|
|
5590
|
+
const value = stmt.init[i];
|
|
5591
|
+
if (target.type === "IdentifierPattern" && value?.type === "ClassExpression" && !value.name) {
|
|
5592
|
+
this.classDisplayNames.set(value, target.name);
|
|
5593
|
+
}
|
|
5594
|
+
});
|
|
5595
|
+
}
|
|
5596
|
+
classTypeParams(stmt) {
|
|
5597
|
+
return stmt.type === "ClassDeclaration" ? stmt.typeParams : [];
|
|
5598
|
+
}
|
|
5599
|
+
/** The class a declaration extends, when it is one written in this file.
|
|
5600
|
+
* An imported class is reached through its type and its value instead. */
|
|
5601
|
+
superDecl(stmt) {
|
|
5602
|
+
if (!stmt.superclass) return void 0;
|
|
5603
|
+
const base = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
|
|
5604
|
+
return base && base !== stmt && !this.extendsThrough(base, stmt) ? base : void 0;
|
|
5605
|
+
}
|
|
5606
|
+
/** Does `from` reach `target` by `extends`? Guards against a cycle turning
|
|
5607
|
+
* resolution into a loop. */
|
|
5608
|
+
extendsThrough(from, target) {
|
|
5609
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5610
|
+
for (let cls = from; cls && !seen.has(cls); ) {
|
|
5611
|
+
if (cls === target) return true;
|
|
5612
|
+
seen.add(cls);
|
|
5613
|
+
cls = cls.superclass ? this.aliasDefs.get(cls.superclass.name)?.runtimeClass : void 0;
|
|
5614
|
+
}
|
|
5615
|
+
return false;
|
|
5616
|
+
}
|
|
5617
|
+
/** The instance type of what `stmt` extends, with the arguments it was
|
|
5618
|
+
* extended with filled in — a class in this file, or any class type a
|
|
5619
|
+
* name in scope stands for (an imported one). */
|
|
5620
|
+
baseInstance(stmt) {
|
|
5621
|
+
if (!stmt.superclass) return void 0;
|
|
5622
|
+
const written = (stmt.superArguments ?? []).map((argument) => this.resolveType(argument));
|
|
5623
|
+
if (this.aliasDefs.get(stmt.superclass.name)?.runtimeClass) {
|
|
5624
|
+
const local = this.superDecl(stmt);
|
|
5625
|
+
if (!local) return void 0;
|
|
5626
|
+
const base = this.instanceType(local);
|
|
5627
|
+
const params = this.classTypeParams(local);
|
|
5628
|
+
if (!params.length) return base;
|
|
5629
|
+
const applied = substitute(base, this.bindTypeArguments(params, written));
|
|
5630
|
+
return applied.kind === "object" ? applied : void 0;
|
|
5631
|
+
}
|
|
5632
|
+
const imported = this.importedTypes.get(stmt.superclass.name);
|
|
5633
|
+
const named = imported ? this.importedType(imported, stmt.superArguments ?? []) : this.aliases.get(stmt.superclass.name);
|
|
5634
|
+
return named && isClassType(named) ? named : void 0;
|
|
5635
|
+
}
|
|
5636
|
+
/** One instance type per declaration, so every mention of the class is the
|
|
5637
|
+
* same object — the `this` of its own methods included. Members are read
|
|
5638
|
+
* lazily for the reason `declare class` reads them lazily: a class can
|
|
5639
|
+
* name itself, and two classes can name each other. */
|
|
5640
|
+
instanceType(stmt) {
|
|
5641
|
+
const cached = this.instanceTypes.get(stmt);
|
|
5642
|
+
if (cached) return cached;
|
|
5643
|
+
const name = this.className(stmt);
|
|
5644
|
+
const identity = this.classIdentity(stmt);
|
|
5645
|
+
const params = this.classTypeParams(stmt);
|
|
5646
|
+
const type = { kind: "object", name };
|
|
5647
|
+
this.instanceTypes.set(stmt, type);
|
|
5648
|
+
const own = params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0));
|
|
5649
|
+
const info = () => {
|
|
5650
|
+
const base = this.baseInstance(stmt)?.class;
|
|
5651
|
+
const typeArguments = new Map(base?.typeArguments ?? []);
|
|
5652
|
+
if (own.length) typeArguments.set(identity, own);
|
|
5653
|
+
return {
|
|
5654
|
+
name: identity,
|
|
5655
|
+
superclass: base?.name,
|
|
5656
|
+
ancestors: [identity, ...base?.ancestors ?? []],
|
|
5657
|
+
typeArguments: typeArguments.size ? typeArguments : void 0
|
|
5658
|
+
};
|
|
5659
|
+
};
|
|
5660
|
+
Object.defineProperties(type, {
|
|
5661
|
+
properties: {
|
|
5662
|
+
enumerable: true,
|
|
5663
|
+
get: () => {
|
|
5664
|
+
const shape = this.shapeOf(stmt);
|
|
5665
|
+
const base = this.baseInstance(stmt)?.properties;
|
|
5666
|
+
const members = base?.size ? new Map([...base, ...shape.instance]) : new Map(shape.instance);
|
|
5667
|
+
members.set("ClassObject", { type: this.classValueType(stmt), optional: false, readonly: true });
|
|
5668
|
+
return members;
|
|
5669
|
+
}
|
|
5670
|
+
},
|
|
5671
|
+
class: { enumerable: true, get: info }
|
|
5672
|
+
});
|
|
5673
|
+
return type;
|
|
5674
|
+
}
|
|
5675
|
+
/** `Box<T>` as its own methods see it — a reference, not the object, so
|
|
5676
|
+
* substituting the arguments in does not have to walk the class. */
|
|
5677
|
+
selfTypeOf(stmt) {
|
|
5678
|
+
const params = this.classTypeParams(stmt);
|
|
5679
|
+
if (!params.length || stmt.type !== "ClassDeclaration") return this.instanceType(stmt);
|
|
5680
|
+
return {
|
|
5681
|
+
kind: "genericRef",
|
|
5682
|
+
name: stmt.name.name,
|
|
5683
|
+
typeArguments: params.map((p) => typeParam(p.name, p.constraint ? this.resolveType(p.constraint) : void 0))
|
|
5684
|
+
};
|
|
5685
|
+
}
|
|
5686
|
+
/** The class table: the statics, what it inherits from the class it
|
|
5687
|
+
* extends, `ParentClass`, `ClassObject`, and `new`. */
|
|
5688
|
+
classValueType(stmt) {
|
|
5689
|
+
const cached = this.classValues.get(stmt);
|
|
5690
|
+
if (cached) return cached;
|
|
5691
|
+
const type = objectType([]);
|
|
5692
|
+
this.classValues.set(stmt, type);
|
|
5693
|
+
type.name = `typeof ${this.className(stmt)}`;
|
|
5694
|
+
const shape = this.shapeOf(stmt);
|
|
5695
|
+
const local = this.superDecl(stmt);
|
|
5696
|
+
const parent = local ? this.classValueType(local) : stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
|
|
5697
|
+
if (parent?.kind === "object") {
|
|
5698
|
+
for (const [key, property] of parent.properties) {
|
|
5699
|
+
if (!CLASS_LINKS.has(key)) type.properties.set(key, property);
|
|
5700
|
+
}
|
|
5701
|
+
}
|
|
5702
|
+
for (const [key, property] of shape.statics) type.properties.set(key, property);
|
|
5703
|
+
const constructor = this.constructorType(stmt);
|
|
5704
|
+
const params = this.classTypeParams(stmt);
|
|
5705
|
+
type.properties.set("new", {
|
|
5706
|
+
type: fn(
|
|
5707
|
+
constructor?.params.filter((p) => p.name !== "this") ?? [],
|
|
5708
|
+
this.selfTypeOf(stmt),
|
|
5709
|
+
constructor?.varargs,
|
|
5710
|
+
params.map((p) => p.name)
|
|
5711
|
+
),
|
|
5712
|
+
optional: false,
|
|
5713
|
+
readonly: true
|
|
5714
|
+
});
|
|
5715
|
+
type.properties.set("ParentClass", { type: parent ?? nilType, optional: false, readonly: true });
|
|
5716
|
+
return type;
|
|
5717
|
+
}
|
|
5718
|
+
/** The value side of a class named by a binding rather than by a
|
|
5719
|
+
* declaration in this file — an imported one. */
|
|
5720
|
+
classStaticsByNameType(name) {
|
|
5721
|
+
const id = this.classBindingByName(name);
|
|
5722
|
+
const declared = id !== void 0 ? this.bindingType.get(id) : void 0;
|
|
5723
|
+
return declared?.kind === "object" ? declared : void 0;
|
|
5724
|
+
}
|
|
5725
|
+
/** The binding a class name stands for, wherever it was declared. */
|
|
5726
|
+
classBindingByName(name) {
|
|
5727
|
+
for (const [id, binding] of this.scopes.bindings) {
|
|
5728
|
+
if (binding.name === name && binding.declaredBy === "class") return id;
|
|
5729
|
+
}
|
|
5730
|
+
return this.scopes.globalsByName.get(name);
|
|
5731
|
+
}
|
|
5732
|
+
/** What `super(...)` takes: the constructor of the class `stmt` extends,
|
|
5733
|
+
* its own skipped. */
|
|
5734
|
+
baseConstructorType(stmt) {
|
|
5735
|
+
return this.constructorType(stmt, /* @__PURE__ */ new Set([stmt]));
|
|
5736
|
+
}
|
|
5737
|
+
/** A class's constructor signature — its own, or the one it inherits. */
|
|
5738
|
+
constructorType(stmt, seen = /* @__PURE__ */ new Set()) {
|
|
5739
|
+
if (seen.has(stmt)) return void 0;
|
|
5740
|
+
seen.add(stmt);
|
|
5741
|
+
const own = this.shapeOf(stmt).ctor;
|
|
5742
|
+
if (own) return own;
|
|
5743
|
+
const local = this.superDecl(stmt);
|
|
5744
|
+
if (local) return this.constructorType(local, seen);
|
|
5745
|
+
const parent = stmt.superclass ? this.classStaticsByNameType(stmt.superclass.name) : void 0;
|
|
5746
|
+
const inherited = parent && this.overloadsOf(this.propertyType(parent, "new"))[0];
|
|
5747
|
+
return inherited;
|
|
5748
|
+
}
|
|
5749
|
+
/** `super` as a value: the base class's instance members with the `this`
|
|
5750
|
+
* slot already filled, because `super.m(a)` passes this instance. */
|
|
5751
|
+
superType(stmt) {
|
|
5752
|
+
const base = this.baseInstance(stmt);
|
|
5753
|
+
if (!base) return anyType;
|
|
5754
|
+
const entries = [];
|
|
5755
|
+
for (const [name, property] of base.properties) {
|
|
5756
|
+
const bound = this.overloadsOf(property.type);
|
|
5757
|
+
entries.push([name, bound.length ? {
|
|
5758
|
+
...property,
|
|
5759
|
+
type: intersection(bound.map((f) => this.takesSelf(f) ? fn(f.params.slice(1), f.returns, f.varargs, f.typeParams) : f))
|
|
5760
|
+
} : property]);
|
|
5761
|
+
}
|
|
5762
|
+
return objectType(entries);
|
|
5763
|
+
}
|
|
5764
|
+
shapeOf(stmt) {
|
|
5765
|
+
const cached = this.classShapes.get(stmt);
|
|
5766
|
+
if (cached) return cached;
|
|
5767
|
+
const shape = { instance: /* @__PURE__ */ new Map(), statics: /* @__PURE__ */ new Map(), filling: true };
|
|
5768
|
+
this.classShapes.set(stmt, shape);
|
|
5769
|
+
const wasEmitting = this.emitDiagnostics;
|
|
5770
|
+
this.emitDiagnostics = false;
|
|
5771
|
+
try {
|
|
5772
|
+
this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
|
|
5773
|
+
this.withSelfType(this.selfTypeOf(stmt), () => this.fillShape(stmt, shape));
|
|
5774
|
+
}));
|
|
5775
|
+
} finally {
|
|
5776
|
+
this.emitDiagnostics = wasEmitting;
|
|
5777
|
+
shape.filling = false;
|
|
5778
|
+
}
|
|
5779
|
+
return shape;
|
|
5780
|
+
}
|
|
5781
|
+
fillShape(stmt, shape) {
|
|
5782
|
+
const put = (isStatic, name, property) => {
|
|
5783
|
+
(isStatic ? shape.statics : shape.instance).set(name, property);
|
|
5784
|
+
};
|
|
5785
|
+
for (const member of stmt.members) {
|
|
5786
|
+
if (member.type !== "ClassField") continue;
|
|
5787
|
+
const type = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : member.init ? widen(this.infer(member.init, /* @__PURE__ */ new Map())) : anyType;
|
|
5788
|
+
put(member.isStatic, member.name.name, { type, optional: false });
|
|
5789
|
+
}
|
|
5790
|
+
for (const member of stmt.members) {
|
|
5791
|
+
switch (member.type) {
|
|
5792
|
+
case "ClassField":
|
|
5793
|
+
break;
|
|
5794
|
+
case "ClassMethod": {
|
|
5795
|
+
this.paramsFromSignatures(member.func, member.signatures);
|
|
5796
|
+
const type = member.signatures?.length ? intersection(member.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
|
|
5797
|
+
put(member.isStatic, member.name.name, { type, optional: false });
|
|
5798
|
+
break;
|
|
5799
|
+
}
|
|
5800
|
+
case "ClassAccessor": {
|
|
5801
|
+
const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
|
|
5802
|
+
if (signature.kind !== "function") break;
|
|
5803
|
+
const target = member.isStatic ? shape.statics : shape.instance;
|
|
5804
|
+
const existing = target.get(member.name.name);
|
|
5805
|
+
if (member.kind === "get") {
|
|
5806
|
+
put(member.isStatic, member.name.name, {
|
|
5807
|
+
type: signature.returns,
|
|
5808
|
+
optional: false,
|
|
5809
|
+
readonly: existing === void 0 || existing.readonly !== false
|
|
5810
|
+
});
|
|
5811
|
+
} else {
|
|
5812
|
+
put(member.isStatic, member.name.name, {
|
|
5813
|
+
type: existing?.type ?? signature.params[signature.params.length - 1]?.type ?? anyType,
|
|
5814
|
+
optional: false,
|
|
5815
|
+
readonly: false
|
|
5816
|
+
});
|
|
5817
|
+
}
|
|
5818
|
+
break;
|
|
5819
|
+
}
|
|
5820
|
+
case "ClassConstructor": {
|
|
5821
|
+
const signature = this.inferFunctionBody(member.func, /* @__PURE__ */ new Map());
|
|
5822
|
+
if (signature.kind === "function") shape.ctor = signature;
|
|
5823
|
+
break;
|
|
5824
|
+
}
|
|
5825
|
+
}
|
|
5826
|
+
}
|
|
5827
|
+
}
|
|
5828
|
+
/** Bind the class's value, and check what its members say. Shared by the
|
|
5829
|
+
* declaration and the expression forms. */
|
|
5830
|
+
visitClass(stmt, env) {
|
|
5831
|
+
this.checkClassDeclaration(stmt);
|
|
5832
|
+
const value = this.classValueType(stmt);
|
|
5833
|
+
this.withClass(stmt, () => this.withTypeParams(this.classTypeParams(stmt), () => {
|
|
5834
|
+
this.withSelfType(this.selfTypeOf(stmt), () => {
|
|
5835
|
+
for (const member of stmt.members) {
|
|
5836
|
+
if (member.type === "ClassField") {
|
|
5837
|
+
if (!member.init) continue;
|
|
5838
|
+
const declared = member.typeAnnotation ? this.resolveType(member.typeAnnotation) : void 0;
|
|
5839
|
+
if (declared) this.applyContext(member.init, declared);
|
|
5840
|
+
const actual = this.infer(member.init, env);
|
|
5841
|
+
if (declared && this.emitDiagnostics && !isAssignable(actual, declared)) {
|
|
5842
|
+
this.diagnostics.push({
|
|
5843
|
+
node: member.init,
|
|
5844
|
+
message: `Type '${formatType(actual)}' is not assignable to type '${formatType(declared)}'`
|
|
5845
|
+
});
|
|
5846
|
+
}
|
|
5847
|
+
continue;
|
|
5848
|
+
}
|
|
5849
|
+
this.checkParamOrder(member.func.params, member);
|
|
5850
|
+
for (const signature of member.signatures ?? []) {
|
|
5851
|
+
this.checkParamOrder(signature.params, member);
|
|
5852
|
+
}
|
|
5853
|
+
this.visitFunctionBody(member.func, env);
|
|
5854
|
+
}
|
|
5855
|
+
});
|
|
5856
|
+
}));
|
|
5857
|
+
return value;
|
|
5858
|
+
}
|
|
5859
|
+
/** What a class gets wrong, reported where it is written. */
|
|
5860
|
+
checkClassDeclaration(stmt) {
|
|
5861
|
+
if (!this.emitDiagnostics) return;
|
|
5862
|
+
const report = (node, message) => {
|
|
5863
|
+
this.diagnostics.push({ node, message });
|
|
5864
|
+
};
|
|
5865
|
+
const name = this.className(stmt);
|
|
5866
|
+
if (stmt.superclass) {
|
|
5867
|
+
const local = this.aliasDefs.get(stmt.superclass.name)?.runtimeClass;
|
|
5868
|
+
if (local && this.extendsThrough(local, stmt)) {
|
|
5869
|
+
report(stmt.superclass, `'${name}' cannot extend itself`);
|
|
5870
|
+
} else if (!this.baseInstance(stmt)) {
|
|
5871
|
+
const known = this.aliases.has(stmt.superclass.name) || this.importedTypes.has(stmt.superclass.name);
|
|
5872
|
+
report(stmt.superclass, known ? `'${stmt.superclass.name}' is not a class; a class can only extend another class` : `Cannot find class '${stmt.superclass.name}'`);
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
for (const member of stmt.members) {
|
|
5876
|
+
if (member.type === "ClassConstructor") continue;
|
|
5877
|
+
if (CLASS_RESERVED.has(member.name.name)) {
|
|
5878
|
+
report(member.name, `'${member.name.name}' is what the compiler calls part of a class; a member cannot be named that`);
|
|
5879
|
+
}
|
|
5880
|
+
}
|
|
5881
|
+
const seen = /* @__PURE__ */ new Map();
|
|
5882
|
+
for (const member of stmt.members) {
|
|
5883
|
+
if (member.type === "ClassConstructor") {
|
|
5884
|
+
if (seen.has("constructor")) report(member, "A class has one constructor");
|
|
5885
|
+
seen.set("constructor", member.type);
|
|
5886
|
+
continue;
|
|
5887
|
+
}
|
|
5888
|
+
const key = `${member.isStatic ? "static " : ""}${member.name.name}`;
|
|
5889
|
+
const before = seen.get(key);
|
|
5890
|
+
const pair = member.type === "ClassAccessor" && before === "ClassAccessor";
|
|
5891
|
+
if (before !== void 0 && !pair) {
|
|
5892
|
+
report(member.name, `'${member.name.name}' is declared twice in class '${name}'`);
|
|
5893
|
+
}
|
|
5894
|
+
seen.set(key, member.type);
|
|
5895
|
+
}
|
|
5896
|
+
const constructor = stmt.members.find((m) => m.type === "ClassConstructor");
|
|
5897
|
+
if (stmt.superclass && this.baseInstance(stmt) && constructor && !callsSuper(constructor.func.body)) {
|
|
5898
|
+
report(constructor, `'${name}' extends '${stmt.superclass.name}', so its constructor must call 'super(...)'`);
|
|
5899
|
+
}
|
|
5900
|
+
const assigned = constructor ? assignedFields(constructor.func.body) : /* @__PURE__ */ new Set();
|
|
5901
|
+
for (const member of stmt.members) {
|
|
5902
|
+
if (member.type !== "ClassField" || member.isStatic || member.init) continue;
|
|
5903
|
+
if (assigned.has(member.name.name)) continue;
|
|
5904
|
+
const type = member.typeAnnotation ? this.withTypeParams(this.classTypeParams(stmt), () => this.resolveType(member.typeAnnotation)) : anyType;
|
|
5905
|
+
if (isAssignable(nilType, type)) continue;
|
|
5906
|
+
report(member.name, `'${member.name.name}' has no value: give it one, assign it in the constructor, or let its type admit nil`);
|
|
5907
|
+
}
|
|
5908
|
+
}
|
|
4894
5909
|
/** `extends` must name a class, and the chain must end. */
|
|
4895
5910
|
checkClass(stmt) {
|
|
4896
5911
|
if (!stmt.superclass || !this.emitDiagnostics) return;
|
|
@@ -5147,6 +6162,7 @@ var TypeAnalyzer = class {
|
|
|
5147
6162
|
instantiateAlias(def, args) {
|
|
5148
6163
|
if (this.instantiationDepth > 20) return unknownType;
|
|
5149
6164
|
const subst = this.bindTypeArguments(def.params, args);
|
|
6165
|
+
if (def.runtimeClass) return substitute(this.instanceType(def.runtimeClass), subst);
|
|
5150
6166
|
this.instantiationDepth++;
|
|
5151
6167
|
try {
|
|
5152
6168
|
const body = this.withTypeParams(def.params, () => this.resolveType(def.node));
|
|
@@ -5291,15 +6307,17 @@ var TypeAnalyzer = class {
|
|
|
5291
6307
|
case "FunctionTypeNode": {
|
|
5292
6308
|
const names = node.generics.map((g) => g.name);
|
|
5293
6309
|
return this.withTypeParams(node.generics, () => {
|
|
5294
|
-
const params = node.params.map((p) => ({
|
|
6310
|
+
const params = node.params.filter((p) => !p.rest).map((p) => ({
|
|
5295
6311
|
name: p.name,
|
|
5296
6312
|
type: p.optional ? optional(this.resolveType(p.typeAnnotation)) : this.resolveType(p.typeAnnotation),
|
|
5297
6313
|
optional: p.optional
|
|
5298
6314
|
}));
|
|
6315
|
+
const restParam = node.params.find((p) => p.rest);
|
|
6316
|
+
const restElement = restParam ? this.resolveType(restParam.typeAnnotation) : void 0;
|
|
5299
6317
|
return this.withTypeParamDefaults(fn(
|
|
5300
6318
|
params,
|
|
5301
6319
|
this.resolveType(node.returnType),
|
|
5302
|
-
node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
|
|
6320
|
+
restParam ? restElement?.kind === "array" ? restElement.element : unknownType : node.hasVarargs ? node.varargType ? this.resolveType(node.varargType) : anyType : void 0,
|
|
5303
6321
|
names,
|
|
5304
6322
|
this.resolvePredicate(node.predicate, params)
|
|
5305
6323
|
), node.generics);
|
|
@@ -5493,7 +6511,10 @@ var TypeAnalyzer = class {
|
|
|
5493
6511
|
accessType(obj, index) {
|
|
5494
6512
|
if (index.kind === "union") return union(index.types.map((m) => this.accessType(obj, m)));
|
|
5495
6513
|
if (index.kind === "literal" && typeof index.value === "string") {
|
|
5496
|
-
|
|
6514
|
+
const member = this.propertyType(obj, index.value);
|
|
6515
|
+
if (member.kind !== "unknown") return member;
|
|
6516
|
+
const t = this.expand(obj);
|
|
6517
|
+
return t.kind === "object" && !t.class && !t.indexer ? nilType : member;
|
|
5497
6518
|
}
|
|
5498
6519
|
return this.indexedType(obj, index);
|
|
5499
6520
|
}
|
|
@@ -5646,12 +6667,13 @@ var TypeAnalyzer = class {
|
|
|
5646
6667
|
visitStatement(stmt, env) {
|
|
5647
6668
|
switch (stmt.type) {
|
|
5648
6669
|
case "VariableDeclaration": {
|
|
6670
|
+
this.nameClassExpressions(stmt);
|
|
5649
6671
|
stmt.names.forEach((target, i) => {
|
|
5650
6672
|
if (target.type === "IdentifierPattern" && target.typeAnnotation && stmt.init[i]) {
|
|
5651
6673
|
this.applyContext(stmt.init[i], this.resolveType(target.typeAnnotation));
|
|
5652
6674
|
}
|
|
5653
6675
|
});
|
|
5654
|
-
const { types: valueTypes, sources } = this.valueList(stmt.init, env);
|
|
6676
|
+
const { types: valueTypes, sources } = this.valueList(stmt.init, env, stmt.names.length);
|
|
5655
6677
|
stmt.names.forEach((target, i) => {
|
|
5656
6678
|
const inferred = valueTypes[i] ?? (stmt.init.length ? unknownType : nilType);
|
|
5657
6679
|
const source = sources[i];
|
|
@@ -5676,6 +6698,15 @@ var TypeAnalyzer = class {
|
|
|
5676
6698
|
});
|
|
5677
6699
|
return;
|
|
5678
6700
|
}
|
|
6701
|
+
case "ClassDeclaration": {
|
|
6702
|
+
const id = this.bindingIdByName(stmt.name.name, stmt.name);
|
|
6703
|
+
const value = this.visitClass(stmt, env);
|
|
6704
|
+
if (id !== void 0) {
|
|
6705
|
+
this.bindingType.set(id, value);
|
|
6706
|
+
this.setBinding(env, id, value);
|
|
6707
|
+
}
|
|
6708
|
+
return;
|
|
6709
|
+
}
|
|
5679
6710
|
case "FunctionDeclaration": {
|
|
5680
6711
|
this.checkParamOrder(stmt.func.params, stmt);
|
|
5681
6712
|
for (const sig of stmt.signatures ?? []) this.checkParamOrder(sig.params, stmt);
|
|
@@ -5731,7 +6762,7 @@ var TypeAnalyzer = class {
|
|
|
5731
6762
|
if (id !== void 0 && this.annotated.has(id)) this.applyContext(value, this.bindingType.get(id));
|
|
5732
6763
|
}
|
|
5733
6764
|
});
|
|
5734
|
-
const { types: valueTypes, sources } = this.valueList(stmt.values, env);
|
|
6765
|
+
const { types: valueTypes, sources } = this.valueList(stmt.values, env, stmt.targets.length);
|
|
5735
6766
|
stmt.targets.forEach((target, i) => {
|
|
5736
6767
|
const vt = valueTypes[i] ?? unknownType;
|
|
5737
6768
|
const source = sources[i];
|
|
@@ -5839,7 +6870,8 @@ var TypeAnalyzer = class {
|
|
|
5839
6870
|
stmt.arguments.forEach((a, i) => this.applyContext(a, declared.elements[i]));
|
|
5840
6871
|
}
|
|
5841
6872
|
}
|
|
5842
|
-
const
|
|
6873
|
+
const want = declared?.kind === "tuple" && declared.isPack ? declared.elements.length : 0;
|
|
6874
|
+
const { types, sources } = this.valueList(stmt.arguments, env, want);
|
|
5843
6875
|
this.checkReturn(stmt, declared, types, sources, env);
|
|
5844
6876
|
if (this.returnTypes) {
|
|
5845
6877
|
this.returnTypes.push(stmt.arguments.length === 0 ? nilType : types.length === 1 ? types[0] : tuple([...types], true));
|
|
@@ -5850,7 +6882,8 @@ var TypeAnalyzer = class {
|
|
|
5850
6882
|
this.visitStatement(stmt.declaration, env);
|
|
5851
6883
|
return;
|
|
5852
6884
|
case "ExportDefaultStatement":
|
|
5853
|
-
this.
|
|
6885
|
+
if (stmt.declaration.type === "ClassDeclaration") this.visitStatement(stmt.declaration, env);
|
|
6886
|
+
else this.infer(stmt.declaration, env);
|
|
5854
6887
|
return;
|
|
5855
6888
|
case "ExportNamedStatement": {
|
|
5856
6889
|
if (stmt.source) {
|
|
@@ -5929,12 +6962,35 @@ var TypeAnalyzer = class {
|
|
|
5929
6962
|
* `sources` maps each produced value back to the expression it came from
|
|
5930
6963
|
* (undefined for the 2nd and later values of a multi-value call), so the
|
|
5931
6964
|
* caller can still do contextual typing against the written expression. */
|
|
5932
|
-
valueList(exprs, env) {
|
|
6965
|
+
valueList(exprs, env, want = 0) {
|
|
5933
6966
|
const types = [];
|
|
5934
6967
|
const sources = [];
|
|
5935
6968
|
exprs.forEach((e, i) => {
|
|
5936
6969
|
const t = this.infer(e, env);
|
|
5937
6970
|
const last = i === exprs.length - 1;
|
|
6971
|
+
if (e.type === "SpreadElement") {
|
|
6972
|
+
const held = this.typeOf.get(e.argument);
|
|
6973
|
+
const expanded = held && this.expand(held);
|
|
6974
|
+
if (expanded?.kind === "tuple") {
|
|
6975
|
+
for (const element of expanded.elements) {
|
|
6976
|
+
types.push(element);
|
|
6977
|
+
sources.push(e);
|
|
6978
|
+
}
|
|
6979
|
+
return;
|
|
6980
|
+
}
|
|
6981
|
+
do {
|
|
6982
|
+
types.push(t);
|
|
6983
|
+
sources.push(e);
|
|
6984
|
+
} while (last && types.length < want);
|
|
6985
|
+
return;
|
|
6986
|
+
}
|
|
6987
|
+
if (last && e.type === "VarargExpression") {
|
|
6988
|
+
do {
|
|
6989
|
+
types.push(t);
|
|
6990
|
+
sources.push(types.length - 1 === i ? e : void 0);
|
|
6991
|
+
} while (types.length < want);
|
|
6992
|
+
return;
|
|
6993
|
+
}
|
|
5938
6994
|
if (last && t.kind === "tuple" && t.isPack && producesMultipleValues(e)) {
|
|
5939
6995
|
t.elements.forEach((el, j) => {
|
|
5940
6996
|
types.push(el);
|
|
@@ -6023,7 +7079,8 @@ var TypeAnalyzer = class {
|
|
|
6023
7079
|
/** A parameter's type: annotation, else a shape synthesized from a
|
|
6024
7080
|
* destructuring pattern, else inferred from its default, else `any`. */
|
|
6025
7081
|
paramType(p, env) {
|
|
6026
|
-
|
|
7082
|
+
const receiver = p.name === "self" || p.name === "this";
|
|
7083
|
+
if (!p.typeAnnotation && !p.pattern && !p.default && receiver && this.selfType) {
|
|
6027
7084
|
return this.selfType;
|
|
6028
7085
|
}
|
|
6029
7086
|
if (p.typeAnnotation) {
|
|
@@ -6031,6 +7088,7 @@ var TypeAnalyzer = class {
|
|
|
6031
7088
|
if (p.default) this.applyContext(p.default, t);
|
|
6032
7089
|
return p.optional ? optional(t) : t;
|
|
6033
7090
|
}
|
|
7091
|
+
if (p.rest) return arrayOf(unknownType);
|
|
6034
7092
|
if (p.pattern) return this.patternToType(p.pattern, env);
|
|
6035
7093
|
if (p.default) return widen(this.infer(p.default, env));
|
|
6036
7094
|
return this.contextualParams.get(p) ?? anyType;
|
|
@@ -6051,6 +7109,11 @@ var TypeAnalyzer = class {
|
|
|
6051
7109
|
this.expectedTypeOf.set(e, expected);
|
|
6052
7110
|
if (e.type === "ArrayExpression") return this.applyArrayContext(e, expected);
|
|
6053
7111
|
if (e.type === "TableExpression") return this.applyTableContext(e, expected);
|
|
7112
|
+
if (e.type === "BinaryExpression" && (e.operator === "or" || e.operator === "and")) {
|
|
7113
|
+
if (e.operator === "or") this.applyContext(e.left, expected);
|
|
7114
|
+
this.applyContext(e.right, expected);
|
|
7115
|
+
return;
|
|
7116
|
+
}
|
|
6054
7117
|
if (e.type !== "FunctionExpression") return;
|
|
6055
7118
|
const members = expected.kind === "union" ? expected.types : [expected];
|
|
6056
7119
|
const signatures = members.flatMap((m) => this.overloadsOf(this.expand(m)));
|
|
@@ -6076,7 +7139,7 @@ var TypeAnalyzer = class {
|
|
|
6076
7139
|
const target = this.expectedMembers(expected).find((m) => m.kind === "array" || m.kind === "tuple");
|
|
6077
7140
|
if (!target) return;
|
|
6078
7141
|
if (!e.elements.length) {
|
|
6079
|
-
|
|
7142
|
+
this.contextualArrays.set(e, target);
|
|
6080
7143
|
return;
|
|
6081
7144
|
}
|
|
6082
7145
|
e.elements.forEach((element, i) => {
|
|
@@ -6136,13 +7199,26 @@ var TypeAnalyzer = class {
|
|
|
6136
7199
|
}
|
|
6137
7200
|
return tuple(target.elements.map((el) => el ? leaf(el.value, el.default) : anyType));
|
|
6138
7201
|
}
|
|
7202
|
+
/** What a vararg function's `...` holds, one value at a time.
|
|
7203
|
+
*
|
|
7204
|
+
* Written three ways, and they mean the same call: `...` says nothing,
|
|
7205
|
+
* `...: T` says each value is a `T`, and `...rest: T[]` collects them
|
|
7206
|
+
* into an array the body reads by name. Only the last changes what the
|
|
7207
|
+
* body sees — the signature is the same either way. */
|
|
7208
|
+
varargElement(func) {
|
|
7209
|
+
if (!func.hasVarargs) return void 0;
|
|
7210
|
+
const rest = func.params.find((p) => p.rest);
|
|
7211
|
+
if (!rest) return func.varargTypeAnnotation ? this.resolveType(func.varargTypeAnnotation) : anyType;
|
|
7212
|
+
const declared = rest.typeAnnotation ? this.resolveType(rest.typeAnnotation) : void 0;
|
|
7213
|
+
return declared?.kind === "array" ? declared.element : unknownType;
|
|
7214
|
+
}
|
|
6139
7215
|
/** The type of `...` in each function body being walked. */
|
|
6140
7216
|
varargs = [];
|
|
6141
7217
|
/** What each function body being walked declared it returns. */
|
|
6142
7218
|
declaredReturns = [];
|
|
6143
7219
|
/** Run `body` with `...` and `return` as `func` declares them. */
|
|
6144
7220
|
withVarargs(func, body) {
|
|
6145
|
-
this.varargs.push(
|
|
7221
|
+
this.varargs.push(this.varargElement(func));
|
|
6146
7222
|
this.declaredReturns.push(func.predicate ? booleanType : func.returnType ? this.resolveType(func.returnType) : void 0);
|
|
6147
7223
|
try {
|
|
6148
7224
|
return body();
|
|
@@ -6223,6 +7299,13 @@ var TypeAnalyzer = class {
|
|
|
6223
7299
|
const param = p.type.kind === "typeParam" && p.type.constraint ? { ...p.type, constraint: this.reduceType(p.type.constraint) } : p.type;
|
|
6224
7300
|
unify(p.type, keepsLiterals(param) ? arg : widen(arg), vars, subst);
|
|
6225
7301
|
});
|
|
7302
|
+
if (f.varargs) {
|
|
7303
|
+
const keeps = keepsLiterals(f.varargs);
|
|
7304
|
+
for (let i = f.params.length; i < argTypes.length; i++) {
|
|
7305
|
+
const arg = argTypes[i];
|
|
7306
|
+
if (arg !== void 0) unify(f.varargs, keeps ? arg : widen(arg), vars, subst);
|
|
7307
|
+
}
|
|
7308
|
+
}
|
|
6226
7309
|
for (const name of f.typeParams ?? []) {
|
|
6227
7310
|
if (!subst.has(name)) subst.set(name, f.typeParamDefaults?.[name] ?? unknownType);
|
|
6228
7311
|
}
|
|
@@ -6247,11 +7330,11 @@ var TypeAnalyzer = class {
|
|
|
6247
7330
|
* after every concrete signature has been tried. That ordering is what
|
|
6248
7331
|
* lets `typeof` declare `(v: number) -> "number"` alongside a trailing
|
|
6249
7332
|
* `<T>(v: T) -> string` and still pick the precise one. */
|
|
6250
|
-
pickOverload(fns, argTypes, argsFor) {
|
|
7333
|
+
pickOverload(fns, argTypes, argsFor, spread) {
|
|
6251
7334
|
for (const generic of [false, true]) {
|
|
6252
7335
|
for (const f of fns) {
|
|
6253
7336
|
if ((f.typeParams?.length ?? 0) > 0 !== generic) continue;
|
|
6254
|
-
if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes)) return f;
|
|
7337
|
+
if (this.overloadAccepts(f, argsFor ? argsFor(f) : argTypes, spread)) return f;
|
|
6255
7338
|
}
|
|
6256
7339
|
}
|
|
6257
7340
|
return void 0;
|
|
@@ -6286,12 +7369,27 @@ var TypeAnalyzer = class {
|
|
|
6286
7369
|
* own type parameters stand for what the call would infer, so each is
|
|
6287
7370
|
* checked only against its constraint — `<K extends keyof Services>`
|
|
6288
7371
|
* accepts `"Players"` but not `""`. */
|
|
6289
|
-
overloadAccepts(f, argTypes) {
|
|
6290
|
-
|
|
7372
|
+
overloadAccepts(f, argTypes, spread) {
|
|
7373
|
+
const at = (i) => {
|
|
7374
|
+
if (!spread || i < spread.index) return argTypes[i];
|
|
7375
|
+
if (!spread.elements) return argTypes[spread.index];
|
|
7376
|
+
const held = spread.elements[i - spread.index];
|
|
7377
|
+
return held ?? argTypes[i - spread.index + 1 + spread.elements.length - 1];
|
|
7378
|
+
};
|
|
7379
|
+
const written = spread?.elements ? argTypes.length + spread.elements.length - 1 : argTypes.length;
|
|
7380
|
+
if (!f.varargs && (!spread || spread.elements) && written > f.params.length) return false;
|
|
7381
|
+
if (f.varargs && !f.typeParams?.length) {
|
|
7382
|
+
const last = spread && !spread.elements ? Math.max(f.params.length + 1, written) : written;
|
|
7383
|
+
for (let i = f.params.length; i < last; i++) {
|
|
7384
|
+
const arg = at(i);
|
|
7385
|
+
if (arg !== void 0 && !isAssignable(arg, f.varargs)) return false;
|
|
7386
|
+
}
|
|
7387
|
+
}
|
|
6291
7388
|
const params = this.boundParams(f);
|
|
6292
7389
|
return f.params.every((p, i) => {
|
|
6293
|
-
|
|
6294
|
-
|
|
7390
|
+
const arg = at(i);
|
|
7391
|
+
if (arg === void 0) return p.optional === true;
|
|
7392
|
+
return isAssignable(arg, params[i]);
|
|
6295
7393
|
});
|
|
6296
7394
|
}
|
|
6297
7395
|
/** A signature's parameter types as a call site sees them before inference:
|
|
@@ -6321,17 +7419,40 @@ var TypeAnalyzer = class {
|
|
|
6321
7419
|
}
|
|
6322
7420
|
/** Record what each written argument is expected to be — see
|
|
6323
7421
|
* `TypeAnalysis.expectedTypeOf`. */
|
|
6324
|
-
recordExpected(written, fns, selfOf) {
|
|
7422
|
+
recordExpected(written, fns, selfOf, argsOf = () => []) {
|
|
7423
|
+
const paramsOf = /* @__PURE__ */ new Map();
|
|
7424
|
+
for (const f of fns) paramsOf.set(f, this.paramsAsCalled(f, argsOf(f)));
|
|
6325
7425
|
written.forEach((arg, j) => {
|
|
6326
7426
|
const candidates = [];
|
|
6327
7427
|
for (const f of fns) {
|
|
6328
7428
|
const i = j + selfOf(f);
|
|
6329
|
-
const
|
|
7429
|
+
const params = paramsOf.get(f);
|
|
7430
|
+
const param = i < params.length ? params[i] : f.varargs;
|
|
6330
7431
|
if (param) candidates.push(param);
|
|
6331
7432
|
}
|
|
6332
7433
|
if (candidates.length) this.expectedTypeOf.set(arg, union(candidates));
|
|
6333
7434
|
});
|
|
6334
7435
|
}
|
|
7436
|
+
/** The parameters as *this* call makes them read: a type argument the
|
|
7437
|
+
* arguments already written pin down is substituted in, and one nothing
|
|
7438
|
+
* has pinned down yet falls back to its constraint.
|
|
7439
|
+
*
|
|
7440
|
+
* It is what makes the second argument of
|
|
7441
|
+
* `get(page, skill: Extract<Rows, { Page: Page }>["Skills"][number])`
|
|
7442
|
+
* worth completing — with `page` written, `skill` is the skills of that
|
|
7443
|
+
* page, not of every page. */
|
|
7444
|
+
paramsAsCalled(f, argTypes) {
|
|
7445
|
+
const fallback = this.boundParams(f);
|
|
7446
|
+
if (!f.typeParams?.length || !argTypes.length) return fallback;
|
|
7447
|
+
return f.params.map((p, i) => {
|
|
7448
|
+
if (!containsTypeParam(p.type)) return p.type;
|
|
7449
|
+
const subst = this.inferTypeArgs(f, argTypes.map((t, k) => k === i ? void 0 : t));
|
|
7450
|
+
for (const [name, bound] of [...subst]) if (bound.kind === "unknown") subst.delete(name);
|
|
7451
|
+
if (!subst.size) return fallback[i];
|
|
7452
|
+
const applied = this.reduceType(substitute(p.type, subst));
|
|
7453
|
+
return containsTypeParam(applied) ? fallback[i] : applied;
|
|
7454
|
+
});
|
|
7455
|
+
}
|
|
6335
7456
|
/** No signature accepts the call, and the argument count is not the
|
|
6336
7457
|
* problem: say which argument is wrong, the way TypeScript does. */
|
|
6337
7458
|
/** Check what was written against the parameters as this call's own type
|
|
@@ -6342,10 +7463,11 @@ var TypeAnalyzer = class {
|
|
|
6342
7463
|
if (!this.emitDiagnostics || !f.typeParams?.length) return;
|
|
6343
7464
|
const subst = this.inferTypeArgs(f, [...argTypes]);
|
|
6344
7465
|
for (const bound of subst.values()) if (bound.kind === "unknown") return;
|
|
6345
|
-
|
|
7466
|
+
const declaredAt = (i) => i < f.params.length ? f.params[i].type : f.varargs;
|
|
7467
|
+
for (let i = 0; i < Math.max(f.params.length, argTypes.length); i++) {
|
|
6346
7468
|
const arg = argTypes[i];
|
|
6347
|
-
const declared =
|
|
6348
|
-
if (arg === void 0 || !containsTypeParam(declared)) continue;
|
|
7469
|
+
const declared = declaredAt(i);
|
|
7470
|
+
if (arg === void 0 || declared === void 0 || !containsTypeParam(declared)) continue;
|
|
6349
7471
|
const expected = this.reduceType(substitute(declared, subst));
|
|
6350
7472
|
if (containsTypeParam(expected) || expected.kind === "any" || expected.kind === "unknown") continue;
|
|
6351
7473
|
if (isAssignable(arg, expected) || isAssignable(widen(arg), expected)) continue;
|
|
@@ -6366,15 +7488,26 @@ var TypeAnalyzer = class {
|
|
|
6366
7488
|
const args = argsFor(f);
|
|
6367
7489
|
const params = this.boundParams(f);
|
|
6368
7490
|
const self = selfOf(f);
|
|
7491
|
+
const spread = this.spreadOf(written, self);
|
|
6369
7492
|
for (let i = 0; i < f.params.length; i++) {
|
|
6370
|
-
const arg = args[i];
|
|
6371
|
-
if (
|
|
7493
|
+
const arg = spread && i >= spread.index ? this.spreadValue(spread, i) : args[i];
|
|
7494
|
+
if (spread && i > spread.index && !spread.elements) break;
|
|
7495
|
+
if (arg === void 0 || arg.kind === "never" || isAssignable(arg, params[i])) continue;
|
|
6372
7496
|
this.diagnostics.push({
|
|
6373
|
-
node: written[i - self] ?? call,
|
|
7497
|
+
node: (spread && i >= spread.index ? written[spread.index - self] : written[i - self]) ?? call,
|
|
6374
7498
|
message: `Argument of type '${formatType(arg)}' is not assignable to parameter of type '${briefType(params[i])}'`
|
|
6375
7499
|
});
|
|
6376
7500
|
return;
|
|
6377
7501
|
}
|
|
7502
|
+
if (!f.varargs || f.typeParams?.length) return;
|
|
7503
|
+
for (let i = f.params.length; i < args.length; i++) {
|
|
7504
|
+
if (isAssignable(args[i], f.varargs)) continue;
|
|
7505
|
+
this.diagnostics.push({
|
|
7506
|
+
node: written[i - self] ?? call,
|
|
7507
|
+
message: `Argument of type '${formatType(args[i])}' is not assignable to parameter of type '${briefType(f.varargs)}'`
|
|
7508
|
+
});
|
|
7509
|
+
return;
|
|
7510
|
+
}
|
|
6378
7511
|
}
|
|
6379
7512
|
/** A required parameter may not follow an optional one — otherwise the
|
|
6380
7513
|
* optional one could never actually be omitted. Same rule as TypeScript,
|
|
@@ -6383,6 +7516,10 @@ var TypeAnalyzer = class {
|
|
|
6383
7516
|
if (!this.emitDiagnostics) return;
|
|
6384
7517
|
let seenOptional;
|
|
6385
7518
|
for (const p of params) {
|
|
7519
|
+
if (p.rest === true) {
|
|
7520
|
+
this.checkRestType(p, node);
|
|
7521
|
+
continue;
|
|
7522
|
+
}
|
|
6386
7523
|
const isOptional = p.optional === true || p.default !== void 0;
|
|
6387
7524
|
if (isOptional) {
|
|
6388
7525
|
if (seenOptional === void 0) seenOptional = p.name ?? "parameter";
|
|
@@ -6408,8 +7545,10 @@ var TypeAnalyzer = class {
|
|
|
6408
7545
|
* when *no* overload accepts the count, so an overload set still reports
|
|
6409
7546
|
* once, against its first signature. Returns whether the count fits, so
|
|
6410
7547
|
* an argument's type is only complained about when its count is right. */
|
|
6411
|
-
checkArity(node, fns, argCount, selfArgs) {
|
|
7548
|
+
checkArity(node, fns, argCount, selfArgs, spread) {
|
|
6412
7549
|
if (!fns.length) return true;
|
|
7550
|
+
if (spread && !spread.elements) return true;
|
|
7551
|
+
if (spread?.elements) argCount += spread.elements.length - 1;
|
|
6413
7552
|
const fits = fns.some((f) => {
|
|
6414
7553
|
const { min: min2, max: max2 } = this.arityOf(f);
|
|
6415
7554
|
const n = argCount + selfArgs;
|
|
@@ -6452,7 +7591,7 @@ var TypeAnalyzer = class {
|
|
|
6452
7591
|
return type;
|
|
6453
7592
|
};
|
|
6454
7593
|
return record(this.withTypeParams(sig.generics, () => {
|
|
6455
|
-
const params = sig.params.map((p) => ({
|
|
7594
|
+
const params = sig.params.filter((p) => !p.rest).map((p) => ({
|
|
6456
7595
|
name: p.pattern ? void 0 : p.name,
|
|
6457
7596
|
type: this.paramType(p, /* @__PURE__ */ new Map()),
|
|
6458
7597
|
optional: p.optional || p.default !== void 0
|
|
@@ -6460,12 +7599,23 @@ var TypeAnalyzer = class {
|
|
|
6460
7599
|
return fn(
|
|
6461
7600
|
params,
|
|
6462
7601
|
sig.returnType ? this.resolveType(sig.returnType) : sig.predicate ? booleanType : anyType,
|
|
6463
|
-
|
|
7602
|
+
this.varargElement(sig),
|
|
6464
7603
|
names,
|
|
6465
7604
|
this.resolvePredicate(sig.predicate, params)
|
|
6466
7605
|
);
|
|
6467
7606
|
}));
|
|
6468
7607
|
}
|
|
7608
|
+
/** `...rest: T[]` holds every argument from its position on, so its type
|
|
7609
|
+
* is an array of what each one is. */
|
|
7610
|
+
checkRestType(p, node) {
|
|
7611
|
+
if (!this.emitDiagnostics || !p.typeAnnotation) return;
|
|
7612
|
+
const declared = this.resolveType(p.typeAnnotation);
|
|
7613
|
+
if (declared.kind === "array" || declared.kind === "any" || declared.kind === "typeParam") return;
|
|
7614
|
+
this.diagnostics.push({
|
|
7615
|
+
node,
|
|
7616
|
+
message: `A rest parameter holds every argument from its position on, so '${p.name ?? "..."}' is an array: '${formatType(declared)}[]', not '${formatType(declared)}'`
|
|
7617
|
+
});
|
|
7618
|
+
}
|
|
6469
7619
|
/** Turn a parsed `v is T` / `asserts v` annotation into a `TypePredicate`,
|
|
6470
7620
|
* resolving the named parameter to its index. A guard naming a parameter
|
|
6471
7621
|
* the function does not have is dropped rather than mis-narrowing an
|
|
@@ -6483,17 +7633,18 @@ var TypeAnalyzer = class {
|
|
|
6483
7633
|
inferFunctionBody(func, env) {
|
|
6484
7634
|
const names = func.generics.map((g) => g.name);
|
|
6485
7635
|
return this.withTypeParams(func.generics, () => {
|
|
6486
|
-
const params = func.params.
|
|
7636
|
+
const params = func.params.flatMap((p) => {
|
|
6487
7637
|
const type = this.paramType(p, env);
|
|
6488
7638
|
if (!p.pattern) {
|
|
6489
7639
|
const id = this.bindingIdByName(p.name, p);
|
|
6490
7640
|
if (id !== void 0 && !this.bindingType.has(id)) this.bindingType.set(id, type);
|
|
6491
7641
|
}
|
|
6492
|
-
return
|
|
7642
|
+
if (p.rest) return [];
|
|
7643
|
+
return [{
|
|
6493
7644
|
name: p.pattern ? void 0 : p.name,
|
|
6494
7645
|
type,
|
|
6495
7646
|
optional: p.optional || p.default !== void 0
|
|
6496
|
-
};
|
|
7647
|
+
}];
|
|
6497
7648
|
});
|
|
6498
7649
|
const bodyEnv = forkEnv(env);
|
|
6499
7650
|
for (const p of func.params) {
|
|
@@ -6518,7 +7669,7 @@ var TypeAnalyzer = class {
|
|
|
6518
7669
|
return fn(
|
|
6519
7670
|
params,
|
|
6520
7671
|
returns,
|
|
6521
|
-
|
|
7672
|
+
this.varargElement(func),
|
|
6522
7673
|
names,
|
|
6523
7674
|
this.resolvePredicate(func.predicate, params)
|
|
6524
7675
|
);
|
|
@@ -6732,6 +7883,13 @@ var TypeAnalyzer = class {
|
|
|
6732
7883
|
if (src.kind === "array") return [numberType, src.element];
|
|
6733
7884
|
}
|
|
6734
7885
|
}
|
|
7886
|
+
const iterator = this.expand(iterType);
|
|
7887
|
+
if (iterator.kind === "function") {
|
|
7888
|
+
const returns = this.expand(iterator.returns);
|
|
7889
|
+
const parts = returns.kind === "tuple" ? returns.elements.map((m) => this.expand(m)) : [returns];
|
|
7890
|
+
const at = (i) => parts[i] ?? (parts.length === 1 ? parts[0] : unknownType);
|
|
7891
|
+
return [at(0), at(1)];
|
|
7892
|
+
}
|
|
6735
7893
|
const t = this.expand(iterType);
|
|
6736
7894
|
if (t.kind === "array") return varCount >= 2 ? [numberType, t.element] : [t.element, unknownType];
|
|
6737
7895
|
if (t.kind === "object") {
|
|
@@ -6883,7 +8041,21 @@ var TypeAnalyzer = class {
|
|
|
6883
8041
|
expand(t) {
|
|
6884
8042
|
if (t.kind !== "genericRef") return t;
|
|
6885
8043
|
const def = this.aliasDefs.get(t.name);
|
|
6886
|
-
if (!def
|
|
8044
|
+
if (!def) {
|
|
8045
|
+
const imported = this.importedTypes.get(t.name);
|
|
8046
|
+
if (!imported) return t;
|
|
8047
|
+
if (!imported.params.length) return imported.type;
|
|
8048
|
+
const subst = /* @__PURE__ */ new Map();
|
|
8049
|
+
imported.params.forEach((name, i) => subst.set(name, t.typeArguments[i] ?? unknownType));
|
|
8050
|
+
const key2 = `import ${formatType(t)}`;
|
|
8051
|
+
const cached2 = this.expandCache.get(key2);
|
|
8052
|
+
if (cached2) return cached2;
|
|
8053
|
+
this.expandCache.set(key2, t);
|
|
8054
|
+
const applied = substitute(imported.type, subst);
|
|
8055
|
+
this.expandCache.set(key2, applied);
|
|
8056
|
+
return applied;
|
|
8057
|
+
}
|
|
8058
|
+
if (this.resolvingAliases.has(t.name)) return t;
|
|
6887
8059
|
const key = t.typeArguments.length ? formatType(t) : t.name;
|
|
6888
8060
|
const cached = this.expandCache.get(key);
|
|
6889
8061
|
if (cached) return cached;
|
|
@@ -6904,19 +8076,120 @@ var TypeAnalyzer = class {
|
|
|
6904
8076
|
* those names again replaces the whole set, and nothing here is a special
|
|
6905
8077
|
* case in the analyzer. The build lowers each call to a plain function. */
|
|
6906
8078
|
builtInMethod(t, name) {
|
|
6907
|
-
const
|
|
6908
|
-
const
|
|
8079
|
+
const parts = (t.kind === "union" ? t.types : [t]).map((m) => this.expand(m));
|
|
8080
|
+
const elements = parts.map((m) => m.kind === "array" ? m.element : m.kind === "tuple" ? union(m.elements) : void 0);
|
|
8081
|
+
const element = elements.every((e) => e !== void 0) ? union(elements) : void 0;
|
|
8082
|
+
const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
|
|
8083
|
+
const methodTable = element !== void 0 ? "ArrayMethods" : parts.every(isString) ? "StringMethods" : void 0;
|
|
6909
8084
|
const def = methodTable === void 0 ? void 0 : this.aliasDefs.get(methodTable);
|
|
6910
8085
|
if (!def || def.class) return void 0;
|
|
6911
8086
|
const table = this.expand(this.instantiateAlias(def, element !== void 0 ? [element] : []));
|
|
6912
|
-
const
|
|
6913
|
-
for (let i =
|
|
6914
|
-
const part =
|
|
8087
|
+
const layers = table.kind === "intersection" ? table.types.map((m) => this.expand(m)) : [table];
|
|
8088
|
+
for (let i = layers.length - 1; i >= 0; i--) {
|
|
8089
|
+
const part = layers[i];
|
|
6915
8090
|
const property = part.kind === "object" ? part.properties.get(name) : void 0;
|
|
6916
8091
|
if (property) return property.type;
|
|
6917
8092
|
}
|
|
6918
8093
|
return void 0;
|
|
6919
8094
|
}
|
|
8095
|
+
/** The most a deferred type could turn out to be. A conditional is one of
|
|
8096
|
+
* its branches, and the true branch stands for a member of what was
|
|
8097
|
+
* tested (`T` in `T extends U ? T : never`); an indexed access reads
|
|
8098
|
+
* through the bound of what it indexes. Anything else has no bound worth
|
|
8099
|
+
* giving — `undefined` leaves the comparison as it was. */
|
|
8100
|
+
deferredBound(t, depth = 0) {
|
|
8101
|
+
if (depth > 8) return void 0;
|
|
8102
|
+
switch (t.kind) {
|
|
8103
|
+
case "conditional": {
|
|
8104
|
+
const check = this.reduceType(t.checkType);
|
|
8105
|
+
if (containsTypeParam(check)) return void 0;
|
|
8106
|
+
const subst = /* @__PURE__ */ new Map();
|
|
8107
|
+
if (t.distributeParam) subst.set(t.distributeParam, check);
|
|
8108
|
+
for (const name of t.inferVars ?? []) subst.set(name, unknownType);
|
|
8109
|
+
const branches = [substitute(t.trueType, subst), t.falseType].map((branch) => this.reduceType(branch));
|
|
8110
|
+
if (branches.some((branch) => containsTypeParam(branch))) return void 0;
|
|
8111
|
+
return union(branches);
|
|
8112
|
+
}
|
|
8113
|
+
case "indexedAccess": {
|
|
8114
|
+
const object = this.deferredBound(t.objectType, depth + 1) ?? this.atConstraints(t.objectType);
|
|
8115
|
+
const index = this.atConstraints(this.reduceType(t.indexType));
|
|
8116
|
+
if (!object || !index) return void 0;
|
|
8117
|
+
return this.indexedType(object, index);
|
|
8118
|
+
}
|
|
8119
|
+
default:
|
|
8120
|
+
return void 0;
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
/** `t` with every type parameter standing at its constraint: `Map[K]`
|
|
8124
|
+
* where `K extends "a" | "b"` is at most what those two keys hold. A
|
|
8125
|
+
* parameter with no constraint bounds nothing, and says so. */
|
|
8126
|
+
atConstraints(t) {
|
|
8127
|
+
if (!containsTypeParam(t)) return t;
|
|
8128
|
+
const bounds = /* @__PURE__ */ new Map();
|
|
8129
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
8130
|
+
let open = false;
|
|
8131
|
+
const walk = (value) => {
|
|
8132
|
+
if (!value || typeof value !== "object" || seen.has(value)) return;
|
|
8133
|
+
seen.add(value);
|
|
8134
|
+
if (value instanceof Map) {
|
|
8135
|
+
value.forEach(walk);
|
|
8136
|
+
return;
|
|
8137
|
+
}
|
|
8138
|
+
const part = value;
|
|
8139
|
+
if (part.kind === "object" && part.class) return;
|
|
8140
|
+
if (part.kind === "typeParam" && typeof part.name === "string") {
|
|
8141
|
+
if (part.constraint && !containsTypeParam(part.constraint)) {
|
|
8142
|
+
bounds.set(part.name, this.reduceType(part.constraint));
|
|
8143
|
+
} else {
|
|
8144
|
+
open = true;
|
|
8145
|
+
}
|
|
8146
|
+
}
|
|
8147
|
+
for (const child of Object.values(value)) walk(child);
|
|
8148
|
+
};
|
|
8149
|
+
walk(t);
|
|
8150
|
+
if (open) return void 0;
|
|
8151
|
+
const applied = this.reduceType(substitute(t, bounds));
|
|
8152
|
+
return containsTypeParam(applied) ? void 0 : applied;
|
|
8153
|
+
}
|
|
8154
|
+
/** What `text["upper"]` reads: a string answers only to its methods, the
|
|
8155
|
+
* way Lua's string metatable does. */
|
|
8156
|
+
stringMember(object, index) {
|
|
8157
|
+
if (index.kind !== "literal" || typeof index.value !== "string") return void 0;
|
|
8158
|
+
const parts = this.stringParts(object);
|
|
8159
|
+
if (!parts) return void 0;
|
|
8160
|
+
const found = parts.map((part) => this.builtInMethod(part, index.value));
|
|
8161
|
+
return found.every((t) => t !== void 0) ? union(found) : void 0;
|
|
8162
|
+
}
|
|
8163
|
+
/** The members of `t` when every one of them is a string — `"a" | "b"` is
|
|
8164
|
+
* as much a string as `string` is. */
|
|
8165
|
+
stringParts(t) {
|
|
8166
|
+
let expanded = this.expand(t);
|
|
8167
|
+
if (expanded.kind === "conditional" || expanded.kind === "indexedAccess") {
|
|
8168
|
+
const bound = this.deferredBound(expanded);
|
|
8169
|
+
if (!bound) return void 0;
|
|
8170
|
+
expanded = this.expand(bound);
|
|
8171
|
+
}
|
|
8172
|
+
const parts = (expanded.kind === "union" ? expanded.types : [expanded]).map((m) => this.expand(m));
|
|
8173
|
+
const isString = (m) => m.kind === "primitive" && m.name === "string" || m.kind === "literal" && m.base === "string" || m.kind === "templateLiteral";
|
|
8174
|
+
return parts.length && parts.every(isString) ? parts : void 0;
|
|
8175
|
+
}
|
|
8176
|
+
/** `text.Sans` or `text["Sans"]`: a string is not a table, and the only
|
|
8177
|
+
* members it has are the ones a type library gave it — so a name that is
|
|
8178
|
+
* not one of them is a mistake worth reporting, rather than the nil Lua
|
|
8179
|
+
* would hand back. */
|
|
8180
|
+
checkStringMember(node, object, key) {
|
|
8181
|
+
if (!this.emitDiagnostics) return;
|
|
8182
|
+
const parts = this.stringParts(object);
|
|
8183
|
+
if (!parts) return;
|
|
8184
|
+
const keys = (key.kind === "union" ? key.types : [key]).map((m) => this.expand(m));
|
|
8185
|
+
if (!keys.length || !keys.every((m) => m.kind === "literal" && typeof m.value === "string")) return;
|
|
8186
|
+
const names = keys.map((m) => String(m.value));
|
|
8187
|
+
if (names.some((name) => parts.some((part) => this.builtInMethod(part, name)))) return;
|
|
8188
|
+
this.diagnostics.push({
|
|
8189
|
+
node,
|
|
8190
|
+
message: names.length === 1 ? `'${names[0]}' does not exist on a string` : `'${briefType(key)}' does not name a member of a string`
|
|
8191
|
+
});
|
|
8192
|
+
}
|
|
6920
8193
|
propertyType(raw, name) {
|
|
6921
8194
|
const t = this.deferredAccess(this.expand(raw));
|
|
6922
8195
|
if (t.kind === "object") {
|
|
@@ -6926,7 +8199,11 @@ var TypeAnalyzer = class {
|
|
|
6926
8199
|
}
|
|
6927
8200
|
const built = this.builtInMethod(t, name);
|
|
6928
8201
|
if (built) return built;
|
|
6929
|
-
if (t.kind === "union")
|
|
8202
|
+
if (t.kind === "union") {
|
|
8203
|
+
const built2 = this.builtInMethod(t, name);
|
|
8204
|
+
if (built2) return built2;
|
|
8205
|
+
return union(t.types.map((m) => this.propertyType(m, name)));
|
|
8206
|
+
}
|
|
6930
8207
|
if (t.kind === "intersection") {
|
|
6931
8208
|
const parts = t.types.map((m) => this.propertyType(m, name)).filter((p) => p.kind !== "unknown");
|
|
6932
8209
|
if (parts.length) return intersection(parts);
|
|
@@ -6934,6 +8211,10 @@ var TypeAnalyzer = class {
|
|
|
6934
8211
|
if (t.kind === "typeParam" && t.constraint) return this.propertyType(t.constraint, name);
|
|
6935
8212
|
if (t.kind === "difference") return this.propertyType(t.base, name);
|
|
6936
8213
|
if (t.kind === "any") return anyType;
|
|
8214
|
+
if (t.kind === "conditional" || t.kind === "indexedAccess") {
|
|
8215
|
+
const bound = this.deferredBound(t);
|
|
8216
|
+
if (bound) return this.propertyType(bound, name);
|
|
8217
|
+
}
|
|
6937
8218
|
return unknownType;
|
|
6938
8219
|
}
|
|
6939
8220
|
/** `t[k]`. A statically known string key resolves against the declared
|
|
@@ -7009,6 +8290,20 @@ var TypeAnalyzer = class {
|
|
|
7009
8290
|
// `...` holds what the function declared it takes.
|
|
7010
8291
|
case "VarargExpression":
|
|
7011
8292
|
return this.varargs[this.varargs.length - 1] ?? anyType;
|
|
8293
|
+
// `f(a, ...rest)` — every value the array holds, one after
|
|
8294
|
+
// another. Each of them is an element, so that is what the
|
|
8295
|
+
// parameters it fills are checked against.
|
|
8296
|
+
case "SpreadElement": {
|
|
8297
|
+
const spread = this.infer(expr.argument, env);
|
|
8298
|
+
const element = this.spreadElement(spread);
|
|
8299
|
+
if (element === void 0 && this.emitDiagnostics) {
|
|
8300
|
+
this.diagnostics.push({
|
|
8301
|
+
node: expr,
|
|
8302
|
+
message: `Only an array can be spread, and '${formatType(spread)}' is not one`
|
|
8303
|
+
});
|
|
8304
|
+
}
|
|
8305
|
+
return element ?? anyType;
|
|
8306
|
+
}
|
|
7012
8307
|
// Broken syntax is reported by the parser; nothing more to say.
|
|
7013
8308
|
case "ErrorExpression":
|
|
7014
8309
|
return anyType;
|
|
@@ -7114,6 +8409,7 @@ var TypeAnalyzer = class {
|
|
|
7114
8409
|
const { type: obj, shortCircuits } = this.chainObject(expr, expr.object, env);
|
|
7115
8410
|
const key = this.refKeyOf(expr);
|
|
7116
8411
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8412
|
+
this.checkStringMember(expr, obj, literal(expr.property.name));
|
|
7117
8413
|
return this.chainResult(expr, narrowed ?? this.propertyType(obj, expr.property.name), shortCircuits);
|
|
7118
8414
|
}
|
|
7119
8415
|
case "IndexExpression": {
|
|
@@ -7121,12 +8417,33 @@ var TypeAnalyzer = class {
|
|
|
7121
8417
|
const idx = this.infer(expr.index, env);
|
|
7122
8418
|
const key = this.refKeyOf(expr);
|
|
7123
8419
|
const narrowed = key === void 0 ? void 0 : env.get(key);
|
|
8420
|
+
this.checkStringMember(expr, obj, this.expand(idx));
|
|
8421
|
+
const member = this.stringMember(obj, this.expand(idx));
|
|
8422
|
+
if (member) return this.chainResult(expr, member, shortCircuits);
|
|
7124
8423
|
return this.chainResult(expr, narrowed ?? this.indexedType(obj, idx), shortCircuits);
|
|
7125
8424
|
}
|
|
7126
8425
|
case "CallExpression": {
|
|
8426
|
+
if (expr.callee.type === "SuperExpression") return this.inferSuperCall(expr, env);
|
|
7127
8427
|
const { type: callee, shortCircuits } = this.chainObject(expr, expr.callee, env);
|
|
7128
8428
|
return this.chainResult(expr, this.inferCall(expr, callee, env), shortCircuits);
|
|
7129
8429
|
}
|
|
8430
|
+
case "NewExpression":
|
|
8431
|
+
return this.inferNew(expr, env);
|
|
8432
|
+
case "ClassExpression":
|
|
8433
|
+
return this.visitClass(expr, env);
|
|
8434
|
+
case "SuperExpression": {
|
|
8435
|
+
const stmt = this.currentClass;
|
|
8436
|
+
if (!stmt?.superclass) {
|
|
8437
|
+
if (this.emitDiagnostics) {
|
|
8438
|
+
this.diagnostics.push({
|
|
8439
|
+
node: expr,
|
|
8440
|
+
message: "'super' is only available inside a class that extends another"
|
|
8441
|
+
});
|
|
8442
|
+
}
|
|
8443
|
+
return anyType;
|
|
8444
|
+
}
|
|
8445
|
+
return this.superType(stmt);
|
|
8446
|
+
}
|
|
7130
8447
|
case "MethodCallExpression": {
|
|
7131
8448
|
const { type: objType, shortCircuits } = this.chainObject(expr, expr.object, env);
|
|
7132
8449
|
return this.chainResult(expr, this.inferMethodCall(expr, objType, env), shortCircuits);
|
|
@@ -7145,16 +8462,56 @@ var TypeAnalyzer = class {
|
|
|
7145
8462
|
}
|
|
7146
8463
|
}
|
|
7147
8464
|
}
|
|
8465
|
+
/** `new Name(args)` is `Name.new(args)` — the same function, and the
|
|
8466
|
+
* same check. Saying so here rather than rewriting the tree keeps the
|
|
8467
|
+
* error messages pointing at what was written. */
|
|
8468
|
+
inferNew(expr, env) {
|
|
8469
|
+
const calleeType = this.infer(expr.callee, env);
|
|
8470
|
+
const constructor = this.propertyType(calleeType, "new");
|
|
8471
|
+
if (!this.overloadsOf(constructor).length && calleeType.kind !== "any") {
|
|
8472
|
+
if (this.emitDiagnostics) {
|
|
8473
|
+
const label = expressionLabel(expr.callee) ?? formatType(calleeType);
|
|
8474
|
+
this.diagnostics.push({ node: expr.callee, message: `'${label}' is not a class; 'new' needs one` });
|
|
8475
|
+
}
|
|
8476
|
+
for (const argument of expr.arguments) this.infer(argument, env);
|
|
8477
|
+
return anyType;
|
|
8478
|
+
}
|
|
8479
|
+
return this.inferCall(expr, constructor, env);
|
|
8480
|
+
}
|
|
8481
|
+
/** `super(...)` — the base constructor, run on the instance being built. */
|
|
8482
|
+
inferSuperCall(expr, env) {
|
|
8483
|
+
const stmt = this.currentClass;
|
|
8484
|
+
const constructor = stmt ? this.baseConstructorType(stmt) : void 0;
|
|
8485
|
+
if (!stmt?.superclass) {
|
|
8486
|
+
if (this.emitDiagnostics) {
|
|
8487
|
+
this.diagnostics.push({
|
|
8488
|
+
node: expr,
|
|
8489
|
+
message: "'super(...)' is only available inside the constructor of a class that extends another"
|
|
8490
|
+
});
|
|
8491
|
+
}
|
|
8492
|
+
for (const argument of expr.arguments) this.infer(argument, env);
|
|
8493
|
+
return nilType;
|
|
8494
|
+
}
|
|
8495
|
+
if (!constructor) {
|
|
8496
|
+
for (const argument of expr.arguments) this.infer(argument, env);
|
|
8497
|
+
return nilType;
|
|
8498
|
+
}
|
|
8499
|
+
const callable = fn(constructor.params.filter((p) => p.name !== "this"), nilType, constructor.varargs);
|
|
8500
|
+
this.inferCall(expr, callable, env);
|
|
8501
|
+
return nilType;
|
|
8502
|
+
}
|
|
7148
8503
|
inferCall(expr, callee, env) {
|
|
8504
|
+
this.checkAmbiguousCall(expr);
|
|
7149
8505
|
const fns = this.overloadsOf(callee);
|
|
7150
8506
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
7151
8507
|
const expected = this.expectedArguments(expr.arguments, fns, () => 0);
|
|
7152
8508
|
expr.arguments.forEach((a, i) => this.applyContext(a, expected[i]));
|
|
7153
8509
|
const argTypes = expr.arguments.map((a) => this.infer(a, env));
|
|
7154
8510
|
if (fns.length) {
|
|
7155
|
-
this.recordExpected(expr.arguments, fns, () => 0);
|
|
7156
|
-
const
|
|
7157
|
-
const
|
|
8511
|
+
this.recordExpected(expr.arguments, fns, () => 0, () => argTypes);
|
|
8512
|
+
const spread = this.spreadOf(expr.arguments);
|
|
8513
|
+
const arityFits = this.checkArity(expr, fns, argTypes.length, 0, spread);
|
|
8514
|
+
const picked = this.pickOverload(fns, argTypes, void 0, spread);
|
|
7158
8515
|
const distributed = this.distributedReturn(fns, argTypes, picked, (_, args) => args);
|
|
7159
8516
|
if (distributed) return distributed;
|
|
7160
8517
|
if (picked) {
|
|
@@ -7166,6 +8523,21 @@ var TypeAnalyzer = class {
|
|
|
7166
8523
|
}
|
|
7167
8524
|
return callee.kind === "any" ? anyType : unknownType;
|
|
7168
8525
|
}
|
|
8526
|
+
/** A `(` on a line of its own continues the statement above it:
|
|
8527
|
+
*
|
|
8528
|
+
* const value = map[key]
|
|
8529
|
+
* ("text"):upper()
|
|
8530
|
+
*
|
|
8531
|
+
* calls `map[key]`, in luaut as in Lua and in JavaScript. It is almost
|
|
8532
|
+
* never what was meant, and what it does instead is invisible — so say
|
|
8533
|
+
* so, and name the fix. */
|
|
8534
|
+
checkAmbiguousCall(expr) {
|
|
8535
|
+
if (!this.emitDiagnostics || !expr.argumentsOnNewLine) return;
|
|
8536
|
+
this.diagnostics.push({
|
|
8537
|
+
node: expr,
|
|
8538
|
+
message: "This calls the value the line above ends with \u2014 a line break does not end a statement. Write ';' before '(' if a new statement was meant."
|
|
8539
|
+
});
|
|
8540
|
+
}
|
|
7169
8541
|
inferMethodCall(expr, objType, env) {
|
|
7170
8542
|
const fns = this.overloadsOf(this.propertyType(objType, expr.method.name));
|
|
7171
8543
|
const explicit = this.explicitTypeArguments(expr, fns);
|
|
@@ -7175,9 +8547,11 @@ var TypeAnalyzer = class {
|
|
|
7175
8547
|
if (fns.length) {
|
|
7176
8548
|
const withSelf = (f) => this.takesSelf(f) ? [objType, ...argTypes] : argTypes;
|
|
7177
8549
|
const selfOf = (f) => this.takesSelf(f) ? 1 : 0;
|
|
7178
|
-
this.recordExpected(expr.arguments, fns, selfOf);
|
|
7179
|
-
const
|
|
7180
|
-
const
|
|
8550
|
+
this.recordExpected(expr.arguments, fns, selfOf, withSelf);
|
|
8551
|
+
const self0 = this.takesSelf(fns[0]) ? 1 : 0;
|
|
8552
|
+
const spread = this.spreadOf(expr.arguments, self0);
|
|
8553
|
+
const arityFits = this.checkArity(expr, fns, argTypes.length, self0, spread);
|
|
8554
|
+
const picked = this.pickOverload(fns, argTypes, withSelf, spread);
|
|
7181
8555
|
const distributed = this.distributedReturn(
|
|
7182
8556
|
fns,
|
|
7183
8557
|
argTypes,
|
|
@@ -7826,7 +9200,39 @@ var TypeAnalyzer = class {
|
|
|
7826
9200
|
* out. Every place that has to line arguments up with parameters goes
|
|
7827
9201
|
* through here so the two sides cannot drift apart. */
|
|
7828
9202
|
takesSelf(f) {
|
|
7829
|
-
|
|
9203
|
+
const first = f.params[0]?.name;
|
|
9204
|
+
return first === "self" || first === "this";
|
|
9205
|
+
}
|
|
9206
|
+
/** What one value of a spread array is, or `undefined` when the thing
|
|
9207
|
+
* spread is not a list of values at all. */
|
|
9208
|
+
spreadElement(t) {
|
|
9209
|
+
const spread = this.expand(t);
|
|
9210
|
+
if (spread.kind === "array") return spread.element;
|
|
9211
|
+
if (spread.kind === "tuple") return union(spread.elements);
|
|
9212
|
+
if (spread.kind === "any") return anyType;
|
|
9213
|
+
return void 0;
|
|
9214
|
+
}
|
|
9215
|
+
/** Where a call's arguments stop being one each, and what fills the rest.
|
|
9216
|
+
* From a spread on, every remaining parameter is filled by one of the
|
|
9217
|
+
* array's values — however many that turns out to be, so neither the
|
|
9218
|
+
* count nor the positions after it are known. A *tuple* is the exception:
|
|
9219
|
+
* it holds a known value at each position, and `elements` says which. */
|
|
9220
|
+
spreadOf(args, self = 0) {
|
|
9221
|
+
const index = args.findIndex((a) => a.type === "SpreadElement");
|
|
9222
|
+
if (index < 0) return void 0;
|
|
9223
|
+
const spread = args[index];
|
|
9224
|
+
const held = this.typeOf.get(spread.argument);
|
|
9225
|
+
const expanded = held && this.expand(held);
|
|
9226
|
+
return {
|
|
9227
|
+
index: index + self,
|
|
9228
|
+
element: this.typeOf.get(spread) ?? unknownType,
|
|
9229
|
+
elements: expanded?.kind === "tuple" ? expanded.elements : void 0
|
|
9230
|
+
};
|
|
9231
|
+
}
|
|
9232
|
+
/** One of `spread`'s values, at the position `i` of a call's arguments. */
|
|
9233
|
+
spreadValue(spread, i) {
|
|
9234
|
+
if (!spread.elements) return spread.element;
|
|
9235
|
+
return spread.elements[i - spread.index] ?? neverType;
|
|
7830
9236
|
}
|
|
7831
9237
|
/** A function type as a list of call signatures: a lone function is a
|
|
7832
9238
|
* one-element list, an intersection is the overload set in source order. */
|
|
@@ -7881,6 +9287,8 @@ var TypeAnalyzer = class {
|
|
|
7881
9287
|
type = this.resolveType(statement.valueType);
|
|
7882
9288
|
} else if (statement.type === "FunctionDeclaration") {
|
|
7883
9289
|
type = statement.signatures?.length ? intersection(statement.signatures.map((sig) => this.signatureToFnType(sig))) : this.inferFunctionBody(statement.func, /* @__PURE__ */ new Map());
|
|
9290
|
+
} else if (statement.type === "ClassDeclaration") {
|
|
9291
|
+
type = this.classValueType(statement);
|
|
7884
9292
|
} else if (statement.type === "VariableDeclaration") {
|
|
7885
9293
|
const target = statement.names[index];
|
|
7886
9294
|
if (target.type === "IdentifierPattern" && target.typeAnnotation) {
|
|
@@ -7917,6 +9325,10 @@ var TypeAnalyzer = class {
|
|
|
7917
9325
|
return;
|
|
7918
9326
|
}
|
|
7919
9327
|
const record = node;
|
|
9328
|
+
if (record.type === "ClassDeclaration" && record.name) {
|
|
9329
|
+
const id = this.bindingIdByName(record.name.name, record.name);
|
|
9330
|
+
if (id !== void 0) out.set(id, { statement: node, index: 0 });
|
|
9331
|
+
}
|
|
7920
9332
|
if (record.type === "FunctionDeclaration" && record.name) {
|
|
7921
9333
|
const id = this.bindingIdByName(record.name.name, record.name);
|
|
7922
9334
|
if (id !== void 0) out.set(id, { statement: node, index: 0 });
|
|
@@ -8436,6 +9848,7 @@ export {
|
|
|
8436
9848
|
resolveModulePath,
|
|
8437
9849
|
resolveTypeLibraries,
|
|
8438
9850
|
setAliasExpander,
|
|
9851
|
+
setDeferredBound,
|
|
8439
9852
|
sourceMapTypes,
|
|
8440
9853
|
stringType,
|
|
8441
9854
|
stripJsonComments,
|