@cplieger/deadset-ts 8.0.0-dev.16 → 8.0.0-dev.18

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 (161) hide show
  1. package/dist/src/alias-chain.js +0 -5
  2. package/dist/src/amd.js +81 -0
  3. package/dist/src/analysis.js +82 -91
  4. package/dist/src/augmentations.js +24 -25
  5. package/dist/src/bindings.js +382 -0
  6. package/dist/src/calls.js +279 -43
  7. package/dist/src/commands/tools.js +5 -9
  8. package/dist/src/component-files.js +3 -2
  9. package/dist/src/conformance-record.js +1 -1
  10. package/dist/src/consumers.js +6 -9
  11. package/dist/src/contextual-uses.js +1 -17
  12. package/dist/src/contract/config.generated.js +4 -1
  13. package/dist/src/contract/contract.generated.js +2 -2
  14. package/dist/src/contract/report.generated.js +6 -2
  15. package/dist/src/dependencies.js +25 -29
  16. package/dist/src/destructuring.js +1 -19
  17. package/dist/src/element-access.js +179 -164
  18. package/dist/src/entries/build-scripts.js +2 -2
  19. package/dist/src/entries/configuration-files.js +3 -7
  20. package/dist/src/entries/conventions.js +2 -2
  21. package/dist/src/entries/html-entries.js +7 -10
  22. package/dist/src/entries/mdx-entries.js +2 -2
  23. package/dist/src/entries/static-module.js +2 -1
  24. package/dist/src/exempt.js +0 -26
  25. package/dist/src/exposure.js +400 -0
  26. package/dist/src/facts.js +4 -4
  27. package/dist/src/findings/completion.js +44 -21
  28. package/dist/src/findings/emitters.js +22 -33
  29. package/dist/src/{implementations.js → findings/implementations.js} +3 -3
  30. package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
  31. package/dist/src/findings/intra-function.js +3 -3
  32. package/dist/src/findings/reads-and-writes.js +71 -15
  33. package/dist/src/findings/unused-declarations.js +2 -22
  34. package/dist/src/findings/visibility-narrowing.js +14 -20
  35. package/dist/src/framework-lifecycle.js +41 -14
  36. package/dist/src/import-bindings.js +73 -28
  37. package/dist/src/index/sites.js +557 -0
  38. package/dist/src/indexed-access.js +523 -478
  39. package/dist/src/interface-satisfaction.js +1 -1
  40. package/dist/src/inventory.js +18 -0
  41. package/dist/src/keyof-comparisons.js +124 -103
  42. package/dist/src/{components.js → liveness/components.js} +1 -1
  43. package/dist/src/{graph.js → liveness/graph.js} +45 -20
  44. package/dist/src/liveness/matrix.js +99 -0
  45. package/dist/src/liveness/sweep.js +558 -0
  46. package/dist/src/load/expected-module.js +2 -2
  47. package/dist/src/load/specifiers.js +2 -4
  48. package/dist/src/load/synthesize.js +5 -0
  49. package/dist/src/load/workspace-resolution.js +2 -2
  50. package/dist/src/module-declarations.js +70 -85
  51. package/dist/src/overrides.js +134 -112
  52. package/dist/src/paths.js +46 -7
  53. package/dist/src/platform-calls.js +2 -2
  54. package/dist/src/platform-symbols.js +13 -19
  55. package/dist/src/query.js +46 -0
  56. package/dist/src/references/read-rule.js +1 -0
  57. package/dist/src/references/read-rules.js +48 -0
  58. package/dist/src/references/sink.js +237 -0
  59. package/dist/src/references/sources.js +450 -0
  60. package/dist/src/references.js +110 -1285
  61. package/dist/src/roots/configured-declarations.js +3 -0
  62. package/dist/src/roots/published-reach.js +4 -4
  63. package/dist/src/roots/root-set.js +1 -1
  64. package/dist/src/roots/unseen-loads.js +38 -10
  65. package/dist/src/serialization-contract.js +8 -3
  66. package/dist/src/stores.js +4 -2
  67. package/dist/src/symbol-keys.js +89 -83
  68. package/dist/src/tests/file-class.js +76 -0
  69. package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
  70. package/dist/src/{test-support.js → tests/support.js} +2 -11
  71. package/dist/src/type-parameter-reads.js +178 -139
  72. package/dist/src/unimplemented-callees.js +262 -242
  73. package/dist/src/verbs/analyze.js +3 -3
  74. package/dist/src/verbs/explain.js +75 -132
  75. package/dist/src/verbs/print-retained.js +3 -3
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/alias-chain.ts +0 -7
  79. package/src/amd.ts +130 -0
  80. package/src/analysis.ts +125 -128
  81. package/src/augmentations.ts +42 -28
  82. package/src/bindings.ts +460 -0
  83. package/src/calls.ts +338 -46
  84. package/src/commands/tools.ts +5 -11
  85. package/src/component-files.ts +4 -2
  86. package/src/conformance-record.ts +1 -1
  87. package/src/consumers.ts +8 -11
  88. package/src/contextual-uses.ts +2 -22
  89. package/src/contract/config.generated.ts +4 -1
  90. package/src/contract/contract.generated.ts +2 -2
  91. package/src/contract/report.generated.ts +6 -2
  92. package/src/dependencies.ts +27 -28
  93. package/src/destructuring.ts +1 -31
  94. package/src/element-access.ts +188 -182
  95. package/src/entries/build-scripts.ts +2 -2
  96. package/src/entries/configuration-files.ts +11 -8
  97. package/src/entries/conventions.ts +2 -1
  98. package/src/entries/html-entries.ts +15 -14
  99. package/src/entries/mdx-entries.ts +2 -2
  100. package/src/entries/static-module.ts +2 -1
  101. package/src/exempt.ts +31 -46
  102. package/src/exposure.ts +492 -0
  103. package/src/facts.ts +5 -3
  104. package/src/finding.ts +1 -1
  105. package/src/findings/boundary.ts +9 -0
  106. package/src/findings/completion.ts +48 -23
  107. package/src/findings/emitter.ts +1 -1
  108. package/src/findings/emitters.ts +23 -41
  109. package/src/{implementations.ts → findings/implementations.ts} +4 -4
  110. package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
  111. package/src/findings/interfaces.ts +3 -3
  112. package/src/findings/intra-function.ts +3 -3
  113. package/src/findings/reads-and-writes.ts +99 -26
  114. package/src/findings/unused-declarations.ts +4 -28
  115. package/src/findings/visibility-narrowing.ts +25 -26
  116. package/src/framework-lifecycle.ts +42 -14
  117. package/src/import-bindings.ts +98 -29
  118. package/src/index/sites.ts +781 -0
  119. package/src/indexed-access.ts +570 -527
  120. package/src/interface-satisfaction.ts +2 -2
  121. package/src/inventory.ts +19 -0
  122. package/src/keyof-comparisons.ts +131 -116
  123. package/src/{components.ts → liveness/components.ts} +3 -3
  124. package/src/{graph.ts → liveness/graph.ts} +93 -27
  125. package/src/liveness/matrix.ts +162 -0
  126. package/src/liveness/sweep.ts +855 -0
  127. package/src/load/expected-module.ts +8 -2
  128. package/src/load/specifiers.ts +8 -5
  129. package/src/load/synthesize.ts +6 -0
  130. package/src/load/workspace-resolution.ts +9 -2
  131. package/src/module-declarations.ts +74 -98
  132. package/src/overrides.ts +162 -134
  133. package/src/paths.ts +54 -8
  134. package/src/platform-calls.ts +3 -3
  135. package/src/platform-symbols.ts +11 -27
  136. package/src/query.ts +81 -0
  137. package/src/references/read-rule.ts +60 -0
  138. package/src/references/read-rules.ts +55 -0
  139. package/src/references/sink.ts +314 -0
  140. package/src/references/sources.ts +632 -0
  141. package/src/references.ts +175 -1733
  142. package/src/report.ts +1 -1
  143. package/src/roots/configured-declarations.ts +3 -0
  144. package/src/roots/published-reach.ts +9 -4
  145. package/src/roots/root-set.ts +1 -1
  146. package/src/roots/unseen-loads.ts +49 -11
  147. package/src/serialization-contract.ts +8 -3
  148. package/src/stores.ts +15 -3
  149. package/src/symbol-keys.ts +102 -102
  150. package/src/tests/file-class.ts +135 -0
  151. package/src/{outside-tests.ts → tests/outside.ts} +16 -10
  152. package/src/{test-support.ts → tests/support.ts} +9 -20
  153. package/src/type-parameter-reads.ts +187 -158
  154. package/src/unimplemented-callees.ts +297 -280
  155. package/src/verbs/analyze.ts +3 -2
  156. package/src/verbs/explain.ts +83 -158
  157. package/src/verbs/print-retained.ts +3 -3
  158. package/dist/src/matrix.js +0 -273
  159. package/dist/src/sweep.js +0 -222
  160. package/src/matrix.ts +0 -463
  161. package/src/sweep.ts +0 -361
