tsc-ts 0.1.1 → 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 (33) hide show
  1. package/README.md +6 -0
  2. package/dist/api/compiler/ast.d.ts +6 -0
  3. package/dist/api/compiler/ast.js +19 -3
  4. package/dist/api/compiler/checker-grammar.d.ts +39 -4
  5. package/dist/api/compiler/checker-grammar.js +239 -75
  6. package/dist/api/compiler/checker-heritage.js +20 -14
  7. package/dist/api/compiler/checker-modules.js +4 -2
  8. package/dist/api/compiler/checker-source-file.js +1 -1
  9. package/dist/api/compiler/checker-type-factory.js +17 -7
  10. package/dist/api/compiler/checker-type-nodes.d.ts +1 -1
  11. package/dist/api/compiler/checker-type-nodes.js +5 -5
  12. package/dist/api/compiler/checker-unused.js +15 -10
  13. package/dist/api/compiler/checker.d.ts +2 -0
  14. package/dist/api/compiler/checker.js +4 -0
  15. package/dist/api/compiler/file-loader.d.ts +5 -0
  16. package/dist/api/compiler/file-loader.js +7 -0
  17. package/dist/api/compiler/program-checker.d.ts +18 -0
  18. package/dist/api/compiler/program-checker.js +27 -0
  19. package/package.json +14 -9
  20. package/src/compiler/ast.ts +20 -3
  21. package/src/compiler/checker-grammar.ts +247 -88
  22. package/src/compiler/checker-heritage.ts +31 -17
  23. package/src/compiler/checker-modules.ts +4 -2
  24. package/src/compiler/checker-source-file.ts +1 -1
  25. package/src/compiler/checker-type-factory.ts +14 -7
  26. package/src/compiler/checker-type-nodes.ts +5 -5
  27. package/src/compiler/checker-unused.ts +13 -8
  28. package/src/compiler/checker.ts +4 -0
  29. package/src/compiler/file-loader.ts +8 -0
  30. package/src/compiler/parallel-check.ts +11 -0
  31. package/src/compiler/parse-thread.ts +24 -2
  32. package/src/compiler/program-checker.ts +28 -0
  33. package/src/compiler/tsc-compile.ts +10 -1
@@ -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;
@@ -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;
@@ -1765,20 +1765,26 @@ export function getIntersectionType(c: Checker, types: Type[]): Type {
1765
1765
  return getIntersectionTypeEx(c, types, IntersectionFlags.None, undefined);
1766
1766
  }
1767
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
+ */
1768
1773
  class OrderedTypeSet {
1774
+ private static readonly INDEX_AFTER = 8;
1769
1775
  readonly values: Type[] = [];
1770
- private readonly seen = new Set<Type>();
1776
+ private index: Set<Type> | undefined;
1771
1777
  includes = 0;
1772
1778
 
1773
1779
  has(t: Type): boolean {
1774
- return this.seen.has(t);
1780
+ return this.index !== undefined ? this.index.has(t) : this.values.includes(t);
1775
1781
  }
1776
1782
 
1777
1783
  add(t: Type): void {
1778
- if (!this.seen.has(t)) {
1779
- this.seen.add(t);
1780
- this.values.push(t);
1781
- }
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);
1782
1788
  }
1783
1789
  }
1784
1790
 
