@cplieger/deadset-ts 8.0.0-dev.3 → 8.0.0-dev.5

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.
package/src/query.ts CHANGED
@@ -211,6 +211,8 @@ export interface Queries {
211
211
  symbolOfType(type: Type): Answer<TSSymbol | undefined>;
212
212
  signaturesOf(type: Type, kind: SignatureKind): Answer<readonly Signature[]>;
213
213
  resolvedSignature(node: Node): Answer<Signature | undefined>;
214
+ /** The signature each call-like node resolves to. */
215
+ resolvedSignatures(nodes: readonly Node[], cap?: number): Answer<Signature | undefined>[];
214
216
  parameterType(signature: Signature, index: number): Answer<Type | undefined>;
215
217
  /** The generic signature an instantiated one instantiates; none for another signature. */
216
218
  genericOf(signature: Signature): Answer<Signature | undefined>;
@@ -544,6 +546,15 @@ export function queriesOf(
544
546
  () => nodeLocation(node),
545
547
  () => checker.getResolvedSignature(node),
546
548
  ),
549
+ resolvedSignatures: (nodes, cap = DEFAULT_BATCH_CAP) =>
550
+ each(
551
+ "getResolvedSignature",
552
+ nodes,
553
+ cap,
554
+ nodeLocation,
555
+ (node) => checker.getResolvedSignature.gen(node),
556
+ (node) => checker.getResolvedSignature(node),
557
+ ),
547
558
  parameterType: (signature, index) =>
548
559
  one(
549
560
  "getParameterType",
@@ -1,8 +1,7 @@
1
- import { fails } from "../config.ts";
1
+ import type { Severity } from "../config.ts";
2
2
  import { coalesced } from "../json-chunks.ts";
3
3
  import { KINDS } from "../kinds.ts";
4
4
  import type { Report } from "../report.ts";
5
- import type { RenderOptions } from "./reporter.ts";
6
5
 
7
6
  /** The escapes a workflow command's message needs, so a line break does not end the command. */
8
7
  const DATA_ESCAPES: Readonly<Record<string, string>> = { "%": "%25", "\r": "%0D", "\n": "%0A" };
@@ -27,8 +26,18 @@ function kindName(code: string): string {
27
26
  return KINDS.get(code)?.name ?? code;
28
27
  }
29
28
 
29
+ /** The workflow command a record is written as. */
30
+ type Level = "error" | "warning" | "notice";
31
+
32
+ /** The command each finding severity is annotated with, whatever the failing severity. */
33
+ const LEVELS: Readonly<Record<Severity, Level>> = {
34
+ deny: "error",
35
+ warn: "warning",
36
+ allow: "notice",
37
+ };
38
+
30
39
  function command(
31
- level: "error" | "warning",
40
+ level: Level,
32
41
  at: { readonly path: string; readonly line: number; readonly column: number },
33
42
  endLine: number,
34
43
  title: string,
@@ -43,18 +52,17 @@ const SKIP_MESSAGE =
43
52
  "the analysis did not evaluate the function or statement holding this type error: ";
44
53
 
45
54
  /**
46
- * One workflow annotation per finding, then one per stale suppression, each naming its
47
- * file, line, column and last line, titled by its code and kind, then one warning per
48
- * type-error skip naming its file and line. A finding at or above the failing severity is
49
- * an error and one below it a warning; a stale suppression is an error, because its kind
50
- * is fixed on at the failing severity.
55
+ * The annotations `contract/grammar/annotations.md` states: one per finding, at the
56
+ * command its severity names, and one error per stale suppression, each naming its file,
57
+ * line, column and last line and titled by its code and kind; then one warning per
58
+ * type-error skip naming its file and line, and one per note naming its directory.
51
59
  */
52
- export function annotations(report: Report, options: RenderOptions): Iterable<string> {
60
+ export function annotations(report: Report): Iterable<string> {
53
61
  return coalesced(
54
62
  (function* lines(): Generator<string, void, undefined> {
55
63
  for (const found of report.findings) {
56
64
  yield `${command(
57
- fails(found.severity, options.failOn) ? "error" : "warning",
65
+ LEVELS[found.severity],
58
66
  found.position,
59
67
  found.position.end_line,
60
68
  `${found.code} ${found.kind}`,
@@ -73,6 +81,9 @@ export function annotations(report: Report, options: RenderOptions): Iterable<st
73
81
  for (const skip of report.type_error_skips) {
74
82
  yield `::warning file=${escapeProperty(skip.path)},line=${String(skip.line)},title=${escapeProperty(SKIP_TITLE)}::${escapeData(SKIP_MESSAGE + skip.message)}\n`;
75
83
  }
84
+ for (const note of report.notes) {
85
+ yield `::warning file=${escapeProperty(note.path)},title=${escapeProperty(note.kind)}::${escapeData(note.message)}\n`;
86
+ }
76
87
  })(),
77
88
  );
78
89
  }
@@ -1,11 +1,9 @@
1
- import type { Format, Severity } from "../config.ts";
1
+ import type { Format } from "../config.ts";
2
2
  import type { Report } from "../report.ts";
3
3
  import type { Template } from "./template.ts";
4
4
 
5
5
  /** What a rendering reads beside the report. */
6
6
  export interface RenderOptions {
7
- /** The lowest severity at which a finding fails the run. */
8
- readonly failOn: Severity;
9
7
  /** The text of the file at one path below the target root, or a throw naming why not. */
10
8
  readonly readSource: (path: string) => string;
11
9
  /** The template the template rendering renders, where one was named. */
package/src/roots.ts CHANGED
@@ -34,6 +34,7 @@ import {
34
34
  type SourceFile,
35
35
  } from "@typescript/native/ast";
36
36
  import { aliasChains } from "./alias-chain.ts";
37
+ import { isGlobalBlock } from "./augmentations.ts";
37
38
  import {
38
39
  filesNamedBy,
39
40
  outsideChain,
@@ -680,9 +681,11 @@ const EXPORTING_CONTAINERS: ReadonlySet<string> = new Set(["file", "namespace"])
680
681
  * Every declaration a consumer of the library can name: what the published files export,
681
682
  * and below each the members a consumer writes, a namespace's exported ones and any other
682
683
  * declaration's public and protected ones. A declaration a published one's type names is
683
- * published too, and so is an exported declaration a release tag marks. A type name the
684
- * checker leaves unanswered publishes every declaration spelled as it is, as `guessed`. A
685
- * published file is not itself published.
684
+ * published too, and so is an exported declaration a release tag marks, and every
685
+ * declaration a `declare global` block of a published file, or of a file holding a published
686
+ * declaration, holds: the consumer's compiler loads that file's typings and their global
687
+ * scope with it. A type name the checker leaves unanswered publishes every declaration
688
+ * spelled as it is, as `guessed`. A published file is not itself published.
686
689
  */
687
690
  function publishedAPI<Brand>(
688
691
  project: ProjectView<Brand>,
@@ -715,57 +718,84 @@ function publishedAPI<Brand>(
715
718
  reach(id);
716
719
  }
717
720
  }
718
- while (pending.length > 0) {
719
- const round: string[] = [];
720
- for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
721
- round.push(next);
722
- const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
723
- for (const member of children.get(next) ?? []) {
724
- const reachable = exporting
725
- ? member.exported
726
- : member.visibility !== "private" && member.visibility !== "private-name";
727
- if (member.kind !== "type-parameter" && reachable) {
721
+ const files = new Set(held.symbols.filter((one) => one.kind === "file").map((one) => one.id));
722
+ const blocks = held.symbols.filter((one) => isGlobalBlock(one, held, files));
723
+ const fileOf = (id: string): string | undefined => {
724
+ let at = byId.get(id);
725
+ while (at !== undefined && at.kind !== "file") {
726
+ at = byId.get(at.parent);
727
+ }
728
+ return at?.id;
729
+ };
730
+ const publishedFiles = published.flatMap(
731
+ (file) => held.declarations.get(nodeKey(file, file)) ?? [],
732
+ );
733
+ const globalsReached = (): boolean => {
734
+ const typed = new Set([...publishedFiles, ...[...reached].flatMap((id) => fileOf(id) ?? [])]);
735
+ const before = pending.length;
736
+ for (const block of blocks.filter((one) => typed.has(one.parent))) {
737
+ reach(block.id);
738
+ for (const member of children.get(block.id) ?? []) {
739
+ if (member.kind !== "type-parameter") {
728
740
  reach(member.id);
729
741
  }
730
742
  }
731
743
  }
732
- const named = round.flatMap((id) => {
733
- const symbol = byId.get(id);
734
- return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
735
- });
736
- const inferred = inferredTypeSymbols(
737
- project,
738
- round.flatMap((id) => {
739
- const symbol = byId.get(id);
740
- return symbol === undefined ? [] : nodes.nodesOf(symbol);
741
- }),
742
- );
743
- for (const symbol of inferred.types) {
744
- for (const target of chains.chainOf(symbol)) {
745
- reach(target.id);
744
+ return pending.length > before;
745
+ };
746
+ do {
747
+ while (pending.length > 0) {
748
+ const round: string[] = [];
749
+ for (let next = pending.shift(); next !== undefined; next = pending.shift()) {
750
+ round.push(next);
751
+ const exporting = EXPORTING_CONTAINERS.has(byId.get(next)?.kind ?? "");
752
+ for (const member of children.get(next) ?? []) {
753
+ const reachable = exporting
754
+ ? member.exported
755
+ : member.visibility !== "private" && member.visibility !== "private-name";
756
+ if (member.kind !== "type-parameter" && reachable) {
757
+ reach(member.id);
758
+ }
759
+ }
746
760
  }
747
- }
748
- for (const symbol of inferred.members) {
749
- for (const target of chains.chainOf(symbol)) {
750
- const member = byId.get(target.id);
751
- if (member?.visibility === "public" || member?.visibility === "protected") {
761
+ const named = round.flatMap((id) => {
762
+ const symbol = byId.get(id);
763
+ return symbol === undefined || symbol.kind === "file" ? [] : nodes.typeNamesOf(symbol);
764
+ });
765
+ const inferred = inferredTypeSymbols(
766
+ project,
767
+ round.flatMap((id) => {
768
+ const symbol = byId.get(id);
769
+ return symbol === undefined ? [] : nodes.nodesOf(symbol);
770
+ }),
771
+ );
772
+ for (const symbol of inferred.types) {
773
+ for (const target of chains.chainOf(symbol)) {
752
774
  reach(target.id);
753
775
  }
754
776
  }
755
- }
756
- project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
757
- if (symbol === UNANSWERED) {
758
- for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
759
- guessed.add(id);
760
- reach(id);
761
- }
762
- } else if (symbol !== undefined) {
777
+ for (const symbol of inferred.members) {
763
778
  for (const target of chains.chainOf(symbol)) {
764
- reach(target.id);
779
+ const member = byId.get(target.id);
780
+ if (member?.visibility === "public" || member?.visibility === "protected") {
781
+ reach(target.id);
782
+ }
765
783
  }
766
784
  }
767
- });
768
- }
785
+ project.symbolsAt(named.map((node) => project.handle(node))).forEach((symbol, index) => {
786
+ if (symbol === UNANSWERED) {
787
+ for (const id of held.byName.get(named[index]?.getText() ?? "") ?? []) {
788
+ guessed.add(id);
789
+ reach(id);
790
+ }
791
+ } else if (symbol !== undefined) {
792
+ for (const target of chains.chainOf(symbol)) {
793
+ reach(target.id);
794
+ }
795
+ }
796
+ });
797
+ }
798
+ } while (globalsReached());
769
799
  return { ids: [...reached], guessed: [...guessed] };
770
800
  }
771
801