tsc-ts 0.1.0 → 0.1.1

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 (60) hide show
  1. package/README.md +1 -1
  2. package/dist/api/compiler/ast.d.ts +9 -3
  3. package/dist/api/compiler/ast.js +17 -12
  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-jsx.js +2 -1
  9. package/dist/api/compiler/checker-type-factory.js +53 -12
  10. package/dist/api/compiler/checker.d.ts +5 -1
  11. package/dist/api/compiler/checker.js +10 -4
  12. package/dist/api/compiler/compile-program.d.ts +5 -0
  13. package/dist/api/compiler/compile-program.js +8 -0
  14. package/dist/api/compiler/expr-record.d.ts +11 -0
  15. package/dist/api/compiler/expr-record.js +53 -0
  16. package/dist/api/compiler/identifier-name.d.ts +9 -0
  17. package/dist/api/compiler/identifier-name.js +10 -0
  18. package/dist/api/compiler/jsdoc.js +4 -2
  19. package/dist/api/compiler/parser.d.ts +2 -1
  20. package/dist/api/compiler/parser.js +10 -8
  21. package/dist/api/compiler/printer/factory.js +3 -2
  22. package/dist/api/compiler/printer/printer.js +3 -2
  23. package/dist/api/compiler/reparser.js +4 -3
  24. package/dist/api/compiler/scanner.d.ts +8 -0
  25. package/dist/api/compiler/scanner.js +58 -42
  26. package/dist/api/compiler/shared-results.d.ts +376 -143
  27. package/dist/api/compiler/shared-results.js +1550 -773
  28. package/dist/api/compiler/transformers/declarations.js +2 -1
  29. package/package.json +9 -9
  30. package/src/compiler/ast.ts +19 -12
  31. package/src/compiler/cgroup.ts +157 -0
  32. package/src/compiler/checker-classes.ts +2 -1
  33. package/src/compiler/checker-contextual.ts +104 -0
  34. package/src/compiler/checker-expressions.ts +16 -1
  35. package/src/compiler/checker-jsx.ts +2 -1
  36. package/src/compiler/checker-type-factory.ts +53 -14
  37. package/src/compiler/checker.ts +11 -4
  38. package/src/compiler/compile-program.ts +8 -0
  39. package/src/compiler/decoder.ts +2 -1
  40. package/src/compiler/doctor-report.ts +133 -0
  41. package/src/compiler/expr-record.ts +59 -0
  42. package/src/compiler/identifier-name.ts +13 -0
  43. package/src/compiler/jsdoc.ts +4 -2
  44. package/src/compiler/parallel-check.ts +353 -146
  45. package/src/compiler/parallel-emit.ts +3 -3
  46. package/src/compiler/parser.ts +10 -8
  47. package/src/compiler/printer/factory.ts +3 -2
  48. package/src/compiler/printer/printer.ts +4 -2
  49. package/src/compiler/reparser.ts +7 -3
  50. package/src/compiler/run-config.ts +1383 -0
  51. package/src/compiler/run-record-read.ts +87 -0
  52. package/src/compiler/run-records.ts +575 -0
  53. package/src/compiler/scanner.ts +57 -41
  54. package/src/compiler/shared-results.ts +1626 -896
  55. package/src/compiler/thread-entry.ts +3 -1
  56. package/src/compiler/transformers/declarations.ts +2 -1
  57. package/src/compiler/tsc-build.ts +64 -6
  58. package/src/compiler/tsc-cli.ts +33 -4
  59. package/src/compiler/tsc-compile.ts +213 -8
  60. package/src/compiler/tsc-watch.ts +25 -4
package/README.md CHANGED
@@ -33,7 +33,7 @@ npx tsc-ts --noEmit src/index.ts
33
33
 
