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.
Files changed (81) hide show
  1. package/README.md +7 -1
  2. package/dist/api/compiler/ast.d.ts +15 -3
  3. package/dist/api/compiler/ast.js +36 -15
  4. package/dist/api/compiler/checker-classes.js +2 -1
  5. package/dist/api/compiler/checker-contextual.d.ts +20 -0
  6. package/dist/api/compiler/checker-contextual.js +111 -1
  7. package/dist/api/compiler/checker-expressions.js +14 -1
  8. package/dist/api/compiler/checker-grammar.d.ts +39 -4
  9. package/dist/api/compiler/checker-grammar.js +239 -75
  10. package/dist/api/compiler/checker-heritage.js +20 -14
  11. package/dist/api/compiler/checker-jsx.js +2 -1
  12. package/dist/api/compiler/checker-modules.js +4 -2
  13. package/dist/api/compiler/checker-source-file.js +1 -1
  14. package/dist/api/compiler/checker-type-factory.js +70 -19
  15. package/dist/api/compiler/checker-type-nodes.d.ts +1 -1
  16. package/dist/api/compiler/checker-type-nodes.js +5 -5
  17. package/dist/api/compiler/checker-unused.js +15 -10
  18. package/dist/api/compiler/checker.d.ts +7 -1
  19. package/dist/api/compiler/checker.js +14 -4
  20. package/dist/api/compiler/compile-program.d.ts +5 -0
  21. package/dist/api/compiler/compile-program.js +8 -0
  22. package/dist/api/compiler/expr-record.d.ts +11 -0
  23. package/dist/api/compiler/expr-record.js +53 -0
  24. package/dist/api/compiler/file-loader.d.ts +5 -0
  25. package/dist/api/compiler/file-loader.js +7 -0
  26. package/dist/api/compiler/identifier-name.d.ts +9 -0
  27. package/dist/api/compiler/identifier-name.js +10 -0
  28. package/dist/api/compiler/jsdoc.js +4 -2
  29. package/dist/api/compiler/parser.d.ts +2 -1
  30. package/dist/api/compiler/parser.js +10 -8
  31. package/dist/api/compiler/printer/factory.js +3 -2
  32. package/dist/api/compiler/printer/printer.js +3 -2
  33. package/dist/api/compiler/program-checker.d.ts +18 -0
  34. package/dist/api/compiler/program-checker.js +27 -0
  35. package/dist/api/compiler/reparser.js +4 -3
  36. package/dist/api/compiler/scanner.d.ts +8 -0
  37. package/dist/api/compiler/scanner.js +58 -42
  38. package/dist/api/compiler/shared-results.d.ts +376 -143
  39. package/dist/api/compiler/shared-results.js +1550 -773
  40. package/dist/api/compiler/transformers/declarations.js +2 -1
  41. package/package.json +15 -10
  42. package/src/compiler/ast.ts +39 -15
  43. package/src/compiler/cgroup.ts +157 -0
  44. package/src/compiler/checker-classes.ts +2 -1
  45. package/src/compiler/checker-contextual.ts +104 -0
  46. package/src/compiler/checker-expressions.ts +16 -1
  47. package/src/compiler/checker-grammar.ts +247 -88
  48. package/src/compiler/checker-heritage.ts +31 -17
  49. package/src/compiler/checker-jsx.ts +2 -1
  50. package/src/compiler/checker-modules.ts +4 -2
  51. package/src/compiler/checker-source-file.ts +1 -1
  52. package/src/compiler/checker-type-factory.ts +67 -21
  53. package/src/compiler/checker-type-nodes.ts +5 -5
  54. package/src/compiler/checker-unused.ts +13 -8
  55. package/src/compiler/checker.ts +15 -4
  56. package/src/compiler/compile-program.ts +8 -0
  57. package/src/compiler/decoder.ts +2 -1
  58. package/src/compiler/doctor-report.ts +133 -0
  59. package/src/compiler/expr-record.ts +59 -0
  60. package/src/compiler/file-loader.ts +8 -0
  61. package/src/compiler/identifier-name.ts +13 -0
  62. package/src/compiler/jsdoc.ts +4 -2
  63. package/src/compiler/parallel-check.ts +364 -146
  64. package/src/compiler/parallel-emit.ts +3 -3
  65. package/src/compiler/parse-thread.ts +24 -2
  66. package/src/compiler/parser.ts +10 -8
  67. package/src/compiler/printer/factory.ts +3 -2
  68. package/src/compiler/printer/printer.ts +4 -2
  69. package/src/compiler/program-checker.ts +28 -0
  70. package/src/compiler/reparser.ts +7 -3
  71. package/src/compiler/run-config.ts +1383 -0
  72. package/src/compiler/run-record-read.ts +87 -0
  73. package/src/compiler/run-records.ts +575 -0
  74. package/src/compiler/scanner.ts +57 -41
  75. package/src/compiler/shared-results.ts +1626 -896
  76. package/src/compiler/thread-entry.ts +3 -1
  77. package/src/compiler/transformers/declarations.ts +2 -1
  78. package/src/compiler/tsc-build.ts +64 -6
  79. package/src/compiler/tsc-cli.ts +33 -4
  80. package/src/compiler/tsc-compile.ts +223 -9
  81. 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
