tsc-ts 0.1.0 → 0.1.2
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 +7 -1
- package/dist/api/compiler/ast.d.ts +15 -3
- package/dist/api/compiler/ast.js +36 -15
- package/dist/api/compiler/checker-classes.js +2 -1
- package/dist/api/compiler/checker-contextual.d.ts +20 -0
- package/dist/api/compiler/checker-contextual.js +111 -1
- package/dist/api/compiler/checker-expressions.js +14 -1
- package/dist/api/compiler/checker-grammar.d.ts +39 -4
- package/dist/api/compiler/checker-grammar.js +239 -75
- package/dist/api/compiler/checker-heritage.js +20 -14
- package/dist/api/compiler/checker-jsx.js +2 -1
- package/dist/api/compiler/checker-modules.js +4 -2
- package/dist/api/compiler/checker-source-file.js +1 -1
- package/dist/api/compiler/checker-type-factory.js +70 -19
- package/dist/api/compiler/checker-type-nodes.d.ts +1 -1
- package/dist/api/compiler/checker-type-nodes.js +5 -5
- package/dist/api/compiler/checker-unused.js +15 -10
- package/dist/api/compiler/checker.d.ts +7 -1
- package/dist/api/compiler/checker.js +14 -4
- package/dist/api/compiler/compile-program.d.ts +5 -0
- package/dist/api/compiler/compile-program.js +8 -0
- package/dist/api/compiler/expr-record.d.ts +11 -0
- package/dist/api/compiler/expr-record.js +53 -0
- package/dist/api/compiler/file-loader.d.ts +5 -0
- package/dist/api/compiler/file-loader.js +7 -0
- package/dist/api/compiler/identifier-name.d.ts +9 -0
- package/dist/api/compiler/identifier-name.js +10 -0
- package/dist/api/compiler/jsdoc.js +4 -2
- package/dist/api/compiler/parser.d.ts +2 -1
- package/dist/api/compiler/parser.js +10 -8
- package/dist/api/compiler/printer/factory.js +3 -2
- package/dist/api/compiler/printer/printer.js +3 -2
- package/dist/api/compiler/program-checker.d.ts +18 -0
- package/dist/api/compiler/program-checker.js +27 -0
- package/dist/api/compiler/reparser.js +4 -3
- package/dist/api/compiler/scanner.d.ts +8 -0
- package/dist/api/compiler/scanner.js +58 -42
- package/dist/api/compiler/shared-results.d.ts +376 -143
- package/dist/api/compiler/shared-results.js +1550 -773
- package/dist/api/compiler/transformers/declarations.js +2 -1
- package/package.json +15 -10
- package/src/compiler/ast.ts +39 -15
- package/src/compiler/cgroup.ts +157 -0
- package/src/compiler/checker-classes.ts +2 -1
- package/src/compiler/checker-contextual.ts +104 -0
- package/src/compiler/checker-expressions.ts +16 -1
- package/src/compiler/checker-grammar.ts +247 -88
- package/src/compiler/checker-heritage.ts +31 -17
- package/src/compiler/checker-jsx.ts +2 -1
- package/src/compiler/checker-modules.ts +4 -2
- package/src/compiler/checker-source-file.ts +1 -1
- package/src/compiler/checker-type-factory.ts +67 -21
- package/src/compiler/checker-type-nodes.ts +5 -5
- package/src/compiler/checker-unused.ts +13 -8
- package/src/compiler/checker.ts +15 -4
- package/src/compiler/compile-program.ts +8 -0
- package/src/compiler/decoder.ts +2 -1
- package/src/compiler/doctor-report.ts +133 -0
- package/src/compiler/expr-record.ts +59 -0
- package/src/compiler/file-loader.ts +8 -0
- package/src/compiler/identifier-name.ts +13 -0
- package/src/compiler/jsdoc.ts +4 -2
- package/src/compiler/parallel-check.ts +364 -146
- package/src/compiler/parallel-emit.ts +3 -3
- package/src/compiler/parse-thread.ts +24 -2
- package/src/compiler/parser.ts +10 -8
- package/src/compiler/printer/factory.ts +3 -2
- package/src/compiler/printer/printer.ts +4 -2
- package/src/compiler/program-checker.ts +28 -0
- package/src/compiler/reparser.ts +7 -3
- package/src/compiler/run-config.ts +1383 -0
- package/src/compiler/run-record-read.ts +87 -0
- package/src/compiler/run-records.ts +575 -0
- package/src/compiler/scanner.ts +57 -41
- package/src/compiler/shared-results.ts +1626 -896
- package/src/compiler/thread-entry.ts +3 -1
- package/src/compiler/transformers/declarations.ts +2 -1
- package/src/compiler/tsc-build.ts +64 -6
- package/src/compiler/tsc-cli.ts +33 -4
- package/src/compiler/tsc-compile.ts +223 -9
- package/src/compiler/tsc-watch.ts +25 -4
|
@@ -965,16 +965,25 @@ const enum MemberKind {
|
|
|
965
965
|
* (with `checkPrivateNames`) private names shared by static and instance members.
|
|
966
966
|
*/
|
|
967
967
|
export function checkObjectTypeForDuplicateDeclarations(c: Checker, node: Node, checkPrivateNames: boolean): void {
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
968
|
+
// Only a symbol with several declarations can be a duplicate, so the maps are created for the first one.
|
|
969
|
+
let instanceNames: Map<string, MemberKind> | undefined;
|
|
970
|
+
let staticNames: Map<string, MemberKind> | undefined;
|
|
971
|
+
let privateNames: Map<string, number> | undefined;
|
|
971
972
|
const nodeInAmbientContext = (node.flags & NodeFlags.Ambient) !== 0;
|
|
972
973
|
for (const member of getMembers(node)!.nodes) {
|
|
973
974
|
if (member.kind === SyntaxKind.Constructor) {
|
|
974
975
|
for (const param of getParameters(member)!.nodes)
|
|
975
976
|
if (isParameterPropertyDeclaration(param, member) && !isBindingPattern(getName(param))) {
|
|
976
977
|
const symbol = getSymbolOfDeclaration(c, param);
|
|
977
|
-
if (symbol !== undefined
|
|
978
|
+
if (symbol !== undefined && declarationCount(symbol) > 1)
|
|
979
|
+
checkPropertyOrAccessor(
|
|
980
|
+
c,
|
|
981
|
+
node,
|
|
982
|
+
(instanceNames ??= new Map<string, MemberKind>()),
|
|
983
|
+
symbol,
|
|
984
|
+
MemberKind.Property,
|
|
985
|
+
false,
|
|
986
|
+
);
|
|
978
987
|
}
|
|
979
988
|
continue;
|
|
980
989
|
}
|
|
@@ -988,21 +997,26 @@ export function checkObjectTypeForDuplicateDeclarations(c: Checker, node: Node,
|
|
|
988
997
|
symbol.name,
|
|
989
998
|
symbolToString(c, getSymbolOfDeclaration(c, node)!),
|
|
990
999
|
);
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
member
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1000
|
+
if (declarationCount(symbol) > 1) {
|
|
1001
|
+
const names = memberIsStatic
|
|
1002
|
+
? (staticNames ??= new Map<string, MemberKind>())
|
|
1003
|
+
: (instanceNames ??= new Map<string, MemberKind>());
|
|
1004
|
+
const hasAccessorModifier = hasSyntacticModifier(member, ModifierFlags.Accessor);
|
|
1005
|
+
if (
|
|
1006
|
+
(member.kind === SyntaxKind.PropertyDeclaration && !hasAccessorModifier) ||
|
|
1007
|
+
member.kind === SyntaxKind.PropertySignature
|
|
1008
|
+
)
|
|
1009
|
+
checkPropertyOrAccessor(c, node, names, symbol, MemberKind.Property, memberIsStatic);
|
|
1010
|
+
else if (
|
|
1011
|
+
member.kind === SyntaxKind.GetAccessor ||
|
|
1012
|
+
member.kind === SyntaxKind.SetAccessor ||
|
|
1013
|
+
(member.kind === SyntaxKind.PropertyDeclaration && hasAccessorModifier)
|
|
1014
|
+
)
|
|
1015
|
+
checkPropertyOrAccessor(c, node, names, symbol, MemberKind.Accessor, memberIsStatic);
|
|
1016
|
+
}
|
|
1004
1017
|
const name = getName(member);
|
|
1005
1018
|
if (checkPrivateNames && name !== undefined && name.kind === SyntaxKind.PrivateIdentifier) {
|
|
1019
|
+
privateNames ??= new Map<string, number>();
|
|
1006
1020
|
let flags = privateNames.get(symbol.name) ?? 0;
|
|
1007
1021
|
if (flags === 3) continue;
|
|
1008
1022
|
flags |= isStatic(member) ? 2 : 1;
|
|
@@ -171,6 +171,7 @@ import {
|
|
|
171
171
|
SymbolFlags,
|
|
172
172
|
type SymbolTable,
|
|
173
173
|
} from './symbol.js';
|
|
174
|
+
import { IdentifierName } from './identifier-name.js';
|
|
174
175
|
|
|
175
176
|
const enum JsxFlags {
|
|
176
177
|
None = 0,
|
|
@@ -1067,7 +1068,7 @@ export function createJsxAttributesTypeFromAttributesProperty(
|
|
|
1067
1068
|
// checked as an excess property like the attributes.
|
|
1068
1069
|
const childrenDeclaration = new PropertySignatureDeclaration(
|
|
1069
1070
|
undefined,
|
|
1070
|
-
new Identifier(jsxChildrenPropertyName),
|
|
1071
|
+
new Identifier(new IdentifierName(jsxChildrenPropertyName)),
|
|
1071
1072
|
undefined,
|
|
1072
1073
|
undefined,
|
|
1073
1074
|
undefined,
|
|
@@ -2167,14 +2167,16 @@ function getSymbolFlagsEx(
|
|
|
2167
2167
|
excludeTypeOnlyMeanings: boolean,
|
|
2168
2168
|
excludeLocalMeanings: boolean,
|
|
2169
2169
|
): SymbolFlags {
|
|
2170
|
-
|
|
2170
|
+
// Only a chain of aliases needs the set, so most calls never create it.
|
|
2171
|
+
let seen: Set<Symbol> | undefined;
|
|
2171
2172
|
let flags = excludeLocalMeanings ? SymbolFlags.None : symbol.flags;
|
|
2172
2173
|
while ((symbol.flags & SymbolFlags.Alias) !== 0) {
|
|
2173
2174
|
if (excludeTypeOnlyMeanings && getTypeOnlyAliasDeclaration(c, symbol) !== undefined) break;
|
|
2174
2175
|
const target = getExportSymbolOfValueSymbolIfExported(c, resolveAlias(c, symbol));
|
|
2175
2176
|
if (target === c.unknownSymbol) return SymbolFlags.All;
|
|
2176
2177
|
if ((target.flags & SymbolFlags.Alias) !== 0) {
|
|
2177
|
-
if (target === symbol || seen
|
|
2178
|
+
if (target === symbol || seen?.has(target)) break;
|
|
2179
|
+
seen ??= new Set<Symbol>();
|
|
2178
2180
|
seen.add(symbol);
|
|
2179
2181
|
seen.add(target);
|
|
2180
2182
|
}
|
|
@@ -639,7 +639,7 @@ function checkTypeMemberAccessor(c: Checker, node: Node): void {
|
|
|
639
639
|
/** Variadic tuple elements must be array-like. */
|
|
640
640
|
function checkTupleRestElements(c: Checker, node: TupleTypeNode): void {
|
|
641
641
|
for (const e of node.elements.nodes) {
|
|
642
|
-
if ((getTupleElementFlags(
|
|
642
|
+
if ((getTupleElementFlags(e) & ElementFlags.Variadic) === 0) continue;
|
|
643
643
|
if (!isArrayLikeType(c, getTypeFromTypeNode(c, getType(e)!))) {
|
|
644
644
|
c.error(e, Diagnostics.A_rest_element_type_must_be_an_array_type);
|
|
645
645
|
return;
|
|
@@ -1196,40 +1196,79 @@ export function getUnionTypeEx(
|
|
|
1196
1196
|
): Type {
|
|
1197
1197
|
if (types.length === 0) return c.neverType;
|
|
1198
1198
|
if (types.length === 1) return types[0]!;
|
|
1199
|
-
//
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1199
|
+
// Unioning two types is the common case, both a union with some other type, like `undefined`, and
|
|
1200
|
+
// two constituents of a list, and the same pairs recur. The union is a function of the two types,
|
|
1201
|
+
// the reduction and the alias, so the result is cached by the pair. A union that exceeded the
|
|
1202
|
+
// complexity limit was reported at the expression that needed it and comes back as the error
|
|
1203
|
+
// type: caching that would silence the report at the next expression with the same input list, so
|
|
1204
|
+
// an error result is not cached.
|
|
1205
|
+
if (types.length === 2 && origin === undefined) {
|
|
1206
|
+
const t0 = types[0]!;
|
|
1207
|
+
const t1 = types[1]!;
|
|
1208
|
+
let id1 = t0.id;
|
|
1209
|
+
let id2 = t1.id;
|
|
1207
1210
|
if (id1 > id2) {
|
|
1208
1211
|
const t = id1;
|
|
1209
1212
|
id1 = id2;
|
|
1210
1213
|
id2 = t;
|
|
1211
1214
|
}
|
|
1212
|
-
// Without an alias, both ids and the reduction pack into one
|
|
1213
|
-
|
|
1215
|
+
// Without an alias or a constrained intersection, both ids and the reduction pack into one
|
|
1216
|
+
// exact number. A constrained intersection's reduction resolves a type variable's constraint
|
|
1217
|
+
// (`addTypeToUnion`), so those pairs take the general path.
|
|
1218
|
+
if (
|
|
1219
|
+
alias === undefined &&
|
|
1220
|
+
id2 < UnionPairIdLimit &&
|
|
1221
|
+
!isConstrainedIntersection(t0) &&
|
|
1222
|
+
!isConstrainedIntersection(t1)
|
|
1223
|
+
) {
|
|
1214
1224
|
const id = (id1 * UnionPairIdLimit + id2) * 4 + unionReduction;
|
|
1215
1225
|
let result = c.unionOfUnionTypesById.get(id);
|
|
1216
1226
|
if (result === undefined) {
|
|
1217
1227
|
result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
|
|
1218
|
-
c.unionOfUnionTypesById.set(id, result);
|
|
1228
|
+
if (result !== c.errorType) c.unionOfUnionTypesById.set(id, result);
|
|
1219
1229
|
}
|
|
1220
1230
|
return result;
|
|
1221
1231
|
}
|
|
1222
|
-
|
|
1223
|
-
|
|
1232
|
+
if ((t0.flags & TypeFlags.Union) !== 0 || (t1.flags & TypeFlags.Union) !== 0) {
|
|
1233
|
+
const key = id1 + ',' + id2 + ',' + unionReduction + aliasKey(alias);
|
|
1234
|
+
let result = c.unionOfUnionTypes.get(key);
|
|
1235
|
+
if (result === undefined) {
|
|
1236
|
+
result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
|
|
1237
|
+
if (result !== c.errorType) c.unionOfUnionTypes.set(key, result);
|
|
1238
|
+
}
|
|
1239
|
+
return result;
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
// Longer lists recur too, from nested unions and from constituents mapped in the same order.
|
|
1243
|
+
// Normalizing a list costs a sort of its constituents and the reductions, and the union is a
|
|
1244
|
+
// function of the list as given, the reduction and the alias, so the result is cached by the list.
|
|
1245
|
+
if (origin === undefined) {
|
|
1246
|
+
const hash = typeListHash(types, alias, unionReduction);
|
|
1247
|
+
let result = c.unionTypesByInputList.get(hash, types, alias, unionReduction);
|
|
1224
1248
|
if (result === undefined) {
|
|
1225
1249
|
result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
|
|
1226
|
-
c.
|
|
1250
|
+
if (result !== c.errorType && !hasConstrainedIntersection(types))
|
|
1251
|
+
c.unionTypesByInputList.set(hash, types.slice(), alias, unionReduction, result);
|
|
1227
1252
|
}
|
|
1228
1253
|
return result;
|
|
1229
1254
|
}
|
|
1230
1255
|
return getUnionTypeWorker(c, types, unionReduction, alias, origin);
|
|
1231
1256
|
}
|
|
1232
1257
|
|
|
1258
|
+
/** An intersection that `addTypeToUnion` reduces by resolving a type variable's constraint. */
|
|
1259
|
+
function isConstrainedIntersection(t: Type): boolean {
|
|
1260
|
+
return (t.flags & TypeFlags.Intersection) !== 0 && (t.objectFlags & ObjectFlags.IsConstrainedTypeVariable) !== 0;
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1263
|
+
/** Whether `addTypesToUnion` would set `IncludesConstrainedTypeVariable` for `types`. */
|
|
1264
|
+
function hasConstrainedIntersection(types: readonly Type[]): boolean {
|
|
1265
|
+
for (const t of types) {
|
|
1266
|
+
if (isConstrainedIntersection(t)) return true;
|
|
1267
|
+
if ((t.flags & TypeFlags.Union) !== 0 && hasConstrainedIntersection((t as UnionType).types)) return true;
|
|
1268
|
+
}
|
|
1269
|
+
return false;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1233
1272
|
class TypeSet {
|
|
1234
1273
|
types: Type[] = [];
|
|
1235
1274
|
includes = 0;
|
|
@@ -1726,20 +1765,26 @@ export function getIntersectionType(c: Checker, types: Type[]): Type {
|
|
|
1726
1765
|
return getIntersectionTypeEx(c, types, IntersectionFlags.None, undefined);
|
|
1727
1766
|
}
|
|
1728
1767
|
|
|
1768
|
+
/**
|
|
1769
|
+
* The distinct types of an intersection, in the order they were added. Most intersections have a few
|
|
1770
|
+
* types, which a scan of `values` finds faster than a `Set` costs to build, so the set is created only
|
|
1771
|
+
* once the list grows past `INDEX_AFTER`.
|
|
1772
|
+
*/
|
|
1729
1773
|
class OrderedTypeSet {
|
|
1774
|
+
private static readonly INDEX_AFTER = 8;
|
|
1730
1775
|
readonly values: Type[] = [];
|
|
1731
|
-
private
|
|
1776
|
+
private index: Set<Type> | undefined;
|
|
1732
1777
|
includes = 0;
|
|
1733
1778
|
|
|
1734
1779
|
has(t: Type): boolean {
|
|
1735
|
-
return this.
|
|
1780
|
+
return this.index !== undefined ? this.index.has(t) : this.values.includes(t);
|
|
1736
1781
|
}
|
|
1737
1782
|
|
|
1738
1783
|
add(t: Type): void {
|
|
1739
|
-
if (
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1784
|
+
if (this.has(t)) return;
|
|
1785
|
+
this.values.push(t);
|
|
1786
|
+
if (this.index !== undefined) this.index.add(t);
|
|
1787
|
+
else if (this.values.length > OrderedTypeSet.INDEX_AFTER) this.index = new Set(this.values);
|
|
1743
1788
|
}
|
|
1744
1789
|
}
|
|
1745
1790
|
|
|
@@ -2114,7 +2159,8 @@ function addCrossProductIntersections(
|
|
|
2114
2159
|
intersections: Type[],
|
|
2115
2160
|
): void {
|
|
2116
2161
|
if (index === types.length) {
|
|
2117
|
-
|
|
2162
|
+
// getIntersectionTypeEx copies the types it keeps, so the buffer is reused for the next combination.
|
|
2163
|
+
const t = getIntersectionTypeEx(c, constituents, flags, undefined);
|
|
2118
2164
|
if ((t.flags & TypeFlags.Never) === 0) intersections.push(t);
|
|
2119
2165
|
return;
|
|
2120
2166
|
}
|
|
@@ -882,7 +882,7 @@ function getTypeFromTypeQueryNode(c: Checker, node: Node): Type {
|
|
|
882
882
|
return type;
|
|
883
883
|
}
|
|
884
884
|
|
|
885
|
-
export function getTupleElementFlags(
|
|
885
|
+
export function getTupleElementFlags(node: Node): ElementFlags {
|
|
886
886
|
switch (node.kind) {
|
|
887
887
|
case SyntaxKind.OptionalType:
|
|
888
888
|
return ElementFlags.Optional;
|
|
@@ -901,9 +901,9 @@ export function getTupleElementFlags(c: Checker, node: Node): ElementFlags {
|
|
|
901
901
|
return ElementFlags.Required;
|
|
902
902
|
}
|
|
903
903
|
|
|
904
|
-
function getTupleElementInfo(
|
|
904
|
+
function getTupleElementInfo(node: Node): TupleElementInfo {
|
|
905
905
|
return new TupleElementInfo(
|
|
906
|
-
getTupleElementFlags(
|
|
906
|
+
getTupleElementFlags(node),
|
|
907
907
|
node.kind === SyntaxKind.NamedTupleMember || node.kind === SyntaxKind.Parameter ? node : undefined,
|
|
908
908
|
);
|
|
909
909
|
}
|
|
@@ -934,7 +934,7 @@ function getArrayOrTupleTargetType(c: Checker, node: Node): Type {
|
|
|
934
934
|
parent.kind === SyntaxKind.TypeOperator && (parent as TypeOperatorNode).operator === SyntaxKind.ReadonlyKeyword;
|
|
935
935
|
if (getArrayElementTypeNode(node) !== undefined) return readonly ? c.globalReadonlyArrayType : c.globalArrayType;
|
|
936
936
|
const elementInfos: TupleElementInfo[] = [];
|
|
937
|
-
for (const e of (node as TupleTypeNode).elements.nodes) elementInfos.push(getTupleElementInfo(
|
|
937
|
+
for (const e of (node as TupleTypeNode).elements.nodes) elementInfos.push(getTupleElementInfo(e));
|
|
938
938
|
return getTupleTargetType(c, elementInfos, readonly);
|
|
939
939
|
}
|
|
940
940
|
|
|
@@ -969,7 +969,7 @@ function getTypeFromArrayOrTupleTypeNode(c: Checker, node: Node): Type {
|
|
|
969
969
|
}
|
|
970
970
|
|
|
971
971
|
function hasVariadicTupleElement(c: Checker, node: TupleTypeNode): boolean {
|
|
972
|
-
for (const e of node.elements.nodes) if ((getTupleElementFlags(
|
|
972
|
+
for (const e of node.elements.nodes) if ((getTupleElementFlags(e) & ElementFlags.Variadic) !== 0) return true;
|
|
973
973
|
return false;
|
|
974
974
|
}
|
|
975
975
|
|
|
@@ -421,8 +421,9 @@ function checkUnusedClassMembers(c: Checker, node: Node): void {
|
|
|
421
421
|
function checkUnusedLocalsAndParameters(c: Checker, node: Node): void {
|
|
422
422
|
const locals = getLocals(node);
|
|
423
423
|
if (locals === undefined) return;
|
|
424
|
-
|
|
425
|
-
|
|
424
|
+
// Created on the first unused declaration: most scopes have none.
|
|
425
|
+
let variableParents: Set<Node> | undefined;
|
|
426
|
+
let importClauses: Map<Node, Node[]> | undefined;
|
|
426
427
|
for (const local of locals.values()) {
|
|
427
428
|
const referenceKinds = c.symbolReferenceKinds.get(getSymbolId(local));
|
|
428
429
|
if ((local.flags & SymbolFlags.TypeParameter) !== 0) {
|
|
@@ -438,6 +439,7 @@ function checkUnusedLocalsAndParameters(c: Checker, node: Node): void {
|
|
|
438
439
|
case SyntaxKind.VariableDeclaration:
|
|
439
440
|
case SyntaxKind.Parameter:
|
|
440
441
|
case SyntaxKind.BindingElement:
|
|
442
|
+
variableParents ??= new Set<Node>();
|
|
441
443
|
variableParents.add(getRootDeclaration(declaration).parent!);
|
|
442
444
|
break;
|
|
443
445
|
case SyntaxKind.ImportClause:
|
|
@@ -445,6 +447,7 @@ function checkUnusedLocalsAndParameters(c: Checker, node: Node): void {
|
|
|
445
447
|
case SyntaxKind.NamespaceImport:
|
|
446
448
|
if (!isIdentifierThatStartsWithUnderscore(getName(declaration))) {
|
|
447
449
|
const importClause = importClauseFromImported(declaration);
|
|
450
|
+
importClauses ??= new Map<Node, Node[]>();
|
|
448
451
|
const unuseds = importClauses.get(importClause);
|
|
449
452
|
if (unuseds === undefined) importClauses.set(importClause, [declaration]);
|
|
450
453
|
else unuseds.push(declaration);
|
|
@@ -456,12 +459,14 @@ function checkUnusedLocalsAndParameters(c: Checker, node: Node): void {
|
|
|
456
459
|
}
|
|
457
460
|
}
|
|
458
461
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
462
|
+
if (variableParents !== undefined)
|
|
463
|
+
for (const declaration of variableParents) {
|
|
464
|
+
if (declaration.kind === SyntaxKind.VariableDeclarationList)
|
|
465
|
+
reportUnusedVariables(c, declaration, (declaration as VariableDeclarationList).declarations.nodes);
|
|
466
|
+
else reportUnusedVariableDeclarations(c, getParameters(declaration)!.nodes);
|
|
467
|
+
}
|
|
468
|
+
if (importClauses !== undefined)
|
|
469
|
+
for (const [declaration, unuseds] of importClauses) reportUnusedImports(c, declaration as ImportClause, unuseds);
|
|
465
470
|
}
|
|
466
471
|
|
|
467
472
|
function reportUnusedLocal(c: Checker, node: Node, name: string): void {
|
package/src/compiler/checker.ts
CHANGED
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
PagedIdSet,
|
|
42
42
|
SparseLinkTable,
|
|
43
43
|
} from './link-tables.js';
|
|
44
|
+
import { ExprCheckRecord } from './expr-record.js';
|
|
44
45
|
import { getErrorRangeForNode, getSourceFileOfNode } from './binder-utilities.js';
|
|
45
46
|
import {
|
|
46
47
|
Diagnostic,
|
|
@@ -138,7 +139,7 @@ import type { GrammarChecker } from './checker-grammar.js';
|
|
|
138
139
|
import type { ResolvedModule } from './module-resolution-types.js';
|
|
139
140
|
import type { ModuleSpecifierHost } from './module-specifiers.js';
|
|
140
141
|
import type { PropertyListIndex } from './checker-members.js';
|
|
141
|
-
import {
|
|
142
|
+
import { startThreadSharing, type ThreadSharing } from './shared-results.js';
|
|
142
143
|
|
|
143
144
|
/** What the checker needs from a program. */
|
|
144
145
|
export abstract class CheckerHost {
|
|
@@ -410,6 +411,8 @@ export class Checker implements TypeChecking {
|
|
|
410
411
|
// Counters for type and signature ids.
|
|
411
412
|
typeCount = 0;
|
|
412
413
|
signatureCount = 0;
|
|
414
|
+
/** Transient symbols the checker created (statistics), as tsgo's `Checker.SymbolCount`. */
|
|
415
|
+
symbolCount = 0;
|
|
413
416
|
/** Positions of members by name in the tables and lists that lazily instantiated members come from. */
|
|
414
417
|
readonly memberPositionsInLists = new Map<Symbol[], Map<string, number>>();
|
|
415
418
|
/** The property names of the last instantiation of each such list whose table is built on demand. */
|
|
@@ -489,6 +492,8 @@ export class Checker implements TypeChecking {
|
|
|
489
492
|
readonly resolvedSymbols = new CompactLinkTable<Symbol>();
|
|
490
493
|
/** The resolved type of a type node or expression. */
|
|
491
494
|
readonly resolvedTypes = new CompactLinkTable<Type>();
|
|
495
|
+
/** The type the last completed check of each expression key produced (expr-record.ts). */
|
|
496
|
+
readonly exprCheckRecord = new ExprCheckRecord();
|
|
492
497
|
/** The resolved signature of a call-like node or a signature declaration. */
|
|
493
498
|
readonly resolvedSignatures = new CompactLinkTable<Signature>();
|
|
494
499
|
/** Merged symbols, indexed by the id of the symbol merged into them. */
|
|
@@ -542,6 +547,7 @@ export class Checker implements TypeChecking {
|
|
|
542
547
|
readonly unionOfUnionTypes = new Map<string, Type>();
|
|
543
548
|
/** `unionOfUnionTypes` without an alias, by both ids and the reduction packed into one number. */
|
|
544
549
|
readonly unionOfUnionTypesById = new Map<number, Type>();
|
|
550
|
+
readonly unionTypesByInputList = new TypeListCache<Type>();
|
|
545
551
|
readonly intersectionTypes = new TypeListCache<Type>();
|
|
546
552
|
readonly tupleTypes = new Map<string, Type>();
|
|
547
553
|
readonly indexedAccessTypes = new Map<string, Type>();
|
|
@@ -717,7 +723,7 @@ export class Checker implements TypeChecking {
|
|
|
717
723
|
deferDiagnostic(node: Node, report: () => void): void {
|
|
718
724
|
this.deferredDiagnostics.push(report);
|
|
719
725
|
if (this.deferredDiagnosticFiles !== undefined)
|
|
720
|
-
this.deferredDiagnosticFiles.push(getSourceFileOfNode(node)
|
|
726
|
+
this.deferredDiagnosticFiles.push(this.fileIndex.get(getSourceFileOfNode(node)) ?? -1);
|
|
721
727
|
}
|
|
722
728
|
|
|
723
729
|
/**
|
|
@@ -843,7 +849,7 @@ export class Checker implements TypeChecking {
|
|
|
843
849
|
/** The access flags of the key `getIndexedAccessTypeEx` keeps each indexed access type by. */
|
|
844
850
|
indexedAccessKeyFlags: Map<Type, number> | undefined = undefined;
|
|
845
851
|
/** Where results are shared between checker threads (shared-results.ts): the file of each diagnostic deferred. */
|
|
846
|
-
deferredDiagnosticFiles:
|
|
852
|
+
deferredDiagnosticFiles: number[] | undefined = undefined;
|
|
847
853
|
|
|
848
854
|
constructor(host: CheckerHost) {
|
|
849
855
|
this.host = host;
|
|
@@ -1056,7 +1062,9 @@ export class Checker implements TypeChecking {
|
|
|
1056
1062
|
mergeAmbientModulesAndAugmentations(this);
|
|
1057
1063
|
this.modulesMerged = true;
|
|
1058
1064
|
this.mergeDiagnostics.push(this.diagnostics.takeLog(), this.suggestionDiagnostics.takeLog());
|
|
1059
|
-
|
|
1065
|
+
// Returns unless this thread was given sharing data (`ThreadSharingData` in its worker data): the
|
|
1066
|
+
// main thread, and every thread of a run that didn't share.
|
|
1067
|
+
startThreadSharing(this);
|
|
1060
1068
|
if (this.threadSharing !== undefined) {
|
|
1061
1069
|
this.distributedConditionals = new Map<Type, DistributedConditional>();
|
|
1062
1070
|
this.distributedByKey = new Map<string, Type>();
|
|
@@ -1167,11 +1175,13 @@ export class Checker implements TypeChecking {
|
|
|
1167
1175
|
}
|
|
1168
1176
|
|
|
1169
1177
|
newSymbol(flags: SymbolFlags, name: string): Symbol {
|
|
1178
|
+
this.symbolCount++;
|
|
1170
1179
|
const symbol = new Symbol(flags | SymbolFlags.Transient, name);
|
|
1171
1180
|
return symbol;
|
|
1172
1181
|
}
|
|
1173
1182
|
|
|
1174
1183
|
newSymbolWithCheckFlags(flags: SymbolFlags, name: string, checkFlags: CheckFlags): Symbol {
|
|
1184
|
+
this.symbolCount++;
|
|
1175
1185
|
const symbol = new Symbol(flags | SymbolFlags.Transient, name);
|
|
1176
1186
|
symbol.checkFlags = checkFlags;
|
|
1177
1187
|
return symbol;
|
|
@@ -1271,6 +1281,7 @@ export function disposeChecker(c: Checker): void {
|
|
|
1271
1281
|
c.unionTypes.clear();
|
|
1272
1282
|
c.unionOfUnionTypes.clear();
|
|
1273
1283
|
c.unionOfUnionTypesById.clear();
|
|
1284
|
+
c.unionTypesByInputList.clear();
|
|
1274
1285
|
c.intersectionTypes.clear();
|
|
1275
1286
|
c.tupleTypes.clear();
|
|
1276
1287
|
c.indexedAccessTypes.clear();
|
|
@@ -375,6 +375,12 @@ export abstract class CompilerProgram {
|
|
|
375
375
|
this.emitter = new ProgramEmitter(checked);
|
|
376
376
|
}
|
|
377
377
|
|
|
378
|
+
/**
|
|
379
|
+
* Whether this run checked the program, which `getDiagnosticsOfProgram` sets when it checks it. A run
|
|
380
|
+
* stopped before checking, by syntax errors or `--noCheck`, keeps no record (`run-records.ts`).
|
|
381
|
+
*/
|
|
382
|
+
checkedTheProgram = false;
|
|
383
|
+
|
|
378
384
|
/**
|
|
379
385
|
* The time spent in declaration emit while checking, to count as emit time rather than check time, since
|
|
380
386
|
* the last call. Only an incremental program emits while checking, to compute signatures.
|
|
@@ -530,6 +536,8 @@ export function getDiagnosticsOfProgram(
|
|
|
530
536
|
// Those found by checking come after the semantic diagnostics, as with a single checker.
|
|
531
537
|
for (const d of program.getGlobalDiagnostics(false)) result.push(d);
|
|
532
538
|
if (result.length === configCount) {
|
|
539
|
+
// With `--noCheck` the checker skips every file: the run checked nothing to sample (`run-records.ts`).
|
|
540
|
+
if (options.noCheck !== true) program.checkedTheProgram = true;
|
|
533
541
|
const checkStart = performance.now();
|
|
534
542
|
const semantic = program.getSemanticDiagnostics();
|
|
535
543
|
// Declaration emit that computed signatures while checking counts as emit time, as in TypeScript 7.1.
|
package/src/compiler/decoder.ts
CHANGED
|
@@ -201,6 +201,7 @@ import {
|
|
|
201
201
|
import { modifiersToFlags } from './ast-utilities.js';
|
|
202
202
|
import { protocolVersion } from './encoder.js';
|
|
203
203
|
import { SyntaxKind } from './syntax-kind.js';
|
|
204
|
+
import { IdentifierName } from './identifier-name.js';
|
|
204
205
|
|
|
205
206
|
const nodeSize = 28;
|
|
206
207
|
const headerSize = 64;
|
|
@@ -354,7 +355,7 @@ class Decoder {
|
|
|
354
355
|
createString(kind: SyntaxKind, text: string, common: number): Node {
|
|
355
356
|
switch (kind) {
|
|
356
357
|
case SyntaxKind.Identifier:
|
|
357
|
-
return new Identifier(text);
|
|
358
|
+
return new Identifier(new IdentifierName(text));
|
|
358
359
|
case SyntaxKind.PrivateIdentifier:
|
|
359
360
|
return new PrivateIdentifier(text);
|
|
360
361
|
case SyntaxKind.JsxText:
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The report a `tsc-ts doctor` child writes: `TSC_TS_DOCTOR_REPORT` names a file, and `main.ts` writes
|
|
3
|
+
* what the child's compile measured to it before the process exits. The doctor process reads the file
|
|
4
|
+
* after the child exits; its own runs are unaffected, since nothing here is set for them.
|
|
5
|
+
*
|
|
6
|
+
* The parent times the child and compares its diagnostic output itself, so the report carries what only
|
|
7
|
+
* the child knows: the phase times, the program's shape, the signals, the run's configuration and the
|
|
8
|
+
* key the run's record would have.
|
|
9
|
+
*/
|
|
10
|
+
import { writeFileSync } from 'node:fs';
|
|
11
|
+
import { residentMB } from './parallel-check.js';
|
|
12
|
+
|
|
13
|
+
/** The report path, or `undefined` when this process is not a doctor child. */
|
|
14
|
+
export function doctorReportPath(): string | undefined {
|
|
15
|
+
const path = process.env.TSC_TS_DOCTOR_REPORT;
|
|
16
|
+
return path === undefined || path === '' ? undefined : path;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** What a bound program showed about the cost of checking it (`CheckSignals`), as the report keeps it. */
|
|
20
|
+
export interface DoctorSignals {
|
|
21
|
+
count: number;
|
|
22
|
+
ranges: number;
|
|
23
|
+
library: number;
|
|
24
|
+
own: number;
|
|
25
|
+
redundancy: number;
|
|
26
|
+
declarationShare: number;
|
|
27
|
+
nodeModulesShare: number;
|
|
28
|
+
fileShare: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** The program's size, as a run record keeps it. */
|
|
32
|
+
export interface DoctorShape {
|
|
33
|
+
files: number;
|
|
34
|
+
chars: number;
|
|
35
|
+
declarationChars: number;
|
|
36
|
+
checkChars: number;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** The phase times of a compile, in milliseconds (`CompileTimes`). */
|
|
40
|
+
export interface DoctorTimes {
|
|
41
|
+
parseMs: number;
|
|
42
|
+
checkMs: number;
|
|
43
|
+
totalMs: number;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** What this child's compile measured. */
|
|
47
|
+
export interface DoctorCompile {
|
|
48
|
+
/** The version the executable reports (`help.ts`). */
|
|
49
|
+
version: string;
|
|
50
|
+
/** The project's config file, or `undefined` for root files on the command line. */
|
|
51
|
+
configFile: string | undefined;
|
|
52
|
+
/** The key a record of this run would have (`run-records.ts`). */
|
|
53
|
+
key: string;
|
|
54
|
+
checkers: number;
|
|
55
|
+
share: boolean;
|
|
56
|
+
parseThreads: number;
|
|
57
|
+
times: DoctorTimes;
|
|
58
|
+
shape: DoctorShape;
|
|
59
|
+
signals: DoctorSignals | undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* What the file holds: the compile the child measured, where it measured one, plus the exit status,
|
|
64
|
+
* the CPU time and the peak memory. A child that never compiled writes only the last three.
|
|
65
|
+
*/
|
|
66
|
+
export interface DoctorReport {
|
|
67
|
+
version?: string;
|
|
68
|
+
configFile?: string;
|
|
69
|
+
key?: string;
|
|
70
|
+
checkers?: number;
|
|
71
|
+
share?: boolean;
|
|
72
|
+
parseThreads?: number;
|
|
73
|
+
times?: DoctorTimes;
|
|
74
|
+
shape?: DoctorShape;
|
|
75
|
+
signals?: DoctorSignals;
|
|
76
|
+
status: number;
|
|
77
|
+
cpuMs: number;
|
|
78
|
+
peakMB: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The report the process writes, once its compile measured something. */
|
|
82
|
+
let pending: DoctorCompile | undefined;
|
|
83
|
+
|
|
84
|
+
/** Records what this child's compile measured; `main.ts` writes it before the process exits. */
|
|
85
|
+
export function setDoctorCompile(compile: DoctorCompile): void {
|
|
86
|
+
pending = compile;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Exits with `status`, writing the doctor report first where `TSC_TS_DOCTOR_REPORT` names a file. The
|
|
91
|
+
* compile's exits go through here: the thread drivers in `node-system.ts` exit from their callbacks,
|
|
92
|
+
* without returning to `main.ts`.
|
|
93
|
+
*/
|
|
94
|
+
export function exitWithDoctorReport(status: number): never {
|
|
95
|
+
if (doctorReportPath() !== undefined) {
|
|
96
|
+
const cpu = process.cpuUsage();
|
|
97
|
+
writeDoctorReport(status, (cpu.user + cpu.system) / 1000);
|
|
98
|
+
}
|
|
99
|
+
return process.exit(status);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Writes the report to the file `TSC_TS_DOCTOR_REPORT` names, with the exit status, the CPU time and the
|
|
104
|
+
* peak memory read at exit. Never throws: a report that cannot be written leaves the parent to time the
|
|
105
|
+
* child without one.
|
|
106
|
+
*/
|
|
107
|
+
export function writeDoctorReport(status: number, cpuMs: number): void {
|
|
108
|
+
const path = doctorReportPath();
|
|
109
|
+
if (path === undefined) return;
|
|
110
|
+
const compile = pending;
|
|
111
|
+
const report: DoctorReport =
|
|
112
|
+
compile === undefined
|
|
113
|
+
? { status, cpuMs, peakMB: residentMB()[1] }
|
|
114
|
+
: {
|
|
115
|
+
version: compile.version,
|
|
116
|
+
configFile: compile.configFile,
|
|
117
|
+
key: compile.key,
|
|
118
|
+
checkers: compile.checkers,
|
|
119
|
+
share: compile.share,
|
|
120
|
+
parseThreads: compile.parseThreads,
|
|
121
|
+
times: compile.times,
|
|
122
|
+
shape: compile.shape,
|
|
123
|
+
signals: compile.signals,
|
|
124
|
+
status,
|
|
125
|
+
cpuMs,
|
|
126
|
+
peakMB: residentMB()[1],
|
|
127
|
+
};
|
|
128
|
+
try {
|
|
129
|
+
writeFileSync(path, JSON.stringify(report) + '\n');
|
|
130
|
+
} catch {
|
|
131
|
+
// The parent reports the run without a report file.
|
|
132
|
+
}
|
|
133
|
+
}
|