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
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
 
@@ -84,3 +84,9 @@ The program's diagnostics, with their text and positions, are those `tsc --api`
84
84
  An exception the compiler throws while answering a call reaches the caller as an `InternalCompilerError`, with the exception as its `cause`; the program's checker may be inconsistent after one, so dispose of the program. `program.dispose()` frees the program's checkers; calls on the program or its checker after it throw a `DisposedProgramError`.
85
85
 
86
86
  The package has a JavaScript build with declaration files for Node.js, and the TypeScript sources of the API and the compiler (the `source` export condition) for compilers that build it from source. The sources compile with scriptc: a test builds a program on `tsc-ts/api` natively and checks that it answers as the Node build.
87
+
88
+ The API imports `@scriptc/threads`, an optional peer dependency that the command line doesn't use. Install it next to `tsc-ts` to use `tsc-ts/api`:
89
+
90
+ ```bash
91
+ npm install -D tsc-ts @scriptc/threads
92
+ ```
@@ -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 {
@@ -2325,6 +2327,12 @@ export declare class SourceFile extends Node {
2325
2327
  patternAmbientModules: readonly PatternAmbientModule[];
2326
2328
  /** UMD exports from `export as namespace N`. */
2327
2329
  globalExports: SymbolTable | undefined;
2330
+ /** The diagnostics the parse thread's grammar walk reported for this file. */
2331
+ grammarDiagnostics: Diagnostic[] | undefined;
2332
+ /** The nodes of this file whose grammar check needs the checker, in tree order. */
2333
+ grammarResidualNodes: Node[] | undefined;
2334
+ /** The blocks and statements that reported a statement in an ambient context. */
2335
+ grammarAmbientReports: readonly number[];
2328
2336
  constructor(fileName: string, path: string, text: string, externalModuleIndicatorOptions: ExternalModuleIndicatorOptions, statements: NodeList, endOfFileToken: Token);
2329
2337
  forEachChild(v: NodeVisitor): boolean;
2330
2338
  setParentOfChildren(): void;
@@ -2346,6 +2354,10 @@ export declare class PatternAmbientModule {
2346
2354
  readonly text: string;
2347
2355
  readonly starIndex: number;
2348
2356
  readonly symbol: Symbol;
2357
+ /** The part of `text` before `starIndex`, kept so a match does not copy it. */
2358
+ readonly prefix: string;
2359
+ /** The part of `text` after `starIndex`, kept so a match does not copy it. */
2360
+ readonly suffix: string;
2349
2361
  constructor(text: string, starIndex: number, symbol: Symbol);
2350
2362
  matches(candidate: string): boolean;
2351
2363
  }
@@ -52,9 +52,14 @@ export class NodeVisitor {
52
52
  return this.visitList(list);
53
53
  }
54
54
  }
55
- // Native builds store `pos`, `end` and `id` as 32-bit integers when every write provably stores an
56
- // int32, so writes of values the compiler can't bound (parameters, the id counter) are coerced
57
- // with `| 0`; the values are always int32.
55
+ // Native builds store `pos`, `end` and `id` as 32-bit integers when every write provably stores
56
+ // an int32, so writes of values the compiler can't bound (parameters, the id counter) are
57
+ // coerced with `| 0`; the values are always int32.
58
+ //
59
+ // The fields of a node that the public API's node types declare are stored fields, not values
60
+ // computed from or packed into another field: a native build copies the stored fields of a node
61
+ // into a record when API code narrows one (`node as StringLiteralLike`), and a field behind an
62
+ // accessor is not copied.
58
63
  export class Node {
59
64
  kind;
60
65
  flags;
@@ -221,10 +226,13 @@ export class Token extends Node {
221
226
  }
222
227
  }
223
228
  export class Identifier extends Node {
224
- text;
225
- constructor(text) {
229
+ identifierName;
230
+ constructor(name) {
226
231
  super(79 /* SyntaxKind.Identifier */);
227
- this.text = text;
232
+ this.identifierName = name;
233
+ }
234
+ get text() {
235
+ return this.identifierName.text;
228
236
  }
229
237
  forEachChild(_v) {
230
238
  return false;
@@ -234,7 +242,7 @@ export class Identifier extends Node {
234
242
  return this.copy();
235
243
  }
236
244
  copy() {
237
- const node = new Identifier(this.text);
245
+ const node = new Identifier(this.identifierName);
238
246
  node.flags = this.flags;
239
247
  node.pos = this.pos;
240
248
  node.end = this.end;
@@ -249,12 +257,12 @@ export class Identifier extends Node {
249
257
  export class ReferenceIdentifier extends Identifier {
250
258
  // Set by the binder.
251
259
  flowNode;
252
- constructor(text) {
253
- super(text);
260
+ constructor(name) {
261
+ super(name);
254
262
  this.flowNode = undefined;
255
263
  }
256
264
  copy() {
257
- const node = new ReferenceIdentifier(this.text);
265
+ const node = new ReferenceIdentifier(this.identifierName);
258
266
  node.flags = this.flags;
259
267
  node.pos = this.pos;
260
268
  node.end = this.end;
@@ -6623,6 +6631,7 @@ export const emptyCommentDirectives = [];
6623
6631
  export const emptyPragmas = [];
6624
6632
  export const emptyFileReferences = [];
6625
6633
  export const emptyPatternAmbientModules = [];
6634
+ const emptyNumbers = [];
6626
6635
  /** `list` with `item` appended: a new array when `list` is empty (it may be shared), else `list` itself. */
6627
6636
  export function appendToList(list, item) {
6628
6637
  if (list.length === 0)
@@ -6695,6 +6704,13 @@ export class SourceFile extends Node {
6695
6704
  patternAmbientModules;
6696
6705
  /** UMD exports from `export as namespace N`. */
6697
6706
  globalExports;
6707
+ // Set by the grammar checks of a parse thread (checker-grammar.ts).
6708
+ /** The diagnostics the parse thread's grammar walk reported for this file. */
6709
+ grammarDiagnostics;
6710
+ /** The nodes of this file whose grammar check needs the checker, in tree order. */
6711
+ grammarResidualNodes;
6712
+ /** The blocks and statements that reported a statement in an ambient context. */
6713
+ grammarAmbientReports;
6698
6714
  constructor(fileName, path, text, externalModuleIndicatorOptions, statements, endOfFileToken) {
6699
6715
  super(308 /* SyntaxKind.SourceFile */);
6700
6716
  this.fileName = fileName;
@@ -6741,6 +6757,9 @@ export class SourceFile extends Node {
6741
6757
  this.symbolCount = 0;
6742
6758
  this.patternAmbientModules = emptyPatternAmbientModules;
6743
6759
  this.globalExports = undefined;
6760
+ this.grammarDiagnostics = undefined;
6761
+ this.grammarResidualNodes = undefined;
6762
+ this.grammarAmbientReports = emptyNumbers;
6744
6763
  }
6745
6764
  forEachChild(v) {
6746
6765
  return v.visitList(this.statements) || v.visitNode(this.endOfFileToken);
@@ -6782,17 +6801,19 @@ export class PatternAmbientModule {
6782
6801
  text;
6783
6802
  starIndex;
6784
6803
  symbol;
6804
+ /** The part of `text` before `starIndex`, kept so a match does not copy it. */
6805
+ prefix;
6806
+ /** The part of `text` after `starIndex`, kept so a match does not copy it. */
6807
+ suffix;
6785
6808
  constructor(text, starIndex, symbol) {
6786
6809
  this.text = text;
6787
6810
  this.starIndex = starIndex;
6788
6811
  this.symbol = symbol;
6812
+ this.prefix = text.substring(0, starIndex);
6813
+ this.suffix = text.substring(starIndex + 1);
6789
6814
  }
6790
6815
  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)));
6816
+ return (candidate.length >= this.text.length - 1 && candidate.startsWith(this.prefix) && candidate.endsWith(this.suffix));
6796
6817
  }
6797
6818
  }
6798
6819
  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
  /**
@@ -1,10 +1,17 @@
1
1
  import { type Node, type NodeList, NodeVisitor, type ObjectLiteralExpression, type SourceFile } from './ast.js';
2
2
  import { type Checker } from './checker.js';
3
- import { ModuleKind } from './options.js';
3
+ import { Diagnostic, type DiagnosticMessage } from './diagnostics.js';
4
+ import { type CompilerOptions, ModuleKind } from './options.js';
4
5
  import { SourceFileMetadata } from './program-utilities.js';
5
6
  import { Scanner } from './scanner.js';
6
7
  /** Runs the grammar checks of every node of `file`. */
7
8
  export declare function checkGrammarSourceFile(c: Checker, file: SourceFile): void;
9
+ /**
10
+ * Runs the grammar checks of `file` in a parse thread, before the file is published: the checks that
11
+ * don't need the checker report now, and the nodes of those that do are kept for the checker, which
12
+ * runs them in the same tree order (`checkGrammarSourceFile`).
13
+ */
14
+ export declare function checkGrammarSourceFileInParseThread(file: SourceFile, options: CompilerOptions, metadata: SourceFileMetadata): void;
8
15
  /** Checks the grammar of an object literal each time it is checked, as tsgo does. */
9
16
  export declare function checkGrammarObjectLiteralOfExpression(c: Checker, node: ObjectLiteralExpression, inDestructuring: boolean): void;
10
17
  /** Reports a trailing comma after the rest element of a destructuring assignment. */
@@ -17,7 +24,10 @@ export declare function checkGrammarAwaitExpression(c: Checker, node: Node): voi
17
24
  export declare function checkGrammarYieldOfExpression(c: Checker, node: Node): void;
18
25
  /** Per-file state of the grammar walk. */
19
26
  export declare class GrammarChecker extends NodeVisitor {
20
- readonly c: Checker;
27
+ /** The checker of a walk in the checker thread; undefined for a walk in a parse thread. */
28
+ readonly c: Checker | undefined;
29
+ readonly compilerOptions: CompilerOptions;
30
+ readonly languageVersion: number;
21
31
  readonly file: SourceFile;
22
32
  /** Grammar errors are only reported in files without parse errors. */
23
33
  readonly hasParseErrors: boolean;
@@ -25,10 +35,35 @@ export declare class GrammarChecker extends NodeVisitor {
25
35
  readonly metadata: SourceFileMetadata;
26
36
  readonly legacyDecorators: boolean;
27
37
  /** Blocks and statements that already reported a statement in an ambient context. */
28
- readonly reportedAmbientStatements: Set<number>;
38
+ reportedAmbientStatements: Set<number>;
39
+ /**
40
+ * The IDs `reportedAmbientStatements` gained in a parse thread's walk, in the order it gained them, so
41
+ * that the reports of a node whose check the checker runs are undone without copying the set.
42
+ * Undefined in the checker thread, where no check is undone.
43
+ */
44
+ private readonly ambientReportLog;
45
+ /**
46
+ * The diagnostics of a walk in a parse thread, which has no checker, for the checker to add to its
47
+ * collection; undefined in the checker thread, where they are reported as they are found.
48
+ */
49
+ readonly diagnostics: Diagnostic[] | undefined;
50
+ /** The nodes of a parse thread's walk whose check needs the checker, in tree order. */
51
+ readonly residualNodes: Node[] | undefined;
52
+ /** Set when the check of a parse thread's walk needs the checker: the checker runs that node. */
53
+ needsChecker: boolean;
29
54
  private scanner;
30
- constructor(c: Checker, file: SourceFile);
55
+ constructor(c: Checker | undefined, file: SourceFile, options?: CompilerOptions, metadata?: SourceFileMetadata,
56
+ /** `file.grammarAmbientReports`: what a parse thread's walk already reported. */
57
+ ambientReports?: readonly number[]);
31
58
  visitNode(node: Node): boolean;
59
+ /** Records that `id` reported a statement in an ambient context. */
60
+ reportAmbientStatement(id: number): void;
61
+ /** Undoes the ambient-context reports added since the log had `mark` entries. */
62
+ private undoAmbientReports;
63
+ /** Reports an error at `node`, as `Checker.error`; returns the diagnostic. */
64
+ error(node: Node | undefined, message: DiagnosticMessage, ...args: string[]): Diagnostic;
65
+ /** Reports `diagnostic`, as `Checker.addDiagnostic`. */
66
+ report(diagnostic: Diagnostic): void;
32
67
  /** A scanner over the file's text, for rescanning tokens. */
33
68
  getScanner(): Scanner;
34
69
  }