@cplieger/deadset-ts 7.1.0-dev.2 → 7.2.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.
- package/dist/src/alias-chain.js +6 -2
- package/dist/src/analysis.js +3 -1
- package/dist/src/config.js +4 -0
- package/dist/src/conformance-record.js +2 -2
- package/dist/src/decorator.js +3 -2
- package/dist/src/findings/unused-declarations.js +3 -3
- package/dist/src/injection-container.js +28 -25
- package/dist/src/intra-function-parts.js +72 -11
- package/dist/src/inventory.js +18 -3
- package/dist/src/kinds.js +2 -2
- package/dist/src/ref.js +3 -2
- package/dist/src/references.js +38 -20
- package/dist/src/report.js +1 -1
- package/dist/src/resolve.js +37 -4
- package/dist/src/schema.js +4 -1
- package/dist/src/suppress-file.js +43 -9
- package/dist/src/suppress-inline.js +35 -12
- package/dist/src/suppress.js +3 -2
- package/dist/src/verbs/analyze.js +7 -3
- package/dist/src/version.js +1 -1
- package/jsr.json +1 -1
- package/package.json +1 -1
- package/src/alias-chain.ts +7 -2
- package/src/analysis.ts +3 -3
- package/src/config.ts +9 -1
- package/src/conformance-record.ts +2 -2
- package/src/decorator.ts +8 -2
- package/src/findings/unused-declarations.ts +3 -3
- package/src/injection-container.ts +31 -26
- package/src/intra-function-parts.ts +95 -11
- package/src/inventory.ts +26 -2
- package/src/kinds.ts +2 -2
- package/src/ref.ts +3 -2
- package/src/references.ts +55 -21
- package/src/report.ts +1 -1
- package/src/resolve.ts +56 -6
- package/src/schema.ts +10 -14
- package/src/signatures.ts +6 -1
- package/src/suppress-file.ts +48 -9
- package/src/suppress-inline.ts +57 -15
- package/src/suppress.ts +3 -2
- package/src/verbs/analyze.ts +7 -2
- package/src/version.ts +1 -1
package/src/schema.ts
CHANGED
|
@@ -1,19 +1,12 @@
|
|
|
1
1
|
import { ConfigError } from "./config.ts";
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* How one node of the closed key list is written
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* scalars: the severity and provenance objects.
|
|
11
|
-
* - `list` is an array whose entries are objects with declared members. Where
|
|
12
|
-
* the key list admits an entry in more than one shape, the members are every
|
|
13
|
-
* shape's together, and which of them one entry may hold is the decoder's
|
|
14
|
-
* refusal, not the walk's.
|
|
15
|
-
* - `object` is one setting written as an object: its member names are declared
|
|
16
|
-
* and a source supplies the object whole.
|
|
4
|
+
* How one node of the closed key list is written: `leaf`, a scalar or an array of
|
|
5
|
+
* scalars; `section`, an object of declared members and no value of its own; `map`, an
|
|
6
|
+
* object of open member names and scalar values (severity, provenance); `list`, an
|
|
7
|
+
* array of objects holding every admitted shape's members; `object`, one setting
|
|
8
|
+
* written as an object of declared members and supplied whole. Which shape an entry or
|
|
9
|
+
* a value takes, where the key list admits several, is the decoder's refusal.
|
|
17
10
|
*/
|
|
18
11
|
type KeyKind = "leaf" | "section" | "map" | "list" | "object";
|
|
19
12
|
|
|
@@ -46,7 +39,10 @@ export const SCHEMA_ROOT: KeyNode = {
|
|
|
46
39
|
kind: "section",
|
|
47
40
|
members: {
|
|
48
41
|
contract_version: leaf,
|
|
49
|
-
target: {
|
|
42
|
+
target: {
|
|
43
|
+
kind: "section",
|
|
44
|
+
members: { kind: { kind: "object", members: { go: leaf, ts: leaf } } },
|
|
45
|
+
},
|
|
50
46
|
analysis: {
|
|
51
47
|
kind: "section",
|
|
52
48
|
members: {
|
package/src/signatures.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
isPropertyDeclaration,
|
|
14
14
|
isVariableDeclaration,
|
|
15
15
|
type ArrowFunction,
|
|
16
|
+
type ConstructorDeclaration,
|
|
16
17
|
type FunctionDeclaration,
|
|
17
18
|
type FunctionExpression,
|
|
18
19
|
type MethodDeclaration,
|
|
@@ -21,7 +22,11 @@ import {
|
|
|
21
22
|
|
|
22
23
|
/** A function-like node that carries a body of its own. */
|
|
23
24
|
export type SignatureNode =
|
|
24
|
-
|
|
25
|
+
| FunctionDeclaration
|
|
26
|
+
| MethodDeclaration
|
|
27
|
+
| ArrowFunction
|
|
28
|
+
| FunctionExpression
|
|
29
|
+
| ConstructorDeclaration;
|
|
25
30
|
|
|
26
31
|
/** Whether one node is a function value written in place: an arrow or a function expression. */
|
|
27
32
|
function isFunctionValue(node: Node | undefined): node is ArrowFunction | FunctionExpression {
|
package/src/suppress-file.ts
CHANGED
|
@@ -303,11 +303,12 @@ interface KeptRow {
|
|
|
303
303
|
}
|
|
304
304
|
|
|
305
305
|
/**
|
|
306
|
-
* The rows of
|
|
307
|
-
* document order and as written, which a write keeps. A document that is
|
|
308
|
-
* or a row in a shape no record takes, is refused rather
|
|
306
|
+
* The rows of a scheme other than `own` that the baseline document `text` holds, in
|
|
307
|
+
* document order and as written, which a write of `own`'s rows keeps. A document that is
|
|
308
|
+
* not a baseline, or a row in a shape no record takes, of any scheme, is refused rather
|
|
309
|
+
* than dropped.
|
|
309
310
|
*/
|
|
310
|
-
export function otherSchemeRows(text: string): readonly KeptRow[] {
|
|
311
|
+
export function otherSchemeRows(text: string, own: string = OWN_SCHEME): readonly KeptRow[] {
|
|
311
312
|
const refuse = (detail: string): never => {
|
|
312
313
|
throw new Error(
|
|
313
314
|
`${BASELINE_FILE} cannot be rewritten keeping the other language's rows: ${detail}`,
|
|
@@ -331,7 +332,7 @@ export function otherSchemeRows(text: string): readonly KeptRow[] {
|
|
|
331
332
|
}
|
|
332
333
|
// readRecord has checked every member is a string and the code and symbol are present.
|
|
333
334
|
const { code, symbol, path, reason } = value as Readonly<Record<string, string>>;
|
|
334
|
-
if (code === undefined || symbol === undefined || symbol.startsWith(
|
|
335
|
+
if (code === undefined || symbol === undefined || symbol.startsWith(own)) {
|
|
335
336
|
return [];
|
|
336
337
|
}
|
|
337
338
|
return [
|
|
@@ -345,10 +346,37 @@ export function otherSchemeRows(text: string): readonly KeptRow[] {
|
|
|
345
346
|
});
|
|
346
347
|
}
|
|
347
348
|
|
|
349
|
+
/** The reference schemes in the order a baseline holds their rows. */
|
|
350
|
+
const SCHEME_ORDER: readonly string[] = ["go://", "ts://"];
|
|
351
|
+
|
|
352
|
+
function schemeRank(row: { readonly symbol: string }): number {
|
|
353
|
+
return SCHEME_ORDER.findIndex((scheme) => row.symbol.startsWith(scheme));
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/** A surrogate half no other completes, which Go's encoding/json decodes as U+FFFD. */
|
|
357
|
+
const LONE_SURROGATE = /[\ud800-\udfff]/gu;
|
|
358
|
+
|
|
359
|
+
/** One string with every lone surrogate written as U+FFFD. */
|
|
360
|
+
function wellFormed(text: string): string {
|
|
361
|
+
return text.replace(LONE_SURROGATE, "\ufffd");
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* The pieces of a JSON document with U+2028 and U+2029 escaped, as Go's encoding/json
|
|
366
|
+
* writes them. Neither is JSON syntax, so each stands inside a string.
|
|
367
|
+
*/
|
|
368
|
+
function* separatorsEscaped(pieces: Iterable<string>): Generator<string, void, undefined> {
|
|
369
|
+
for (const piece of pieces) {
|
|
370
|
+
yield piece.replaceAll("\u2028", "\\u2028").replaceAll("\u2029", "\\u2029");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
348
374
|
/**
|
|
349
|
-
* A baseline holding one row per finding, in the order given, which is the report's,
|
|
350
|
-
* the other language's rows `kept`,
|
|
351
|
-
*
|
|
375
|
+
* A baseline holding one row per finding, in the order given, which is the report's, and
|
|
376
|
+
* the other language's rows `kept`, Go's rows ahead of TypeScript's and each scheme's in
|
|
377
|
+
* the order given, so two analyzers writing one document in either order write the same
|
|
378
|
+
* bytes, in the form grammar/suppression.md fixes. Every recorded row carries the
|
|
379
|
+
* provenance as its reason. A finding
|
|
352
380
|
* missing a value a row needs, and an identity missing its name or version, are refused
|
|
353
381
|
* rather than written, because the grammar requires a reason on every row.
|
|
354
382
|
*/
|
|
@@ -371,5 +399,16 @@ export function writeBaseline(
|
|
|
371
399
|
}
|
|
372
400
|
return { code: found.code, symbol: found.symbol, path: found.path, reason };
|
|
373
401
|
});
|
|
374
|
-
|
|
402
|
+
const rows = [...kept, ...baseline]
|
|
403
|
+
.sort((a, b) => schemeRank(a) - schemeRank(b))
|
|
404
|
+
.map((row: KeptRow): KeptRow => {
|
|
405
|
+
const { code, symbol, path, reason } = row;
|
|
406
|
+
return {
|
|
407
|
+
code: wellFormed(code),
|
|
408
|
+
symbol: wellFormed(symbol),
|
|
409
|
+
...(path === undefined ? {} : { path: wellFormed(path) }),
|
|
410
|
+
...(reason === undefined ? {} : { reason: wellFormed(reason) }),
|
|
411
|
+
};
|
|
412
|
+
});
|
|
413
|
+
return separatorsEscaped(jsonChunks({ description: BASELINE_DESCRIPTION, baseline: rows }, 2));
|
|
375
414
|
}
|
package/src/suppress-inline.ts
CHANGED
|
@@ -4,12 +4,18 @@
|
|
|
4
4
|
* below it, or on its own line where it trails a token there.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import {
|
|
8
|
-
|
|
7
|
+
import {
|
|
8
|
+
isConstructorDeclaration,
|
|
9
|
+
SyntaxKind,
|
|
10
|
+
type Node,
|
|
11
|
+
type SourceFile,
|
|
12
|
+
} from "@typescript/native/ast";
|
|
13
|
+
import { isParameterProperty, nodeKey, type Inventory } from "./inventory.ts";
|
|
9
14
|
import { byPosition, positionKey, renderPosition, type Position } from "./position.ts";
|
|
10
15
|
import type { ProjectView } from "./session.ts";
|
|
11
16
|
import { signatureOf } from "./signatures.ts";
|
|
12
17
|
import {
|
|
18
|
+
isPartKind,
|
|
13
19
|
SUPPRESSION_WITHOUT_REASON,
|
|
14
20
|
SuppressionError,
|
|
15
21
|
siteText,
|
|
@@ -166,35 +172,64 @@ interface Declared {
|
|
|
166
172
|
readonly position: Position;
|
|
167
173
|
}
|
|
168
174
|
|
|
175
|
+
/**
|
|
176
|
+
* What one placement binds: the declaration a record for a code that reports a
|
|
177
|
+
* declaration binds, and the one a record for a part kind binds, which holds the part.
|
|
178
|
+
* A parameter property's field is the first and its class the second; a constructor's
|
|
179
|
+
* plain parameter binds only the second, because a constructor is no declaration.
|
|
180
|
+
*/
|
|
181
|
+
interface Binding {
|
|
182
|
+
readonly declared: Declared | undefined;
|
|
183
|
+
readonly part: Declared;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
function bindingKey(one: Binding): string {
|
|
187
|
+
return `${one.declared?.id ?? ""}\u0000${one.part.id}`;
|
|
188
|
+
}
|
|
189
|
+
|
|
169
190
|
/**
|
|
170
191
|
* The declarations of one file by the line each begins on, the line of its first token: a
|
|
171
192
|
* decorator, a modifier or the keyword. A variable's first token is its statement's
|
|
172
193
|
* keyword, and a variable written on a later line of the statement also begins there. A
|
|
173
|
-
* function also begins, for a directive, on the line of each of its parameters
|
|
194
|
+
* function also begins, for a directive, on the line of each of its parameters, and a
|
|
195
|
+
* class, for a part kind, on the lines of its constructor and its constructor's parameters.
|
|
174
196
|
*/
|
|
175
197
|
function declarationLines(
|
|
176
198
|
file: SourceFile,
|
|
177
199
|
held: Inventory,
|
|
178
200
|
symbols: ReadonlyMap<string, Declared>,
|
|
179
|
-
): ReadonlyMap<number, readonly
|
|
180
|
-
const below = new Map<number,
|
|
181
|
-
const add = (offset: number, one:
|
|
201
|
+
): ReadonlyMap<number, readonly Binding[]> {
|
|
202
|
+
const below = new Map<number, Binding[]>();
|
|
203
|
+
const add = (offset: number, one: Binding): void => {
|
|
182
204
|
const line = file.getLineAndCharacterOfPosition(tokenStart(file.text, offset)).line + 1;
|
|
183
205
|
const at = below.get(line) ?? [];
|
|
184
|
-
if (!at.some((known) => known
|
|
206
|
+
if (!at.some((known) => bindingKey(known) === bindingKey(one))) {
|
|
185
207
|
at.push(one);
|
|
186
208
|
}
|
|
187
209
|
below.set(line, at);
|
|
188
210
|
};
|
|
189
|
-
const
|
|
211
|
+
const declaredAt = (node: Node): Declared | undefined => {
|
|
190
212
|
const id = held.declarations.get(nodeKey(file, node));
|
|
191
|
-
|
|
213
|
+
return id === undefined ? undefined : symbols.get(id);
|
|
214
|
+
};
|
|
215
|
+
const visit = (node: Node): void => {
|
|
216
|
+
const one = declaredAt(node);
|
|
217
|
+
const owner = isConstructorDeclaration(node) ? declaredAt(node.parent) : undefined;
|
|
218
|
+
if (owner !== undefined && isConstructorDeclaration(node)) {
|
|
219
|
+
for (const at of [node, ...node.parameters.filter((one) => !isParameterProperty(one))]) {
|
|
220
|
+
add(at.pos, { declared: undefined, part: owner });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
const holder =
|
|
224
|
+
one !== undefined && isParameterProperty(node) ? declaredAt(node.parent.parent) : one;
|
|
225
|
+
if (one !== undefined && holder !== undefined) {
|
|
226
|
+
add(node.pos, { declared: one, part: holder });
|
|
227
|
+
}
|
|
192
228
|
if (one !== undefined) {
|
|
193
|
-
add(node.pos, one);
|
|
194
229
|
// A parameter on a later line of a wrapped signature takes its directive on the
|
|
195
230
|
// line above it, inside the parameter list, and the directive binds the function.
|
|
196
231
|
for (const parameter of signatureOf(node)?.parameters ?? []) {
|
|
197
|
-
add(parameter.pos, one);
|
|
232
|
+
add(parameter.pos, { declared: one, part: one });
|
|
198
233
|
}
|
|
199
234
|
const list = node.parent;
|
|
200
235
|
if (
|
|
@@ -202,7 +237,7 @@ function declarationLines(
|
|
|
202
237
|
list.kind === SyntaxKind.VariableDeclarationList &&
|
|
203
238
|
list.parent.kind === SyntaxKind.VariableStatement
|
|
204
239
|
) {
|
|
205
|
-
add(list.parent.pos, one);
|
|
240
|
+
add(list.parent.pos, { declared: one, part: one });
|
|
206
241
|
}
|
|
207
242
|
}
|
|
208
243
|
node.forEachChild(visit);
|
|
@@ -225,7 +260,7 @@ export interface InlineDirective {
|
|
|
225
260
|
readonly site: Position;
|
|
226
261
|
readonly text: string;
|
|
227
262
|
readonly directive: Directive;
|
|
228
|
-
readonly bound:
|
|
263
|
+
readonly bound: Binding[];
|
|
229
264
|
}
|
|
230
265
|
|
|
231
266
|
/**
|
|
@@ -282,7 +317,7 @@ export function inlineSuppressions(
|
|
|
282
317
|
continue;
|
|
283
318
|
}
|
|
284
319
|
for (const one of written.bound) {
|
|
285
|
-
if (!known.bound.some((other) => other
|
|
320
|
+
if (!known.bound.some((other) => bindingKey(other) === bindingKey(one))) {
|
|
286
321
|
known.bound.push(one);
|
|
287
322
|
}
|
|
288
323
|
}
|
|
@@ -311,8 +346,15 @@ export function inlineSuppressions(
|
|
|
311
346
|
}
|
|
312
347
|
break;
|
|
313
348
|
case "directive": {
|
|
314
|
-
const declared = [...bound].sort((a, b) => byPosition(a.position, b.position));
|
|
315
349
|
for (const code of directive.codes) {
|
|
350
|
+
const targets = new Map<string, Declared>();
|
|
351
|
+
for (const one of bound) {
|
|
352
|
+
const target = isPartKind(code) ? one.part : one.declared;
|
|
353
|
+
if (target !== undefined) {
|
|
354
|
+
targets.set(target.id, target);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
const declared = [...targets.values()].sort((a, b) => byPosition(a.position, b.position));
|
|
316
358
|
const base = {
|
|
317
359
|
code,
|
|
318
360
|
path: site.path,
|
package/src/suppress.ts
CHANGED
|
@@ -88,9 +88,10 @@ export const CODE_FORM = /^DS[0-9]{4}$/u;
|
|
|
88
88
|
|
|
89
89
|
/**
|
|
90
90
|
* A path inside the target: relative to the target root, the solidus as separator, no
|
|
91
|
-
* leading `./`, no trailing solidus, no `.` or `..` segment, never absolute
|
|
91
|
+
* leading `./`, no trailing solidus, no `.` or `..` segment, never absolute, and no
|
|
92
|
+
* control character; a backslash is a character of a name.
|
|
92
93
|
*/
|
|
93
|
-
export const PATH_EXPRESSION = String.raw`^(?:[
|
|
94
|
+
export const PATH_EXPRESSION = String.raw`^(?:[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.\.[^/\x00-\x1f\x7f]+)(?:/(?:[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.[^/.\x00-\x1f\x7f][^/\x00-\x1f\x7f]*|\.\.[^/\x00-\x1f\x7f]+))*$`;
|
|
94
95
|
|
|
95
96
|
const PATH_FORM = new RegExp(PATH_EXPRESSION, "u");
|
|
96
97
|
|
package/src/verbs/analyze.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type RunAnalysis,
|
|
7
7
|
type RunProject,
|
|
8
8
|
} from "../analysis.ts";
|
|
9
|
-
import { ConfigError, type Format } from "../config.ts";
|
|
9
|
+
import { ConfigError, kindOf, type Format } from "../config.ts";
|
|
10
10
|
import { CONFORMANCE, DECLARED_GAPS } from "../conformance.ts";
|
|
11
11
|
import { moduleIdentity } from "../consumers.ts";
|
|
12
12
|
import { analyzerProvenance, verdictOf } from "../findings-pass.ts";
|
|
@@ -16,6 +16,7 @@ import type { NotBuilt } from "../discover.ts";
|
|
|
16
16
|
import { partialNotes } from "../partial.ts";
|
|
17
17
|
import {
|
|
18
18
|
buildReport,
|
|
19
|
+
LANGUAGE,
|
|
19
20
|
capFindings,
|
|
20
21
|
sortFindings,
|
|
21
22
|
type Report,
|
|
@@ -265,7 +266,11 @@ export const analyzeVerb: Verb = ({ err, host, inputs, scope, openClient, option
|
|
|
265
266
|
version,
|
|
266
267
|
conformance: CONFORMANCE,
|
|
267
268
|
declaredGaps: DECLARED_GAPS,
|
|
268
|
-
target: {
|
|
269
|
+
target: {
|
|
270
|
+
kind: kindOf(kind, LANGUAGE),
|
|
271
|
+
root,
|
|
272
|
+
identity: moduleIdentity(host, scoped.target),
|
|
273
|
+
},
|
|
269
274
|
configurations: configurationsOf(run.projects, targetRoot),
|
|
270
275
|
notBuilt: notBuiltOf(run.notBuilt, targetRoot),
|
|
271
276
|
loaded: run.consumers.map((consumer) => ({
|
package/src/version.ts
CHANGED