@cplieger/deadset-ts 8.0.0-dev.20 → 8.0.0-dev.22

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 (72) hide show
  1. package/dist/src/alias-chain.js +33 -2
  2. package/dist/src/analysis.js +18 -77
  3. package/dist/src/conformance-record.js +2 -2
  4. package/dist/src/contract/contract.generated.js +5 -4
  5. package/dist/src/dependencies.js +1 -1
  6. package/dist/src/exemptions/argument-places.js +131 -0
  7. package/dist/src/{calls.js → exemptions/calls.js} +126 -179
  8. package/dist/src/{exempt-classes.js → exemptions/classes.js} +1 -1
  9. package/dist/src/{decorator.js → exemptions/decorator.js} +19 -30
  10. package/dist/src/exemptions/detectors.js +48 -0
  11. package/dist/src/{enum-group.js → exemptions/enum-group.js} +7 -7
  12. package/dist/src/{erased-stores.js → exemptions/erased-stores.js} +4 -6
  13. package/dist/src/{framework-lifecycle.js → exemptions/framework-lifecycle.js} +31 -36
  14. package/dist/src/{generated-file.js → exemptions/generated-file.js} +4 -4
  15. package/dist/src/{injection-container.js → exemptions/injection-container.js} +19 -25
  16. package/dist/src/exemptions/installed-globals.js +173 -0
  17. package/dist/src/{interface-satisfaction.js → exemptions/interface-satisfaction.js} +52 -121
  18. package/dist/src/exemptions/lifecycle.js +150 -0
  19. package/dist/src/{reflective-lookup.js → exemptions/reflective-lookup.js} +3 -3
  20. package/dist/src/{serialization-contract.js → exemptions/serialization-contract.js} +415 -329
  21. package/dist/src/exemptions/sites.js +182 -0
  22. package/dist/src/{template-field.js → exemptions/template-field.js} +7 -7
  23. package/dist/src/{value-reach.js → exemptions/value-reach.js} +14 -7
  24. package/dist/src/findings/intra-function.js +50 -37
  25. package/dist/src/index/sites.js +1 -1
  26. package/dist/src/index/values.js +124 -0
  27. package/dist/src/intra-function-parts.js +1149 -222
  28. package/dist/src/liveness/sweep.js +2 -2
  29. package/dist/src/query.js +48 -0
  30. package/dist/src/roots/configured-declarations.js +1 -1
  31. package/dist/src/verbs/print-retained.js +23 -1
  32. package/jsr.json +1 -1
  33. package/package.json +2 -2
  34. package/src/alias-chain.ts +38 -2
  35. package/src/analysis.ts +19 -100
  36. package/src/bindings.ts +1 -1
  37. package/src/conformance-record.ts +2 -2
  38. package/src/contract/contract.generated.ts +5 -4
  39. package/src/dependencies.ts +1 -1
  40. package/src/exemptions/argument-places.ts +198 -0
  41. package/src/{calls.ts → exemptions/calls.ts} +190 -224
  42. package/src/{exempt-classes.ts → exemptions/classes.ts} +1 -1
  43. package/src/exemptions/decorator.ts +56 -0
  44. package/src/exemptions/detectors.ts +75 -0
  45. package/src/{enum-group.ts → exemptions/enum-group.ts} +9 -9
  46. package/src/{erased-stores.ts → exemptions/erased-stores.ts} +5 -8
  47. package/src/{framework-lifecycle.ts → exemptions/framework-lifecycle.ts} +39 -48
  48. package/src/{generated-file.ts → exemptions/generated-file.ts} +6 -6
  49. package/src/{injection-container.ts → exemptions/injection-container.ts} +28 -33
  50. package/src/exemptions/installed-globals.ts +255 -0
  51. package/src/{interface-satisfaction.ts → exemptions/interface-satisfaction.ts} +69 -160
  52. package/src/exemptions/lifecycle.ts +286 -0
  53. package/src/{reflective-lookup.ts → exemptions/reflective-lookup.ts} +5 -5
  54. package/src/{serialization-contract.ts → exemptions/serialization-contract.ts} +563 -429
  55. package/src/exemptions/sites.ts +293 -0
  56. package/src/{template-field.ts → exemptions/template-field.ts} +11 -11
  57. package/src/{value-reach.ts → exemptions/value-reach.ts} +16 -8
  58. package/src/facts.ts +1 -1
  59. package/src/findings/intra-function.ts +59 -37
  60. package/src/index/sites.ts +1 -1
  61. package/src/index/values.ts +170 -0
  62. package/src/intra-function-parts.ts +1435 -247
  63. package/src/liveness/sweep.ts +2 -2
  64. package/src/query.ts +109 -10
  65. package/src/roots/configured-declarations.ts +1 -2
  66. package/src/stores.ts +1 -1
  67. package/src/verbs/print-retained.ts +29 -1
  68. package/dist/src/exempt.js +0 -164
  69. package/dist/src/value-flow.js +0 -74
  70. package/src/decorator.ts +0 -78
  71. package/src/exempt.ts +0 -295
  72. package/src/value-flow.ts +0 -104
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Which parameter place each argument of a call binds, the one reading every question
3
+ * about a call's arguments asks. A plain argument fills the next place, and a spread or
4
+ * an `apply` list typed as a tuple whose elements are all required fills one place per
5
+ * element. Any other spread or list has no known length: from its first place on, each of
6
+ * its element types, and every later argument, may fill any place, so a value is never
7
+ * read as missing a parameter only because its place could not be read. The type at each
8
+ * place is the checker's answer for it, so this reading names places and never types them.
9
+ */
10
+
11
+ import { isSpreadElement, type Expression } from "@typescript/native/ast";
12
+ import { ElementFlags, type Signature, type Type } from "@typescript/native/sync";
13
+ import { must } from "../query.ts";
14
+ import type { ProjectView } from "../session.ts";
15
+
16
+ /** The places one argument fills. */
17
+ export interface Span {
18
+ /** The argument's index among the invocation's arguments. */
19
+ readonly argument: number;
20
+ /** The first place it can fill. */
21
+ readonly first: number;
22
+ /** The type at each place from `first` on, or, where `open`, at any place from it on. */
23
+ readonly given: readonly (Type | undefined)[];
24
+ /** Whether the argument's length, or the length of a spread before it, is unknown. */
25
+ readonly open: boolean;
26
+ /** Whether the argument is a spread or a list, whose value is its elements. */
27
+ readonly spread: boolean;
28
+ }
29
+
30
+ /** One argument at one parameter place, with the type it gives there. */
31
+ export interface Placed {
32
+ readonly argument: number;
33
+ /** The index in the argument's span `given` of the type it gives there. */
34
+ readonly element: number;
35
+ readonly place: number;
36
+ readonly given: Type | undefined;
37
+ /** Whether the signature has no such place, so it says nothing of what the argument keeps. */
38
+ readonly uncovered: boolean;
39
+ /**
40
+ * Whether it stands for every place of a rest from `place` on, an argument of unknown
41
+ * length there taking any type the rest's element type holds.
42
+ */
43
+ readonly rest: boolean;
44
+ }
45
+
46
+ /**
47
+ * How many places a signature has. Where it `repeats`, its rest has no fixed length and
48
+ * `count` ends at the rest's first place: the checker answers each place past it, and an
49
+ * argument of unknown length meets every one of them.
50
+ */
51
+ export interface Layout {
52
+ readonly count: number;
53
+ readonly repeats: boolean;
54
+ }
55
+
56
+ /** The element flags of a tuple type; none for another type. */
57
+ function elementFlags<Brand>(project: ProjectView<Brand>, type: Type): readonly ElementFlags[] {
58
+ const target = type.isTupleType() ? must(project.queries.targetOf(type)) : undefined;
59
+ return target?.isTupleTypeTarget() === true ? target.elementFlags : [];
60
+ }
61
+
62
+ /** What a spread argument puts at the places it fills, and how many it fills at least. */
63
+ function spread<Brand>(
64
+ project: ProjectView<Brand>,
65
+ type: Type | undefined,
66
+ ): {
67
+ readonly types: readonly (Type | undefined)[];
68
+ readonly fixed: boolean;
69
+ readonly least: number;
70
+ } {
71
+ const { queries } = project;
72
+ if (type === undefined) {
73
+ return { types: [undefined], fixed: false, least: 0 };
74
+ }
75
+ if (type.isTupleType()) {
76
+ const flags = elementFlags(project, type);
77
+ return {
78
+ types: must(queries.typeArguments(type)).slice(0, flags.length),
79
+ fixed: flags.every((flag) => (flag & ElementFlags.Required) !== 0),
80
+ least: flags.filter((flag) => (flag & ElementFlags.Required) !== 0).length,
81
+ };
82
+ }
83
+ if (must(queries.isArray(type))) {
84
+ return { types: must(queries.typeArguments(type)), fixed: false, least: 0 };
85
+ }
86
+ // Another iterable's elements are read off its type arguments, as `Set<T>` holds `T`.
87
+ return { types: [type, ...must(queries.typeArguments(type))], fixed: false, least: 0 };
88
+ }
89
+
90
+ /** The element types of a tuple of `count` required elements; none for another type. */
91
+ export function knownElements<Brand>(
92
+ project: ProjectView<Brand>,
93
+ type: Type,
94
+ count: number,
95
+ ): readonly (Type | undefined)[] | undefined {
96
+ if (!type.isTupleType()) {
97
+ return undefined;
98
+ }
99
+ const elements = spread(project, type);
100
+ return elements.fixed && elements.types.length === count ? elements.types : undefined;
101
+ }
102
+
103
+ /**
104
+ * The places each argument fills, `types` holding the type of each argument's value; it
105
+ * may be empty where no argument is a spread and no given type is read. `listed` reads
106
+ * the last argument as a list whose elements the places bind.
107
+ */
108
+ export function spansOf<Brand>(
109
+ project: ProjectView<Brand>,
110
+ args: readonly Expression[],
111
+ listed: boolean,
112
+ types: readonly (Type | undefined)[],
113
+ ): readonly Span[] {
114
+ const spans: Span[] = [];
115
+ let place = 0;
116
+ let open = false;
117
+ for (const [at, argument] of args.entries()) {
118
+ const type = types[at];
119
+ if (!isSpreadElement(argument) && !(listed && at === args.length - 1)) {
120
+ spans.push({ argument: at, first: place, given: [type], open, spread: false });
121
+ place += 1;
122
+ continue;
123
+ }
124
+ const elements = spread(project, type);
125
+ open ||= !elements.fixed;
126
+ spans.push({ argument: at, first: place, given: elements.types, open, spread: true });
127
+ place += elements.least;
128
+ }
129
+ return spans;
130
+ }
131
+
132
+ /**
133
+ * The places of one signature as the checker counts them: one per parameter, and in
134
+ * place of a rest as many as the `length` the checker answers for its type, where that
135
+ * is a number literal or a union of them. Any other `length`, an array's or an array-like
136
+ * interface's `number` among them, leaves the rest open.
137
+ */
138
+ export function layoutOf<Brand>(project: ProjectView<Brand>, signature: Signature): Layout {
139
+ const { queries } = project;
140
+ const fixed = signature.parameters.length;
141
+ const rest = must(queries.restTypeOf(signature));
142
+ if (rest === undefined) {
143
+ return { count: fixed, repeats: false };
144
+ }
145
+ const length = must(queries.propertyTypeOf(rest, "length"));
146
+ const lengths = length === undefined ? [] : must(queries.constituents(length));
147
+ const bounds = lengths.flatMap((one) => (one.isNumberLiteralType() ? [one.value] : []));
148
+ if (bounds.length > 0 && bounds.length === lengths.length) {
149
+ return { count: fixed - 1 + Math.max(...bounds), repeats: false };
150
+ }
151
+ return { count: fixed, repeats: true };
152
+ }
153
+
154
+ /**
155
+ * The parameter place each argument binds in a signature of `layout`, with the type it
156
+ * gives there. A span of unknown length binds every place from its first to the last the
157
+ * layout counts, and past them, where the rest is open, one `rest` placement for every
158
+ * later place, or else its first place alone. A place past every place of a layout that
159
+ * does not repeat is `uncovered`.
160
+ */
161
+ export function placesIn(spans: readonly Span[], layout: Layout): readonly Placed[] {
162
+ const placed: Placed[] = [];
163
+ const add = (span: Span, place: number, rest: boolean): void => {
164
+ const uncovered = place >= layout.count && !layout.repeats;
165
+ const elements = span.open
166
+ ? span.given.map((given, element) => ({ given, element }))
167
+ : [{ given: span.given[place - span.first], element: place - span.first }];
168
+ placed.push(
169
+ ...elements.map((one) => ({ argument: span.argument, place, uncovered, rest, ...one })),
170
+ );
171
+ };
172
+ for (const span of spans) {
173
+ const open = span.open && layout.repeats;
174
+ const end = !span.open
175
+ ? span.first + span.given.length
176
+ : Math.max(layout.count, open ? span.first : span.first + 1);
177
+ for (let place = span.first; place < end; place += 1) {
178
+ add(span, place, false);
179
+ }
180
+ if (open) {
181
+ add(span, Math.max(layout.count, span.first), true);
182
+ }
183
+ }
184
+ return placed;
185
+ }
186
+
187
+ /**
188
+ * Whether a span fills the parameter declared at `index`, a rest parameter every place
189
+ * from it on; with `element`, whether that element of a span that is not open does.
190
+ */
191
+ export function fills(span: Span, index: number, rest: boolean, element?: number): boolean {
192
+ if (element !== undefined && !span.open) {
193
+ const place = span.first + element;
194
+ return rest ? place >= index : place === index;
195
+ }
196
+ const last = span.open ? Number.POSITIVE_INFINITY : span.first + span.given.length - 1;
197
+ return rest ? last >= index : span.first <= index && index <= last;
198
+ }