@cplieger/deadset-ts 7.2.0-dev.3 → 8.0.0-dev.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 (153) hide show
  1. package/dist/bin/mapper-connection.js +7 -4
  2. package/dist/src/analysis.js +116 -16
  3. package/dist/src/augmentations.js +21 -8
  4. package/dist/src/automatic-types.js +189 -0
  5. package/dist/src/base-url.js +18 -0
  6. package/dist/src/component-files.js +96 -31
  7. package/dist/src/components.js +7 -6
  8. package/dist/src/config.js +1 -0
  9. package/dist/src/configuration-files.js +54 -15
  10. package/dist/src/configured-declarations.js +24 -2
  11. package/dist/src/conformance-record.js +2 -2
  12. package/dist/src/contextual-uses.js +26 -26
  13. package/dist/src/convention-rows.js +75 -0
  14. package/dist/src/conventions.js +101 -5
  15. package/dist/src/decorator.js +21 -18
  16. package/dist/src/dependencies.js +185 -30
  17. package/dist/src/destructuring.js +77 -2
  18. package/dist/src/discover.js +188 -85
  19. package/dist/src/edges.js +1 -1
  20. package/dist/src/entry-point-gaps.js +7 -10
  21. package/dist/src/entry-points.js +112 -11
  22. package/dist/src/findings/completion.js +116 -21
  23. package/dist/src/findings/dependencies-and-module-machinery.js +71 -25
  24. package/dist/src/findings/emitters.js +1 -1
  25. package/dist/src/findings/intra-function.js +44 -13
  26. package/dist/src/findings/non-code-artifacts.js +66 -33
  27. package/dist/src/findings/unused-declarations.js +21 -1
  28. package/dist/src/findings/visibility-narrowing.js +22 -3
  29. package/dist/src/generated-file.js +11 -0
  30. package/dist/src/git-ignore.js +86 -0
  31. package/dist/src/graph.js +4 -1
  32. package/dist/src/import-bindings.js +97 -0
  33. package/dist/src/indexed-access.js +130 -28
  34. package/dist/src/interface-satisfaction.js +30 -12
  35. package/dist/src/intra-function-parts.js +313 -50
  36. package/dist/src/inventory.js +63 -8
  37. package/dist/src/json-document.js +5 -5
  38. package/dist/src/kinds.js +13 -13
  39. package/dist/src/matrix.js +18 -9
  40. package/dist/src/mdx-entries.js +197 -0
  41. package/dist/src/module-declarations.js +9 -13
  42. package/dist/src/namespace-objects.js +28 -18
  43. package/dist/src/overrides.js +45 -9
  44. package/dist/src/paths.js +9 -0
  45. package/dist/src/print.js +1 -0
  46. package/dist/src/ref.js +10 -0
  47. package/dist/src/references.js +29 -13
  48. package/dist/src/reflective-lookup.js +8 -6
  49. package/dist/src/report.js +4 -2
  50. package/dist/src/reporters/template.js +22 -13
  51. package/dist/src/resolve.js +2 -0
  52. package/dist/src/roots.js +110 -38
  53. package/dist/src/schema.js +1 -0
  54. package/dist/src/serialization-contract.js +97 -20
  55. package/dist/src/session.js +26 -1
  56. package/dist/src/source-files.js +39 -14
  57. package/dist/src/spelled-names.js +58 -0
  58. package/dist/src/suppress-file.js +12 -4
  59. package/dist/src/suppress-inline.js +21 -3
  60. package/dist/src/suppress.js +7 -5
  61. package/dist/src/sweep.js +27 -10
  62. package/dist/src/symbol-keys.js +103 -2
  63. package/dist/src/template-field.js +9 -6
  64. package/dist/src/test-support.js +121 -17
  65. package/dist/src/type-errors.js +116 -35
  66. package/dist/src/type-parameter-reads.js +172 -0
  67. package/dist/src/value-reach.js +17 -5
  68. package/dist/src/verbs/analyze.js +19 -1
  69. package/dist/src/verbs/explain.js +4 -2
  70. package/dist/src/version-range.js +10 -5
  71. package/dist/src/version.js +1 -1
  72. package/dist/src/workflow-steps.js +119 -22
  73. package/dist/src/workspace-programs.js +7 -3
  74. package/dist/src/workspace-resolution.js +72 -13
  75. package/dist/src/workspace.js +1 -1
  76. package/jsr.json +1 -1
  77. package/package.json +2 -2
  78. package/src/analysis.ts +175 -16
  79. package/src/augmentations.ts +21 -10
  80. package/src/automatic-types.ts +246 -0
  81. package/src/base-url.ts +24 -0
  82. package/src/component-files.ts +116 -31
  83. package/src/components.ts +7 -6
  84. package/src/config.ts +3 -0
  85. package/src/configuration-files.ts +80 -18
  86. package/src/configured-declarations.ts +33 -4
  87. package/src/conformance-record.ts +2 -2
  88. package/src/contextual-uses.ts +27 -27
  89. package/src/convention-rows.ts +103 -0
  90. package/src/conventions.ts +121 -7
  91. package/src/decorator.ts +28 -18
  92. package/src/dependencies.ts +249 -43
  93. package/src/destructuring.ts +93 -2
  94. package/src/discover.ts +234 -86
  95. package/src/edges.ts +1 -1
  96. package/src/entry-point-gaps.ts +8 -11
  97. package/src/entry-points.ts +133 -11
  98. package/src/finding.ts +10 -0
  99. package/src/findings/completion.ts +147 -24
  100. package/src/findings/dependencies-and-module-machinery.ts +90 -28
  101. package/src/findings/emitters.ts +1 -1
  102. package/src/findings/intra-function.ts +56 -14
  103. package/src/findings/non-code-artifacts.ts +81 -41
  104. package/src/findings/unused-declarations.ts +25 -1
  105. package/src/findings/visibility-narrowing.ts +32 -3
  106. package/src/generated-file.ts +16 -0
  107. package/src/git-ignore.ts +98 -0
  108. package/src/graph.ts +6 -0
  109. package/src/host.ts +5 -9
  110. package/src/import-bindings.ts +128 -0
  111. package/src/indexed-access.ts +152 -30
  112. package/src/interface-satisfaction.ts +33 -13
  113. package/src/intra-function-parts.ts +415 -64
  114. package/src/inventory.ts +83 -7
  115. package/src/json-document.ts +7 -7
  116. package/src/kinds.ts +13 -13
  117. package/src/matrix.ts +32 -14
  118. package/src/mdx-entries.ts +241 -0
  119. package/src/module-declarations.ts +11 -13
  120. package/src/namespace-objects.ts +34 -21
  121. package/src/overrides.ts +56 -9
  122. package/src/paths.ts +10 -0
  123. package/src/print.ts +1 -0
  124. package/src/ref.ts +12 -0
  125. package/src/references.ts +38 -17
  126. package/src/reflective-lookup.ts +9 -7
  127. package/src/report.ts +16 -3
  128. package/src/reporters/template.ts +21 -13
  129. package/src/resolve.ts +3 -0
  130. package/src/roots.ts +163 -45
  131. package/src/schema.ts +1 -0
  132. package/src/serialization-contract.ts +108 -17
  133. package/src/session.ts +29 -1
  134. package/src/source-files.ts +66 -16
  135. package/src/spelled-names.ts +102 -0
  136. package/src/suppress-file.ts +14 -3
  137. package/src/suppress-inline.ts +21 -2
  138. package/src/suppress.ts +9 -5
  139. package/src/sweep.ts +32 -11
  140. package/src/symbol-keys.ts +129 -2
  141. package/src/template-field.ts +9 -6
  142. package/src/test-support.ts +149 -16
  143. package/src/type-errors.ts +132 -41
  144. package/src/type-parameter-reads.ts +227 -0
  145. package/src/value-reach.ts +19 -6
  146. package/src/verbs/analyze.ts +28 -1
  147. package/src/verbs/explain.ts +5 -2
  148. package/src/version-range.ts +10 -5
  149. package/src/version.ts +1 -1
  150. package/src/workflow-steps.ts +127 -22
  151. package/src/workspace-programs.ts +7 -3
  152. package/src/workspace-resolution.ts +86 -14
  153. package/src/workspace.ts +1 -1
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Module namespace objects: the binding of a namespace import, the value an `await` of a
3
3
  * dynamic `import()` produces, the first parameter of the first callback passed to `then`