- const instanceNames = new Map<string, MemberKind>();
969
- const staticNames = new Map<string, MemberKind>();
970
- const privateNames = new Map<string, number>();
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) checkPropertyOrAccessor(c, node, instanceNames, symbol, MemberKind.Property, false);
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
- const names = memberIsStatic ? staticNames : instanceNames;
992
- const hasAccessorModifier = hasSyntacticModifier(member, ModifierFlags.Accessor);
993
- if (
994
- (member.kind === SyntaxKind.PropertyDeclaration && !hasAccessorModifier) ||
995
- member.kind === SyntaxKind.PropertySignature
996
- )
997
- checkPropertyOrAccessor(c, node, names, symbol, MemberKind.Property, memberIsStatic);
998
- else if (
999
- member.kind === SyntaxKind.GetAccessor ||
1000
- member.kind === SyntaxKind.SetAccessor ||
1001
- (member.kind === SyntaxKind.PropertyDeclaration && hasAccessorModifier)
1002
- )
1003
- checkPropertyOrAccessor(c, node, names, symbol, MemberKind.Accessor, memberIsStatic);
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
- const seen = new Set<Symbol>();
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.has(target)) break;
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(c, e) & ElementFlags.Variadic) === 0) continue;
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
- // Optimize the common case of unioning a union with some other type, like `undefined`.
1200
- if (
1201
- types.length === 2 &&
1202
- origin === undefined &&
1203
- ((types[0]!.flags & TypeFlags.Union) !== 0 || (types[1]!.flags & TypeFlags.Union) !== 0)
1204
- ) {
1205
- let id1 = types[0]!.id;
1206
- let id2 = types[1]!.id;
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 exact number.
1213
- if (alias === undefined && id2 < UnionPairIdLimit) {
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
- const key = id1 + ',' + id2 + ',' + unionReduction + aliasKey(alias);
1223
- let result = c.unionOfUnionTypes.get(key);
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.unionOfUnionTypes.set(key, result);
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 readonly seen = new Set<Type>();
1776
+ private index: Set<Type> | undefined;
1732
1777
  includes = 0;
1733
1778
 
1734
1779
  has(t: Type): boolean {
1735
- return this.seen.has(t);
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 (!this.seen.has(t)) {
1740
- this.seen.add(t);
1741
- this.values.push(t);
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
- const t = getIntersectionTypeEx(c, constituents.slice(), flags, undefined);
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(c: Checker, node: Node): ElementFlags {
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(c: Checker, node: Node): TupleElementInfo {
904
+ function getTupleElementInfo(node: Node): TupleElementInfo {
905
905
  return new TupleElementInfo(
906
- getTupleElementFlags(c, node),
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(c, e));
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(c, e) & ElementFlags.Variadic) !== 0) return true;
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
- const variableParents = new Set<Node>();
425
- const importClauses = new Map<Node, Node[]>();
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
- for (const declaration of variableParents) {
460
- if (declaration.kind === SyntaxKind.VariableDeclarationList)
461
- reportUnusedVariables(c, declaration, (declaration as VariableDeclarationList).declarations.nodes);
462
- else reportUnusedVariableDeclarations(c, getParameters(declaration)!.nodes);
463
- }
464
- for (const [declaration, unuseds] of importClauses) reportUnusedImports(c, declaration as ImportClause, unuseds);
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 {
@@ -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 { shareThreads, startThreadSharing, type ThreadSharing } from './shared-results.js';
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).fileName);
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: string[] | undefined = undefined;
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
- if (shareThreads) startThreadSharing(this);
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.
@@ -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
+ }