@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.
- package/dist/src/alias-chain.js +0 -5
- package/dist/src/amd.js +81 -0
- package/dist/src/analysis.js +82 -91
- package/dist/src/augmentations.js +24 -25
- package/dist/src/bindings.js +382 -0
- package/dist/src/calls.js +279 -43
- package/dist/src/commands/tools.js +5 -9
- package/dist/src/component-files.js +3 -2
- package/dist/src/conformance-record.js +1 -1
- package/dist/src/consumers.js +6 -9
- package/dist/src/contextual-uses.js +1 -17
- package/dist/src/contract/config.generated.js +4 -1
- package/dist/src/contract/contract.generated.js +2 -2
- package/dist/src/contract/report.generated.js +6 -2
- package/dist/src/dependencies.js +25 -29
- package/dist/src/destructuring.js +1 -19
- package/dist/src/element-access.js +179 -164
- package/dist/src/entries/build-scripts.js +2 -2
- package/dist/src/entries/configuration-files.js +3 -7
- package/dist/src/entries/conventions.js +2 -2
- package/dist/src/entries/html-entries.js +7 -10
- package/dist/src/entries/mdx-entries.js +2 -2
- package/dist/src/entries/static-module.js +2 -1
- package/dist/src/exempt.js +0 -26
- package/dist/src/exposure.js +400 -0
- package/dist/src/facts.js +4 -4
- package/dist/src/findings/completion.js +44 -21
- package/dist/src/findings/emitters.js +22 -33
- package/dist/src/{implementations.js → findings/implementations.js} +3 -3
- package/dist/src/{in-operands.js → findings/in-operands.js} +2 -2
- package/dist/src/findings/intra-function.js +3 -3
- package/dist/src/findings/reads-and-writes.js +71 -15
- package/dist/src/findings/unused-declarations.js +2 -22
- package/dist/src/findings/visibility-narrowing.js +14 -20
- package/dist/src/framework-lifecycle.js +41 -14
- package/dist/src/import-bindings.js +73 -28
- package/dist/src/index/sites.js +557 -0
- package/dist/src/indexed-access.js +523 -478
- package/dist/src/interface-satisfaction.js +1 -1
- package/dist/src/inventory.js +18 -0
- package/dist/src/keyof-comparisons.js +124 -103
- package/dist/src/{components.js → liveness/components.js} +1 -1
- package/dist/src/{graph.js → liveness/graph.js} +45 -20
- package/dist/src/liveness/matrix.js +99 -0
- package/dist/src/liveness/sweep.js +558 -0
- package/dist/src/load/expected-module.js +2 -2
- package/dist/src/load/specifiers.js +2 -4
- package/dist/src/load/synthesize.js +5 -0
- package/dist/src/load/workspace-resolution.js +2 -2
- package/dist/src/module-declarations.js +70 -85
- package/dist/src/overrides.js +134 -112
- package/dist/src/paths.js +46 -7
- package/dist/src/platform-calls.js +2 -2
- package/dist/src/platform-symbols.js +13 -19
- package/dist/src/query.js +46 -0
- package/dist/src/references/read-rule.js +1 -0
- package/dist/src/references/read-rules.js +48 -0
- package/dist/src/references/sink.js +237 -0
- package/dist/src/references/sources.js +450 -0
- package/dist/src/references.js +110 -1285
- package/dist/src/roots/configured-declarations.js +3 -0
- package/dist/src/roots/published-reach.js +4 -4
- package/dist/src/roots/root-set.js +1 -1
- package/dist/src/roots/unseen-loads.js +38 -10
- package/dist/src/serialization-contract.js +8 -3
- package/dist/src/stores.js +4 -2
- package/dist/src/symbol-keys.js +89 -83
- package/dist/src/tests/file-class.js +76 -0
- package/dist/src/{outside-tests.js → tests/outside.js} +7 -7
- package/dist/src/{test-support.js → tests/support.js} +2 -11
- package/dist/src/type-parameter-reads.js +178 -139
- package/dist/src/unimplemented-callees.js +262 -242
- package/dist/src/verbs/analyze.js +3 -3
- package/dist/src/verbs/explain.js +75 -132
- package/dist/src/verbs/print-retained.js +3 -3
- package/jsr.json +1 -1
- package/package.json +2 -2
- package/src/alias-chain.ts +0 -7
- package/src/amd.ts +130 -0
- package/src/analysis.ts +125 -128
- package/src/augmentations.ts +42 -28
- package/src/bindings.ts +460 -0
- package/src/calls.ts +338 -46
- package/src/commands/tools.ts +5 -11
- package/src/component-files.ts +4 -2
- package/src/conformance-record.ts +1 -1
- package/src/consumers.ts +8 -11
- package/src/contextual-uses.ts +2 -22
- package/src/contract/config.generated.ts +4 -1
- package/src/contract/contract.generated.ts +2 -2
- package/src/contract/report.generated.ts +6 -2
- package/src/dependencies.ts +27 -28
- package/src/destructuring.ts +1 -31
- package/src/element-access.ts +188 -182
- package/src/entries/build-scripts.ts +2 -2
- package/src/entries/configuration-files.ts +11 -8
- package/src/entries/conventions.ts +2 -1
- package/src/entries/html-entries.ts +15 -14
- package/src/entries/mdx-entries.ts +2 -2
- package/src/entries/static-module.ts +2 -1
- package/src/exempt.ts +31 -46
- package/src/exposure.ts +492 -0
- package/src/facts.ts +5 -3
- package/src/finding.ts +1 -1
- package/src/findings/boundary.ts +9 -0
- package/src/findings/completion.ts +48 -23
- package/src/findings/emitter.ts +1 -1
- package/src/findings/emitters.ts +23 -41
- package/src/{implementations.ts → findings/implementations.ts} +4 -4
- package/src/{in-operands.ts → findings/in-operands.ts} +3 -3
- package/src/findings/interfaces.ts +3 -3
- package/src/findings/intra-function.ts +3 -3
- package/src/findings/reads-and-writes.ts +99 -26
- package/src/findings/unused-declarations.ts +4 -28
- package/src/findings/visibility-narrowing.ts +25 -26
- package/src/framework-lifecycle.ts +42 -14
- package/src/import-bindings.ts +98 -29
- package/src/index/sites.ts +781 -0
- package/src/indexed-access.ts +570 -527
- package/src/interface-satisfaction.ts +2 -2
- package/src/inventory.ts +19 -0
- package/src/keyof-comparisons.ts +131 -116
- package/src/{components.ts → liveness/components.ts} +3 -3
- package/src/{graph.ts → liveness/graph.ts} +93 -27
- package/src/liveness/matrix.ts +162 -0
- package/src/liveness/sweep.ts +855 -0
- package/src/load/expected-module.ts +8 -2
- package/src/load/specifiers.ts +8 -5
- package/src/load/synthesize.ts +6 -0
- package/src/load/workspace-resolution.ts +9 -2
- package/src/module-declarations.ts +74 -98
- package/src/overrides.ts +162 -134
- package/src/paths.ts +54 -8
- package/src/platform-calls.ts +3 -3
- package/src/platform-symbols.ts +11 -27
- package/src/query.ts +81 -0
- package/src/references/read-rule.ts +60 -0
- package/src/references/read-rules.ts +55 -0
- package/src/references/sink.ts +314 -0
- package/src/references/sources.ts +632 -0
- package/src/references.ts +175 -1733
- package/src/report.ts +1 -1
- package/src/roots/configured-declarations.ts +3 -0
- package/src/roots/published-reach.ts +9 -4
- package/src/roots/root-set.ts +1 -1
- package/src/roots/unseen-loads.ts +49 -11
- package/src/serialization-contract.ts +8 -3
- package/src/stores.ts +15 -3
- package/src/symbol-keys.ts +102 -102
- package/src/tests/file-class.ts +135 -0
- package/src/{outside-tests.ts → tests/outside.ts} +16 -10
- package/src/{test-support.ts → tests/support.ts} +9 -20
- package/src/type-parameter-reads.ts +187 -158
- package/src/unimplemented-callees.ts +297 -280
- package/src/verbs/analyze.ts +3 -2
- package/src/verbs/explain.ts +83 -158
- package/src/verbs/print-retained.ts +3 -3
- package/dist/src/matrix.js +0 -273
- package/dist/src/sweep.js +0 -222
- package/src/matrix.ts +0 -463
- package/src/sweep.ts +0 -361
package/dist/src/calls.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The calls and decorators of one project that the configured classes read: what each
|
|
3
3
|
* callee resolves to, and which of the target's classes a call passes or a decorator
|
|
4
|
-
* is attached to. A class is passed
|
|
5
|
-
*
|
|
4
|
+
* is attached to. A class is passed by each value an argument can be, at any depth inside
|
|
5
|
+
* the array and object literals it writes, named directly or through variables, and a
|
|
6
6
|
* decorator written as a call is a call.
|
|
7
7
|
*/
|
|
8
|
-
import { isArrayLiteralExpression, isCallExpression, isClassDeclaration, isDecorator, isIdentifier, isObjectLiteralExpression, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isShorthandPropertyAssignment, isSpreadElement, } from "@typescript/native/ast";
|
|
9
|
-
import { SymbolFlags } from "@typescript/native/sync";
|
|
8
|
+
import { isArrayLiteralExpression, isArrowFunction, isBinaryExpression, isBindingElement, isCallExpression, isClassDeclaration, isConditionalExpression, isDecorator, isFunctionDeclaration, isFunctionExpression, isIdentifier, isMethodDeclaration, isObjectLiteralExpression, isParameterDeclaration, isParenthesizedExpression, isPropertyAccessExpression, isPropertyAssignment, isPropertyDeclaration, isShorthandPropertyAssignment, isOmittedExpression, isSpreadAssignment, isSpreadElement, isVariableDeclaration, SyntaxKind, } from "@typescript/native/ast";
|
|
9
|
+
import { ObjectFlags, SymbolFlags, } from "@typescript/native/sync";
|
|
10
10
|
import { nodeKey } from "./inventory.js";
|
|
11
11
|
import { must } from "./query.js";
|
|
12
|
+
import { unwrapped } from "./unwrap.js";
|
|
12
13
|
/** The name node a callee or a decorator expression resolves through, where it has one. */
|
|
13
14
|
export function calleeName(expression) {
|
|
14
15
|
if (isParenthesizedExpression(expression)) {
|
|
@@ -65,61 +66,296 @@ export function resolvedTargets(project, nodes) {
|
|
|
65
66
|
});
|
|
66
67
|
return targets;
|
|
67
68
|
}
|
|
68
|
-
/** The
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
if (isArrayLiteralExpression(expression)) {
|
|
75
|
-
for (const element of expression.elements) {
|
|
76
|
-
passedIn(isSpreadElement(element) ? element.expression : element, found);
|
|
77
|
-
}
|
|
78
|
-
return;
|
|
69
|
+
/** The declaration a shorthand property's value resolves to, an import followed to what it names. */
|
|
70
|
+
function shorthandTarget(project, node) {
|
|
71
|
+
const value = must(project.shorthandValueAt(project.handle(node)));
|
|
72
|
+
if (value === undefined || (value.flags & SymbolFlags.Alias) === 0) {
|
|
73
|
+
return value;
|
|
79
74
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
75
|
+
const aliased = must(project.queries.aliased(value));
|
|
76
|
+
return aliased.declarations.length === 0 ? undefined : aliased;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* The values one expression passes: each value it can be ({@link leavesOf}), or what an
|
|
80
|
+
* array or object literal it can be holds, at any depth.
|
|
81
|
+
*/
|
|
82
|
+
function passedIn(written, found) {
|
|
83
|
+
for (const expression of leavesOf(written)) {
|
|
84
|
+
if (isArrayLiteralExpression(expression)) {
|
|
85
|
+
for (const element of expression.elements) {
|
|
86
|
+
passedIn(element, found);
|
|
84
87
|
}
|
|
85
|
-
|
|
86
|
-
|
|
88
|
+
}
|
|
89
|
+
else if (isObjectLiteralExpression(expression)) {
|
|
90
|
+
for (const property of expression.properties) {
|
|
91
|
+
if (isPropertyAssignment(property)) {
|
|
92
|
+
passedIn(property.initializer, found);
|
|
93
|
+
}
|
|
94
|
+
else if (isSpreadAssignment(property)) {
|
|
95
|
+
passedIn(property.expression, found);
|
|
96
|
+
}
|
|
97
|
+
else if (isShorthandPropertyAssignment(property)) {
|
|
98
|
+
found.push({ site: property.name, name: property, shorthand: true });
|
|
99
|
+
}
|
|
87
100
|
}
|
|
88
101
|
}
|
|
89
|
-
|
|
102
|
+
else if (!isOmittedExpression(expression)) {
|
|
103
|
+
found.push({ site: expression, name: calleeName(expression), shorthand: false });
|
|
104
|
+
}
|
|
90
105
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
}
|
|
107
|
+
/** The operators whose value is one of their operands. */
|
|
108
|
+
const CHOOSING = new Set([
|
|
109
|
+
SyntaxKind.QuestionQuestionToken,
|
|
110
|
+
SyntaxKind.BarBarToken,
|
|
111
|
+
SyntaxKind.AmpersandAmpersandToken,
|
|
112
|
+
]);
|
|
113
|
+
/**
|
|
114
|
+
* The expressions one value can be: itself, or each branch of a conditional, each operand
|
|
115
|
+
* of `??`, `||` and `&&` and the last of a comma, inside any wrapper that changes no value.
|
|
116
|
+
* Each is read alone because the checker reduces a union by subtype, so the type at the
|
|
117
|
+
* whole drops a class structurally equal to, or a subtype of, another branch's.
|
|
118
|
+
*/
|
|
119
|
+
function leavesOf(written) {
|
|
120
|
+
const expression = unwrapped(isSpreadElement(written) ? written.expression : written);
|
|
121
|
+
if (isConditionalExpression(expression)) {
|
|
122
|
+
return [...leavesOf(expression.whenTrue), ...leavesOf(expression.whenFalse)];
|
|
94
123
|
}
|
|
124
|
+
if (isBinaryExpression(expression)) {
|
|
125
|
+
const operator = expression.operatorToken.kind;
|
|
126
|
+
if (CHOOSING.has(operator)) {
|
|
127
|
+
return [...leavesOf(expression.left), ...leavesOf(expression.right)];
|
|
128
|
+
}
|
|
129
|
+
if (operator === SyntaxKind.CommaToken) {
|
|
130
|
+
return leavesOf(expression.right);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return [expression];
|
|
95
134
|
}
|
|
96
135
|
/** The values one argument passes, a spread argument's included. */
|
|
97
|
-
|
|
136
|
+
function leavesPassed(argument) {
|
|
98
137
|
const found = [];
|
|
99
|
-
passedIn(
|
|
138
|
+
passedIn(argument, found);
|
|
100
139
|
return found;
|
|
101
140
|
}
|
|
141
|
+
/** The values one argument passes that name their declaration. */
|
|
142
|
+
export function valuesPassed(argument) {
|
|
143
|
+
return leavesPassed(argument).filter((value) => value.name !== undefined);
|
|
144
|
+
}
|
|
102
145
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
146
|
+
* What one passed value holds: the declaration its name resolves to, and the classes and
|
|
147
|
+
* functions the checker's type at it holds ({@link constructedAt}). `known` holds names
|
|
148
|
+
* already resolved in a batch.
|
|
149
|
+
*/
|
|
150
|
+
function heldBy(project, value, known) {
|
|
151
|
+
const { name } = value;
|
|
152
|
+
if (name === undefined) {
|
|
153
|
+
return { named: undefined, typed: constructedAt(project, value.site) };
|
|
154
|
+
}
|
|
155
|
+
if (value.shorthand) {
|
|
156
|
+
return { named: shorthandTarget(project, name), typed: [] };
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
named: known?.get(name) ?? resolvedTargets(project, [name]).get(name),
|
|
160
|
+
typed: constructedAt(project, value.site),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* The classes of the target each call passes, by the call: each class a value an argument
|
|
165
|
+
* can be holds as a constructor, as the checker types it. A name whose type erases the
|
|
166
|
+
* class is followed through what its declaration binds ({@link boundTo}) and, for a
|
|
167
|
+
* parameter, through what each call of its function passes at its place, so a class handed
|
|
168
|
+
* to a wrapper is passed. A class is used at each value that passes it. A class of another
|
|
169
|
+
* program passes nothing.
|
|
105
170
|
*/
|
|
106
171
|
export function classesPassed(project, held, chains, calls) {
|
|
107
|
-
const passed = new Map(calls.map((call) => [call, call.arguments.flatMap(
|
|
108
|
-
const
|
|
109
|
-
const targets = resolvedTargets(project, all.filter((value) => !value.shorthand).map((value) => value.name));
|
|
172
|
+
const passed = new Map(calls.map((call) => [call, call.arguments.flatMap(leavesPassed)]));
|
|
173
|
+
const targets = resolvedTargets(project, [...passed.values()].flat().flatMap((value) => (value.shorthand ? [] : (value.name ?? []))));
|
|
110
174
|
const classes = new Set(held.symbols.filter((symbol) => symbol.kind === "class").map((symbol) => symbol.id));
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
175
|
+
let calling;
|
|
176
|
+
const callsOfFunction = (symbol) => {
|
|
177
|
+
calling ??= callsByDeclaration(project);
|
|
178
|
+
return symbol.declarations.flatMap((handle) => calling?.get(keyOf(handle)) ?? []);
|
|
179
|
+
};
|
|
180
|
+
const classesOf = (value, seen) => {
|
|
181
|
+
const { named, typed } = heldBy(project, value, targets);
|
|
182
|
+
return [
|
|
183
|
+
...typed.flatMap((symbol) => chains.declarationsOf(symbol).filter((id) => classes.has(id))),
|
|
184
|
+
...(named === undefined ? [] : reached(named, seen)),
|
|
185
|
+
];
|
|
186
|
+
};
|
|
187
|
+
const reached = (symbol, seen) => {
|
|
188
|
+
const ids = chains.declarationsOf(symbol).filter((id) => classes.has(id));
|
|
189
|
+
if (ids.length > 0 || seen.has(symbol.id)) {
|
|
190
|
+
return ids;
|
|
191
|
+
}
|
|
192
|
+
seen.add(symbol.id);
|
|
193
|
+
const through = (written) => leavesPassed(written).flatMap((value) => classesOf(value, seen));
|
|
194
|
+
return symbol.declarations.flatMap((handle) => {
|
|
195
|
+
const node = project.declarationAt(handle)?.node;
|
|
196
|
+
if (node === undefined) {
|
|
197
|
+
return [];
|
|
198
|
+
}
|
|
199
|
+
if (isParameterDeclaration(node)) {
|
|
200
|
+
return argumentsAt(project, node, callsOfFunction).flatMap(through);
|
|
201
|
+
}
|
|
202
|
+
const bound = boundTo(project, node);
|
|
203
|
+
return [
|
|
204
|
+
...(bound.written === undefined ? [] : through(bound.written)),
|
|
205
|
+
...bound.symbols.flatMap((next) => reached(next, seen)),
|
|
206
|
+
];
|
|
207
|
+
});
|
|
118
208
|
};
|
|
119
|
-
return new Map([...passed].map(([call, values]) =>
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
209
|
+
return new Map([...passed].map(([call, values]) => {
|
|
210
|
+
const uses = new Map();
|
|
211
|
+
for (const value of values) {
|
|
212
|
+
for (const id of classesOf(value, new Set())) {
|
|
213
|
+
uses.set(`${id}\u0000${String(value.site.pos)}`, { id, site: value.site });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return [call, [...uses.values()]];
|
|
217
|
+
}));
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The symbols of the classes and functions the checker's type at one expression holds as
|
|
221
|
+
* values, each member of a union: the static side of a class or a function is an
|
|
222
|
+
* anonymous type its declaration's symbol names, which an instance's type is not.
|
|
223
|
+
*/
|
|
224
|
+
function constructedAt(project, written) {
|
|
225
|
+
return heldIn(project, must(project.queries.typeAt(unwrapped(written))));
|
|
226
|
+
}
|
|
227
|
+
/** The classes and functions one type holds as values, as {@link constructedAt} reads them. */
|
|
228
|
+
function heldIn(project, type) {
|
|
229
|
+
const { queries } = project;
|
|
230
|
+
return (type === undefined ? [] : must(queries.constituents(type))).flatMap((part) => {
|
|
231
|
+
if (!part.isObjectType() || (part.objectFlags & ObjectFlags.Anonymous) === 0) {
|
|
232
|
+
return [];
|
|
233
|
+
}
|
|
234
|
+
return must(queries.symbolOfType(part)) ?? [];
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* What a variable, a property or a destructured binding binds its name to: a variable's
|
|
239
|
+
* or a property's initializer, the value a shorthand property names, or the property of
|
|
240
|
+
* the destructured value a binding element reads.
|
|
241
|
+
*/
|
|
242
|
+
function boundTo(project, node) {
|
|
243
|
+
if (isShorthandPropertyAssignment(node)) {
|
|
244
|
+
const value = shorthandTarget(project, node);
|
|
245
|
+
return { symbols: value === undefined ? [] : [value] };
|
|
246
|
+
}
|
|
247
|
+
if (isBindingElement(node)) {
|
|
248
|
+
const key = node.propertyName ?? node.name;
|
|
249
|
+
if (key === undefined || !isIdentifier(key)) {
|
|
250
|
+
return { symbols: [] };
|
|
251
|
+
}
|
|
252
|
+
const source = must(project.queries.typeAt(node.parent));
|
|
253
|
+
const property = source === undefined
|
|
254
|
+
? undefined
|
|
255
|
+
: must(project.queries.propertiesOf(source)).find((one) => one.name === key.text);
|
|
256
|
+
return { symbols: property === undefined ? [] : [property] };
|
|
257
|
+
}
|
|
258
|
+
const written = initializerOf(node);
|
|
259
|
+
return written === undefined ? { symbols: [] } : { written, symbols: [] };
|
|
260
|
+
}
|
|
261
|
+
/** The value a variable or a property declaration binds its name to, where it writes one. */
|
|
262
|
+
function initializerOf(node) {
|
|
263
|
+
return isVariableDeclaration(node) || isPropertyAssignment(node) || isPropertyDeclaration(node)
|
|
264
|
+
? node.initializer
|
|
265
|
+
: undefined;
|
|
266
|
+
}
|
|
267
|
+
/** The key of one declaration a symbol names, the same for each symbol the checker makes of it. */
|
|
268
|
+
function keyOf(handle) {
|
|
269
|
+
return `${handle.path}#${String(handle.index)}`;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Every call of the project by the declarations its callee can be: the function or the
|
|
273
|
+
* method it names, or else each function the checker's type at it holds, wherever that
|
|
274
|
+
* value came from. A name is also followed through what its binding copies
|
|
275
|
+
* ({@link boundTo}), which is how a call reaches a function an annotation erases.
|
|
276
|
+
* Only the target's own declarations are keys, as only its own functions are asked for.
|
|
277
|
+
*/
|
|
278
|
+
function callsByDeclaration(project) {
|
|
279
|
+
const calls = callsOf(project);
|
|
280
|
+
const own = project.ownPaths();
|
|
281
|
+
const callees = resolvedTargets(project, calls.flatMap((call) => calleeName(call.expression) ?? []));
|
|
282
|
+
const named = new Map();
|
|
283
|
+
const keysOf = (symbol, seen) => {
|
|
284
|
+
const known = named.get(symbol.id);
|
|
285
|
+
if (known !== undefined || seen.has(symbol.id)) {
|
|
286
|
+
return known ?? [];
|
|
287
|
+
}
|
|
288
|
+
seen.add(symbol.id);
|
|
289
|
+
const keys = symbol.declarations
|
|
290
|
+
.filter((handle) => own.has(handle.path))
|
|
291
|
+
.flatMap((handle) => {
|
|
292
|
+
const node = project.declarationAt(handle)?.node;
|
|
293
|
+
const bound = node === undefined ? { symbols: [] } : boundTo(project, node);
|
|
294
|
+
const copied = [
|
|
295
|
+
...bound.symbols,
|
|
296
|
+
...(bound.written === undefined ? [] : leavesPassed(bound.written)).flatMap((value) => {
|
|
297
|
+
const { named: next, typed } = heldBy(project, value);
|
|
298
|
+
return [...typed, ...(next === undefined ? [] : [next])];
|
|
299
|
+
}),
|
|
300
|
+
];
|
|
301
|
+
return [keyOf(handle), ...copied.flatMap((next) => keysOf(next, seen))];
|
|
302
|
+
});
|
|
303
|
+
named.set(symbol.id, keys);
|
|
304
|
+
return keys;
|
|
305
|
+
};
|
|
306
|
+
const calleeOf = (call) => {
|
|
307
|
+
const name = calleeName(call.expression);
|
|
308
|
+
return name === undefined ? undefined : callees.get(name);
|
|
309
|
+
};
|
|
310
|
+
const declared = (call) => ((calleeOf(call)?.flags ?? 0) & (SymbolFlags.Function | SymbolFlags.Method)) !== 0;
|
|
311
|
+
const typed = calls.filter((call) => !declared(call));
|
|
312
|
+
const types = new Map(project.queries
|
|
313
|
+
.typesAt(typed.map((call) => unwrapped(call.expression)))
|
|
314
|
+
.map((type, index) => [typed[index], must(type)]));
|
|
315
|
+
const found = new Map();
|
|
316
|
+
for (const call of calls) {
|
|
317
|
+
const callee = calleeOf(call);
|
|
318
|
+
const held = [
|
|
319
|
+
...(callee === undefined ? [] : [callee]),
|
|
320
|
+
...(types.has(call) ? heldIn(project, types.get(call)) : []),
|
|
321
|
+
];
|
|
322
|
+
for (const key of new Set(held.flatMap((symbol) => keysOf(symbol, new Set())))) {
|
|
323
|
+
found.set(key, [...(found.get(key) ?? []), call]);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
return found;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* What each call of the function a parameter belongs to passes at the parameter's place,
|
|
330
|
+
* every argument from there on for a rest parameter. The function is the one the checker's
|
|
331
|
+
* type at its own declaration names, so a function with no name of its own is called too.
|
|
332
|
+
* A `this` parameter takes no place, and an argument at or after a spread argument passes
|
|
333
|
+
* at every place the spread's length can move it to.
|
|
334
|
+
*/
|
|
335
|
+
function argumentsAt(project, parameter, callsOfFunction) {
|
|
336
|
+
const owner = parameter.parent;
|
|
337
|
+
if (!isFunctionDeclaration(owner) &&
|
|
338
|
+
!isMethodDeclaration(owner) &&
|
|
339
|
+
!isArrowFunction(owner) &&
|
|
340
|
+
!isFunctionExpression(owner)) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
const calls = new Set(constructedAt(project, owner).flatMap(callsOfFunction));
|
|
344
|
+
const [first] = owner.parameters;
|
|
345
|
+
const erased = first !== undefined && isIdentifier(first.name) && first.name.text === "this";
|
|
346
|
+
const place = owner.parameters.indexOf(parameter) - (erased ? 1 : 0);
|
|
347
|
+
const rest = parameter.dotDotDotToken !== undefined;
|
|
348
|
+
return [...calls].flatMap((call) => {
|
|
349
|
+
let spreads = 0;
|
|
350
|
+
return call.arguments.flatMap((argument, index) => {
|
|
351
|
+
const spread = isSpreadElement(argument);
|
|
352
|
+
const earliest = index - spreads;
|
|
353
|
+
spreads += spread ? 1 : 0;
|
|
354
|
+
const open = spread || earliest < index;
|
|
355
|
+
const lands = open ? rest || earliest <= place : rest ? index >= place : index === place;
|
|
356
|
+
return lands ? [spread ? argument.expression : argument] : [];
|
|
357
|
+
});
|
|
358
|
+
});
|
|
123
359
|
}
|
|
124
360
|
/** Every class declaration of the target that carries a decorator. */
|
|
125
361
|
export function decoratedClasses(project, held) {
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* never guesses: an option it does not handle, or a dynamic word where it needs a value, leaves
|
|
6
6
|
* the command unread.
|
|
7
7
|
*/
|
|
8
|
-
import { resolvePath } from "../paths.js";
|
|
8
|
+
import { hasURLScheme, isPathSpecifier, resolvePath } from "../paths.js";
|
|
9
9
|
/** The options `words` start with, or `undefined` where one is unhandled or a needed word is dynamic. */
|
|
10
10
|
function optionsOf(words, options) {
|
|
11
11
|
const values = new Map();
|
|
@@ -46,10 +46,6 @@ function optionsOf(words, options) {
|
|
|
46
46
|
}
|
|
47
47
|
return { values, at: words.length, ended: false };
|
|
48
48
|
}
|
|
49
|
-
/** A specifier that starts with a URL scheme, `file:` and `node:` among them. */
|
|
50
|
-
const SCHEME = /^[a-z][a-z\d+.-]*:/iu;
|
|
51
|
-
/** A specifier written as a path: `.`, `..`, or one starting `./`, `../` or `/`. */
|
|
52
|
-
const RELATIVE = /^(?:\.{1,2}(?:\/|$)|\/)/u;
|
|
53
49
|
/**
|
|
54
50
|
* The path a URL's path text names, percent-decoded as Node's `fileURLToPath` does, with a
|
|
55
51
|
* backslash read as the URL parser reads it, or `undefined` where it does not decode or
|
|
@@ -76,19 +72,19 @@ function urlPath(text) {
|
|
|
76
72
|
export function preloadTarget(preload, dir) {
|
|
77
73
|
const { specifier, resolution } = preload;
|
|
78
74
|
if (resolution === "require") {
|
|
79
|
-
return
|
|
75
|
+
return isPathSpecifier(specifier)
|
|
80
76
|
? { path: resolvePath(dir, specifier) }
|
|
81
|
-
:
|
|
77
|
+
: hasURLScheme(specifier)
|
|
82
78
|
? undefined
|
|
83
79
|
: { package: specifier };
|
|
84
80
|
}
|
|
85
|
-
if (!
|
|
81
|
+
if (!isPathSpecifier(specifier) && !hasURLScheme(specifier)) {
|
|
86
82
|
return { package: specifier };
|
|
87
83
|
}
|
|
88
84
|
const local = /^file:(?:\/\/(?=\/))?/iu.exec(specifier);
|
|
89
85
|
const rest = specifier.slice(local?.[0].length ?? 0);
|
|
90
86
|
// `file://host/…` names a file on another host.
|
|
91
|
-
if ((
|
|
87
|
+
if ((hasURLScheme(specifier) && local === null) || rest.startsWith("//")) {
|
|
92
88
|
return undefined;
|
|
93
89
|
}
|
|
94
90
|
const path = urlPath(rest);
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* of the module is the same position of the file. A default export and the imports a `src`
|
|
6
6
|
* attribute names are appended past the file's end.
|
|
7
7
|
*/
|
|
8
|
+
import { isPathRelativeURL, startsRelativePath } from "./paths.js";
|
|
8
9
|
const LANGUAGES = new Map([
|
|
9
10
|
["ts", "ts"],
|
|
10
11
|
["tsx", "tsx"],
|
|
@@ -346,10 +347,10 @@ function actionOf(text) {
|
|
|
346
347
|
/** Whether a `src` value names a file: no scheme, no root-relative path, not empty. */
|
|
347
348
|
function specifierOf(value) {
|
|
348
349
|
const trimmed = value.trim();
|
|
349
|
-
if (
|
|
350
|
+
if (!isPathRelativeURL(trimmed)) {
|
|
350
351
|
return undefined;
|
|
351
352
|
}
|
|
352
|
-
return
|
|
353
|
+
return startsRelativePath(trimmed) ? trimmed : `./${trimmed}`;
|
|
353
354
|
}
|
|
354
355
|
/** The line-start `<script` tags one block's content holds. */
|
|
355
356
|
function scriptsInside(text, from, to) {
|
|
@@ -4,7 +4,7 @@ export const RECORD = {
|
|
|
4
4
|
conformance: {
|
|
5
5
|
corpusVersion: "6.0.0",
|
|
6
6
|
result: "pass",
|
|
7
|
-
digest: "sha256:
|
|
7
|
+
digest: "sha256:140f095d52fa8140de070f46d13a132b723c634ecf24a8b96e6a92c410c4b848",
|
|
8
8
|
},
|
|
9
9
|
gaps: [],
|
|
10
10
|
};
|
package/dist/src/consumers.js
CHANGED
|
@@ -3,22 +3,19 @@ import { inventory } from "./inventory/enumerate.js";
|
|
|
3
3
|
import { relativePath } from "./paths.js";
|
|
4
4
|
import { references } from "./references.js";
|
|
5
5
|
import { narrowedTo } from "./session.js";
|
|
6
|
+
import { patternTestFile } from "./tests/file-class.js";
|
|
6
7
|
/**
|
|
7
8
|
* Every reference one consumer project's own files make to a declaration of the target.
|
|
8
9
|
* The target's declarations are enumerated over the target's files the consumer's
|
|
9
10
|
* program holds, against the target root, so each carries the identifier the target's
|
|
10
|
-
* own projects give it. A reference from a consumer's test file is a test reference
|
|
11
|
-
*
|
|
11
|
+
* own projects give it. A reference from a consumer's test file is a test reference;
|
|
12
|
+
* whether the run counts it as a production one is the sweep's mode.
|
|
12
13
|
*/
|
|
13
14
|
export function consumerReferences(project, host, targetRoot, consumer, config, parse) {
|
|
14
15
|
const held = inventory(narrowedTo(project, (file) => relativePath(targetRoot, file.fileName) !== undefined), host, targetRoot);
|
|
15
|
-
|
|
16
|
-
|
|
16
|
+
return references(project, held, targetRoot, {
|
|
17
|
+
testFile: patternTestFile(config.ts.test_files, consumer.root),
|
|
17
18
|
consumer,
|
|
18
19
|
directories: runDirectories(host, consumer.root, parse),
|
|
19
|
-
})
|
|
20
|
-
if (config.analysis.consumer_tests !== "production") {
|
|
21
|
-
return found;
|
|
22
|
-
}
|
|
23
|
-
return found.map((reference) => (reference.test ? { ...reference, test: false } : reference));
|
|
20
|
+
});
|
|
24
21
|
}
|
|
@@ -63,9 +63,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
63
63
|
const privateNames = new Set(held.symbols
|
|
64
64
|
.filter((symbol) => symbol.visibility === "private-name")
|
|
65
65
|
.map((symbol) => symbol.id));
|
|
66
|
-
let batches = 0;
|
|
67
|
-
let lookups = 0;
|
|
68
|
-
const batchesOf = (count) => Math.ceil(count / cap);
|
|
69
66
|
const parts = new Map();
|
|
70
67
|
/** The object types one type is: itself, or its constituents at any depth. */
|
|
71
68
|
const partsOf = (type) => {
|
|
@@ -75,7 +72,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
75
72
|
}
|
|
76
73
|
let found = [];
|
|
77
74
|
if (type.isUnionType() || type.isIntersectionType()) {
|
|
78
|
-
lookups += 1;
|
|
79
75
|
const constituents = queries.constituents(type);
|
|
80
76
|
found = isAnswered(constituents) ? constituents.flatMap(partsOf) : [];
|
|
81
77
|
}
|
|
@@ -94,7 +90,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
94
90
|
}
|
|
95
91
|
const table = new Map();
|
|
96
92
|
const read = properties.of(type);
|
|
97
|
-
lookups += read.asked ? 1 : 0;
|
|
98
93
|
for (const property of isAnswered(read.properties) ? read.properties : []) {
|
|
99
94
|
table.set(property.name, {
|
|
100
95
|
ids: chains
|
|
@@ -112,7 +107,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
112
107
|
return table;
|
|
113
108
|
};
|
|
114
109
|
const writes = [];
|
|
115
|
-
batches += batchesOf(literals.length);
|
|
116
110
|
const contexts = queries.contextualTypes(literals.map((one) => one.literal), cap);
|
|
117
111
|
// A computed name that is not literal text names the property the literal declares
|
|
118
112
|
// under the checker's own key, which is how a type declares a member a unique symbol
|
|
@@ -123,7 +117,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
123
117
|
const name = propertyName(property);
|
|
124
118
|
return name !== undefined && name.text === undefined ? [name.node] : [];
|
|
125
119
|
}));
|
|
126
|
-
batches += batchesOf(computed.length);
|
|
127
120
|
const keys = new Map();
|
|
128
121
|
(computed.length === 0
|
|
129
122
|
? []
|
|
@@ -152,7 +145,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
152
145
|
}
|
|
153
146
|
});
|
|
154
147
|
const carried = flows.filter((flow) => !isObjectLiteralExpression(flow.value));
|
|
155
|
-
batches += batchesOf(carried.length);
|
|
156
148
|
const types = queries.typesAt(carried.map((flow) => flow.value), cap);
|
|
157
149
|
/** The members a position may read structurally: those of object types, not of classes. */
|
|
158
150
|
const readable = (member) => [
|
|
@@ -198,11 +190,9 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
198
190
|
if (elements.has(type.id)) {
|
|
199
191
|
return elements.get(type.id);
|
|
200
192
|
}
|
|
201
|
-
lookups += 1;
|
|
202
193
|
const isArray = queries.isArray(type);
|
|
203
194
|
let element;
|
|
204
195
|
if (isAnswered(isArray) && isArray) {
|
|
205
|
-
lookups += 1;
|
|
206
196
|
const held = queries.typeArguments(type);
|
|
207
197
|
element = isAnswered(held) ? held[0] : undefined;
|
|
208
198
|
}
|
|
@@ -216,7 +206,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
216
206
|
if (known !== undefined) {
|
|
217
207
|
return known;
|
|
218
208
|
}
|
|
219
|
-
lookups += type.isTypeReference() ? 1 : 0;
|
|
220
209
|
const held = queries.typeArguments(type);
|
|
221
210
|
const found = isAnswered(held) ? held : [];
|
|
222
211
|
argumentsOf.set(type.id, found);
|
|
@@ -232,18 +221,15 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
232
221
|
if (known !== undefined) {
|
|
233
222
|
return known;
|
|
234
223
|
}
|
|
235
|
-
lookups += 1;
|
|
236
224
|
let kind = SignatureKind.Call;
|
|
237
225
|
let signatures = queries.signaturesOf(type, kind);
|
|
238
226
|
if (isAnswered(signatures) && signatures.length === 0) {
|
|
239
|
-
lookups += 1;
|
|
240
227
|
kind = SignatureKind.Construct;
|
|
241
228
|
signatures = queries.signaturesOf(type, kind);
|
|
242
229
|
}
|
|
243
230
|
const called = (isAnswered(signatures) ? signatures : []).map((signature) => {
|
|
244
231
|
const parameters = signature.parameters.map((_parameter, index) => queries.parameterType(signature, index));
|
|
245
232
|
const result = queries.returnTypeOf(signature);
|
|
246
|
-
lookups += parameters.length + 1;
|
|
247
233
|
return {
|
|
248
234
|
kind,
|
|
249
235
|
parameters: parameters.map((one) => (isAnswered(one) ? one : undefined)),
|
|
@@ -335,7 +321,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
335
321
|
const type = types[index];
|
|
336
322
|
return type === undefined || !isAnswered(type) || !mayCarry(type) ? [] : [{ flow, type }];
|
|
337
323
|
});
|
|
338
|
-
batches += batchesOf(sourced.length);
|
|
339
324
|
const positions = queries.contextualTypes(sourced.map((one) => one.flow.value), cap);
|
|
340
325
|
const reads = [];
|
|
341
326
|
// A literal property writes a position's members once, however many values carry it there.
|
|
@@ -408,7 +393,6 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
408
393
|
return known;
|
|
409
394
|
};
|
|
410
395
|
const spreads = literals.flatMap(({ literal, from }) => literal.properties.flatMap((property) => isSpreadAssignment(property) ? [{ value: property.expression, from }] : []));
|
|
411
|
-
batches += batchesOf(spreads.length);
|
|
412
396
|
const spreadTypes = queries.typesAt(spreads.map((one) => one.value), cap);
|
|
413
397
|
spreads.forEach(({ value, from }, index) => {
|
|
414
398
|
const type = spreadTypes[index];
|
|
@@ -426,5 +410,5 @@ export function contextualUses(project, held, chains, literals, flows, cap, prop
|
|
|
426
410
|
reads.push({ at: value, from, ids });
|
|
427
411
|
}
|
|
428
412
|
});
|
|
429
|
-
return { writes, reads
|
|
413
|
+
return { writes, reads };
|
|
430
414
|
}
|
|
@@ -483,7 +483,10 @@ export const SETTINGS = [
|
|
|
483
483
|
key: "provenance",
|
|
484
484
|
role: "ignored",
|
|
485
485
|
read: true,
|
|
486
|
-
value: {
|
|
486
|
+
value: {
|
|
487
|
+
type: "string",
|
|
488
|
+
pattern: "^(default|(repository|central|flag): ([^\\r\\n]|[\\r\\n])+)$",
|
|
489
|
+
},
|
|
487
490
|
keyPattern: "^[a-z][a-z_]*(\\.[A-Za-z0-9_]+)*$",
|
|
488
491
|
},
|
|
489
492
|
];
|
|
@@ -372,7 +372,7 @@ export const KIND_ROWS = [
|
|
|
372
372
|
subject: "declaration",
|
|
373
373
|
holdsLive: true,
|
|
374
374
|
rule: "An exported symbol whose every reference is inside the symbol's own package or module, reported as a candidate for unexporting. The subject is a package-level declaration or a method. Three subjects are excluded. An interface method is excluded because its exportedness is the contract of the interface that declares it. A struct field is excluded because an encoder reads it by name. A method that satisfies an interface some symbol uses as a type is excluded because it cannot be unexported without its type ceasing to satisfy that interface, and so is a method whose name and signature are those of a method of an exported interface type declared by a package outside the target that the import closure holds, because that interface fixes them whether or not the program converts the method's type to it. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference. Where the edge's other side is unknown to the analyzer, the finding is emitted pending. A reference from a Go external test package, a test file whose package clause names its directory's package with the suffix _test, is a reference from outside the package, because that test builds as another package and needs the export.",
|
|
375
|
-
precondition: "Closed world only. A Go main package, and an internal/ directory tree for importers outside its parent, are closed worlds whatever the configuration declares, because no other program can import them, so this finding is reported there always. A published package is a closed world only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type
|
|
375
|
+
precondition: "Closed world only. A Go main package, and an internal/ directory tree for importers outside its parent, are closed worlds whatever the configuration declares, because no other program can import them, so this finding is reported there always. A published package is a closed world only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type is not reported when a reference from outside the package reaches its values through its own package's exported declarations: a reference to an exported function or method whose parameters or results name the type, to an exported variable or constant whose type names it, to an exported field or property whose type names it, an embedded field included, or to an exported member of the type itself, a field promoted through an embedding included. A type a reference from outside the package names, or whose values such a reference reaches, passes the reach on to each type its definition names other than through an unexported member, whether it is exported or not, so an unexported type an exported function returns passes it to the types its exported fields name. A type names another through every composite that spells it: a pointer, an array, a slice, a map key or value, a channel, a function signature and a type argument. The referencing code holds values of the type, so unexporting it would leave an exported declaration naming a type its users cannot name. A package-level declaration whose name a string literal spells carries the class grammar/analysis/confidence.md gives it.",
|
|
376
376
|
details: { narrower_visibility: "required", name_literal: "optional" },
|
|
377
377
|
defaultEnabled: true,
|
|
378
378
|
defaultSeverity: "warn",
|
|
@@ -387,7 +387,7 @@ export const KIND_ROWS = [
|
|
|
387
387
|
subject: "declaration",
|
|
388
388
|
holdsLive: true,
|
|
389
389
|
rule: "An exported symbol of a non-internal package whose every reference is inside the target module, reported as a candidate for relocation behind an internal boundary. The subject is a package-level declaration or a method. Three subjects are excluded. An interface method is excluded because its exportedness is the contract of the interface that declares it. A struct field is excluded because an encoder reads it by name. A method that satisfies an interface some symbol uses as a type is excluded because it cannot be unexported without its type ceasing to satisfy that interface, and so is a method whose name and signature are those of a method of an exported interface type declared by a package outside the target that the import closure holds, because that interface fixes them whether or not the program converts the method's type to it. The finding names the narrower visibility the references support. A declared cross-language edge counts as an out-of-package reference. Where the edge's other side is unknown to the analyzer, the finding is emitted pending. A reference from a Go external test package, a test file whose package clause names its directory's package with the suffix _test, is a reference from outside the package, because that test builds as another package and needs the export.",
|
|
390
|
-
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type
|
|
390
|
+
precondition: "Closed world only. Reported always for a main package and an internal/ directory tree, and for a published package only when the configuration declares the consumer set complete and every declared consumer loads. A library with no consumer loaded and no complete consumer set declared gets this finding on its internal/ tree and its main packages and never on its published API. A symbol the rule of DS1301 holds for is not reported under this code, whatever severity the configuration gives DS1301, because the write-only finding names the defect to fix first. A type is not reported when a reference from outside the target module reaches its values through its own package's exported declarations, as DS1501 defines that reach, because the referencing code holds values of the type, and moving it behind an internal boundary would leave an exported declaration naming a type its users cannot name.",
|
|
391
391
|
details: { narrower_visibility: "required", name_literal: "optional" },
|
|
392
392
|
defaultEnabled: true,
|
|
393
393
|
defaultSeverity: "warn",
|
|
@@ -43,6 +43,11 @@ export const REPORT_SCHEMAS = {
|
|
|
43
43
|
},
|
|
44
44
|
else: { properties: { configurations: { minItems: 1 }, target: { required: ["kind"] } } },
|
|
45
45
|
},
|
|
46
|
+
{
|
|
47
|
+
if: { required: ["merged_from"], properties: { merged_from: { maxItems: 0 } } },
|
|
48
|
+
then: { properties: { analyzer: { properties: { languages: { maxItems: 0 } } } } },
|
|
49
|
+
else: { properties: { analyzer: { properties: { languages: { minItems: 1 } } } } },
|
|
50
|
+
},
|
|
46
51
|
],
|
|
47
52
|
properties: {
|
|
48
53
|
schema_version: { type: "string", pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+$" },
|
|
@@ -59,7 +64,6 @@ export const REPORT_SCHEMAS = {
|
|
|
59
64
|
},
|
|
60
65
|
languages: {
|
|
61
66
|
type: "array",
|
|
62
|
-
minItems: 1,
|
|
63
67
|
uniqueItems: true,
|
|
64
68
|
items: { type: "string", enum: ["go", "ts", "proto"] },
|
|
65
69
|
},
|
|
@@ -95,7 +99,7 @@ export const REPORT_SCHEMAS = {
|
|
|
95
99
|
pattern: "^[0-9]+\\.[0-9]+\\.[0-9]+(-[0-9A-Za-z.-]+)?(\\+[0-9A-Za-z.-]+)?$",
|
|
96
100
|
},
|
|
97
101
|
digest: { type: "string", pattern: "^sha256:[0-9a-f]{64}$" },
|
|
98
|
-
languages: { $ref: "#/properties/analyzer/properties/languages" },
|
|
102
|
+
languages: { $ref: "#/properties/analyzer/properties/languages", minItems: 1 },
|
|
99
103
|
totals: { $ref: "#/properties/totals" },
|
|
100
104
|
},
|
|
101
105
|
},
|