34
34
  `tsc-ts --api` serves the TypeScript API over stdin and stdout, like `tsc --api`. On Windows, `--api --pipe \\.\pipe\<name>` serves it over a named pipe it creates, as `tsc --api --pipe` does, for clients that connect to the pipe (such as scriptc under Node.js); only the native executable supports `--pipe`. On macOS and Linux, `--api --listen <path>` serves it on a Unix socket it creates, with mode 0600, to one client of the same user at a time, each until the client closes its end. Each client gets the answers a new `tsc-ts --api` process would give, except that symbol ids continue from earlier clients, and the server reads every file through the client again, but it keeps parsed files between clients and reuses those whose text is unchanged, along with the encoding `getSourceFile` sends for each, so a client that opens the same project again loads it faster. The server exits when no client connects for 60 seconds (`--idle-exit <seconds>` sets the time), or after a client when its resident memory is above 2.5 times what it held after its first client (`TSC_TS_LISTEN_MEMORY` sets the limit in megabytes). To stop, it removes the socket file, serves the clients that had connected, and exits. It replaces a socket file that no server answers on, such as one left by a server that was killed. Only the native executable supports `--listen`. With `--default-libraries-from-disk`, the server reads the files in its default library directory (`TSC_TS_LIB_DIR`, or the executable's directory) from the disk itself, even when the client handles file system calls (`--callbacks`), which saves a round trip to the client for each library file.
35
35
 
36
- `--checkers N` checks the program on N threads. Unless `--checkers` or `--singleThreaded` is given, the native executable uses one checker per CPU it may run on (up to 16), and at least tsgo's four; it uses fewer than the CPUs, but never fewer than four, when the estimated peak memory of more checkers would not fit 85% of the machine's memory (`TSC_TS_CHECKERS=fixed` keeps tsgo's four), and checkers beyond the fourth stop taking files, one at a time, when the process's memory reaches that share; it shares one program between its threads and also parses and binds the program's files on them. Unless `--singleThreaded` is given, the program's files are parsed and bound on threads also with `--checkers 1` and in the first build of `--watch`, with or without build info, as tsgo does. Files that share costly declarations, such as a large generated schema and the few files importing it, are checked together on one checker. Under Node.js, where every thread builds its own copy of the program, tsc-ts checks on one thread unless `--checkers` is given. `--incremental`, `--watch` and `--build` always check on one thread. The diagnostics are the same as with one checker.
36
+ `--checkers N` checks the program on N threads. Unless `--checkers` or `--singleThreaded` is given, the native executable uses one checker per CPU it may run on (up to 16), and at least tsgo's four; it uses fewer than the CPUs, but never fewer than four, when the estimated peak memory of more checkers would not fit 85% of the machine's memory (`TSC_TS_CHECKERS=fixed` keeps tsgo's four), and checkers beyond the fourth stop taking files, one at a time, when the process's memory reaches that share; it shares one program between its threads and also parses and binds the program's files on them. Unless `--singleThreaded` is given, the program's files are parsed and bound on threads also with `--checkers 1` and in the first build of `--watch`, with or without build info, as tsgo does. Files that share costly declarations, such as a large generated schema and the few files importing it, are checked together on one checker. The checker threads share the results they compute where the run expects that to pay: at 12 or more checkers, when 40% of the program's files are declarations or come from `node_modules` and the checkers' ranges mostly resolve the same large libraries. `TSC_TS_SHARE=1` turns sharing on for any program and `TSC_TS_SHARE=0` keeps it off, and a run whose sharing stops paying stops sharing its results as it goes. A sharing run may also bound how many results it posts at a time, which costs a few percent on a project that does not gain by it and saves more on one that does: the learned choice among a project's runs, off until its record shows it faster, and `TSC_TS_SHARE_BOUNDED=1` turns it on for any program while `TSC_TS_SHARE_BOUNDED=0` keeps it off. `TSC_TS_AUTO=0` keeps the choices of a run before automatic selection (fewer checkers where the ranges resolve large libraries, no sharing), and `TSC_TS_AUTO=static` keeps the rules and the in-run switches but reads and writes no records, and `TSC_TS_AUTO=explore` adds the experimental exploration described below. Unless `--singleThreaded` is given, the files are parsed on the CPUs the process may run on, but one, whatever the checker count, and `TSC_TS_PARSE_THREADS` sets how many. `--extendedDiagnostics` prints the checker threads, the sharing and the parse threads after the statistics table, under `Suggestions:`, with where each value came from (`pinned: --checkers`, `default`, `automatic: ...`, `learned: check 14% faster over 3 runs`, `exploring`, `control`) and how to pin it; `--build` prints one section per project it builds. The section also names the run's record under `Records:`. Each run that checks a program writes what it measured to `~/.cache/tsc-ts/runs-v1/` (`TSC_TS_CACHE_DIR` sets the directory, `TSC_TS_CACHE=0` turns records off), for later runs of the same project on the same machine to read. A later run chooses from that record where the rules left a setting open: a configuration the samples show faster than the static choice — by 3% or twice the standard error of the gains each of its samples makes against the nearest run of the other arm, on three samples of each taken while the machine was not busy — is adopted, every sixteenth run (and the first after two weeks) re-measures the static choice as a control run, and an adopted configuration whose last three samples all fall behind the control runs reverts at once. A run never tries a configuration that could make itself slower: it adopts only what the samples already show faster, from `tsc-ts doctor`, from a pinned run or from a run that already used it. `TSC_TS_AUTO=explore` turns on an experimental alternative in which a run also tries a setting the signals leave uncertain: one run in eight tries three quarters of the count where the library text puts it near the cut, sharing at eight checkers near the rule's limits, or half the parse threads, and such a run is slower than the configuration it would have used. A program whose shape changed by more than 15% discards the record's samples. Watch mode, a `--build` of several projects at once, `--singleThreaded`, Node builds and `TSC_TS_AUTO=0` or `static` keep no record, and the section says why; a run with `CI` set reads and adopts but never explores. `tsc-ts doctor [-p <tsconfig>] [--runs N] [--budget SECONDS] [--emit] [--json] [--no-save]` measures these settings on this machine: it runs the project's check under the default configuration, with sharing on, with up to 16 checkers, with both, and with half the parse threads, compares the phase times by the same rule automatic selection uses, and prints what it recommends with the evidence for it. Each measured run is a child process, so it runs as a user's run would, and it writes what it measured to the file named by `TSC_TS_DOCTOR_REPORT`; runs without the variable are unaffected. Doctor stores its samples in the record with role `doctor`, unless `--no-save` is given. The command is experimental: the configurations it compares and what it recommends may change. Under Node.js, where every thread builds its own copy of the program, tsc-ts checks on one thread unless `--checkers` is given. `--incremental`, `--watch` and `--build` always check on one thread. The diagnostics are the same as with one checker.
37
37
 
38
38
  The native executable never frees syntax trees, types, signatures or symbols: it keeps them until it exits, which saves the cost of tracking them during a build. A one-shot build is unaffected, but a long-running process grows with each rebuild or project update. Native `--watch` (also with `--build`) runs in a child process, and when a change arrives while that process holds more than 2.5 times the memory it held after its first build, a new process replaces it and builds the change, reporting what the old process would have. `TSC_TS_WATCH_MEMORY` sets that limit in megabytes, and `TSC_TS_WATCH_MEMORY=0` turns the replacement off. A `--api` process grows with each project update; restart it to reclaim the memory.
39
39
 
@@ -1,4 +1,5 @@
1
1
  import type { ProgramLifetime } from '@scriptc/types';
2
+ import { IdentifierName } from './identifier-name.js';
2
3
  import { SyntaxKind } from './syntax-kind.js';
3
4
  import { type CommentDirective, type CommentRange, type LanguageVariant, TokenFlags } from './types.js';
4
5
  import { type Diagnostic, DiagnosticFile } from './diagnostics.js';
@@ -242,8 +243,9 @@ export declare class Token extends Node {
242
243
  copy(): Token;
243
244
  }
244
245
  export declare class Identifier extends Node {
245
- text: string;
246
- constructor(text: string);
246
+ readonly identifierName: IdentifierName;
247
+ constructor(name: IdentifierName);
248
+ get text(): string;
247
249
  forEachChild(_v: NodeVisitor): boolean;
248
250
  setParentOfChildren(): void;
249
251
  deepClone(): Node;
@@ -256,7 +258,7 @@ export declare class Identifier extends Node {
256
258
  */
257
259
  export declare class ReferenceIdentifier extends Identifier {
258
260
  flowNode: FlowNode | undefined;
259
- constructor(text: string);
261
+ constructor(name: IdentifierName);
260
262
  copy(): Identifier;
261
263
  }
262
264
  export declare class PrivateIdentifier extends Node {
@@ -2346,6 +2348,10 @@ export declare class PatternAmbientModule {
2346
2348
  readonly text: string;
2347
2349
  readonly starIndex: number;
2348
2350
  readonly symbol: Symbol;
2351
+ /** The part of `text` before `starIndex`, kept so a match does not copy it. */
2352
+ readonly prefix: string;
2353
+ /** The part of `text` after `starIndex`, kept so a match does not copy it. */
2354
+ readonly suffix: string;
2349
2355
  constructor(text: string, starIndex: number, symbol: Symbol);
2350
2356
  matches(candidate: string): boolean;
2351
2357
  }
@@ -221,10 +221,13 @@ export class Token extends Node {
221
221
  }
222
222
  }
223
223
  export class Identifier extends Node {
224
- text;
225
- constructor(text) {
224
+ identifierName;
225
+ constructor(name) {
226
226
  super(79 /* SyntaxKind.Identifier */);
227
- this.text = text;
227
+ this.identifierName = name;
228
+ }
229
+ get text() {
230
+ return this.identifierName.text;
228
231
  }
229
232
  forEachChild(_v) {
230
233
  return false;
@@ -234,7 +237,7 @@ export class Identifier extends Node {
234
237
  return this.copy();
235
238
  }
236
239
  copy() {
237
- const node = new Identifier(this.text);
240
+ const node = new Identifier(this.identifierName);
238
241
  node.flags = this.flags;
239
242
  node.pos = this.pos;
240
243
  node.end = this.end;
@@ -249,12 +252,12 @@ export class Identifier extends Node {
249
252
  export class ReferenceIdentifier extends Identifier {
250
253
  // Set by the binder.
251
254
  flowNode;
252
- constructor(text) {
253
- super(text);
255
+ constructor(name) {
256
+ super(name);
254
257
  this.flowNode = undefined;
255
258
  }
256
259
  copy() {
257
- const node = new ReferenceIdentifier(this.text);
260
+ const node = new ReferenceIdentifier(this.identifierName);
258
261
  node.flags = this.flags;
259
262
  node.pos = this.pos;
260
263
  node.end = this.end;
@@ -6782,17 +6785,19 @@ export class PatternAmbientModule {
6782
6785
  text;
6783
6786
  starIndex;
6784
6787
  symbol;
6788
+ /** The part of `text` before `starIndex`, kept so a match does not copy it. */
6789
+ prefix;
6790
+ /** The part of `text` after `starIndex`, kept so a match does not copy it. */
6791
+ suffix;
6785
6792
  constructor(text, starIndex, symbol) {
6786
6793
  this.text = text;
6787
6794
  this.starIndex = starIndex;
6788
6795
  this.symbol = symbol;
6796
+ this.prefix = text.substring(0, starIndex);
6797
+ this.suffix = text.substring(starIndex + 1);
6789
6798
  }
6790
6799
  matches(candidate) {
6791
- const text = this.text;
6792
- const star = this.starIndex;
6793
- return (candidate.length >= text.length - 1 &&
6794
- candidate.startsWith(text.substring(0, star)) &&
6795
- candidate.endsWith(text.substring(star + 1)));
6800
+ return (candidate.length >= this.text.length - 1 && candidate.startsWith(this.prefix) && candidate.endsWith(this.suffix));
6796
6801
  }
6797
6802
  }
6798
6803
  function containsNonASCII(text) {
@@ -28,6 +28,7 @@ import { checkFunctionOrConstructorSymbol, checkFunctionOrMethodSymbols } from '
28
28
  import { checkClassHeritage, checkClassOrInterfaceForDuplicateIndexSignatures, checkInterfaceHeritage, } from './checker-heritage.js';
29
29
  import { checkDecorators, getFirstTransformableStaticClassElement } from './checker-decorators.js';
30
30
  import { getTypeOfNode } from './checker-services.js';
31
+ import { IdentifierName } from './identifier-name.js';
31
32
  export function checkClassDeclaration(c, node) {
32
33
  checkClassLikeDeclaration(c, node);
33
34
  checkClassMembers(c, node);
@@ -464,7 +465,7 @@ function newThisPropertyReference(symbol, container, flowNode) {
464
465
  if (name.startsWith('\ufffe#'))
465
466
  accessName = new PrivateIdentifier(name.substring(name.indexOf('@') + 1));
466
467
  else
467
- accessName = new Identifier(name);
468
+ accessName = new Identifier(new IdentifierName(name));
468
469
  const thisExpression = new KeywordExpression(109 /* SyntaxKind.ThisKeyword */);
469
470
  const reference = new PropertyAccessExpression(thisExpression, undefined, accessName, 0 /* NodeFlags.None */);
470
471
  thisExpression.parent = reference;
@@ -18,6 +18,26 @@ export declare function pushCachedContextualType(c: Checker, node: Node): void;
18
18
  export declare function popContextualType(c: Checker): void;
19
19
  /** The index of the contextual type pushed onto `node`, or -1. */
20
20
  export declare function findContextualNode(c: Checker, node: Node, includeCaches: boolean): number;
21
+ /**
22
+ * The id of the contextual type in force for `node`, or -1 if the node's contextual type is not on
23
+ * the stack. A key built from -1 can't tell a check under a contextual type that was not pushed from
24
+ * a check with no contextual type, so callers record nothing under -1.
25
+ */
26
+ export declare function getContextualTypeId(c: Checker, node: Node): number;
27
+ /**
28
+ * Whether a check of `node` has no contextual type and no inference context, so its type is a
29
+ * function of the node and the check mode alone: the node is not on the contextual stack, no
30
+ * inference context contains it, and its parent gives it no contextual type. The parents answered
31
+ * here are the ones `getContextualTypeEx` returns `undefined` for without computing anything; a
32
+ * parent it computes a type for is not answered, so the result is never "yes" for a check that
33
+ * could have a contextual type.
34
+ *
35
+ * Types computed inside a flow loop are provisional (the checker does not cache them either), so a
36
+ * check in a loop is not context-free. The same holds while a reduce label is in force, as when
37
+ * flow analysis revisits a `finally` block with the antecedents of one outgoing path: the flow
38
+ * state differs from the one the earlier check of the block saw.
39
+ */
40
+ export declare function isCheckedWithoutContextualType(c: Checker, node: Node): boolean;
21
41
  export declare function pushInferenceContext(c: Checker, node: Node, context: InferenceContext | undefined): void;
22
42
  export declare function popInferenceContext(c: Checker): void;
23
43
  /** The inference context of the innermost call argument containing `node`. */
@@ -1,6 +1,6 @@
1
1
  // Contextual typing: the type an expression's position implies, and contextual signatures.
2
2
  import { resolvedReturnTypeOf } from './side-state.js';
3
- import { getInitializer, getName, getParameters, getType, getText, getSymbol, getTypeParameters, JsxAttribute, JsxElement, } from './ast.js';
3
+ import { getArguments, getInitializer, getName, getParameters, getType, getText, getSymbol, getTypeParameters, JsxAttribute, JsxElement, } from './ast.js';
4
4
  import { getImmediatelyInvokedFunctionExpression, isFunctionExpressionOrArrowFunction, isObjectLiteralMethod, isStatic, } from './binder-utilities.js';
5
5
  import { filterType, getIntersectionType, getNumberLiteralType, getRegularTypeOfLiteralType, getUnionType, getUnionTypeEx, getWidenedLiteralType, getEndElementCount, getStringLiteralType, getTypeArguments, getTypeReferenceArity, mapType, mapTypeEx, isTupleType, tupleTargetOf, typesOf, } from './checker-type-factory.js';
6
6
  import { combineUnionOrIntersectionMemberSignatures, compareTypeParametersIdentical, findApplicableIndexInfo, getApparentType, getBaseConstraintOrType, getIndexInfosOfStructuredType, getPropertiesOfType, getPropertyOfType, getReducedType, getSignaturesOfType, } from './checker-members.js';
@@ -68,6 +68,116 @@ export function findContextualNode(c, node, includeCaches) {
68
68
  return i;
69
69
  return -1;
70
70
  }
71
+ /**
72
+ * The id of the contextual type in force for `node`, or -1 if the node's contextual type is not on
73
+ * the stack. A key built from -1 can't tell a check under a contextual type that was not pushed from
74
+ * a check with no contextual type, so callers record nothing under -1.
75
+ */
76
+ export function getContextualTypeId(c, node) {
77
+ const index = findContextualNode(c, node, true);
78
+ if (index < 0)
79
+ return -1;
80
+ const t = c.inferenceCaches.contextualTypes[index];
81
+ return t === undefined ? -1 : t.id;
82
+ }
83
+ /**
84
+ * Whether a check of `node` has no contextual type and no inference context, so its type is a
85
+ * function of the node and the check mode alone: the node is not on the contextual stack, no
86
+ * inference context contains it, and its parent gives it no contextual type. The parents answered
87
+ * here are the ones `getContextualTypeEx` returns `undefined` for without computing anything; a
88
+ * parent it computes a type for is not answered, so the result is never "yes" for a check that
89
+ * could have a contextual type.
90
+ *
91
+ * Types computed inside a flow loop are provisional (the checker does not cache them either), so a
92
+ * check in a loop is not context-free. The same holds while a reduce label is in force, as when
93
+ * flow analysis revisits a `finally` block with the antecedents of one outgoing path: the flow
94
+ * state differs from the one the earlier check of the block saw.
95
+ */
96
+ export function isCheckedWithoutContextualType(c, node) {
97
+ if (c.flowLoopStack.length !== 0)
98
+ return false;
99
+ if (c.flowReduceLabels.length !== 0)
100
+ return false;
101
+ if (findContextualNode(c, node, true) >= 0)
102
+ return false;
103
+ if (getInferenceContext(c, node) !== undefined)
104
+ return false;
105
+ const parent = node.parent;
106
+ if (parent === undefined)
107
+ return true;
108
+ switch (parent.kind) {
109
+ case 262 /* SyntaxKind.VariableDeclaration */:
110
+ case 171 /* SyntaxKind.Parameter */:
111
+ case 174 /* SyntaxKind.PropertyDeclaration */:
112
+ case 173 /* SyntaxKind.PropertySignature */:
113
+ case 210 /* SyntaxKind.BindingElement */:
114
+ // Only the initializer is contextually typed by the declaration.
115
+ return getInitializer(parent) !== node;
116
+ case 215 /* SyntaxKind.CallExpression */:
117
+ case 216 /* SyntaxKind.NewExpression */: {
118
+ // Only the arguments are contextually typed by the parameters; the callee is not.
119
+ const args = getArguments(parent);
120
+ if (args !== undefined) {
121
+ for (const arg of args.nodes)
122
+ if (arg === node)
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ case 212 /* SyntaxKind.ObjectLiteralExpression */:
128
+ // Only a method takes the property's contextual type.
129
+ return node.kind !== 176 /* SyntaxKind.MethodDeclaration */;
130
+ case 228 /* SyntaxKind.BinaryExpression */: {
131
+ const binary = parent;
132
+ if (binary.type !== undefined)
133
+ return false;
134
+ switch (binary.operatorToken.kind) {
135
+ case 63 /* SyntaxKind.EqualsToken */:
136
+ case 76 /* SyntaxKind.AmpersandAmpersandEqualsToken */:
137
+ case 75 /* SyntaxKind.BarBarEqualsToken */:
138
+ case 77 /* SyntaxKind.QuestionQuestionEqualsToken */:
139
+ return node !== binary.right;
140
+ case 56 /* SyntaxKind.BarBarToken */:
141
+ case 60 /* SyntaxKind.QuestionQuestionToken */:
142
+ case 55 /* SyntaxKind.AmpersandAmpersandToken */:
143
+ case 27 /* SyntaxKind.CommaToken */:
144
+ return false;
145
+ }
146
+ return true;
147
+ }
148
+ case 229 /* SyntaxKind.ConditionalExpression */: {
149
+ const conditional = parent;
150
+ return node !== conditional.whenTrue && node !== conditional.whenFalse;
151
+ }
152
+ case 241 /* SyntaxKind.TemplateSpan */: {
153
+ // Only a tagged template types its spans.
154
+ const template = parent.parent;
155
+ return template.parent === undefined || template.parent.kind !== 217 /* SyntaxKind.TaggedTemplateExpression */;
156
+ }
157
+ case 221 /* SyntaxKind.ArrowFunction */:
158
+ case 255 /* SyntaxKind.ReturnStatement */:
159
+ case 231 /* SyntaxKind.YieldExpression */:
160
+ case 225 /* SyntaxKind.AwaitExpression */:
161
+ case 172 /* SyntaxKind.Decorator */:
162
+ case 236 /* SyntaxKind.AsExpression */:
163
+ case 218 /* SyntaxKind.TypeAssertionExpression */:
164
+ case 240 /* SyntaxKind.SatisfiesExpression */:
165
+ case 279 /* SyntaxKind.ExportAssignment */:
166
+ case 304 /* SyntaxKind.PropertyAssignment */:
167
+ case 305 /* SyntaxKind.ShorthandPropertyAssignment */:
168
+ case 306 /* SyntaxKind.SpreadAssignment */:
169
+ case 211 /* SyntaxKind.ArrayLiteralExpression */:
170
+ case 219 /* SyntaxKind.ParenthesizedExpression */:
171
+ case 237 /* SyntaxKind.NonNullExpression */:
172
+ case 296 /* SyntaxKind.JsxExpression */:
173
+ case 293 /* SyntaxKind.JsxAttribute */:
174
+ case 295 /* SyntaxKind.JsxSpreadAttribute */:
175
+ case 288 /* SyntaxKind.JsxOpeningElement */:
176
+ case 287 /* SyntaxKind.JsxSelfClosingElement */:
177
+ return false;
178
+ }
179
+ return true;
180
+ }
71
181
  export function pushInferenceContext(c, node, context) {
72
182
  const caches = c.inferenceCaches;
73
183
  const depth = caches.inferenceDepth;
@@ -43,7 +43,7 @@ import { isFunctionLike, isImportCall, isInJSFile, isRequireCall } from './ast-u
43
43
  import { isLiteralKind } from './printer/utilities.js';
44
44
  import { convertAutoToAny, isEvolvingArrayOperationTarget, getAssignmentTargetKind, getControlFlowContainer, isConstantVariable, isInCompoundLikeAssignment, isMutableLocalVariableDeclaration, isParameterOrMutableLocalVariable, isPastLastAssignment, isSymbolAssignedDefinitely, getFlowTypeOfReference, getNarrowedTypeOfSymbol, } from './checker-narrowing.js';
45
45
  import { getFlowTypeOfAutoTypedProperty, getThisTypeOfAssignedFunction } from './checker-javascript.js';
46
- import { checkExpressionWithContextualType, getThisTypeOfObjectLiteralMember } from './checker-contextual.js';
46
+ import { checkExpressionWithContextualType, getThisTypeOfObjectLiteralMember, isCheckedWithoutContextualType, } from './checker-contextual.js';
47
47
  import { getInferenceContext, instantiateContextualType, popContextualType, pushCachedContextualType, } from './checker-contextual.js';
48
48
  import { addIntraExpressionInferenceSite, isConstTypeVariable } from './checker-inference.js';
49
49
  import { isContextSensitiveFunctionOrObjectLiteralMethod } from './checker-contextual.js';
@@ -56,6 +56,17 @@ export function checkExpression(c, node) {
56
56
  return checkExpressionEx(c, node, 0 /* CheckMode.Normal */);
57
57
  }
58
58
  export function checkExpressionEx(c, node, checkMode) {
59
+ // A completed check of this node under this mode answers this one when the check has no
60
+ // contextual type and no inference context: the checker's state only grew since, so this check
61
+ // would produce the type the completed one produced, report the diagnostics it reported and
62
+ // defer the nodes it deferred. Those diagnostics are equal to ones already reported and a node
63
+ // is deferred at most once, so serving the completed type changes neither.
64
+ const recordable = isCheckedWithoutContextualType(c, node);
65
+ if (recordable) {
66
+ const recorded = c.exprCheckRecord.get(node.id, checkMode);
67
+ if (recorded !== undefined)
68
+ return recorded;
69
+ }
59
70
  const saveCurrentNode = c.currentNode;
60
71
  c.currentNode = node;
61
72
  c.instantiationCount = 0;
@@ -63,6 +74,8 @@ export function checkExpressionEx(c, node, checkMode) {
63
74
  if (isConstEnumObjectTypeLocal(t))
64
75
  checkConstEnumAccess(c, node, t);
65
76
  c.currentNode = saveCurrentNode;
77
+ if (recordable)
78
+ c.exprCheckRecord.set(node.id, checkMode, t);
66
79
  return t;
67
80
  }
68
81
  /**
@@ -38,6 +38,7 @@ import { filterType, isTupleType } from './checker-type-factory.js';
38
38
  import { createTypeFromGenericGlobalType } from './checker-type-factory.js';
39
39
  import { isTypeAssignableTo } from './relations.js';
40
40
  import { copyDeclarations, declarationCount, getSymbolId, } from './symbol.js';
41
+ import { IdentifierName } from './identifier-name.js';
41
42
  // Names in the `JSX` namespace.
42
43
  const jsxNamespaceName = 'JSX';
43
44
  const jsxIntrinsicElementsName = 'IntrinsicElements';
@@ -824,7 +825,7 @@ export function createJsxAttributesTypeFromAttributesProperty(c, openingLikeElem
824
825
  c.symbolTypes.set(id, createArrayType(c, getUnionType(c, childTypes)));
825
826
  // A property signature in the attributes declares the synthetic `children` property, so it is
826
827
  // checked as an excess property like the attributes.
827
- const childrenDeclaration = new PropertySignatureDeclaration(undefined, new Identifier(jsxChildrenPropertyName), undefined, undefined, undefined);
828
+ const childrenDeclaration = new PropertySignatureDeclaration(undefined, new Identifier(new IdentifierName(jsxChildrenPropertyName)), undefined, undefined, undefined);
828
829
  childrenDeclaration.parent = attributeParent;
829
830
  childrenDeclaration.symbol = childrenPropSymbol;
830
831
  childrenPropSymbol.valueDeclaration = childrenDeclaration;
@@ -1187,37 +1187,78 @@ export function getUnionTypeEx(c, types, unionReduction, alias, origin) {
1187
1187
  return c.neverType;
1188
1188
  if (types.length === 1)
1189
1189
  return types[0];
1190
- // Optimize the common case of unioning a union with some other type, like `undefined`.
1191
- if (types.length === 2 &&
1192
- origin === undefined &&
1193
- ((types[0].flags & 134217728 /* TypeFlags.Union */) !== 0 || (types[1].flags & 134217728 /* TypeFlags.Union */) !== 0)) {
1194
- let id1 = types[0].id;
1195
- let id2 = types[1].id;
1190
+ // Unioning two types is the common case, both a union with some other type, like `undefined`, and
1191
+ // two constituents of a list, and the same pairs recur. The union is a function of the two types,
1192
+ // the reduction and the alias, so the result is cached by the pair. A union that exceeded the
1193
+ // complexity limit was reported at the expression that needed it and comes back as the error
1194
+ // type: caching that would silence the report at the next expression with the same input list, so
1195
+ // an error result is not cached.
1196
+ if (types.length === 2 && origin === undefined) {
1197
+ const t0 = types[0];
1198
+ const t1 = types[1];
1199
+ let id1 = t0.id;
1200
+ let id2 = t1.id;
1196
1201
  if (id1 > id2) {
1197
1202
  const t = id1;
1198
1203
  id1 = id2;
1199
1204
  id2 = t;
1200
1205
  }
1201
- // Without an alias, both ids and the reduction pack into one exact number.
1202
- if (alias === undefined && id2 < UnionPairIdLimit) {
1206
+ // Without an alias or a constrained intersection, both ids and the reduction pack into one
1207
+ // exact number. A constrained intersection's reduction resolves a type variable's constraint
1208
+ // (`addTypeToUnion`), so those pairs take the general path.
1209
+ if (alias === undefined &&
1210
+ id2 < UnionPairIdLimit &&
1211
+ !isConstrainedIntersection(t0) &&
1212
+ !isConstrainedIntersection(t1)) {
1203
1213
  const id = (id1 * UnionPairIdLimit + id2) * 4 + unionReduction;
1204
1214
  let result = c.unionOfUnionTypesById.get(id);
1205
1215
  if (result === undefined) {
1206
1216
  result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
1207
- c.unionOfUnionTypesById.set(id, result);
1217
+ if (result !== c.errorType)
1218
+ c.unionOfUnionTypesById.set(id, result);
1208
1219
  }
1209
1220
  return result;
1210
1221
  }
1211
- const key = id1 + ',' + id2 + ',' + unionReduction + aliasKey(alias);
1212
- let result = c.unionOfUnionTypes.get(key);
1222
+ if ((t0.flags & 134217728 /* TypeFlags.Union */) !== 0 || (t1.flags & 134217728 /* TypeFlags.Union */) !== 0) {
1223
+ const key = id1 + ',' + id2 + ',' + unionReduction + aliasKey(alias);
1224
+ let result = c.unionOfUnionTypes.get(key);
1225
+ if (result === undefined) {
1226
+ result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
1227
+ if (result !== c.errorType)
1228
+ c.unionOfUnionTypes.set(key, result);
1229
+ }
1230
+ return result;
1231
+ }
1232
+ }
1233
+ // Longer lists recur too, from nested unions and from constituents mapped in the same order.
1234
+ // Normalizing a list costs a sort of its constituents and the reductions, and the union is a
1235
+ // function of the list as given, the reduction and the alias, so the result is cached by the list.
1236
+ if (origin === undefined) {
1237
+ const hash = typeListHash(types, alias, unionReduction);
1238
+ let result = c.unionTypesByInputList.get(hash, types, alias, unionReduction);
1213
1239
  if (result === undefined) {
1214
1240
  result = getUnionTypeWorker(c, types, unionReduction, alias, undefined);
1215
- c.unionOfUnionTypes.set(key, result);
1241
+ if (result !== c.errorType && !hasConstrainedIntersection(types))
1242
+ c.unionTypesByInputList.set(hash, types.slice(), alias, unionReduction, result);
1216
1243
  }
1217
1244
  return result;
1218
1245
  }
1219
1246
  return getUnionTypeWorker(c, types, unionReduction, alias, origin);
1220
1247
  }
1248
+ /** An intersection that `addTypeToUnion` reduces by resolving a type variable's constraint. */
1249
+ function isConstrainedIntersection(t) {
1250
+ return (t.flags & 268435456 /* TypeFlags.Intersection */) !== 0 && (t.objectFlags & 134217728 /* ObjectFlags.IsConstrainedTypeVariable */) !== 0;
1251
+ }
1252
+ /** Whether `addTypesToUnion` would set `IncludesConstrainedTypeVariable` for `types`. */
1253
+ function hasConstrainedIntersection(types) {
1254
+ for (const t of types) {
1255
+ if (isConstrainedIntersection(t))
1256
+ return true;
1257
+ if ((t.flags & 134217728 /* TypeFlags.Union */) !== 0 && hasConstrainedIntersection(t.types))
1258
+ return true;
1259
+ }
1260
+ return false;
1261
+ }
1221
1262
  class TypeSet {
1222
1263
  types = [];
1223
1264
  includes = 0;
@@ -2,6 +2,7 @@ import type { ModuleSpecifierResult } from './node-builder-names.js';
2
2
  import type { EmitHelperFileLinks } from './checker-emit-helpers.js';
3
3
  import type { Node, PatternAmbientModule, SourceFile } from './ast.js';
4
4
  import { CompactIntLinkTable, CompactLinkTable, IntLinkTable, LinkTable, PagedIdSet, SparseLinkTable } from './link-tables.js';
5
+ import { ExprCheckRecord } from './expr-record.js';
5
6
  import { Diagnostic, type DiagnosticMessage, Diagnostics } from './diagnostics.js';
6
7
  import { type CompilerOptions, ModuleKind, ScriptTarget } from './options.js';
7
8
  import { CheckFlags, Symbol, SymbolFlags, type SymbolTable } from './symbol.js';
@@ -225,6 +226,8 @@ export declare class Checker implements TypeChecking {
225
226
  readonly resolvedSymbols: CompactLinkTable<Symbol>;
226
227
  /** The resolved type of a type node or expression. */
227
228
  readonly resolvedTypes: CompactLinkTable<Type>;
229
+ /** The type the last completed check of each expression key produced (expr-record.ts). */
230
+ readonly exprCheckRecord: ExprCheckRecord;
228
231
  /** The resolved signature of a call-like node or a signature declaration. */
229
232
  readonly resolvedSignatures: CompactLinkTable<Signature>;
230
233
  /** Merged symbols, indexed by the id of the symbol merged into them. */
@@ -274,6 +277,7 @@ export declare class Checker implements TypeChecking {
274
277
  readonly unionOfUnionTypes: Map<string, Type>;
275
278
  /** `unionOfUnionTypes` without an alias, by both ids and the reduction packed into one number. */
276
279
  readonly unionOfUnionTypesById: Map<number, Type>;
280
+ readonly unionTypesByInputList: TypeListCache<Type>;
277
281
  readonly intersectionTypes: TypeListCache<Type>;
278
282
  readonly tupleTypes: Map<string, Type>;
279
283
  readonly indexedAccessTypes: Map<string, Type>;
@@ -548,7 +552,7 @@ export declare class Checker implements TypeChecking {
548
552
  /** The access flags of the key `getIndexedAccessTypeEx` keeps each indexed access type by. */
549
553
  indexedAccessKeyFlags: Map<Type, number> | undefined;
550
554
  /** Where results are shared between checker threads (shared-results.ts): the file of each diagnostic deferred. */
551
- deferredDiagnosticFiles: string[] | undefined;
555
+ deferredDiagnosticFiles: number[] | undefined;
552
556
  constructor(host: CheckerHost);
553
557
  /** Checks `file` if needed and returns its semantic diagnostics, sorted. */
554
558
  getDiagnostics(file: SourceFile): Diagnostic[];
@@ -1,5 +1,6 @@
1
1
  import { sideOf } from './side-state.js';
2
2
  import { CompactIntLinkTable, CompactLinkTable, IntLinkTable, LinkTable, PagedIdSet, SparseLinkTable, } from './link-tables.js';
3
+ import { ExprCheckRecord } from './expr-record.js';
3
4
  import { getErrorRangeForNode, getSourceFileOfNode } from './binder-utilities.js';
4
5
  import { Diagnostic, compareDiagnostics, equalDiagnostics, } from './diagnostics.js';
5
6
  import { ModuleKind, ScriptTarget, getEmitScriptTarget, getIsolatedModules, getStrictOptionValue, shouldPreserveConstEnums, } from './options.js';
@@ -17,7 +18,7 @@ import { InferenceCaches } from './checker-inference.js';
17
18
  import { AssignmentMarker } from './checker-narrowing.js';
18
19
  import { JsxState } from './checker-jsx.js';
19
20
  import { SourceFileMetadata, getEmitModuleFormatOfFile } from './program-utilities.js';
20
- import { shareThreads, startThreadSharing } from './shared-results.js';
21
+ import { startThreadSharing } from './shared-results.js';
21
22
  /** What the checker needs from a program. */
22
23
  export class CheckerHost {
23
24
  /** How a module name in `file` resolved in `mode`, if the program resolved it. */
@@ -358,6 +359,8 @@ export class Checker {
358
359
  resolvedSymbols = new CompactLinkTable();
359
360
  /** The resolved type of a type node or expression. */
360
361
  resolvedTypes = new CompactLinkTable();
362
+ /** The type the last completed check of each expression key produced (expr-record.ts). */
363
+ exprCheckRecord = new ExprCheckRecord();
361
364
  /** The resolved signature of a call-like node or a signature declaration. */
362
365
  resolvedSignatures = new CompactLinkTable();
363
366
  /** Merged symbols, indexed by the id of the symbol merged into them. */
@@ -409,6 +412,7 @@ export class Checker {
409
412
  unionOfUnionTypes = new Map();
410
413
  /** `unionOfUnionTypes` without an alias, by both ids and the reduction packed into one number. */
411
414
  unionOfUnionTypesById = new Map();
415
+ unionTypesByInputList = new TypeListCache();
412
416
  intersectionTypes = new TypeListCache();
413
417
  tupleTypes = new Map();
414
418
  indexedAccessTypes = new Map();
@@ -576,7 +580,7 @@ export class Checker {
576
580
  deferDiagnostic(node, report) {
577
581
  this.deferredDiagnostics.push(report);
578
582
  if (this.deferredDiagnosticFiles !== undefined)
579
- this.deferredDiagnosticFiles.push(getSourceFileOfNode(node).fileName);
583
+ this.deferredDiagnosticFiles.push(this.fileIndex.get(getSourceFileOfNode(node)) ?? -1);
580
584
  }
581
585
  /**
582
586
  * The checked object literals of each file whose members may set deprecated properties of their contextual
@@ -886,8 +890,9 @@ export class Checker {
886
890
  mergeAmbientModulesAndAugmentations(this);
887
891
  this.modulesMerged = true;
888
892
  this.mergeDiagnostics.push(this.diagnostics.takeLog(), this.suggestionDiagnostics.takeLog());
889
- if (shareThreads)
890
- startThreadSharing(this);
893
+ // Returns unless this thread was given sharing data (`ThreadSharingData` in its worker data): the
894
+ // main thread, and every thread of a run that didn't share.
895
+ startThreadSharing(this);
891
896
  if (this.threadSharing !== undefined) {
892
897
  this.distributedConditionals = new Map();
893
898
  this.distributedByKey = new Map();
@@ -1059,6 +1064,7 @@ export function disposeChecker(c) {
1059
1064
  c.unionTypes.clear();
1060
1065
  c.unionOfUnionTypes.clear();
1061
1066
  c.unionOfUnionTypesById.clear();
1067
+ c.unionTypesByInputList.clear();
1062
1068
  c.intersectionTypes.clear();
1063
1069
  c.tupleTypes.clear();
1064
1070
  c.indexedAccessTypes.clear();
@@ -105,6 +105,11 @@ export declare abstract class CompilerProgram {
105
105
  readonly times: CompileTimes;
106
106
  readonly emitter: ProgramEmitter;
107
107
  constructor(checked: CheckedProgram, times: CompileTimes);
108
+ /**
109
+ * Whether this run checked the program, which `getDiagnosticsOfProgram` sets when it checks it. A run
110
+ * stopped before checking, by syntax errors or `--noCheck`, keeps no record (`run-records.ts`).
111
+ */
112
+ checkedTheProgram: boolean;
108
113
  /**
109
114
  * The time spent in declaration emit while checking, to count as emit time rather than check time, since
110
115
  * the last call. Only an incremental program emits while checking, to compute signatures.
@@ -311,6 +311,11 @@ export class CompilerProgram {
311
311
  this.times = times;
312
312
  this.emitter = new ProgramEmitter(checked);
313
313
  }
314
+ /**
315
+ * Whether this run checked the program, which `getDiagnosticsOfProgram` sets when it checks it. A run
316
+ * stopped before checking, by syntax errors or `--noCheck`, keeps no record (`run-records.ts`).
317
+ */
318
+ checkedTheProgram = false;
314
319
  /**
315
320
  * The time spent in declaration emit while checking, to count as emit time rather than check time, since
316
321
  * the last call. Only an incremental program emits while checking, to compute signatures.
@@ -447,6 +452,9 @@ export function getDiagnosticsOfProgram(program, withDeclarationDiagnostics, dec
447
452
  for (const d of program.getGlobalDiagnostics(false))
448
453
  result.push(d);
449
454
  if (result.length === configCount) {
455
+ // With `--noCheck` the checker skips every file: the run checked nothing to sample (`run-records.ts`).
456
+ if (options.noCheck !== true)
457
+ program.checkedTheProgram = true;
450
458
  const checkStart = performance.now();
451
459
  const semantic = program.getSemanticDiagnostics();
452
460
  // Declaration emit that computed signatures while checking counts as emit time, as in TypeScript 7.1.
@@ -0,0 +1,11 @@
1
+ import type { Type } from './checker-types.js';
2
+ export declare class ExprCheckRecord {
3
+ /** The node id of the check at each entry, plus one; zero where no check is recorded. */
4
+ private readonly nodeIds;
5
+ private readonly modes;
6
+ private readonly types;
7
+ /** The type the last completed check of the node under `mode` produced, if the entry holds it. */
8
+ get(nodeId: number, mode: number): Type | undefined;
9
+ /** Records that a check of the node under `mode` produced `type`. */
10
+ set(nodeId: number, mode: number, type: Type): void;
11
+ }