@ttsc/evidence 0.25.0 → 0.26.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/README.md +24 -6
- package/lib/index.d.ts +7 -2
- package/lib/index.js +7 -2
- package/lib/internal/canonicalDigest.d.ts +1 -0
- package/lib/internal/canonicalDigest.js +97 -0
- package/lib/internal/loadPrismaModels.js +9 -2
- package/lib/internal/loadSwaggerOperations.js +96 -5
- package/lib/structures/ITtscEvidenceGraphReferenceBase.d.ts +48 -0
- package/lib/structures/ITtscEvidenceGraphTypeScriptClaim.d.ts +3 -3
- package/lib/structures/ITtscEvidenceGraphTypeScriptReference.d.ts +4 -4
- package/lib/typings/ITtscLintContributorRules.d.ts +23 -0
- package/lib/typings/TtscEvidenceGraphTypeScriptSymbol.d.ts +64 -29
- package/native/comments.go +162 -0
- package/native/config.go +2 -0
- package/native/declaration.go +213 -0
- package/native/documented.go +12 -12
- package/native/exports.go +64 -7
- package/native/fingerprint.go +351 -0
- package/native/graph.go +252 -16
- package/native/markdown.go +263 -0
- package/native/model.go +179 -1
- package/native/prisma.go +59 -3
- package/native/prisma_cache.go +7 -0
- package/native/review.go +355 -0
- package/native/swagger.go +8 -0
- package/native/typescript.go +697 -64
- package/native/unreadable.go +155 -0
- package/package.json +3 -3
package/native/typescript.go
CHANGED
|
@@ -14,7 +14,7 @@ func loadTypeScriptInventories(
|
|
|
14
14
|
config graphConfig,
|
|
15
15
|
) map[string]*artifactInventory {
|
|
16
16
|
inventories := map[string]*artifactInventory{}
|
|
17
|
-
extendTypeScriptInventories(root, sources, config, inventories)
|
|
17
|
+
extendTypeScriptInventories(root, sources, config, inventories, nil)
|
|
18
18
|
return inventories
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -30,6 +30,7 @@ func extendTypeScriptInventories(
|
|
|
30
30
|
sources []*shimast.SourceFile,
|
|
31
31
|
config graphConfig,
|
|
32
32
|
inventories map[string]*artifactInventory,
|
|
33
|
+
governed map[string]bool,
|
|
33
34
|
) {
|
|
34
35
|
bases := configuredBases(config, artifactTypeScript)
|
|
35
36
|
for _, file := range sources {
|
|
@@ -42,6 +43,9 @@ func extendTypeScriptInventories(
|
|
|
42
43
|
continue
|
|
43
44
|
}
|
|
44
45
|
address := base.addressOf(relative)
|
|
46
|
+
if governed != nil && matchesConfiguredTypeScriptFile(config, base, relative) {
|
|
47
|
+
governed[address.Key] = true
|
|
48
|
+
}
|
|
45
49
|
if inventories[address.Key] != nil {
|
|
46
50
|
continue
|
|
47
51
|
}
|
|
@@ -129,12 +133,13 @@ func scanTypeScriptInventoryAt(
|
|
|
129
133
|
file *shimast.SourceFile,
|
|
130
134
|
) *artifactInventory {
|
|
131
135
|
inventory := &artifactInventory{
|
|
132
|
-
Address:
|
|
133
|
-
Path:
|
|
134
|
-
Type:
|
|
135
|
-
Imports:
|
|
136
|
-
Exports:
|
|
137
|
-
UnitNodes:
|
|
136
|
+
Address: address.Key,
|
|
137
|
+
Path: address.Display,
|
|
138
|
+
Type: artifactTypeScript,
|
|
139
|
+
Imports: collectImportBindings(file),
|
|
140
|
+
Exports: collectModuleExports(file),
|
|
141
|
+
UnitNodes: map[string][]*shimast.Node{},
|
|
142
|
+
UnitContent: map[string][]*shimast.Node{},
|
|
138
143
|
}
|
|
139
144
|
supportedHosts := map[*shimast.Node]symbolSet{}
|
|
140
145
|
unitsByID := map[string]*evidenceUnit{}
|
|
@@ -151,6 +156,7 @@ func scanTypeScriptInventoryAt(
|
|
|
151
156
|
false,
|
|
152
157
|
"",
|
|
153
158
|
)
|
|
159
|
+
withdrawHiddenHosts(inventory, supportedHosts)
|
|
154
160
|
collectTypeScriptDeclarations(
|
|
155
161
|
file,
|
|
156
162
|
address.Key,
|
|
@@ -162,6 +168,7 @@ func scanTypeScriptInventoryAt(
|
|
|
162
168
|
// used to derive them. Declarations now retain those IDs directly, so release
|
|
163
169
|
// the transient index before this inventory enters the immutable graph.
|
|
164
170
|
inventory.UnitNodes = nil
|
|
171
|
+
inventory.UnitContent = nil
|
|
165
172
|
sort.Slice(inventory.Units, func(left int, right int) bool {
|
|
166
173
|
if inventory.Units[left].Target != inventory.Units[right].Target {
|
|
167
174
|
return inventory.Units[left].Target < inventory.Units[right].Target
|
|
@@ -171,6 +178,87 @@ func scanTypeScriptInventoryAt(
|
|
|
171
178
|
return inventory
|
|
172
179
|
}
|
|
173
180
|
|
|
181
|
+
// withdrawHiddenHosts takes every declaration of a withdrawn identity out of
|
|
182
|
+
// the host set, once materialization has decided which identities are withdrawn.
|
|
183
|
+
//
|
|
184
|
+
// Withdrawal is a property of the **identity**, while `supportedHosts` is filled
|
|
185
|
+
// in one node at a time by whichever collector walked that container. Those two
|
|
186
|
+
// facts disagree whenever an identity spans more than one declaration and the
|
|
187
|
+
// author tagged only one of them, which is every overload run and every merged
|
|
188
|
+
// interface: the unit came out marked, and the untagged sibling stayed a host,
|
|
189
|
+
// so a declaration the author had taken out of the API went on discharging
|
|
190
|
+
// coverage and acting as an exclusion carrier. Measured on both shapes; both
|
|
191
|
+
// were silent, because the unit really was marked.
|
|
192
|
+
//
|
|
193
|
+
// Reconciling here rather than in each collector is what turns a list of
|
|
194
|
+
// witnesses into one rule. A per-container withdrawal index closes the container
|
|
195
|
+
// it indexes and nothing else, and there is one such container per declaration
|
|
196
|
+
// form. This runs once, over the identities the whole file produced, so it
|
|
197
|
+
// reaches a form nobody has written yet — but only as far as that form's
|
|
198
|
+
// unit-to-node association goes, which is the condition below and the one place
|
|
199
|
+
// this is not yet a closed class.
|
|
200
|
+
//
|
|
201
|
+
// A position is given up only when **every** identity that reaches it is
|
|
202
|
+
// withdrawn. One node can host several: `export var price: number, live:
|
|
203
|
+
// number` is two property identities sharing the statement TypeScript attaches
|
|
204
|
+
// their block to, and withdrawing one of them must not take the other's only
|
|
205
|
+
// host position with it. Judging per node alone did exactly that, and refused a
|
|
206
|
+
// citation on a public declaration nobody had tagged. The exemplar is `var`
|
|
207
|
+
// rather than `const` because a partial withdrawal needs a second declaration
|
|
208
|
+
// of one identity, which for `const` is `TS2451`.
|
|
209
|
+
//
|
|
210
|
+
// What this cannot reach is a host node no unit records, so a form that
|
|
211
|
+
// registers a position owes that position to its unit. The variable declarator
|
|
212
|
+
// was the one that did not, and a withdrawn variable identity kept it until
|
|
213
|
+
// `collectTypeScriptVariables` began recording it. `documentedHosts` filters on
|
|
214
|
+
// the same association and was measured to answer identically either way, since
|
|
215
|
+
// it takes the first of a unit's *host* nodes and a declarator is a host only
|
|
216
|
+
// when its statement is one for the same symbol; the invariant is what was
|
|
217
|
+
// broken there rather than an observable answer.
|
|
218
|
+
//
|
|
219
|
+
// A second fault travelled with that one and has its own cause, which is why
|
|
220
|
+
// they are stated apart: a withdrawal tag read from a container is not the tag
|
|
221
|
+
// of the declarations
|
|
222
|
+
// inside it, so an inner declarator owes its own read, and recording the node
|
|
223
|
+
// would have left that exactly where it was.
|
|
224
|
+
//
|
|
225
|
+
// `documentedHosts` needs no equivalent: it skips a withdrawn unit before it
|
|
226
|
+
// ever consults the host set.
|
|
227
|
+
func withdrawHiddenHosts(
|
|
228
|
+
inventory *artifactInventory,
|
|
229
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
230
|
+
) {
|
|
231
|
+
type hostPosition struct {
|
|
232
|
+
node *shimast.Node
|
|
233
|
+
symbol string
|
|
234
|
+
}
|
|
235
|
+
live := map[hostPosition]bool{}
|
|
236
|
+
withdrawn := map[hostPosition]bool{}
|
|
237
|
+
for _, unit := range inventory.Units {
|
|
238
|
+
for _, node := range inventory.UnitNodes[unit.ID] {
|
|
239
|
+
position := hostPosition{node: node, symbol: unit.Symbol}
|
|
240
|
+
if unit.Hidden == "" {
|
|
241
|
+
live[position] = true
|
|
242
|
+
continue
|
|
243
|
+
}
|
|
244
|
+
withdrawn[position] = true
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
for position := range withdrawn {
|
|
248
|
+
if live[position] {
|
|
249
|
+
continue
|
|
250
|
+
}
|
|
251
|
+
hosts := supportedHosts[position.node]
|
|
252
|
+
if hosts == nil {
|
|
253
|
+
continue
|
|
254
|
+
}
|
|
255
|
+
delete(hosts, position.symbol)
|
|
256
|
+
if len(hosts) == 0 {
|
|
257
|
+
delete(supportedHosts, position.node)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
174
262
|
// collectTypeScriptStatements materializes the public units one statement list
|
|
175
263
|
// declares.
|
|
176
264
|
//
|
|
@@ -197,9 +285,10 @@ func collectTypeScriptStatements(
|
|
|
197
285
|
exports := collectLocalExportNames(statements)
|
|
198
286
|
hiddenNames := collectHiddenDeclarationNames(file, statements)
|
|
199
287
|
// Built on the first namespace this list holds rather than up front, so a
|
|
200
|
-
// file declaring none pays nothing.
|
|
201
|
-
// source, and most of them have no namespace at all.
|
|
288
|
+
// file declaring none pays nothing.
|
|
202
289
|
var functionNames map[string]bool
|
|
290
|
+
// The same, for the class an interface in this list may merge with.
|
|
291
|
+
var classNames map[string]bool
|
|
203
292
|
for _, statement := range statements.Nodes {
|
|
204
293
|
if statement == nil {
|
|
205
294
|
continue
|
|
@@ -210,7 +299,7 @@ func collectTypeScriptStatements(
|
|
|
210
299
|
if name == "" {
|
|
211
300
|
continue
|
|
212
301
|
}
|
|
213
|
-
targets :=
|
|
302
|
+
targets := publicTypeScriptExports(
|
|
214
303
|
statement,
|
|
215
304
|
name,
|
|
216
305
|
exports,
|
|
@@ -224,26 +313,57 @@ func collectTypeScriptStatements(
|
|
|
224
313
|
if memberHidden == "" {
|
|
225
314
|
addTypeScriptHost(supportedHosts, statement, "type")
|
|
226
315
|
}
|
|
227
|
-
|
|
228
|
-
|
|
316
|
+
// An interface merged with a class declares that class's instance
|
|
317
|
+
// members, so they take the instance address the class body form takes.
|
|
318
|
+
// Addressing them from the bare name instead published `Sale.charge` for
|
|
319
|
+
// a member reached as `Sale.prototype.charge`: a path no consumer can
|
|
320
|
+
// walk, a second unit for a method the class already declared, and an
|
|
321
|
+
// obligation that stayed owed however the real member was cited.
|
|
322
|
+
//
|
|
323
|
+
// The merge also brings the type-only rule with it. The members of an
|
|
324
|
+
// interface no class merges with are type-space and project through a
|
|
325
|
+
// type-only alias, a namespace or function partner included, but a
|
|
326
|
+
// class-merged one's are reached through the class value that alias does
|
|
327
|
+
// not expose, which is the address the class branch is already
|
|
328
|
+
// suppressed for. Publishing them here would breach that guard at
|
|
329
|
+
// exactly the address it exists to keep empty. What `target.TypeOnly`
|
|
330
|
+
// reaches, and what it does not, is stated once on the class branch this
|
|
331
|
+
// guard mirrors.
|
|
332
|
+
if classNames == nil {
|
|
333
|
+
classNames = collectClassDeclarationNames(statements)
|
|
334
|
+
}
|
|
335
|
+
mergedWithClass := classNames[name]
|
|
336
|
+
for _, target := range targets {
|
|
337
|
+
identity := qualifyTypeScriptName(prefix, target.Public)
|
|
229
338
|
unit := addTypeScriptUnit(
|
|
230
339
|
inventory,
|
|
231
340
|
unitsByID,
|
|
232
341
|
statement,
|
|
342
|
+
statement,
|
|
233
343
|
"type",
|
|
234
344
|
identity,
|
|
235
345
|
parentID,
|
|
236
346
|
memberHidden,
|
|
237
347
|
)
|
|
348
|
+
memberOwner := identity
|
|
349
|
+
if mergedWithClass {
|
|
350
|
+
if typeOnlyProjection || target.TypeOnly {
|
|
351
|
+
continue
|
|
352
|
+
}
|
|
353
|
+
memberOwner = qualifyTypeScriptName(identity, "prototype")
|
|
354
|
+
}
|
|
238
355
|
collectPropertyMembers(
|
|
239
356
|
file,
|
|
240
357
|
statement.AsInterfaceDeclaration().Members,
|
|
241
|
-
|
|
358
|
+
memberOwner,
|
|
242
359
|
unit.ID,
|
|
243
360
|
inventory,
|
|
244
361
|
supportedHosts,
|
|
245
362
|
unitsByID,
|
|
246
363
|
memberHidden,
|
|
364
|
+
// Only the class merge makes these value-space. An interface's own
|
|
365
|
+
// members are type-space and a type-only export publishes them.
|
|
366
|
+
mergedWithClass,
|
|
247
367
|
)
|
|
248
368
|
}
|
|
249
369
|
case shimast.KindTypeAliasDeclaration:
|
|
@@ -272,6 +392,7 @@ func collectTypeScriptStatements(
|
|
|
272
392
|
inventory,
|
|
273
393
|
unitsByID,
|
|
274
394
|
statement,
|
|
395
|
+
statement,
|
|
275
396
|
"type",
|
|
276
397
|
identity,
|
|
277
398
|
parentID,
|
|
@@ -287,6 +408,7 @@ func collectTypeScriptStatements(
|
|
|
287
408
|
supportedHosts,
|
|
288
409
|
unitsByID,
|
|
289
410
|
memberHidden,
|
|
411
|
+
false,
|
|
290
412
|
)
|
|
291
413
|
}
|
|
292
414
|
}
|
|
@@ -317,11 +439,12 @@ func collectTypeScriptStatements(
|
|
|
317
439
|
inventory,
|
|
318
440
|
unitsByID,
|
|
319
441
|
statement,
|
|
442
|
+
statement,
|
|
320
443
|
"function",
|
|
321
444
|
qualifyTypeScriptName(prefix, name),
|
|
322
445
|
parentID,
|
|
323
446
|
memberHidden,
|
|
324
|
-
)
|
|
447
|
+
).markSpace(true)
|
|
325
448
|
}
|
|
326
449
|
case shimast.KindVariableStatement:
|
|
327
450
|
if typeOnlyProjection {
|
|
@@ -329,6 +452,7 @@ func collectTypeScriptStatements(
|
|
|
329
452
|
}
|
|
330
453
|
memberHidden := typeScriptHidingTag(file, statement, hidden)
|
|
331
454
|
for symbol := range collectTypeScriptVariables(
|
|
455
|
+
file,
|
|
332
456
|
statement,
|
|
333
457
|
prefix,
|
|
334
458
|
parentID,
|
|
@@ -347,23 +471,57 @@ func collectTypeScriptStatements(
|
|
|
347
471
|
addTypeScriptHost(supportedHosts, statement, symbol)
|
|
348
472
|
}
|
|
349
473
|
case shimast.KindClassDeclaration:
|
|
350
|
-
|
|
474
|
+
name := declarationName(statement.Name())
|
|
475
|
+
if name == "" {
|
|
351
476
|
continue
|
|
352
477
|
}
|
|
353
|
-
name
|
|
354
|
-
|
|
355
|
-
|
|
478
|
+
// A class name is type-space, so a type-only alias exposes it exactly
|
|
479
|
+
// as it exposes an interface. What the alias withholds is the members:
|
|
480
|
+
// `C.prototype.field` and `C.staticField` are paths through the class
|
|
481
|
+
// *value*, and a type-only alias exposes no value to walk them from.
|
|
482
|
+
//
|
|
483
|
+
// This guard answers an export written in this file, where the
|
|
484
|
+
// declaration kind is in hand: `target.TypeOnly` sees `export type { C }`
|
|
485
|
+
// and `export { type C }` alike, and `typeOnlyProjection` sees a
|
|
486
|
+
// type-only alias of an enclosing namespace. A re-export naming another
|
|
487
|
+
// module has no such context and is answered at traversal time instead,
|
|
488
|
+
// from `evidenceUnit.ValueSpace`, which is why these members are marked
|
|
489
|
+
// there. The interface branch mirrors this guard for a class-merged
|
|
490
|
+
// interface and points here for the reason.
|
|
491
|
+
targets := publicTypeScriptExports(
|
|
356
492
|
statement,
|
|
357
493
|
name,
|
|
358
494
|
exports,
|
|
359
|
-
|
|
495
|
+
true,
|
|
360
496
|
implicitlyExported,
|
|
361
|
-
)
|
|
362
|
-
|
|
363
|
-
|
|
497
|
+
)
|
|
498
|
+
if len(targets) == 0 {
|
|
499
|
+
continue
|
|
500
|
+
}
|
|
501
|
+
memberHidden := hidingTagFor(hidden, hiddenNames, name)
|
|
502
|
+
if memberHidden == "" {
|
|
503
|
+
addTypeScriptHost(supportedHosts, statement, "type")
|
|
504
|
+
}
|
|
505
|
+
for _, target := range targets {
|
|
506
|
+
identity := qualifyTypeScriptName(prefix, target.Public)
|
|
507
|
+
unit := addTypeScriptUnit(
|
|
508
|
+
inventory,
|
|
509
|
+
unitsByID,
|
|
510
|
+
statement,
|
|
364
511
|
statement,
|
|
365
|
-
|
|
512
|
+
"type",
|
|
513
|
+
identity,
|
|
366
514
|
parentID,
|
|
515
|
+
memberHidden,
|
|
516
|
+
)
|
|
517
|
+
if typeOnlyProjection || target.TypeOnly {
|
|
518
|
+
continue
|
|
519
|
+
}
|
|
520
|
+
collectClassMembers(
|
|
521
|
+
file,
|
|
522
|
+
statement,
|
|
523
|
+
identity,
|
|
524
|
+
unit.ID,
|
|
367
525
|
inventory,
|
|
368
526
|
supportedHosts,
|
|
369
527
|
unitsByID,
|
|
@@ -405,6 +563,7 @@ func collectTypeScriptStatements(
|
|
|
405
563
|
inventory,
|
|
406
564
|
unitsByID,
|
|
407
565
|
statement,
|
|
566
|
+
statement,
|
|
408
567
|
"type",
|
|
409
568
|
identity,
|
|
410
569
|
parentID,
|
|
@@ -434,11 +593,12 @@ func collectTypeScriptStatements(
|
|
|
434
593
|
//
|
|
435
594
|
// A namespace merges only with a function, a class, or an enum in the same
|
|
436
595
|
// scope; a `const` or `let` of the same name is `TS2451`, measured against the
|
|
437
|
-
// pinned compiler.
|
|
438
|
-
// materializes
|
|
439
|
-
//
|
|
440
|
-
//
|
|
441
|
-
//
|
|
596
|
+
// pinned compiler. Only the function partner makes the namespace a static side
|
|
597
|
+
// that materializes nothing. A class partner keeps its namespace, because a
|
|
598
|
+
// companion namespace beside a class is authored contract — `namespace Sale`
|
|
599
|
+
// holding `Sale.IProps` beside `class Sale` — rather than the generated
|
|
600
|
+
// accessor machinery `get.path` and `get.Output` are. The collector has no enum
|
|
601
|
+
// case, so an enum partner reaches nothing either way.
|
|
442
602
|
//
|
|
443
603
|
// The name alone decides, without consulting export modifiers, because
|
|
444
604
|
// TypeScript refuses a merged declaration whose halves disagree on export
|
|
@@ -464,7 +624,76 @@ func collectFunctionDeclarationNames(
|
|
|
464
624
|
return names
|
|
465
625
|
}
|
|
466
626
|
|
|
627
|
+
// collectClassDeclarationNames indexes the local names a statement list
|
|
628
|
+
// declares as classes, which is what an interface in the same list merges with.
|
|
629
|
+
//
|
|
630
|
+
// The merge is what decides a member's address rather than its kind. An
|
|
631
|
+
// interface merged with a class describes that class's instance side, so
|
|
632
|
+
// `interface Sale { charge(): void }` beside `class Sale` declares the same
|
|
633
|
+
// member the class body would and takes the same `Sale.prototype.charge`
|
|
634
|
+
// address. A namespace is the other partner and adds statics instead, which is
|
|
635
|
+
// why only the interface case moves.
|
|
636
|
+
//
|
|
637
|
+
// The whole list is indexed rather than only the statements already collected,
|
|
638
|
+
// because merging does not depend on which declaration is written first, and
|
|
639
|
+
// export modifiers are not consulted for the reason
|
|
640
|
+
// `collectFunctionDeclarationNames` gives: TypeScript refuses a merge whose
|
|
641
|
+
// halves disagree on export.
|
|
642
|
+
func collectClassDeclarationNames(
|
|
643
|
+
statements *shimast.NodeList,
|
|
644
|
+
) map[string]bool {
|
|
645
|
+
names := map[string]bool{}
|
|
646
|
+
if statements == nil {
|
|
647
|
+
return names
|
|
648
|
+
}
|
|
649
|
+
for _, statement := range statements.Nodes {
|
|
650
|
+
if statement == nil ||
|
|
651
|
+
statement.Kind != shimast.KindClassDeclaration {
|
|
652
|
+
continue
|
|
653
|
+
}
|
|
654
|
+
if name := declarationName(statement.Name()); name != "" {
|
|
655
|
+
names[name] = true
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return names
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// collectTypeScriptVariables materializes a variable statement's exported
|
|
662
|
+
// declarators.
|
|
663
|
+
//
|
|
664
|
+
// A variable is a function only when a `const` is initialized with a function
|
|
665
|
+
// value, and that is the inverse of the member rule in memberSymbol:
|
|
666
|
+
// here the annotation decides nothing, because a variable's declared type
|
|
667
|
+
// describes a value that already exists rather than stating a contract. The
|
|
668
|
+
// three conditions below are each load-bearing and each measured separately, at
|
|
669
|
+
// module and at namespace scope: a binding pattern's initializer belongs to the
|
|
670
|
+
// pattern rather than to any leaf, `let` and `var` are excluded whatever they
|
|
671
|
+
// hold, and the initializer is read syntactically because these rules run with
|
|
672
|
+
// no type checker.
|
|
673
|
+
// A declarator is both a host position and a unit node, and it has to be both.
|
|
674
|
+
// It was only the first: `supportedHosts` held it while no unit recorded it, so
|
|
675
|
+
// nothing that walks from a unit to its declarations could see it. Two answers
|
|
676
|
+
// were wrong and both were silent. `withdrawHiddenHosts` could not take the
|
|
677
|
+
// position away from a withdrawn identity, so a declaration the author had
|
|
678
|
+
// removed from the API went on discharging coverage and carrying exclusions.
|
|
679
|
+
// And a citation written on it resolved to no semantic host, so
|
|
680
|
+
// `singleEvidencePerSymbol` counted the statement's identities as citing zero
|
|
681
|
+
// units while the same run reported the obligation satisfied.
|
|
682
|
+
//
|
|
683
|
+
// A third consumer reads the same association and was measured to answer
|
|
684
|
+
// identically either way. `hostNodesOf` keeps a unit's host nodes and takes the
|
|
685
|
+
// first, and a declarator is a host only when its statement is one for the same
|
|
686
|
+
// symbol, so `evidence/documented` always had the wrapper to look at. The
|
|
687
|
+
// invariant was broken there rather than the answer, which is reason enough:
|
|
688
|
+
// the next declaration form to register a position will not be so lucky.
|
|
689
|
+
//
|
|
690
|
+
// Its own withdrawal tag is read here for a separate fault rather than the same
|
|
691
|
+
// one: the statement wrapper's tag was taken for every declarator it holds, so
|
|
692
|
+
// `@internal` written on an inner declarator withdrew nothing. Recording the
|
|
693
|
+
// node closes the first two and leaves this one standing, which is why #1126
|
|
694
|
+
// states them apart.
|
|
467
695
|
func collectTypeScriptVariables(
|
|
696
|
+
file *shimast.SourceFile,
|
|
468
697
|
statement *shimast.Node,
|
|
469
698
|
prefix []string,
|
|
470
699
|
parentID string,
|
|
@@ -495,6 +724,7 @@ func collectTypeScriptVariables(
|
|
|
495
724
|
isFunctionValue(value.Initializer) {
|
|
496
725
|
symbol = "function"
|
|
497
726
|
}
|
|
727
|
+
declaratorHidden := typeScriptHidingTag(file, declaration, hidden)
|
|
498
728
|
for _, binding := range bindingIdentifierNodes(declaration.Name()) {
|
|
499
729
|
name := declarationName(binding)
|
|
500
730
|
targets := publicTypeScriptNames(
|
|
@@ -507,7 +737,7 @@ func collectTypeScriptVariables(
|
|
|
507
737
|
if len(targets) == 0 {
|
|
508
738
|
continue
|
|
509
739
|
}
|
|
510
|
-
if
|
|
740
|
+
if declaratorHidden == "" {
|
|
511
741
|
addTypeScriptHost(supportedHosts, declaration, symbol)
|
|
512
742
|
}
|
|
513
743
|
for _, name := range targets {
|
|
@@ -515,14 +745,18 @@ func collectTypeScriptVariables(
|
|
|
515
745
|
inventory,
|
|
516
746
|
unitsByID,
|
|
517
747
|
binding,
|
|
748
|
+
declaration,
|
|
518
749
|
symbol,
|
|
519
750
|
qualifyTypeScriptName(prefix, name),
|
|
520
751
|
parentID,
|
|
521
|
-
|
|
752
|
+
declaratorHidden,
|
|
522
753
|
)
|
|
523
|
-
//
|
|
524
|
-
//
|
|
525
|
-
// is
|
|
754
|
+
// TypeScript attaches a variable's leading JSDoc to the statement
|
|
755
|
+
// wrapper, so that is where a citation for this unit lives and the
|
|
756
|
+
// wrapper is a position this identity owns. It is not this
|
|
757
|
+
// identity's content: the same wrapper declares every sibling of
|
|
758
|
+
// this declarator, and their text belongs to them.
|
|
759
|
+
unit.markSpace(true)
|
|
526
760
|
inventory.recordUnitNode(unit.ID, statement)
|
|
527
761
|
}
|
|
528
762
|
found[symbol] = true
|
|
@@ -531,11 +765,33 @@ func collectTypeScriptVariables(
|
|
|
531
765
|
return found
|
|
532
766
|
}
|
|
533
767
|
|
|
534
|
-
|
|
768
|
+
// collectClassMembers materializes the public contract a class declares.
|
|
769
|
+
//
|
|
770
|
+
// A method is a function unit and a field is a property unit, which is the
|
|
771
|
+
// mapping a reader already assumes: the class is the subject, its methods are
|
|
772
|
+
// what the subject does, and its fields are the measured facts it carries.
|
|
773
|
+
// `memberSymbol` owns the one exception, where a field written as a
|
|
774
|
+
// callable joins the methods, and "written as" is literal: see
|
|
775
|
+
// `isDirectFunctionType`.
|
|
776
|
+
// Every member hangs below the class unit, so a citation on the class
|
|
777
|
+
// acknowledges the members it selected.
|
|
778
|
+
//
|
|
779
|
+
// A constructor is not a unit of its own, now that the class is: construction
|
|
780
|
+
// is how the subject comes to be, and the subject already carries that
|
|
781
|
+
// obligation. It is still read, because the fields it declares through the
|
|
782
|
+
// parameter-property shorthand are the class's fields; `collectParameterProperties`
|
|
783
|
+
// takes those.
|
|
784
|
+
//
|
|
785
|
+
// Two shapes stay out entirely. An accessor, including an auto-accessor, is a
|
|
786
|
+
// get/set pair rather than a member variable, which is the exclusion the
|
|
787
|
+
// published contract has always stated. A member with no citable name — a
|
|
788
|
+
// private identifier, an index signature, a computed name, a static block — has
|
|
789
|
+
// no target an author could write.
|
|
790
|
+
func collectClassMembers(
|
|
535
791
|
file *shimast.SourceFile,
|
|
536
792
|
statement *shimast.Node,
|
|
537
793
|
classIdentity []string,
|
|
538
|
-
|
|
794
|
+
classID string,
|
|
539
795
|
inventory *artifactInventory,
|
|
540
796
|
supportedHosts map[*shimast.Node]symbolSet,
|
|
541
797
|
unitsByID map[string]*evidenceUnit,
|
|
@@ -545,46 +801,246 @@ func collectClassCallables(
|
|
|
545
801
|
if class.Members == nil {
|
|
546
802
|
return
|
|
547
803
|
}
|
|
804
|
+
// A member's own tag is read from the node in hand, and that is enough for
|
|
805
|
+
// the unit: `addTypeScriptUnit` keeps the first tag any declaration of an
|
|
806
|
+
// identity carries, so an overload run tagged on one half comes out marked
|
|
807
|
+
// whichever half is written first. The host set is reconciled against those
|
|
808
|
+
// identities afterwards by `withdrawHiddenHosts`, which is what stops the
|
|
809
|
+
// untagged half of a withdrawn member from staying a claim host.
|
|
810
|
+
//
|
|
811
|
+
// The constructor is resolved separately and up front, because it is the one
|
|
812
|
+
// container here that declares units without being one: its tag has to reach
|
|
813
|
+
// the parameter properties rather than a member of its own, and no unit of
|
|
814
|
+
// its own will carry it there.
|
|
815
|
+
constructorHidden := ""
|
|
816
|
+
constructorResolved := false
|
|
548
817
|
for _, member := range class.Members.Nodes {
|
|
549
|
-
if member == nil
|
|
818
|
+
if member == nil {
|
|
819
|
+
continue
|
|
820
|
+
}
|
|
821
|
+
// A constructor is judged before the public check, because its own
|
|
822
|
+
// visibility is not its parameter properties'. A `private constructor`
|
|
823
|
+
// closes construction from outside; `public readonly price` on one of its
|
|
824
|
+
// parameters is still an instance field every holder of the object reads.
|
|
825
|
+
if member.Kind == shimast.KindConstructor {
|
|
826
|
+
// Resolved once per class rather than per declaration, so an overload
|
|
827
|
+
// run does not rescan the member list for every signature it holds.
|
|
828
|
+
if !constructorResolved {
|
|
829
|
+
constructorHidden = constructorHidingTag(file, class.Members, hidden)
|
|
830
|
+
constructorResolved = true
|
|
831
|
+
}
|
|
832
|
+
collectParameterProperties(
|
|
833
|
+
file,
|
|
834
|
+
member,
|
|
835
|
+
classIdentity,
|
|
836
|
+
classID,
|
|
837
|
+
inventory,
|
|
838
|
+
supportedHosts,
|
|
839
|
+
unitsByID,
|
|
840
|
+
constructorHidden,
|
|
841
|
+
)
|
|
842
|
+
continue
|
|
843
|
+
}
|
|
844
|
+
if !isPublicClassMember(member) {
|
|
550
845
|
continue
|
|
551
846
|
}
|
|
552
|
-
|
|
847
|
+
symbol := ""
|
|
553
848
|
switch member.Kind {
|
|
554
849
|
case shimast.KindMethodDeclaration:
|
|
555
|
-
|
|
850
|
+
symbol = "function"
|
|
556
851
|
case shimast.KindPropertyDeclaration:
|
|
557
852
|
if member.ModifierFlags()&shimast.ModifierFlagsAccessor != 0 {
|
|
558
853
|
continue
|
|
559
854
|
}
|
|
560
855
|
property := member.AsPropertyDeclaration()
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
if !callable {
|
|
856
|
+
symbol = memberSymbol(property.Initializer, property.Type)
|
|
857
|
+
default:
|
|
565
858
|
continue
|
|
566
859
|
}
|
|
567
|
-
|
|
568
|
-
|
|
860
|
+
addClassMemberUnit(
|
|
861
|
+
member,
|
|
862
|
+
declarationName(member.Name()),
|
|
863
|
+
symbol,
|
|
864
|
+
shimast.GetCombinedModifierFlags(member)&shimast.ModifierFlagsStatic != 0,
|
|
865
|
+
classIdentity,
|
|
866
|
+
classID,
|
|
867
|
+
inventory,
|
|
868
|
+
supportedHosts,
|
|
869
|
+
unitsByID,
|
|
870
|
+
typeScriptHidingTag(file, member, hidden),
|
|
871
|
+
)
|
|
872
|
+
}
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
// collectParameterProperties materializes the fields a constructor declares
|
|
876
|
+
// through the parameter-property shorthand.
|
|
877
|
+
//
|
|
878
|
+
// `constructor(public readonly price: number)` declares the same public
|
|
879
|
+
// instance field as `readonly price: number` written in the class body, and
|
|
880
|
+
// TypeScript attaches a leading documentation block to the parameter, so it is
|
|
881
|
+
// a real position a citation can live in. Reading only `class.Members` would
|
|
882
|
+
// leave the shorthand invisible while the body form was selected, which is one
|
|
883
|
+
// defect wearing two syntaxes.
|
|
884
|
+
//
|
|
885
|
+
// Every parameter property is an instance field. A constructor cannot be
|
|
886
|
+
// static, and TypeScript refuses a parameter property on an overload
|
|
887
|
+
// signature, so the implementation constructor is the only one that reaches
|
|
888
|
+
// here with any.
|
|
889
|
+
//
|
|
890
|
+
// The tag `hidden` carries is the constructor's, already resolved across every
|
|
891
|
+
// declaration of it by `constructorHidingTag`. The constructor is the only
|
|
892
|
+
// container in this collector that declares units without being one, so
|
|
893
|
+
// forwarding the class's tag alone would leave `@internal` on a constructor
|
|
894
|
+
// inert while the same tag on a class or on the field itself withdraws.
|
|
895
|
+
func collectParameterProperties(
|
|
896
|
+
file *shimast.SourceFile,
|
|
897
|
+
constructor *shimast.Node,
|
|
898
|
+
classIdentity []string,
|
|
899
|
+
classID string,
|
|
900
|
+
inventory *artifactInventory,
|
|
901
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
902
|
+
unitsByID map[string]*evidenceUnit,
|
|
903
|
+
hidden string,
|
|
904
|
+
) {
|
|
905
|
+
if constructor.ParameterList() == nil {
|
|
906
|
+
return
|
|
907
|
+
}
|
|
908
|
+
for _, parameter := range constructor.Parameters() {
|
|
909
|
+
if parameter == nil || !isParameterProperty(parameter) {
|
|
569
910
|
continue
|
|
570
911
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
identity = qualifyTypeScriptName(classIdentity, memberName)
|
|
912
|
+
if !isPublicClassMember(parameter) {
|
|
913
|
+
continue
|
|
574
914
|
}
|
|
575
|
-
|
|
576
|
-
|
|
915
|
+
value := parameter.AsParameterDeclaration()
|
|
916
|
+
addClassMemberUnit(
|
|
917
|
+
parameter,
|
|
918
|
+
declarationName(parameter.Name()),
|
|
919
|
+
memberSymbol(value.Initializer, value.Type),
|
|
920
|
+
false,
|
|
921
|
+
classIdentity,
|
|
922
|
+
classID,
|
|
577
923
|
inventory,
|
|
924
|
+
supportedHosts,
|
|
578
925
|
unitsByID,
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
parentID,
|
|
583
|
-
memberHidden,
|
|
926
|
+
// A parameter property is declared exactly once, so its own tag is the
|
|
927
|
+
// only one below the constructor's and no identity index is needed.
|
|
928
|
+
typeScriptHidingTag(file, parameter, hidden),
|
|
584
929
|
)
|
|
585
|
-
|
|
586
|
-
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
|
|
933
|
+
// constructorHidingTag resolves the withdrawal tag of a class's constructor
|
|
934
|
+
// across every declaration of it.
|
|
935
|
+
//
|
|
936
|
+
// An overload run is one constructor written several times, and the tag may sit
|
|
937
|
+
// on any of them: a signature is where a reader looks for the documentation,
|
|
938
|
+
// while only the implementation declares parameter properties. Reading the tag
|
|
939
|
+
// per node would make the withdrawal depend on which half the author documented,
|
|
940
|
+
// which is the source-order accident every other merged declaration in this
|
|
941
|
+
// collector already refuses.
|
|
942
|
+
func constructorHidingTag(
|
|
943
|
+
file *shimast.SourceFile,
|
|
944
|
+
members *shimast.NodeList,
|
|
945
|
+
inherited string,
|
|
946
|
+
) string {
|
|
947
|
+
if inherited != "" {
|
|
948
|
+
return inherited
|
|
949
|
+
}
|
|
950
|
+
if file == nil || members == nil {
|
|
951
|
+
return ""
|
|
952
|
+
}
|
|
953
|
+
for _, member := range members.Nodes {
|
|
954
|
+
if member == nil || member.Kind != shimast.KindConstructor {
|
|
955
|
+
continue
|
|
587
956
|
}
|
|
957
|
+
if tag := typeScriptHidingTag(file, member, ""); tag != "" {
|
|
958
|
+
return tag
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
return ""
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
// isParameterProperty reports whether a constructor parameter also declares a
|
|
965
|
+
// field, which is exactly what a property modifier says.
|
|
966
|
+
//
|
|
967
|
+
// The mask is TypeScript's own and holds five: the three accessibility
|
|
968
|
+
// modifiers, `readonly`, and `override`. Naming the familiar four is what a
|
|
969
|
+
// summary reaches for, and it is short by `override`, whose own meaning is
|
|
970
|
+
// about the base class rather than about the field, so it does not read as a
|
|
971
|
+
// field declaration at all. On a class extending one that declares `rate`,
|
|
972
|
+
// `constructor(override rate: number)` compiles, emits the assignment, and
|
|
973
|
+
// declares a public instance field. Take the mask rather than restating it;
|
|
974
|
+
// upstream's own comment beside the constant enumerates four.
|
|
975
|
+
func isParameterProperty(parameter *shimast.Node) bool {
|
|
976
|
+
return shimast.GetCombinedModifierFlags(parameter)&
|
|
977
|
+
shimast.ModifierFlagsParameterPropertyModifier != 0
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
// memberSymbol classifies one member variable by how it is written.
|
|
981
|
+
//
|
|
982
|
+
// Not by what it resolves to: an arrow or function initializer, or a type
|
|
983
|
+
// annotation spelled as a function type, makes a member a callable, and one
|
|
984
|
+
// annotated with an alias of that type does not. `isDirectFunctionType` says
|
|
985
|
+
// why.
|
|
986
|
+
//
|
|
987
|
+
// Spelled once because the answer may not depend on which syntax declared the
|
|
988
|
+
// member. A field in a class body, the same field written as a constructor
|
|
989
|
+
// parameter property, and the same member spelled on an interface or an
|
|
990
|
+
// object-shaped type alias are one contract in four spellings, and letting any
|
|
991
|
+
// of them classify separately is the syntax dependence this collector exists to
|
|
992
|
+
// remove. An interface once answered `property` to `charge: () => void` while a
|
|
993
|
+
// class answered `function`, so a `symbol: "function"` claim over a file of
|
|
994
|
+
// interfaces selected nothing, deactivated, and passed with no coverage.
|
|
995
|
+
func memberSymbol(initializer *shimast.Node, declared *shimast.Node) string {
|
|
996
|
+
if isFunctionValue(initializer) || isDirectFunctionType(declared) {
|
|
997
|
+
return "function"
|
|
998
|
+
}
|
|
999
|
+
return "property"
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// addClassMemberUnit registers one public class member under the address that
|
|
1003
|
+
// reaches it from the class.
|
|
1004
|
+
//
|
|
1005
|
+
// An instance member is addressed through `prototype` and a static member
|
|
1006
|
+
// directly, because those are the two paths a consumer can actually write. The
|
|
1007
|
+
// class unit is the parent, so the member is a structural descendant of the
|
|
1008
|
+
// subject that declares it rather than a sibling of the class's neighbours.
|
|
1009
|
+
// `memberHidden` is this declaration's own withdrawal tag, inherited from its
|
|
1010
|
+
// class. A sibling declaration of the same identity may carry one this node does
|
|
1011
|
+
// not; `addTypeScriptUnit` folds that into the unit, and `withdrawHiddenHosts`
|
|
1012
|
+
// then takes this node out of the host set.
|
|
1013
|
+
func addClassMemberUnit(
|
|
1014
|
+
node *shimast.Node,
|
|
1015
|
+
name string,
|
|
1016
|
+
symbol string,
|
|
1017
|
+
static bool,
|
|
1018
|
+
classIdentity []string,
|
|
1019
|
+
classID string,
|
|
1020
|
+
inventory *artifactInventory,
|
|
1021
|
+
supportedHosts map[*shimast.Node]symbolSet,
|
|
1022
|
+
unitsByID map[string]*evidenceUnit,
|
|
1023
|
+
memberHidden string,
|
|
1024
|
+
) {
|
|
1025
|
+
if name == "" {
|
|
1026
|
+
return
|
|
1027
|
+
}
|
|
1028
|
+
identity := qualifyTypeScriptName(classIdentity, "prototype", name)
|
|
1029
|
+
if static {
|
|
1030
|
+
identity = qualifyTypeScriptName(classIdentity, name)
|
|
1031
|
+
}
|
|
1032
|
+
addTypeScriptUnit(
|
|
1033
|
+
inventory,
|
|
1034
|
+
unitsByID,
|
|
1035
|
+
node,
|
|
1036
|
+
node,
|
|
1037
|
+
symbol,
|
|
1038
|
+
identity,
|
|
1039
|
+
classID,
|
|
1040
|
+
memberHidden,
|
|
1041
|
+
).markSpace(true)
|
|
1042
|
+
if memberHidden == "" {
|
|
1043
|
+
addTypeScriptHost(supportedHosts, node, symbol)
|
|
588
1044
|
}
|
|
589
1045
|
}
|
|
590
1046
|
|
|
@@ -612,6 +1068,19 @@ func isFunctionValue(node *shimast.Node) bool {
|
|
|
612
1068
|
return false
|
|
613
1069
|
}
|
|
614
1070
|
|
|
1071
|
+
// isDirectFunctionType reports whether a type annotation is written as a
|
|
1072
|
+
// function type, rather than whether it resolves to one.
|
|
1073
|
+
//
|
|
1074
|
+
// Syntactic on purpose, and this is the sentence every summary of the class
|
|
1075
|
+
// field rule gets wrong. `evidence/graph` declares `NeedsTypeChecker() false`,
|
|
1076
|
+
// so nothing here can follow `type Handler = () => void` to its target: a field
|
|
1077
|
+
// annotated `Handler` is a property, while the same field annotated
|
|
1078
|
+
// `() => void` is a function. Parentheses are unwrapped because they change
|
|
1079
|
+
// nothing a reader means; a constructor type, a union with a callable in it,
|
|
1080
|
+
// and an alias are all left alone because resolving them is the checker's job.
|
|
1081
|
+
//
|
|
1082
|
+
// Anyone restating this as "a field that holds a function" has restated
|
|
1083
|
+
// something else. Twice now, on shipped surfaces.
|
|
615
1084
|
func isDirectFunctionType(node *shimast.Node) bool {
|
|
616
1085
|
for node != nil && node.Kind == shimast.KindParenthesizedType {
|
|
617
1086
|
parenthesized := node.AsParenthesizedTypeNode()
|
|
@@ -666,6 +1135,13 @@ func collectTypeScriptModule(
|
|
|
666
1135
|
if name != "" {
|
|
667
1136
|
identity := qualifyTypeScriptName(qualified, name)
|
|
668
1137
|
innerHidden := typeScriptHidingTag(file, module.Body, hidden)
|
|
1138
|
+
// No citation reaches this position today. TypeScript attaches a leading
|
|
1139
|
+
// block to the outer declaration of a dotted namespace, so the tag on
|
|
1140
|
+
// `export namespace Outer.Inner {}` resolves through the outer host and
|
|
1141
|
+
// the inner one is written and never read. It is registered anyway,
|
|
1142
|
+
// because the host set is derived from the unit set and a position a unit
|
|
1143
|
+
// records must be in it; the day a form does attach a block here, the
|
|
1144
|
+
// guard is the thing that was already right.
|
|
669
1145
|
if innerHidden == "" {
|
|
670
1146
|
addTypeScriptHost(supportedHosts, module.Body, "type")
|
|
671
1147
|
}
|
|
@@ -673,6 +1149,7 @@ func collectTypeScriptModule(
|
|
|
673
1149
|
inventory,
|
|
674
1150
|
unitsByID,
|
|
675
1151
|
module.Body,
|
|
1152
|
+
module.Body,
|
|
676
1153
|
"type",
|
|
677
1154
|
identity,
|
|
678
1155
|
parentID,
|
|
@@ -695,6 +1172,29 @@ func collectTypeScriptModule(
|
|
|
695
1172
|
}
|
|
696
1173
|
}
|
|
697
1174
|
|
|
1175
|
+
// collectPropertyMembers materializes the members an interface or an
|
|
1176
|
+
// object-shaped type alias declares.
|
|
1177
|
+
//
|
|
1178
|
+
// This is the other half of the four-spelling rule `collectClassMembers` owns,
|
|
1179
|
+
// and the two must stay in step: a member signature is a function unit and a
|
|
1180
|
+
// data member is a property unit, decided by `memberSymbol` from the annotation
|
|
1181
|
+
// as spelled. The classification is single-sourced there, while the member-kind
|
|
1182
|
+
// switch is written twice, once per container, so a kind added to one and not
|
|
1183
|
+
// the other is the drift this pair is most exposed to.
|
|
1184
|
+
//
|
|
1185
|
+
// The allowlist and the name check are the exclusion, and both gates matter. A
|
|
1186
|
+
// get or set signature is an accessor, which is a get/set pair rather than a
|
|
1187
|
+
// member variable and stays out on every container. A call signature, a
|
|
1188
|
+
// construct signature, and an index signature have no citable name, so an
|
|
1189
|
+
// author could not write a target for one. A computed name passes the switch
|
|
1190
|
+
// and is refused after it, for the same reason.
|
|
1191
|
+
//
|
|
1192
|
+
// That leaves a consequence worth stating rather than discovering: a
|
|
1193
|
+
// call-signature-only `interface Handler { (input: string): void }` contributes
|
|
1194
|
+
// no *member* unit, so a claim narrowed to `symbol: "function"` or `"property"`
|
|
1195
|
+
// over a file of them selects no host and deactivates silently. The interface
|
|
1196
|
+
// itself is still a `type` unit, so the default selector keeps such a claim
|
|
1197
|
+
// active; the qualifier is the whole statement here, not a detail of it.
|
|
698
1198
|
func collectPropertyMembers(
|
|
699
1199
|
file *shimast.SourceFile,
|
|
700
1200
|
members *shimast.NodeList,
|
|
@@ -704,12 +1204,22 @@ func collectPropertyMembers(
|
|
|
704
1204
|
supportedHosts map[*shimast.Node]symbolSet,
|
|
705
1205
|
unitsByID map[string]*evidenceUnit,
|
|
706
1206
|
hidden string,
|
|
1207
|
+
valueSpace bool,
|
|
707
1208
|
) {
|
|
708
1209
|
if members == nil {
|
|
709
1210
|
return
|
|
710
1211
|
}
|
|
711
1212
|
for _, member := range members.Nodes {
|
|
712
|
-
if member == nil
|
|
1213
|
+
if member == nil {
|
|
1214
|
+
continue
|
|
1215
|
+
}
|
|
1216
|
+
symbol := ""
|
|
1217
|
+
switch member.Kind {
|
|
1218
|
+
case shimast.KindPropertySignature:
|
|
1219
|
+
symbol = memberSymbol(nil, member.AsPropertySignatureDeclaration().Type)
|
|
1220
|
+
case shimast.KindMethodSignature:
|
|
1221
|
+
symbol = "function"
|
|
1222
|
+
default:
|
|
713
1223
|
continue
|
|
714
1224
|
}
|
|
715
1225
|
name := declarationName(member.Name())
|
|
@@ -722,13 +1232,14 @@ func collectPropertyMembers(
|
|
|
722
1232
|
inventory,
|
|
723
1233
|
unitsByID,
|
|
724
1234
|
member,
|
|
725
|
-
|
|
1235
|
+
member,
|
|
1236
|
+
symbol,
|
|
726
1237
|
identity,
|
|
727
1238
|
parentID,
|
|
728
1239
|
memberHidden,
|
|
729
|
-
)
|
|
1240
|
+
).markSpace(valueSpace)
|
|
730
1241
|
if memberHidden == "" {
|
|
731
|
-
addTypeScriptHost(supportedHosts, member,
|
|
1242
|
+
addTypeScriptHost(supportedHosts, member, symbol)
|
|
732
1243
|
}
|
|
733
1244
|
}
|
|
734
1245
|
}
|
|
@@ -816,10 +1327,20 @@ func typeScriptHidingTag(
|
|
|
816
1327
|
return ""
|
|
817
1328
|
}
|
|
818
1329
|
|
|
1330
|
+
// addTypeScriptUnit materializes one declaration of an identity.
|
|
1331
|
+
//
|
|
1332
|
+
// `named` is the node that names the identity here and answers for where it is
|
|
1333
|
+
// reported. `content` is the node whose text is the identity's content at this
|
|
1334
|
+
// declaration. They are the same node for every form whose declaration carries
|
|
1335
|
+
// its own name, and they part for a variable, whose unit is named by a binding
|
|
1336
|
+
// identifier inside a declarator. One declarator can name several identities:
|
|
1337
|
+
// a destructuring pattern's leaves share it, and so do two aliases of one local
|
|
1338
|
+
// in an export list.
|
|
819
1339
|
func addTypeScriptUnit(
|
|
820
1340
|
inventory *artifactInventory,
|
|
821
1341
|
unitsByID map[string]*evidenceUnit,
|
|
822
|
-
|
|
1342
|
+
named *shimast.Node,
|
|
1343
|
+
content *shimast.Node,
|
|
823
1344
|
symbol string,
|
|
824
1345
|
identity []string,
|
|
825
1346
|
parentID string,
|
|
@@ -835,7 +1356,7 @@ func addTypeScriptUnit(
|
|
|
835
1356
|
// declaration that spells it. `interface I` beside `namespace I` is one
|
|
836
1357
|
// unit and two nodes, and a rule asking where that unit's JSDoc may live
|
|
837
1358
|
// has to see both.
|
|
838
|
-
inventory.
|
|
1359
|
+
inventory.recordUnitContent(id, content)
|
|
839
1360
|
if unit := unitsByID[id]; unit != nil {
|
|
840
1361
|
// A merged identity is one unit, so one declaration marking itself
|
|
841
1362
|
// internal marks the identity. Both halves of `interface I` beside
|
|
@@ -854,7 +1375,7 @@ func addTypeScriptUnit(
|
|
|
854
1375
|
Type: artifactTypeScript,
|
|
855
1376
|
Symbol: symbol,
|
|
856
1377
|
Path: inventory.Path,
|
|
857
|
-
Line: lineAtNode(inventory.Path,
|
|
1378
|
+
Line: lineAtNode(inventory.Path, named),
|
|
858
1379
|
Readable: "TypeScript " + symbol + " '" + target + "'",
|
|
859
1380
|
Hidden: hidden,
|
|
860
1381
|
}
|
|
@@ -880,6 +1401,19 @@ func addTypeScriptHost(
|
|
|
880
1401
|
// lineAtNode stores a byte offset until declarations are scanned against the
|
|
881
1402
|
// complete source text. A position inside the name is on the declaration
|
|
882
1403
|
// itself, while both the parent and name full starts may include leading trivia.
|
|
1404
|
+
//
|
|
1405
|
+
// An identifier is its own name and reports none, so it answers from its own
|
|
1406
|
+
// end rather than from the full start it would otherwise fall through to. A
|
|
1407
|
+
// full start begins where the previous token ended, so it lies in the node's
|
|
1408
|
+
// leading trivia rather than among its own tokens whenever any trivia sits
|
|
1409
|
+
// between the two. A variable unit is created from its binding identifier and
|
|
1410
|
+
// took that answer, which is a line above each leaf of a multi-line
|
|
1411
|
+
// destructuring pattern and two above a declarator whose documentation block
|
|
1412
|
+
// sits between it and the token before it.
|
|
1413
|
+
//
|
|
1414
|
+
// The last fallback is left as it was because no unit kind reaches it: every
|
|
1415
|
+
// form is created either from a declaration that carries a name or from an
|
|
1416
|
+
// identifier.
|
|
883
1417
|
func lineAtNode(_ string, node *shimast.Node) int {
|
|
884
1418
|
if node == nil {
|
|
885
1419
|
return 0
|
|
@@ -887,6 +1421,9 @@ func lineAtNode(_ string, node *shimast.Node) int {
|
|
|
887
1421
|
if name := node.Name(); name != nil && name.End() > 0 {
|
|
888
1422
|
return name.End() - 1
|
|
889
1423
|
}
|
|
1424
|
+
if node.Kind == shimast.KindIdentifier && node.End() > 0 {
|
|
1425
|
+
return node.End() - 1
|
|
1426
|
+
}
|
|
890
1427
|
return node.Pos()
|
|
891
1428
|
}
|
|
892
1429
|
|
|
@@ -958,10 +1495,15 @@ func collectTypeScriptDeclarations(
|
|
|
958
1495
|
docs[key] = current
|
|
959
1496
|
}
|
|
960
1497
|
})
|
|
1498
|
+
attached := make(attachedCommentEnds, len(docs))
|
|
961
1499
|
keys := make([]string, 0, len(docs))
|
|
962
|
-
for key := range docs {
|
|
1500
|
+
for key, entry := range docs {
|
|
963
1501
|
keys = append(keys, key)
|
|
1502
|
+
if start, taken := attached[entry.node.End()]; !taken || entry.node.Pos() < start {
|
|
1503
|
+
attached[entry.node.End()] = entry.node.Pos()
|
|
1504
|
+
}
|
|
964
1505
|
}
|
|
1506
|
+
reportUnreadableTypeScriptTags(file, location, attached, inventory)
|
|
965
1507
|
sort.Slice(keys, func(left int, right int) bool {
|
|
966
1508
|
leftNode := docs[keys[left]].node
|
|
967
1509
|
rightNode := docs[keys[right]].node
|
|
@@ -1006,11 +1548,31 @@ func collectTypeScriptDeclarations(
|
|
|
1006
1548
|
Sequence: sequence,
|
|
1007
1549
|
})
|
|
1008
1550
|
}
|
|
1551
|
+
// Reviews are read from the same block by a separate pass into a separate
|
|
1552
|
+
// slice. Sharing the declaration loop would put a review one field away
|
|
1553
|
+
// from every acknowledgement map in evaluation.
|
|
1554
|
+
for _, review := range parseReviews(content[entry.node.Pos():entry.node.End()]) {
|
|
1555
|
+
inventory.Reviews = append(inventory.Reviews, &evidenceReview{
|
|
1556
|
+
HostID: hostID,
|
|
1557
|
+
SemanticHostIDs: semanticHostIDs,
|
|
1558
|
+
Reviews: review.Reviews,
|
|
1559
|
+
Type: artifactTypeScript,
|
|
1560
|
+
Target: review.Target,
|
|
1561
|
+
Fingerprint: review.Fingerprint,
|
|
1562
|
+
Description: review.Description,
|
|
1563
|
+
Path: location,
|
|
1564
|
+
Line: baseLine + review.LineOffset,
|
|
1565
|
+
})
|
|
1566
|
+
}
|
|
1009
1567
|
}
|
|
1010
1568
|
for _, unit := range inventory.Units {
|
|
1011
1569
|
// TypeScript AST positions are byte offsets; translate them only after
|
|
1012
1570
|
// the complete source text is available.
|
|
1013
1571
|
unit.Line = lineAt(content, unit.Line)
|
|
1572
|
+
// The digest is deferred to the same pass for the same reason: the nodes
|
|
1573
|
+
// are recorded while walking, and their text needs the source file that
|
|
1574
|
+
// only the caller holds.
|
|
1575
|
+
unit.Digest = typeScriptUnitDigest(file, inventory.UnitContent[unit.ID])
|
|
1014
1576
|
}
|
|
1015
1577
|
}
|
|
1016
1578
|
|
|
@@ -1197,3 +1759,74 @@ func encodeTypeScriptIdentity(identity []string) string {
|
|
|
1197
1759
|
}
|
|
1198
1760
|
return builder.String()
|
|
1199
1761
|
}
|
|
1762
|
+
|
|
1763
|
+
// matchesConfiguredTypeScriptFile reports whether a population rooted at this
|
|
1764
|
+
// base selects the file.
|
|
1765
|
+
//
|
|
1766
|
+
// The base is compared before the globs, for the reason the Markdown side
|
|
1767
|
+
// states: a walk covers one base at a time, and another base's patterns say
|
|
1768
|
+
// nothing about a path inside this one.
|
|
1769
|
+
func matchesConfiguredTypeScriptFile(
|
|
1770
|
+
config graphConfig,
|
|
1771
|
+
base populationBase,
|
|
1772
|
+
path string,
|
|
1773
|
+
) bool {
|
|
1774
|
+
for _, claim := range config.Claims {
|
|
1775
|
+
if claim.Type == artifactTypeScript &&
|
|
1776
|
+
claim.Base.Absolute == base.Absolute &&
|
|
1777
|
+
claim.Files.matches(path) {
|
|
1778
|
+
return true
|
|
1779
|
+
}
|
|
1780
|
+
for _, reference := range claim.References {
|
|
1781
|
+
// A package reference reads an installed package from disk, and its
|
|
1782
|
+
// globs are written as a consumer thinks of that package, so they
|
|
1783
|
+
// resolve against the package root rather than the project. Matching
|
|
1784
|
+
// them here against a project-relative path made `lib/**` claim a
|
|
1785
|
+
// project's own `lib/`, and `**/*.ts` claim every file it has,
|
|
1786
|
+
// including the `node_modules` this confinement exists to release.
|
|
1787
|
+
if reference.Package != "" {
|
|
1788
|
+
continue
|
|
1789
|
+
}
|
|
1790
|
+
if reference.Type == artifactTypeScript &&
|
|
1791
|
+
reference.Base.Absolute == base.Absolute &&
|
|
1792
|
+
reference.Files.matches(path) {
|
|
1793
|
+
return true
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
return false
|
|
1798
|
+
}
|
|
1799
|
+
|
|
1800
|
+
// recordGovernedTypeScriptFiles marks every address a declared population
|
|
1801
|
+
// selects.
|
|
1802
|
+
//
|
|
1803
|
+
// Separate from the scan because the two answer different questions. Scanning
|
|
1804
|
+
// is driven by the active configuration, since an inactive claim must make no
|
|
1805
|
+
// loader do work; governance is a property of what an author wrote, and a claim
|
|
1806
|
+
// that deactivated still declared its population. Reading them from one pass
|
|
1807
|
+
// silenced a file whose declarations were all commented out, which is exactly
|
|
1808
|
+
// the shape the diagnostic's second repair clause exists for.
|
|
1809
|
+
func recordGovernedTypeScriptFiles(
|
|
1810
|
+
sources []*shimast.SourceFile,
|
|
1811
|
+
declared graphConfig,
|
|
1812
|
+
governed map[string]bool,
|
|
1813
|
+
) {
|
|
1814
|
+
if governed == nil {
|
|
1815
|
+
return
|
|
1816
|
+
}
|
|
1817
|
+
bases := configuredBases(declared, artifactTypeScript)
|
|
1818
|
+
for _, file := range sources {
|
|
1819
|
+
if file == nil || !isTypeScriptPath(file.FileName()) {
|
|
1820
|
+
continue
|
|
1821
|
+
}
|
|
1822
|
+
for _, base := range bases {
|
|
1823
|
+
relative, ok := relativeProjectPath(base.Absolute, file.FileName())
|
|
1824
|
+
if !ok || !isTypeScriptPath(relative) {
|
|
1825
|
+
continue
|
|
1826
|
+
}
|
|
1827
|
+
if matchesConfiguredTypeScriptFile(declared, base, relative) {
|
|
1828
|
+
governed[base.addressOf(relative).Key] = true
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|