@@ -5,7 +5,7 @@
5
5
  */
6
6
  import { isExportDeclaration, isImportDeclaration, isStringLiteral, } from "@typescript/native/ast";
7
7
  import { resolveRelativeSpecifier } from "./configuration-files.js";
8
- import { ancestors, dirnamePath, joinPath, resolvePath } from "../paths.js";
8
+ import { ancestors, dirnamePath, hasURLScheme, isAbsolutePath, isExplicitPath, joinPath, resolvePath, } from "../paths.js";
9
9
  import { filesBelow } from "../fs/tree.js";
10
10
  import { holdsManifest } from "../fs/manifest.js";
11
11
  /** A `<script` start tag, its attribute text captured. */
@@ -14,12 +14,8 @@ const SCRIPT_TAG = /<script(?=[\s>/])([^>]*)>/giu;
14
14
  const SCRIPT_END = /<\/script\s*>/iu;
15
15
  /** One attribute: a name, then a double-quoted, single-quoted or unquoted value. */
16
16
  const ATTRIBUTE = /([^\s"'=<>/]+)(?:\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+)))?/gu;
17
- /** A value that names a resource on another origin, or by another scheme. */
18
- const ELSEWHERE = /^(?:\/\/|[A-Za-z][A-Za-z\d+.-]*:)/u;
19
17
  /** The `type` values a classic script carries, compared in lowercase. */
20
18
  const CLASSIC_TYPES = new Set(["text/javascript", "application/javascript"]);
21
- /** The prefixes an inline script's specifier names a file of the target with. */
22
- const FILE_PREFIXES = ["/", "./", "../"];
23
19
  /** Each attribute of one start tag's attribute text, by its lowercase name. */
24
20
  function attributes(text) {
25
21
  const found = new Map();
@@ -69,7 +65,7 @@ function scriptValues(parse, path, text) {
69
65
  const rest = text.slice(start);
70
66
  const end = rest.search(SCRIPT_END);
71
67
  const content = end === -1 ? rest : rest.slice(0, end);
72
- values.push(...specifiersOf(parse, `${path}.${String(start)}.ts`, content).filter((specifier) => FILE_PREFIXES.some((prefix) => specifier.startsWith(prefix))));
68
+ values.push(...specifiersOf(parse, `${path}.${String(start)}.ts`, content).filter(isExplicitPath));
73
69
  }
74
70
  return values;
75
71
  }
@@ -98,18 +94,19 @@ export function htmlEntries(host, targetRoot, files, parse) {
98
94
  }
99
95
  const dir = dirnamePath(path);
100
96
  for (const value of scriptValues(parse, path, text)) {
101
- if (ELSEWHERE.test(value)) {
97
+ // A value with a scheme, or one starting `//`, names a resource on another origin.
98
+ if (value.startsWith("//") || hasURLScheme(value)) {
102
99
  continue;
103
100
  }
104
- const base = value.startsWith("/") ? manifestDirectory(host, root, dir) : dir;
105
- const relative = value.startsWith("/") ? `.${value}` : value;
101
+ const base = isAbsolutePath(value) ? manifestDirectory(host, root, dir) : dir;
102
+ const relative = isAbsolutePath(value) ? `.${value}` : value;
106
103
  const reached = resolveRelativeSpecifier(host, base, relative);
107
104
  const file = reached === undefined ? undefined : files.byName.get(reached);
108
105
  if (file !== undefined) {
109
106
  found.push({ file, source: value });
110
107
  continue;
111
108
  }
112
- const served = value.startsWith("/")
109
+ const served = isAbsolutePath(value)
113
110
  ? files.outputDirs.filter((outDir) => outDir === dir)
114
111
  : [];
115
112
  const emitting = [base, ...served].flatMap((against) => files.named(resolvePath(against, relative)));
@@ -4,7 +4,7 @@
4
4
  * is an import declaration, which an MDX compiler hoists into the module it builds.
5
5
  */
6
6
  import { resolveRelativeSpecifier } from "./configuration-files.js";
7
- import { dirnamePath, joinPath, resolvePath } from "../paths.js";
7
+ import { dirnamePath, joinPath, resolvePath, startsRelativePath } from "../paths.js";
8
8
  import { filesBelow } from "../fs/tree.js";
9
9
  /** The extensions of a document whose imports an MDX compiler reads. */
10
10
  const DOCUMENT = /\.mdx?$/u;
@@ -122,7 +122,7 @@ function importedBy(text) {
122
122
  }
123
123
  const matched = open === undefined ? IMPORT_LINE.exec(line) : null;
124
124
  const specifier = matched?.[2];
125
- if (specifier?.startsWith("./") === true || specifier?.startsWith("../") === true) {
125
+ if (specifier !== undefined && startsRelativePath(specifier)) {
126
126
  found.push({ specifier, names: clauseNames(matched?.[1] ?? "") });
127
127
  }
128
128
  }
@@ -7,6 +7,7 @@
7
7
  import { isArrayLiteralExpression, isBindingElement, isExternalModuleReference, isImportClause, isImportEqualsDeclaration, isImportSpecifier, isStringLiteralLikeNode, isCallExpression, isClassDeclaration, isClassExpression, isComputedPropertyName, isExportAssignment, isExportDeclaration, isExpressionStatement, isFunctionDeclaration, isFunctionExpression, isFunctionLikeDeclaration, isIdentifier, isImportDeclaration, isNamedExports, isNamespaceImport, isNoSubstitutionTemplateLiteral, isNumericLiteral, isObjectBindingPattern, isObjectLiteralExpression, isPropertyAccessExpression, isPropertyAssignment, isSpreadAssignment, isStringLiteral, isVariableDeclaration, isVariableStatement, isSourceFile, isBlock, isModuleBlock, isCaseBlock, isCatchClause, isForStatement, isForInStatement, isForOfStatement, isVariableDeclarationList, isMetaProperty, isAssignmentOperator, isBinaryExpression, isParenthesizedExpression, isPostfixUnaryExpression, isPrefixUnaryExpression, isShorthandPropertyAssignment, isSpreadElement, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
8
8
  import { commonJSModuleValue } from "../inventory/commonjs.js";
9
9
  import { callsPlatformBy, lexicalBinding, writtenName } from "../platform-calls.js";
10
+ import { startsRelativePath } from "../paths.js";
10
11
  import { unwrapped } from "../unwrap.js";
11
12
  /** A JSON or YAML document's value, which carries no positions. */
12
13
  export function documentValue(value) {
@@ -443,7 +444,7 @@ export function relativeSpecifiers(file) {
443
444
  }
444
445
  const found = [];
445
446
  const named = (specifier) => {
446
- if (specifier?.startsWith("./") === true || specifier?.startsWith("../") === true) {
447
+ if (specifier !== undefined && startsRelativePath(specifier)) {
447
448
  found.push(specifier);
448
449
  }
449
450
  };
@@ -10,7 +10,6 @@ import { EXEMPTION_CLASSES } from "./contract/contract.generated.js";
10
10
  import { TS_EXEMPTION_CLASSES, typescriptVisibilityOf, } from "./exempt-classes.js";
11
11
  import { byPosition, positionKey } from "./position.js";
12
12
  import { Unanswerable } from "./query.js";
13
- import { sweep } from "./sweep.js";
14
13
  import { bytewise } from "./text/compare.js";
15
14
  /**
16
15
  * The classes `exemptions.disabled` switches off. A class that runs on another language
@@ -130,13 +129,6 @@ export function exemptionsOf(found) {
130
129
  return true;
131
130
  });
132
131
  }
133
- /**
134
- * The records that hold while the component files at `live` are live: every record that
135
- * holds whatever is live, and each one a live file's markup holds, as a record of the run.
136
- */
137
- export function holdingWhile(records, live) {
138
- return exemptionsOf(records.flatMap(({ whileLive, ...record }) => whileLive === undefined || live.has(whileLive) ? [record] : []));
139
- }
140
132
  /** The records per declaration, each declaration's in the order given. */
141
133
  function byDeclaration(records) {
142
134
  const grouped = new Map();
@@ -151,24 +143,6 @@ function byDeclaration(records) {
151
143
  }
152
144
  return grouped;
153
145
  }
154
- /**
155
- * The records that held back a declaration: one some configuration's graph, swept
156
- * again with no exemption and its marks, judges a candidate, whatever another
157
- * configuration answers, or one in `writeOnly`, which the run would report as written
158
- * and never read without the record's read. A declaration a relation or a mark holds
159
- * live in every configuration is held back by no record. The order is the
160
- * declarations' site order, and for one declaration the input's.
161
- */
162
- export function retainedIn(matrix, input, writeOnly = new Set()) {
163
- const exempt = input.exempt ?? [];
164
- if (exempt.length === 0) {
165
- return [];
166
- }
167
- const bare = { marked: input.marked, mode: input.mode };
168
- const dead = new Set(matrix.graphs.flatMap((graph) => sweep(graph, bare).candidates.map((candidate) => candidate.id)));
169
- const held = byDeclaration(exempt.filter((record) => dead.has(record.id) || writeOnly.has(record.id)));
170
- return matrix.union.symbols.flatMap((symbol) => held.get(symbol.id) ?? []);
171
- }
172
146
  /**
173
147
  * One line per held-back declaration, in site order: its reference and position, then
174
148
  * each record's class, site and detail, all separated by tabs.
@@ -0,0 +1,400 @@
1
+ /**
2
+ * Which types of the target each of its declarations hands to code outside the package:
3
+ * the types each declaration a reference from outside reaches names, as written or as the
4
+ * checker reads it, as the precondition of `DS1501` in the Contract's kinds reads that
5
+ * reach.
6
+ */
7
+ import { SignatureKind, SymbolFlags, } from "@typescript/native/sync";
8
+ import { isArrowFunction, isAsExpression, isBindingElement, isCallSignatureDeclaration, isClassExpression, isConstructorTypeNode, isConstructSignatureDeclaration, isDecorator, isEnumMember, isFunctionExpression, isFunctionTypeNode, isGetAccessorDeclaration, isIdentifier, isMethodDeclaration, isMethodSignatureDeclaration, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAssignment, isPropertyDeclaration, isPropertySignatureDeclaration, isSatisfiesExpression, isSetAccessorDeclaration, isSpreadAssignment, isTypeAssertion, isTypeReferenceNode, } from "@typescript/native/ast";
9
+ import { aliasChains } from "./alias-chain.js";
10
+ import { OUTSIDE } from "./liveness/graph.js";
11
+ import { nodeKey } from "./inventory.js";
12
+ import { isAnswered } from "./query.js";
13
+ import { typeNamesOf } from "./roots/published-reach.js";
14
+ /** The declarations whose values a reference holds without naming them. */
15
+ const TYPES = new Set(["class", "interface", "type", "enum"]);
16
+ /** The symbol flags of a type the source spells as a literal, whose members are its own. */
17
+ const LITERAL = SymbolFlags.TypeLiteral | SymbolFlags.ObjectLiteral;
18
+ /** The members a type's definition names other types through, where exported. */
19
+ const MEMBERS = new Set([
20
+ "method",
21
+ "class-member",
22
+ "interface-method",
23
+ "type-member",
24
+ ]);
25
+ /** The declarations of a module or a namespace whose signature or type hands values on. */
26
+ const CARRYING = new Set(["function", "variable", ...TYPES]);
27
+ /** The containers whose own declarations a reference from outside can reach. */
28
+ const SCOPES = new Set(["file", "namespace"]);
29
+ /** What a project whose carriers no rule reads carries: nothing. */
30
+ export const NO_CARRIERS = { named: new Map(), unread: new Set() };
31
+ /** Whether a member is named from outside its type: neither `private` nor a private name. */
32
+ function exportedMember(symbol) {
33
+ return symbol.visibility === "public" || symbol.visibility === "protected";
34
+ }
35
+ /**
36
+ * The declarations of one project that carry values out: a function, a variable or a type
37
+ * a module or a namespace declares, and an exported member of a class, an interface or a
38
+ * type. Neither is filtered by its export: only a declaration the reach meets passes it
39
+ * on, and an unexported type an exported function returns passes it to what it names.
40
+ */
41
+ function carrying(held) {
42
+ const byId = new Map(held.symbols.map((symbol) => [symbol.id, symbol]));
43
+ return held.symbols.filter((symbol) => {
44
+ const parent = byId.get(symbol.parent);
45
+ if (MEMBERS.has(symbol.kind)) {
46
+ return parent !== undefined && TYPES.has(parent.kind) && exportedMember(symbol);
47
+ }
48
+ return CARRYING.has(symbol.kind) && parent !== undefined && SCOPES.has(parent.kind);
49
+ });
50
+ }
51
+ /** The node a declaration's symbol is resolved at: its name, or itself where it has none. */
52
+ function nameNode(node) {
53
+ return node.name ?? node;
54
+ }
55
+ /** Whether a value expression is one whose type the declaration output prints as written. */
56
+ function printedAsWritten(node) {
57
+ return (isParenthesizedExpression(node) ||
58
+ isAsExpression(node) ||
59
+ isTypeAssertion(node) ||
60
+ isSatisfiesExpression(node) ||
61
+ isObjectLiteralExpression(node) ||
62
+ isArrowFunction(node) ||
63
+ isFunctionExpression(node) ||
64
+ isClassExpression(node));
65
+ }
66
+ /** Whether a type node is the `const` of a const assertion, which names no type. */
67
+ function isConstName(node) {
68
+ return isTypeReferenceNode(node) && isIdentifier(node.typeName) && node.typeName.text === "const";
69
+ }
70
+ /**
71
+ * Whether the declaration output prints a type or a member it meets by this declaration's
72
+ * written types: a function type's, a signature's, a member's, or an expression's that
73
+ * {@link printedAsWritten} holds. A type literal or a mapped type it prints from the
74
+ * checker's members, so only each member's own declaration is read.
75
+ */
76
+ function reused(node) {
77
+ return (isFunctionTypeNode(node) ||
78
+ isConstructorTypeNode(node) ||
79
+ isPropertySignatureDeclaration(node) ||
80
+ isMethodSignatureDeclaration(node) ||
81
+ isCallSignatureDeclaration(node) ||
82
+ isConstructSignatureDeclaration(node) ||
83
+ isPropertyAssignment(node) ||
84
+ isPropertyDeclaration(node) ||
85
+ isMethodDeclaration(node) ||
86
+ isGetAccessorDeclaration(node) ||
87
+ isSetAccessorDeclaration(node) ||
88
+ printedAsWritten(node));
89
+ }
90
+ /**
91
+ * Whether the declaration output leaves out the types one node of a declaration writes.
92
+ * It prints an initializer only where its declaration has no annotation, and then as the
93
+ * checker types it, except through what {@link printedAsWritten} holds: an assertion's
94
+ * type in place of its operand, a function's or a class's signatures, an object literal's
95
+ * properties, and the operand of a `satisfies`. A decorator and an enum member's or a
96
+ * binding element's initializer are not printed.
97
+ */
98
+ function unprinted(node) {
99
+ const parent = node.parent;
100
+ if (parent === undefined) {
101
+ return false;
102
+ }
103
+ if (isDecorator(node)) {
104
+ return true;
105
+ }
106
+ const held = parent;
107
+ if (held.initializer === node) {
108
+ return (isEnumMember(parent) ||
109
+ isBindingElement(parent) ||
110
+ held.type !== undefined ||
111
+ !printedAsWritten(node));
112
+ }
113
+ if (isAsExpression(parent) || isTypeAssertion(parent)) {
114
+ return isConstName(parent.type)
115
+ ? node === parent.type || !printedAsWritten(node)
116
+ : node === parent.expression;
117
+ }
118
+ if (isSatisfiesExpression(parent)) {
119
+ return node === parent.type || !printedAsWritten(node);
120
+ }
121
+ if (isParenthesizedExpression(parent) || isSpreadAssignment(parent)) {
122
+ return !printedAsWritten(node);
123
+ }
124
+ return false;
125
+ }
126
+ /**
127
+ * The declarations of the target each declaration of one project carries: those it names
128
+ * as written, and those its type names as the checker reads it, so an inferred result names
129
+ * its type as an annotation does. The walk reads a type's constituents, type arguments,
130
+ * alias arguments, `partsOf`, literal members and index signatures, and each signature's
131
+ * parameters, result and `signaturePartsOf`. A type of the target ends the walk, since its
132
+ * own definition is read as its own.
133
+ */
134
+ export function carriersOf(project, held) {
135
+ const { queries } = project;
136
+ const chains = aliasChains(project, held);
137
+ const kinds = new Map(held.symbols.map((symbol) => [symbol.id, symbol.kind]));
138
+ const parents = new Map(held.symbols.map((symbol) => [symbol.id, symbol.parent]));
139
+ const wanted = new Set(carrying(held).map((symbol) => symbol.id));
140
+ const declared = [];
141
+ // A nested carrier and any other member of a type are read on that member, not on the
142
+ // declaration that holds it; a type parameter is part of its declaration.
143
+ const readAloneIn = (file) => (nested) => {
144
+ const id = held.declarations.get(nodeKey(file, nested));
145
+ const kind = id === undefined ? undefined : kinds.get(id);
146
+ return (unprinted(nested) ||
147
+ (id !== undefined &&
148
+ kind !== "type-parameter" &&
149
+ (wanted.has(id) || TYPES.has(kinds.get(parents.get(id) ?? "") ?? "file"))));
150
+ };
151
+ for (const file of project.ownSourceFiles()) {
152
+ const readAlone = readAloneIn(file);
153
+ const visit = (node) => {
154
+ const id = held.declarations.get(nodeKey(file, node));
155
+ if (id !== undefined && wanted.has(id)) {
156
+ declared.push({
157
+ id,
158
+ handle: project.handle(nameNode(node)),
159
+ written: typeNamesOf(node, readAlone).map((name) => project.handle(name)),
160
+ });
161
+ }
162
+ node.forEachChild(visit);
163
+ };
164
+ file.forEachChild(visit);
165
+ }
166
+ const unread = new Set();
167
+ let reading = "";
168
+ const answer = (found, otherwise) => {
169
+ if (isAnswered(found)) {
170
+ return found;
171
+ }
172
+ unread.add(reading);
173
+ return otherwise;
174
+ };
175
+ const typesOf = (symbol) => chains.declarationsOf(symbol).filter((id) => TYPES.has(kinds.get(id) ?? "file"));
176
+ // The declaration output prints an anonymous type or a literal's member by its own
177
+ // declaration's written types where it has them, which name an alias the checker erases,
178
+ // so the walk of one declaration keeps the target's own declarations of those it meets.
179
+ const own = project.ownPaths();
180
+ let meeting = new Map();
181
+ const meet = (symbol) => {
182
+ for (const handle of symbol.declarations) {
183
+ if (own.has(handle.path)) {
184
+ meeting.set(`${handle.path}#${String(handle.index)}`, handle);
185
+ }
186
+ }
187
+ };
188
+ const signatures = (type, into, seen) => {
189
+ for (const kind of [SignatureKind.Call, SignatureKind.Construct]) {
190
+ for (const signature of answer(queries.signaturesOf(type, kind), [])) {
191
+ signature.parameters.forEach((_parameter, index) => {
192
+ walk(answer(queries.parameterType(signature, index), undefined), into, seen);
193
+ });
194
+ walk(answer(queries.returnTypeOf(signature), undefined), into, seen);
195
+ for (const part of answer(queries.signaturePartsOf(signature), [])) {
196
+ walk(part, into, seen);
197
+ }
198
+ }
199
+ }
200
+ };
201
+ // The parts of a type that spell other types, not counting the type itself.
202
+ const structure = (type, into, seen) => {
203
+ for (const part of answer(queries.constituents(type), [])) {
204
+ if (part.id !== type.id) {
205
+ walk(part, into, seen);
206
+ }
207
+ }
208
+ for (const argument of answer(queries.typeArguments(type), [])) {
209
+ walk(argument, into, seen);
210
+ }
211
+ for (const part of answer(queries.partsOf(type), [])) {
212
+ walk(part, into, seen);
213
+ }
214
+ const symbol = answer(queries.symbolOfType(type), undefined);
215
+ if (symbol !== undefined && (symbol.flags & LITERAL) !== 0) {
216
+ for (const property of answer(queries.propertiesOf(type), [])) {
217
+ meet(property);
218
+ walk(answer(queries.typeOfSymbol(property), undefined), into, seen);
219
+ }
220
+ for (const info of answer(queries.indexInfosOf(type), [])) {
221
+ walk(info.valueType, into, seen);
222
+ }
223
+ }
224
+ signatures(type, into, seen);
225
+ };
226
+ const walk = (type, into, seen) => {
227
+ if (type === undefined || seen.has(type.id)) {
228
+ return;
229
+ }
230
+ seen.add(type.id);
231
+ const alias = answer(queries.aliasSymbolOf(type), undefined);
232
+ if (alias !== undefined) {
233
+ for (const id of typesOf(alias)) {
234
+ into.add(id);
235
+ }
236
+ for (const argument of answer(queries.aliasArgumentsOf(type), [])) {
237
+ walk(argument, into, seen);
238
+ }
239
+ if (typesOf(alias).length > 0) {
240
+ return;
241
+ }
242
+ }
243
+ const symbol = answer(queries.symbolOfType(type), undefined);
244
+ const named = symbol === undefined ? [] : typesOf(symbol);
245
+ if (named.length > 0) {
246
+ for (const id of named) {
247
+ into.add(id);
248
+ }
249
+ for (const argument of answer(queries.typeArguments(type), [])) {
250
+ walk(argument, into, seen);
251
+ }
252
+ return;
253
+ }
254
+ if (symbol !== undefined) {
255
+ meet(symbol);
256
+ }
257
+ structure(type, into, seen);
258
+ };
259
+ // What one declaration's checker type names, by its kind.
260
+ const definition = (symbol, kind, into) => {
261
+ const seen = new Set();
262
+ // A class's or an interface's own definition: its properties are members, which carry
263
+ // on their own, so only its bases, signatures and index signatures are read here.
264
+ const own = (type) => {
265
+ for (const info of answer(queries.indexInfosOf(type), [])) {
266
+ walk(info.valueType, into, seen);
267
+ }
268
+ signatures(type, into, seen);
269
+ };
270
+ if (kind === "class" || kind === "interface") {
271
+ const instance = answer(queries.declaredTypeOf(symbol), undefined);
272
+ const statics = kind === "class" ? answer(queries.typeOfSymbol(symbol), undefined) : undefined;
273
+ const sides = [instance, statics].filter((side) => side !== undefined);
274
+ for (const side of sides) {
275
+ seen.add(side.id);
276
+ }
277
+ for (const base of instance === undefined ? [] : answer(queries.baseTypes(instance), [])) {
278
+ walk(base, into, seen);
279
+ }
280
+ sides.forEach(own);
281
+ }
282
+ else if (kind === "type") {
283
+ const aliased = answer(queries.declaredTypeOf(symbol), undefined);
284
+ if (aliased !== undefined) {
285
+ seen.add(aliased.id);
286
+ structure(aliased, into, seen);
287
+ }
288
+ }
289
+ else if (kind !== "enum") {
290
+ walk(answer(queries.typeOfSymbol(symbol), undefined), into, seen);
291
+ }
292
+ };
293
+ const symbols = project.symbolsAt(declared.map((one) => one.handle));
294
+ const spelled = project.symbolsAt(declared.flatMap((one) => one.written));
295
+ const reached = [];
296
+ const add = (answered, into) => {
297
+ const name = answer(answered, undefined);
298
+ for (const target of name === undefined ? [] : chains.chainOf(name)) {
299
+ if (TYPES.has(kinds.get(target.id) ?? "file")) {
300
+ into.add(target.id);
301
+ }
302
+ }
303
+ };
304
+ let from = 0;
305
+ declared.forEach(({ id, written }, index) => {
306
+ reading = id;
307
+ const into = new Set();
308
+ for (const answered of spelled.slice(from, from + written.length)) {
309
+ add(answered, into);
310
+ }
311
+ from += written.length;
312
+ meeting = new Map();
313
+ const symbol = answer(symbols[index], undefined);
314
+ if (symbol !== undefined) {
315
+ definition(symbol, kinds.get(id), into);
316
+ }
317
+ reached.push({ id, into, met: [...meeting.values()] });
318
+ });
319
+ const namesAt = new Map();
320
+ const writtenAt = (handle) => {
321
+ const key = `${handle.path}#${String(handle.index)}`;
322
+ let names = namesAt.get(key);
323
+ if (names === undefined) {
324
+ const node = project.declarationAt(handle)?.node;
325
+ names =
326
+ node === undefined || !reused(node)
327
+ ? []
328
+ : typeNamesOf(node, readAloneIn(node.getSourceFile())).map((name) => project.handle(name));
329
+ namesAt.set(key, names);
330
+ }
331
+ return names;
332
+ };
333
+ const metNames = reached.map((one) => one.met.flatMap(writtenAt));
334
+ const metSpelled = project.symbolsAt(metNames.flat());
335
+ const named = new Map();
336
+ from = 0;
337
+ reached.forEach(({ id, into }, index) => {
338
+ reading = id;
339
+ const count = metNames[index]?.length ?? 0;
340
+ for (const answered of metSpelled.slice(from, from + count)) {
341
+ add(answered, into);
342
+ }
343
+ from += count;
344
+ into.delete(id);
345
+ named.set(id, [...into]);
346
+ });
347
+ return { named, unread };
348
+ }
349
+ /**
350
+ * The types whose values a reference from outside the package reaches through the
351
+ * package's own declarations, or every type where such a reference reaches a declaration
352
+ * whose surface the checker left unread. `outside` flags each declaration of the graph a
353
+ * reference from outside names; each passes the reach on to what it carries, a type to
354
+ * each member it exports, a namespace to each declaration it exports and a member to its
355
+ * type, until no declaration joins.
356
+ */
357
+ export function exposedTypes(graph, outside, carriers) {
358
+ const { symbols, parent } = graph;
359
+ const members = symbols.map(() => []);
360
+ symbols.forEach((symbol, at) => {
361
+ const container = parent[at] ?? OUTSIDE;
362
+ const holder = symbols[container];
363
+ if (holder !== undefined &&
364
+ ((TYPES.has(holder.kind) && MEMBERS.has(symbol.kind) && exportedMember(symbol)) ||
365
+ (holder.kind === "namespace" && symbol.exported))) {
366
+ members[container]?.push(at);
367
+ }
368
+ });
369
+ const reached = symbols.map((_symbol, at) => outside[at] === true);
370
+ const queue = reached.flatMap((one, at) => (one ? [at] : []));
371
+ const join = (at) => {
372
+ if (at !== OUTSIDE && reached[at] !== true) {
373
+ reached[at] = true;
374
+ queue.push(at);
375
+ }
376
+ };
377
+ for (let at = queue.pop(); at !== undefined; at = queue.pop()) {
378
+ const symbol = symbols[at];
379
+ if (symbol === undefined) {
380
+ continue;
381
+ }
382
+ if (carriers?.unread.has(symbol.id) === true) {
383
+ return "every";
384
+ }
385
+ for (const id of carriers?.named.get(symbol.id) ?? []) {
386
+ join(graph.at(id));
387
+ }
388
+ for (const member of members[at] ?? []) {
389
+ join(member);
390
+ }
391
+ if (MEMBERS.has(symbol.kind)) {
392
+ join(parent[at] ?? OUTSIDE);
393
+ }
394
+ }
395
+ return new Set(symbols.flatMap((symbol, at) => reached[at] === true && TYPES.has(symbol.kind) ? [symbol.id] : []));
396
+ }
397
+ /** Whether code outside the package holds values of a type through the package's declarations. */
398
+ export function exposes(exposed, symbol) {
399
+ return TYPES.has(symbol.kind) && (exposed === "every" || exposed.has(symbol.id));
400
+ }
package/dist/src/facts.js CHANGED
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * The facts document `--facts` writes, `bindings.1` and `bindings.2`: the run's generated
3
- * files under the one decision the generated-file class reads. It carries no binding
4
- * record, so it states `evaluated: false`.
3
+ * files under the one decision the generated-file class reads, and the binding identities
4
+ * they publish. No binding state is computed, so it states `evaluated: false` (`bindings.10`).
5
5
  */
6
6
  import { FACTS_VERSION } from "./contract/contract.generated.js";
7
7
  import { LANGUAGE, PRODUCT } from "./product.js";
8
8
  import { bytewise } from "./text/compare.js";
9
9
  /** The facts document of one run, as the text written to the path `--facts` names. */
10
- export function factsDocument(version, generated) {
10
+ export function factsDocument(version, generated, bindings) {
11
11
  const document = {
12
12
  facts_version: FACTS_VERSION,
13
13
  analyzer: { name: PRODUCT, version },
@@ -32,7 +32,7 @@ export function factsDocument(version, generated) {
32
32
  schema_files: fact.generator.schemaFiles,
33
33
  }),
34
34
  })),
35
- bindings: [],
35
+ bindings,
36
36
  };
37
37
  return `${JSON.stringify(document, null, 2)}\n`;
38
38
  }