@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/README.md +1 -1
- package/dist/src/analysis.js +34 -5
- package/dist/src/augmentations.js +1 -1
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/contextual-uses.js +56 -9
- package/dist/src/convention-rows.js +1 -1
- package/dist/src/dependencies.js +22 -14
- package/dist/src/discover.js +14 -82
- package/dist/src/exempt.js +8 -0
- package/dist/src/findings/completion.js +4 -2
- package/dist/src/kinds.js +1 -1
- package/dist/src/query.js +1 -0
- package/dist/src/reporters/annotations.js +15 -8
- package/dist/src/roots.js +69 -41
- package/dist/src/serialization-contract.js +337 -9
- package/dist/src/verbs/analyze.js +0 -1
- package/dist/src/workflow-steps.js +32 -15
- package/jsr.json +1 -1
- package/package.json +3 -3
- package/src/analysis.ts +64 -5
- package/src/augmentations.ts +1 -1
- package/src/conformance-record.ts +1 -1
- package/src/contextual-uses.ts +64 -12
- package/src/convention-rows.ts +1 -1
- package/src/dependencies.ts +27 -14
- package/src/discover.ts +14 -94
- package/src/exempt.ts +16 -0
- package/src/findings/boundary.ts +2 -0
- package/src/findings/completion.ts +4 -2
- package/src/kinds.ts +1 -1
- package/src/query.ts +11 -0
- package/src/reporters/annotations.ts +21 -10
- package/src/reporters/reporter.ts +1 -3
- package/src/roots.ts +73 -43
- package/src/serialization-contract.ts +455 -11
- package/src/sweep.ts +2 -0
- package/src/verbs/analyze.ts +0 -1
- package/src/workflow-steps.ts +35 -17
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 {
|
|
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:
|
|
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
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
684
|
-
*
|
|
685
|
-
*
|
|
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
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
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
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
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
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
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
|
-
|
|
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
|
|