@@ -2153,7 +2159,8 @@ function addCrossProductIntersections(
2153
2159
  intersections: Type[],
2154
2160
  ): void {
2155
2161
  if (index === types.length) {
2156
- 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);
2157
2164
  if ((t.flags & TypeFlags.Never) === 0) intersections.push(t);
2158
2165
  return;
2159
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 {
@@ -411,6 +411,8 @@ export class Checker implements TypeChecking {
411
411
  // Counters for type and signature ids.
412
412
  typeCount = 0;
413
413
  signatureCount = 0;
414
+ /** Transient symbols the checker created (statistics), as tsgo's `Checker.SymbolCount`. */
415
+ symbolCount = 0;
414
416
  /** Positions of members by name in the tables and lists that lazily instantiated members come from. */
415
417
  readonly memberPositionsInLists = new Map<Symbol[], Map<string, number>>();
416
418
  /** The property names of the last instantiation of each such list whose table is built on demand. */
@@ -1173,11 +1175,13 @@ export class Checker implements TypeChecking {
1173
1175
  }
1174
1176
 
1175
1177
  newSymbol(flags: SymbolFlags, name: string): Symbol {
1178
+ this.symbolCount++;
1176
1179
  const symbol = new Symbol(flags | SymbolFlags.Transient, name);
1177
1180
  return symbol;
1178
1181
  }
1179
1182
 
1180
1183
  newSymbolWithCheckFlags(flags: SymbolFlags, name: string, checkFlags: CheckFlags): Symbol {
1184
+ this.symbolCount++;
1181
1185
  const symbol = new Symbol(flags | SymbolFlags.Transient, name);
1182
1186
  symbol.checkFlags = checkFlags;
1183
1187
  return symbol;
@@ -131,6 +131,12 @@ export class CompilerHost extends ModuleResolutionHost {
131
131
  return this.fs.getAccessibleEntries(path).directories;
132
132
  }
133
133
 
134
+ /**
135
+ * The metadata of the file `FileLoader` is parsing, which it computes from the file's package scope
136
+ * before it parses it; a host that walks the grammar of the parsed file reads it (parse-thread.ts).
137
+ */
138
+ loadingSourceFileMetadata: SourceFileMetadata | undefined = undefined;
139
+
134
140
  /** Parses a file, or returns `undefined` if it can't be read. */
135
141
  getSourceFile(
136
142
  fileName: string,
@@ -1222,6 +1228,8 @@ export class FileLoader {
1222
1228
  ? libFileMetadata
1223
1229
  : (loaded?.metadata ?? this.loadSourceFileMetadata(task.normalizedFilePath));
1224
1230
  const options = this.mapper.getCompilerOptionsForFile(task.path, task.normalizedFilePath);
1231
+ // For the host that walks the grammar of the file as it parses it (parse-thread.ts).
1232
+ this.host.loadingSourceFileMetadata = task.metadata;
1225
1233
  const file = this.host.getSourceFile(
1226
1234
  task.normalizedFilePath,
1227
1235
  task.path,
@@ -199,6 +199,11 @@ export class CheckerResult {
199
199
  readonly units: number[] = [],
200
200
  /** Those of `units` after which its checker had diagnostics in their file (`checkSourceFileRange`). */
201
201
  readonly reportingUnits: number[] = [],
202
+ /**
203
+ * The types, instantiations and transient symbols its checker created, which `--diagnostics` adds to
204
+ * the main thread's (`CheckedProgram.addCheckerCounts`); empty for a result without a checker of its own.
205
+ */
206
+ readonly counts: number[] = [],
202
207
  ) {}
203
208
  }
204
209
 
@@ -243,6 +248,7 @@ export type PackedResult = {
243
248
  readonly reserved: number[];
244
249
  readonly units: Int32Array;
245
250
  readonly reportingUnits: Int32Array;
251
+ readonly counts: number[];
246
252
  };
247
253
 
248
254
  class ResultPacker {
@@ -346,6 +352,7 @@ export function packResult(result: CheckerResult, program: Program): PackedResul
346
352
  reserved: result.reserved,
347
353
  units: Int32Array.from(result.units),
348
354
  reportingUnits: Int32Array.from(result.reportingUnits),
355
+ counts: result.counts,
349
356
  };
350
357
  }
351
358
 
@@ -475,6 +482,7 @@ export function unpackResult(
475
482
  emits,
476
483
  Array.from(packed.units),
477
484
  Array.from(packed.reportingUnits),
485
+ packed.counts,
478
486
  );
479
487
  }
480
488
 
@@ -1928,6 +1936,7 @@ export function checkFromQueue(
1928
1936
  emits,
1929
1937
  unitsDone,
1930
1938
  reportingUnits,
1939
+ onMainThread ? [] : [checked.checker.typeCount, checked.checker.totalInstantiations, checked.checker.symbolCount],
1931
1940
  );
1932
1941
  }
1933
1942
 
@@ -2326,6 +2335,7 @@ export function startParallelCheck(
2326
2335
  done(performance.now() - start);
2327
2336
  };
2328
2337
  const record = (result: CheckerResult): void => {
2338
+ checked.addCheckerCounts(result.counts);
2329
2339
  if (declarationsTo !== undefined) {
2330
2340
  for (let k = 0; k < result.declarations.length; k++) declarationsTo[result.files[k]!] = result.declarations[k]!;
2331
2341
  if (trace)
@@ -2569,6 +2579,7 @@ class Revival {
2569
2579
  copy.emits.map((emit) => this.emit(emit)),
2570
2580
  copy.units,
2571
2581
  copy.reportingUnits,
2582
+ copy.counts,
2572
2583
  );
2573
2584
  }
2574
2585
 
@@ -6,10 +6,11 @@
6
6
  import { publish } from '@scriptc/threads';
7
7
  import { type ExternalModuleIndicatorOptions, ParseOptions, ScriptKind, type SourceFile } from './ast.js';
8
8
  import { bindSourceFile } from './binder.js';
9
+ import { checkGrammarSourceFileInParseThread } from './checker-grammar.js';
9
10
  import { useThreadTextCaches } from './diagnostics.js';
10
11
  import { CompilerHost, FileLoader } from './file-loader.js';
11
12
  import { computeHash } from './incremental.js';
12
- import { isIncremental } from './options.js';
13
+ import { type CompilerOptions, isIncremental } from './options.js';
13
14
  import type { FileSystem } from './file-system.js';
14
15
  import {
15
16
  fileSize,
@@ -29,6 +30,18 @@ class PreparingHost extends CompilerHost {
29
30
  prepared: PreparedFile[] = [];
30
31
  /** Whether the files' build info versions are computed (an incremental program). */
31
32
  computesVersions = false;
33
+ /**
34
+ * Whether the files the loader parses here run their grammar checks before they are published. The
35
+ * walk is the checker's, run off the checking threads (checker-grammar.ts).
36
+ */
37
+ grammarChecks = true;
38
+ /** The program's options, which the grammar checks of a file are given. */
39
+ readonly options: CompilerOptions;
40
+
41
+ constructor(fs: FileSystem, currentDirectory: string, defaultLibraryPath: string, options: CompilerOptions) {
42
+ super(fs, currentDirectory, defaultLibraryPath);
43
+ this.options = options;
44
+ }
32
45
  readMs = 0;
33
46
  parseMs = 0;
34
47
  bindMs = 0;
@@ -49,6 +62,12 @@ class PreparingHost extends CompilerHost {
49
62
  );
50
63
  const t2 = timeline ? performance.now() : 0;
51
64
  bindSourceFile(file);
65
+ // The checker does not check the declaration files `skipLibCheck` names, so it does not walk their
66
+ // grammar either. The walk reads the metadata the loader computed for the file, which is the one the
67
+ // checking thread reads it with, so it looks up no package.json the load has not looked up.
68
+ const metadata = this.loadingSourceFileMetadata;
69
+ if (this.grammarChecks && metadata !== undefined && !(this.options.skipLibCheck === true && file.isDeclarationFile))
70
+ checkGrammarSourceFileInParseThread(file, this.options, metadata);
52
71
  // Published now, while its nodes are in cache: the chunk's publish (`ParseThread.load`) then skips
53
72
  // them, where walking the whole chunk's files at once reads each node from memory again.
54
73
  publish(file);
@@ -85,11 +104,14 @@ export class ParseThread {
85
104
  private sentPackageJsons = 0;
86
105
 
87
106
  constructor(config: ParsedCommandLine, fs: FileSystem, currentDirectory: string, defaultLibraryPath: string) {
88
- this.host = new PreparingHost(fs, currentDirectory, defaultLibraryPath);
107
+ this.host = new PreparingHost(fs, currentDirectory, defaultLibraryPath, config.options);
89
108
  // The versions of an incremental program's files are computed here rather than one after another
90
109
  // on the main thread (programToSnapshot, incremental.ts).
91
110
  this.host.computesVersions = isIncremental(config.options);
92
111
  this.loader = new FileLoader(this.host, config);
112
+ // A program that does not check has no grammar diagnostics to report. An incremental program checks
113
+ // the files a change affects and parses all of them, so the walk stays with the checker there.
114
+ this.host.grammarChecks = config.options.noCheck !== true && !isIncremental(config.options);
93
115
  // The files this thread loads are published with their chunk: text caches it computes later (of
94
116
  // declaration files, which the parser leaves out) go to copies.
95
117
  useThreadTextCaches();
@@ -248,6 +248,28 @@ export class CheckedProgram {
248
248
  this.reportingChecker = undefined;
249
249
  }
250
250
 
251
+ /** Adds the counts a checker thread sent (`CheckerResult.counts`): types, instantiations, symbols. */
252
+ addCheckerCounts(counts: readonly number[]): void {
253
+ if (counts.length < 3) return;
254
+ this.threadTypeCount += counts[0]!;
255
+ this.threadInstantiations += counts[1]!;
256
+ this.threadSymbolCount += counts[2]!;
257
+ }
258
+
259
+ /**
260
+ * The types, instantiations and transient symbols of every checker of the program: this thread's and
261
+ * the checker threads', as tsgo's `Program.TypeCount`, `InstantiationCount` and `SymbolCount` add them up.
262
+ * Reading them doesn't create this thread's checker.
263
+ */
264
+ checkerCounts(): { types: number; instantiations: number; symbols: number } {
265
+ const own = this.reportingChecker;
266
+ return {
267
+ types: (own === undefined ? 0 : own.typeCount) + this.threadTypeCount,
268
+ instantiations: (own === undefined ? 0 : own.totalInstantiations) + this.threadInstantiations,
269
+ symbols: (own === undefined ? 0 : own.symbolCount) + this.threadSymbolCount,
270
+ };
271
+ }
272
+
251
273
  getBindDiagnostics(file: SourceFile | undefined): Diagnostic[] {
252
274
  return this.collect(file, (f) => f.bindDiagnostics);
253
275
  }
@@ -272,6 +294,12 @@ export class CheckedProgram {
272
294
  */
273
295
  referenceThreads = 1;
274
296
  readonly parallelGlobalDiagnostics: Diagnostic[] = [];
297
+ /** The types checker threads created (`CheckerResult.counts`), which `--diagnostics` adds to `checker`'s. */
298
+ threadTypeCount = 0;
299
+ /** The instantiations checker threads made. */
300
+ threadInstantiations = 0;
301
+ /** The transient symbols checker threads created. */
302
+ threadSymbolCount = 0;
275
303
  /**
276
304
  * Whether the semantic diagnostics of a file include the global diagnostics its checker found while
277
305
  * checking it, as an incremental program caches them (IncrementalProgram). Such a program doesn't report
@@ -878,6 +878,8 @@ export class Statistics {
878
878
  rows.push([prefix + 'Types', String(this.types)]);
879
879
  rows.push([prefix + 'Instantiations', String(this.instantiations)]);
880
880
  rows.push([prefix + 'Memory used', `${Math.floor(this.memoryUsed / 1024)}K`]);
881
+ // The table has tsgo's rows. No runtime counter matches its allocation count (runtime.MemStats.Mallocs),
882
+ // so this one stays 0.
881
883
  rows.push([prefix + 'Memory allocs', String(this.memoryAllocs)]);
882
884
  const t = this.times;
883
885
  if (t.configTime !== 0) rows.push([prefix + 'Config time', formatDuration(t.configTime)]);
@@ -916,7 +918,14 @@ export function statisticsOf(compilerProgram: CompilerProgram): Statistics {
916
918
  statistics.identifiers += file.identifierCount;
917
919
  statistics.symbols += file.symbolCount;
918
920
  }
919
- statistics.types = compilerProgram.checked.checker.typeCount;
921
+ // Every checker's counts, the checker threads' included, as tsgo's statisticsFromProgram reports them.
922
+ const counts = compilerProgram.checked.checkerCounts();
923
+ statistics.symbols += counts.symbols;
924
+ statistics.types = counts.types;
925
+ statistics.instantiations = counts.instantiations;
926
+ // tsgo reports the Go heap in use (runtime.MemStats.Alloc); the closest this runtime has is the
927
+ // process's resident set, which includes the checker threads' heaps.
928
+ statistics.memoryUsed = process.memoryUsage.rss();
920
929
  const t = statistics.times;
921
930
  const source = compilerProgram.times;
922
931
  t.configTime = source.configTime;