4
- * on one, and a `const` binding initialized with one of these. A use reads the exports it
4
+ * on one, and a variable binding initialized with one of these. A use reads the exports it
5
5
  * names and no other: a property access by name or by a literal index, or a destructured
6
6
  * property, names one export, and any other use may read every export.
7
7
  */
8
- import { isArrowFunction, isAwaitExpression, isBindingElement, isCallExpression, isElementAccessExpression, isExpressionStatement, isFunctionExpression, isIdentifier, isImportDeclaration, isNamespaceImport, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParenthesizedExpression, isPropertyAccessExpression, isQualifiedName, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
8
+ import { isArrowFunction, isAwaitExpression, isCallExpression, isClassStaticBlockDeclaration, isElementAccessExpression, isExpressionStatement, isFunctionExpression, isFunctionLikeDeclaration, isIdentifier, isImportDeclaration, isModuleBlock, isNamespaceImport, isNoSubstitutionTemplateLiteral, isObjectBindingPattern, isParenthesizedExpression, isPropertyAccessExpression, isQualifiedName, isSourceFile, isStringLiteral, isVariableDeclaration, isVariableDeclarationList, isVariableStatement, isVoidExpression, NodeFlags, SyntaxKind, } from "@typescript/native/ast";
9
+ import { spellsNoBinding } from "./spelled-names.js";
9
10
  function isLiteralText(node) {
10
11
  return isStringLiteral(node) || isNoSubstitutionTemplateLiteral(node);
11
12
  }
@@ -25,21 +26,30 @@ function isLiteralImport(node) {
25
26
  const [argument] = node.arguments;
26
27
  return argument !== undefined && isLiteralText(argument);
27
28
  }
28
- /** The node a `const` binding's name is visible below: the block or file its statement is in. */
29
- function scopeOf(declaration) {
30
- const statement = declaration.parent.parent;
31
- return statement.parent;
29
+ /**
30
+ * The node the binding one declaration list declares is visible below: for `let` and
31
+ * `const`, the loop whose head declares it, or else the block or file its statement is
32
+ * in; for `var`, the function, namespace body, static block or file that holds it.
33
+ */
34
+ function scopeOf(list) {
35
+ if ((list.flags & (NodeFlags.Let | NodeFlags.Const | NodeFlags.Using)) !== 0) {
36
+ return isVariableStatement(list.parent) ? list.parent.parent : list.parent;
37
+ }
38
+ let at = list.parent;
39
+ while (!isSourceFile(at) &&
40
+ !isFunctionLikeDeclaration(at) &&
41
+ !isModuleBlock(at) &&
42
+ !isClassStaticBlockDeclaration(at)) {
43
+ at = at.parent;
44
+ }
45
+ return at;
32
46
  }
33
- /** Every identifier below `scope` that spells `name`, other than `declared` and a member's name. */
47
+ /** Every identifier below `scope` that spells `name` as a reference, other than `declared`. */
34
48
  function occurrences(scope, name, declared) {
35
49
  const found = [];
36
50
  const visit = (node) => {
37
51
  if (isIdentifier(node)) {
38
- const parent = node.parent;
39
- const member = (isPropertyAccessExpression(parent) && parent.name === node) ||
40
- (isQualifiedName(parent) && parent.right === node) ||
41
- (isBindingElement(parent) && parent.propertyName === node);
42
- if (node !== declared && node.text === name && !member) {
52
+ if (node !== declared && node.text === name && !spellsNoBinding(node)) {
43
53
  found.push(node);
44
54
  }
45
55
  return;
@@ -52,8 +62,8 @@ function occurrences(scope, name, declared) {
52
62
  /**
53
63
  * Every use of the namespace objects one file holds, keyed by the node the use is written
54
64
  * at, that reads more than a name the checker resolves: an indexed read by a literal, and
55
- * any use that may read every export. A shadowing declaration of a binding's name inside
56
- * its scope is read as the binding, which can only keep an export live.
65
+ * any use that may read every export. A use of a shadowing declaration of a binding's
66
+ * name inside its scope is read as a use of the binding, which can only keep an export live.
57
67
  */
58
68
  export function namespaceUses(file) {
59
69
  const uses = new Map();
@@ -92,11 +102,11 @@ export function namespaceUses(file) {
92
102
  }
93
103
  return;
94
104
  }
105
+ // A binding that is not `const` is followed too: each assignment to it is an
106
+ // occurrence `value` cannot narrow, so a reassigned binding still reads every export.
95
107
  const list = parent.parent;
96
- if (isIdentifier(pattern) &&
97
- isVariableDeclarationList(list) &&
98
- (list.flags & NodeFlags.Const) !== 0) {
99
- binding(pattern, scopeOf(parent), specifier, seen);
108
+ if (isIdentifier(pattern) && isVariableDeclarationList(list)) {
109
+ binding(pattern, scopeOf(list), specifier, seen);
100
110
  return;
101
111
  }
102
112
  }
@@ -1,10 +1,9 @@
1
1
  /**
2
- * The overrides of the target's class members. A call through the base class names the
3
- * base's member and runs the override of whichever subclass the value is, so the base
4
- * member's declaration references every override. A method, an accessor or a property,
5
- * static or not, overriding a member of a class declared outside the target, at any depth
6
- * of its `extends` chain, is used by outside code holding the instance or the class
7
- * through that class, so its own class references it.
2
+ * The overrides of the target's class members. A call through the base class runs the
3
+ * override of whichever subclass the value is, so the base member references every
4
+ * override. A member, static or not, overriding one of a class declared outside the target
5
+ * at any depth of its `extends` chain, a library's interface-and-variable class such as
6
+ * `Error` included, is used by outside code holding the value, so its own class references it.
8
7
  */
9
8
  import { isClassDeclaration, isClassExpression, isExpressionWithTypeArguments, isPropertyAccessExpression, isQualifiedName, SyntaxKind, } from "@typescript/native/ast";
10
9
  import { memberComponent } from "./class-members.js";
@@ -12,6 +11,10 @@ import { nodeKey } from "./inventory.js";
12
11
  import { SymbolFlags } from "@typescript/native/sync";
13
12
  import { UNANSWERED } from "./query.js";
14
13
  import { nameComponent } from "./ref.js";
14
+ const CLASS_DECLARATIONS = new Set([
15
+ SyntaxKind.ClassDeclaration,
16
+ SyntaxKind.ClassExpression,
17
+ ]);
15
18
  /** The member kinds a subclass overrides. */
16
19
  const OVERRIDABLE = new Set(["method", "class-member"]);
17
20
  /** The node naming the class one `extends` entry names. */
@@ -81,9 +84,12 @@ export function overrideReferences(project, held, chains, testFiles) {
81
84
  const declared = (symbol.flags & SymbolFlags.Alias) === 0 ? symbol : project.queries.aliased(symbol);
82
85
  if (inTarget.length === 0 &&
83
86
  declared !== UNANSWERED &&
84
- (declared.flags & SymbolFlags.Class) !== 0 &&
85
- declared.declarations.length > 0 &&
86
- declared.declarations.every((handle) => !own.has(handle.path))) {
87
+ (((declared.flags & SymbolFlags.Class) !== 0 &&
88
+ // The target's augmentation of an outside class merges an interface into it,
89
+ // so only a class declaration says where the class is.
90
+ declared.declarations.some((handle) => CLASS_DECLARATIONS.has(handle.kind) && !own.has(handle.path))) ||
91
+ ((declared.flags & SymbolFlags.Interface) !== 0 &&
92
+ declared.declarations.some((handle) => handle.kind === SyntaxKind.VariableDeclaration && !own.has(handle.path))))) {
87
93
  outsideBases.set(id, declared);
88
94
  }
89
95
  }
@@ -140,11 +146,13 @@ export function overrideReferences(project, held, chains, testFiles) {
140
146
  */
141
147
  function outsideMemberNames(project, bases, outsideBases) {
142
148
  const direct = new Map();
149
+ const error = errorType(project);
143
150
  for (const [classId, base] of outsideBases) {
144
151
  const instance = project.queries.declaredTypeOf(base);
145
152
  const statics = project.queries.typeOfSymbol(base);
146
153
  const components = [
147
154
  ...namesOf(project, instance === UNANSWERED ? undefined : instance).map(nameComponent),
155
+ ...(instance !== UNANSWERED && reaches(project, instance, error) ? ["toString"] : []),
148
156
  ...namesOf(project, statics === UNANSWERED ? undefined : statics).map((name) => `${nameComponent(name)}:static`),
149
157
  ];
150
158
  if (components.length > 0) {
@@ -170,6 +178,34 @@ function outsideMemberNames(project, bases, outsideBases) {
170
178
  }
171
179
  return names;
172
180
  }
181
+ /** The global `Error` instance type's identifier, where the program's library declares one. */
182
+ function errorType(project) {
183
+ const symbol = project.queries.resolveName("Error", SymbolFlags.Type, undefined, false);
184
+ if (symbol === UNANSWERED || symbol === undefined) {
185
+ return undefined;
186
+ }
187
+ const type = project.queries.declaredTypeOf(symbol);
188
+ return type === UNANSWERED ? undefined : type.id;
189
+ }
190
+ /**
191
+ * Whether one type is the `Error` instance type or extends it at any depth: the runtime
192
+ * prints an error through its `toString`, a member the library's `Error` interface omits.
193
+ */
194
+ function reaches(project, type, target) {
195
+ const seen = new Set();
196
+ const pending = [type];
197
+ for (let next = pending.pop(); next !== undefined; next = pending.pop()) {
198
+ if (next.id === target) {
199
+ return true;
200
+ }
201
+ if (!seen.has(next.id)) {
202
+ seen.add(next.id);
203
+ const bases = project.queries.baseTypes(next);
204
+ pending.push(...(bases === UNANSWERED ? [] : bases));
205
+ }
206
+ }
207
+ return false;
208
+ }
173
209
  /** The names of a type's properties; none where the type or its properties are unanswered. */
174
210
  function namesOf(project, type) {
175
211
  const properties = type === undefined ? UNANSWERED : project.queries.propertiesOf(type);
package/dist/src/paths.js CHANGED
@@ -61,6 +61,15 @@ export function dirnamePath(path) {
61
61
  }
62
62
  return cut === 0 ? SEPARATOR : normalized.slice(0, cut);
63
63
  }
64
+ /**
65
+ * The extension of one path's last segment, from its last full stop, or the empty string
66
+ * where it has none. A segment that only starts with a full stop has none.
67
+ */
68
+ export function extensionOf(path) {
69
+ const name = path.slice(path.lastIndexOf(SEPARATOR) + 1);
70
+ const dot = name.lastIndexOf(".");
71
+ return dot <= 0 ? "" : name.slice(dot);
72
+ }
64
73
  /** One path resolved against a directory, absolute where that directory is. */
65
74
  export function resolvePath(against, path) {
66
75
  return isAbsolutePath(path) ? normalizePath(path) : joinPath(against, path);
package/dist/src/print.js CHANGED
@@ -73,6 +73,7 @@ export function printConfig(config, provenance) {
73
73
  members: entry.members,
74
74
  })),
75
75
  serializers: config.ts.serializers.map(declarationOf),
76
+ roots_complete: config.ts.rootsComplete,
76
77
  },
77
78
  provenance: Object.fromEntries([...provenance]
78
79
  .sort(([a], [b]) => (a < b ? -1 : 1))
package/dist/src/ref.js CHANGED
@@ -45,9 +45,11 @@ export const REF_EXPRESSIONS = [
45
45
  `^${SCOPE}#$`,
46
46
  `^${SCOPE}#${TS_HEAD}$`,
47
47
  `^${SCOPE}#(?:${TS_QUOTED}\\.)?${TS_HEAD}:alias$`,
48
+ `^${SCOPE}#${TS_HEAD}:import$`,
48
49
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+$`,
49
50
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})+:static$`,
50
51
  `^${SCOPE}#${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?<${TS_IDENT}>$`,
52
+ `^${SCOPE}#(?:${TS_HEAD}(?:\\.${TS_MEMBER})*(?::static)?)?\\(${TS_IDENT}\\)$`,
51
53
  `^${MANIFEST_SCOPE}#${TS_PACKAGE}:(?:dependency|dev-dependency|peer-dependency)$`,
52
54
  ];
53
55
  const REF_FORMS = REF_EXPRESSIONS.map((expression) => new RegExp(expression, "u"));
@@ -86,6 +88,14 @@ function quoted(name) {
86
88
  export function computedComponent(text) {
87
89
  return `[${text.replaceAll(ASCII_WHITESPACE, "")}]`;
88
90
  }
91
+ /** The reference of an import binding, read against the reference of the module that holds it. */
92
+ export function importRef(moduleRef, name) {
93
+ return `${moduleRef}${nameComponent(name)}:import`;
94
+ }
95
+ /** The reference of a parameter, read against the reference of the declaration that holds it. */
96
+ export function parameterRef(holderRef, name) {
97
+ return `${holderRef}(${name})`;
98
+ }
89
99
  /** The fragment of an export alias, inside the `declare module` block named, if one is. */
90
100
  export function aliasFragment(name, block) {
91
101
  const head = block === undefined ? "" : `${quoted(block)}.`;
@@ -16,8 +16,9 @@ import { overrideReferences } from "./overrides.js";
16
16
  import { dirnamePath, queriedModulePath, relativePath, resolvePath } from "./paths.js";
17
17
  import { byPosition, isComponentFile, renderPosition } from "./position.js";
18
18
  import { DEFAULT_BATCH_CAP, propertyTables, UNANSWERED } from "./query.js";
19
+ import { typeParameterReads } from "./type-parameter-reads.js";
19
20
  import { typeQueryAliases } from "./type-query-alias.js";
20
- import { computedKeyNames, keyNameOf, keyedUses } from "./symbol-keys.js";
21
+ import { computedKeyNames, iterationReferences, keyNameOf, keyedUses, } from "./symbol-keys.js";
21
22
  import { handedOn, valuesOf } from "./value-flow.js";
22
23
  /**
23
24
  * The operators whose left side one assignment stores into. A compound assignment is
@@ -661,19 +662,29 @@ export function references(project, held, targetRoot, options) {
661
662
  relativePath(targetRoot, file.fileName) === undefined);
662
663
  const ownPaths = project.ownPaths();
663
664
  const fileIds = new Set(held.symbols.filter((symbol) => symbol.kind === "file").map((symbol) => symbol.id));
664
- let topLevel;
665
- /** The declarations one file's top level holds. */
666
- const declaredIn = (fileId) => {
667
- if (topLevel === undefined) {
668
- const byParent = new Map();
665
+ let moduleBodies;
666
+ /**
667
+ * The declarations one module holds directly: a file's top level, or the body of a
668
+ * `declare module` whose name is a string. Any other declaration holds none here.
669
+ */
670
+ const declaredIn = (moduleId) => {
671
+ if (moduleBodies === undefined) {
672
+ const byModule = new Map();
669
673
  for (const symbol of held.symbols) {
670
- if (fileIds.has(symbol.parent)) {
671
- byParent.set(symbol.parent, [...(byParent.get(symbol.parent) ?? []), symbol]);
674
+ if (!fileIds.has(symbol.parent) && !held.modules.has(symbol.parent)) {
675
+ continue;
676
+ }
677
+ const siblings = byModule.get(symbol.parent);
678
+ if (siblings === undefined) {
679
+ byModule.set(symbol.parent, [symbol]);
680
+ }
681
+ else {
682
+ siblings.push(symbol);
672
683
  }
673
684
  }
674
- topLevel = byParent;
685
+ moduleBodies = byModule;
675
686
  }
676
- return topLevel.get(fileId) ?? [];
687
+ return moduleBodies.get(moduleId) ?? [];
677
688
  };
678
689
  const exportsCache = new Map();
679
690
  /**
@@ -975,8 +986,11 @@ export function references(project, held, targetRoot, options) {
975
986
  }
976
987
  for (const url of urls) {
977
988
  const constructor = url.constructor === undefined ? undefined : answered.get(url.constructor);
989
+ // An augmentation of the global `URL` merges into it, which keeps the
990
+ // library's declaration; a `URL` the target declares has no other.
978
991
  if (typeof constructor === "object" &&
979
- constructor.declarations.some((handle) => ownPaths.has(handle.path))) {
992
+ constructor.declarations.length > 0 &&
993
+ constructor.declarations.every((handle) => ownPaths.has(handle.path))) {
980
994
  continue;
981
995
  }
982
996
  const loaded = specifierCandidates(dirnamePath(file.fileName), url.specifier)
@@ -1133,14 +1147,16 @@ export function references(project, held, targetRoot, options) {
1133
1147
  }
1134
1148
  let typedLookups = 0;
1135
1149
  if (consumer === undefined) {
1136
- found.push(...overrideReferences(project, held, chains, new Set(tests)));
1137
1150
  const before = options.requestCount?.() ?? 0;
1151
+ found.push(...overrideReferences(project, held, chains, new Set(tests)));
1138
1152
  found.push(...indexedAccessReferences(project, held, chains, root, new Set(tests)));
1139
1153
  found.push(...elementAccessReferences(project, held, chains, root, new Set(tests)));
1140
1154
  found.push(...keyofComparisonReferences(project, held, chains, root, new Set(tests)));
1155
+ found.push(...iterationReferences(project, held, chains, root, new Set(tests)));
1156
+ found.push(...typeParameterReads(project, held, chains, root, new Set(tests)));
1141
1157
  found.push(...unimplementedCalleeReads(project, held, chains, root, new Set(tests)));
1142
- typedLookups = (options.requestCount?.() ?? 0) - before;
1143
1158
  found.push(...moduleDeclarationReferences(project, held, root, new Set(tests)));
1159
+ typedLookups = (options.requestCount?.() ?? 0) - before;
1144
1160
  }
1145
1161
  const scripts = new Set(walked.filter((one) => one.file.externalModuleIndicator === undefined).map((one) => one.fileId));
1146
1162
  found.push(...augmentationReferences(held, found, scripts));
@@ -90,8 +90,9 @@ export function reflectiveLookup(input) {
90
90
  if (lookups.length === 0) {
91
91
  return [];
92
92
  }
93
- // Whether each written `Reflect` is the global one, asked in one batch: a
94
- // declaration of that name in one of the project's own files shadows it.
93
+ // Whether each written `Reflect` is the global one, asked in one batch: a name
94
+ // declared only in the project's own files shadows it, and an augmentation of the
95
+ // global merges into it, which keeps the library's declaration.
95
96
  const ownFiles = project.ownPaths();
96
97
  const reflects = lookups.flatMap((lookup) => lookup.reflect === undefined ? [] : [lookup.reflect]);
97
98
  const global = new Set();
@@ -99,10 +100,11 @@ export function reflectiveLookup(input) {
99
100
  project.symbolsAt(reflects.map((node) => project.handle(node))).forEach((symbol, index) => {
100
101
  const node = reflects[index];
101
102
  // A name the checker did not answer for is read as the global, which retains.
102
- const declaredHere = symbol === UNANSWERED
103
- ? false
104
- : symbol?.declarations.some((declaration) => ownFiles.has(declaration.path));
105
- if (node !== undefined && declaredHere === false) {
103
+ const isGlobal = symbol === UNANSWERED ||
104
+ (symbol !== undefined &&
105
+ (symbol.declarations.length === 0 ||
106
+ symbol.declarations.some((declaration) => !ownFiles.has(declaration.path))));
107
+ if (node !== undefined && isGlobal) {
106
108
  global.add(node);
107
109
  }
108
110
  });
@@ -4,7 +4,7 @@
4
4
  * renders this document, so no two renderings disagree about which findings exist.
5
5
  */
6
6
  /** The version of the report schema this analyzer writes a report to. */
7
- const SCHEMA_VERSION = "8.2.0";
7
+ const SCHEMA_VERSION = "9.0.0";
8
8
  /** Every report schema version this analyzer reads: the one the Contract it implements admits. */
9
9
  export const SCHEMA_VERSIONS_ACCEPTED = [SCHEMA_VERSION];
10
10
  /** The name this analyzer writes every document under and mints every identifier with. */
@@ -247,7 +247,9 @@ export function buildReport(input) {
247
247
  ]
248
248
  .sort(([a, x], [b, y]) => compare(x.path, y.path) || x.line - y.line || compare(a, b))
249
249
  .map(([, skip]) => skip),
250
- notes: [],
250
+ notes: [...new Map((input.notes ?? []).map((one) => [JSON.stringify(one), one])).entries()]
251
+ .sort(([a, x], [b, y]) => compare(x.kind, y.kind) || compare(x.path, y.path) || compare(a, b))
252
+ .map(([, note]) => note),
251
253
  unanswered_questions: [...input.unanswered]
252
254
  .sort((a, b) => compare(a.configuration, b.configuration) ||
253
255
  compare(JSON.stringify(a), JSON.stringify(b)))
@@ -793,7 +793,7 @@ class Execution {
793
793
  case "lit":
794
794
  return arg.value;
795
795
  case "pipe":
796
- return this.pipeline(arg.pipe, dot, false);
796
+ return this.pipeline(arg.pipe, dot);
797
797
  case "field":
798
798
  return this.field(arg.recv === undefined ? dot : this.arg(arg.recv, dot), arg.names);
799
799
  case "func":
@@ -837,19 +837,18 @@ class Execution {
837
837
  }
838
838
  return call(head.name, operands());
839
839
  }
840
- pipeline(pipe, dot, declares) {
840
+ pipeline(pipe, dot) {
841
841
  let value;
842
842
  for (const command of pipe.commands) {
843
843
  value = this.command(command, dot, value);
844
844
  }
845
845
  const result = value ?? null;
846
- const [only] = pipe.vars;
847
- if (only !== undefined && (declares || pipe.assign)) {
846
+ for (const name of pipe.vars) {
848
847
  if (pipe.assign) {
849
- this.assign(only, result);
848
+ this.assign(name, result);
850
849
  }
851
850
  else {
852
- this.scopes.at(-1)?.set(only, result);
851
+ this.scopes.at(-1)?.set(name, result);
853
852
  }
854
853
  }
855
854
  return result;
@@ -861,7 +860,7 @@ class Execution {
861
860
  this.write(node.text);
862
861
  break;
863
862
  case "action": {
864
- const value = this.pipeline(node.pipe, dot, true);
863
+ const value = this.pipeline(node.pipe, dot);
865
864
  if (node.pipe.vars.length === 0) {
866
865
  this.write(formatted(value, false));
867
866
  }
@@ -874,7 +873,7 @@ class Execution {
874
873
  case "with": {
875
874
  this.scopes.push(new Map());
876
875
  try {
877
- const value = this.pipeline(node.pipe, dot, true);
876
+ const value = this.pipeline(node.pipe, dot);
878
877
  const taken = truthy(value);
879
878
  this.run(taken ? node.list : node.otherwise, taken && node.k === "with" ? value : dot);
880
879
  }
@@ -892,7 +891,9 @@ class Execution {
892
891
  range(node, dot) {
893
892
  this.scopes.push(new Map());
894
893
  try {
895
- const value = this.pipeline({ ...node.pipe, vars: [] }, dot, false);
894
+ // Go's `evalPipeline` gives every variable the range declares or assigns the whole value first:
895
+ // https://github.com/golang/go/blob/go1.27.2/src/text/template/exec.go#L525
896
+ const value = this.pipeline(node.pipe, dot);
896
897
  let entries;
897
898
  if (value === null) {
898
899
  entries = [];
@@ -913,14 +914,22 @@ class Execution {
913
914
  return;
914
915
  }
915
916
  const [first, second] = node.pipe.vars;
917
+ const scope = this.scopes.at(-1);
918
+ const bind = (name, held) => {
919
+ if (node.pipe.assign) {
920
+ this.assign(name, held);
921
+ }
922
+ else {
923
+ scope?.set(name, held);
924
+ }
925
+ };
916
926
  for (const [key, element] of entries) {
917
- const scope = this.scopes.at(-1);
918
927
  if (second !== undefined && first !== undefined) {
919
- scope?.set(first, key);
920
- scope?.set(second, element);
928
+ bind(first, key);
929
+ bind(second, element);
921
930
  }
922
931
  else if (first !== undefined) {
923
- scope?.set(first, element);
932
+ bind(first, element);
924
933
  }
925
934
  try {
926
935
  this.run(node.list, element);
@@ -567,6 +567,7 @@ function readDoc(value, label) {
567
567
  injectionRegistrations: readDeclarations(ts, "injection_registrations", "ts.injection_registrations", label),
568
568
  lifecycleContracts: readLifecycleContracts(ts, label),
569
569
  serializers: readDeclarations(ts, "serializers", "ts.serializers", label),
570
+ rootsComplete: readBoolean(ts, "roots_complete", "ts.roots_complete", label),
570
571
  };
571
572
  }
572
573
  /** Parses one document's text and walks it against the closed key list. */
@@ -776,6 +777,7 @@ function settleAll(inputs) {
776
777
  injectionRegistrations: at("ts.injection_registrations", defaults.ts.injectionRegistrations, (doc) => doc.injectionRegistrations),
777
778
  lifecycleContracts: at("ts.lifecycle_contracts", defaults.ts.lifecycleContracts, (doc) => doc.lifecycleContracts),
778
779
  serializers: at("ts.serializers", defaults.ts.serializers, (doc) => doc.serializers),
780
+ rootsComplete: at("ts.roots_complete", defaults.ts.rootsComplete, (doc) => doc.rootsComplete),
779
781
  },
780
782
  };
781
783
  return { config, provenance };