@ttsc/evidence 0.25.0 → 0.26.0
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/graph.go
CHANGED
|
@@ -70,14 +70,24 @@ func (graphRule) Check(ctx *rule.ProjectContext) {
|
|
|
70
70
|
)
|
|
71
71
|
problems = append(problems, markdownClaimProblems...)
|
|
72
72
|
problems = append(problems, prismaClaimProblems...)
|
|
73
|
+
// Governance is judged against the configuration as declared, before
|
|
74
|
+
// activation drops a claim whose population materialized no unit of its
|
|
75
|
+
// symbol kind. The question is whether an author put this file in a
|
|
76
|
+
// population, and a claim that deactivated still declared one: judging after
|
|
77
|
+
// activation silenced a file whose every declaration the author had
|
|
78
|
+
// commented out, which is the shape the second repair clause exists for.
|
|
79
|
+
declared := config
|
|
73
80
|
config = activeGraphConfig(config, markdownClaims, prismaClaims, typescript)
|
|
74
|
-
|
|
81
|
+
governed := map[string]bool{}
|
|
82
|
+
extendTypeScriptInventories(root, ctx.Sources, config, typescript, nil)
|
|
83
|
+
recordGovernedTypeScriptFiles(ctx.Sources, declared, governed)
|
|
75
84
|
markdown, markdownProblems := loadMarkdownInventories(root, config)
|
|
76
85
|
prisma, prismaProblems := loadPrismaInventories(root, config)
|
|
77
86
|
swagger, swaggerProblems := loadSwaggerInventories(root, config)
|
|
78
87
|
problems = append(problems, markdownProblems...)
|
|
79
88
|
problems = append(problems, prismaProblems...)
|
|
80
89
|
problems = append(problems, swaggerProblems...)
|
|
90
|
+
problems = append(problems, unreadableTypeScriptTags(typescript, governed)...)
|
|
81
91
|
loader := newTypeScriptLoader(root, typescript)
|
|
82
92
|
states, stateProblems := materializeClaimStates(
|
|
83
93
|
config,
|
|
@@ -267,6 +277,10 @@ func materializeClaimStates(
|
|
|
267
277
|
state.Declarations,
|
|
268
278
|
inventories[path].Declarations...,
|
|
269
279
|
)
|
|
280
|
+
// Reviews travel beside declarations and never among them. They carry no
|
|
281
|
+
// obligation of their own; they are looked up by host and target when a
|
|
282
|
+
// reference demands one.
|
|
283
|
+
state.Reviews = append(state.Reviews, inventories[path].Reviews...)
|
|
270
284
|
if len(claim.ExclusionCarriers.Patterns) != 0 && !carrierPaths[path] {
|
|
271
285
|
for _, declaration := range inventories[path].Declarations {
|
|
272
286
|
state.OutsideCarrier[declaration.ID] = true
|
|
@@ -389,13 +403,26 @@ func materializeClaimStates(
|
|
|
389
403
|
}
|
|
390
404
|
}
|
|
391
405
|
sortUnits(referenceState.Scopes)
|
|
406
|
+
// The complete materialized population is kept because a review
|
|
407
|
+
// fingerprint must not depend on this reference's `symbol` selector.
|
|
408
|
+
// Units and Scopes hold only what this reference selected plus the
|
|
409
|
+
// ancestors of those, so an unselected descendant appears in neither, and
|
|
410
|
+
// composing a scope digest from them made the value a function of the
|
|
411
|
+
// reference rather than of the cited address: a narrowed selector dropped
|
|
412
|
+
// the subtree from the digest, and two references over one scope demanded
|
|
413
|
+
// two different values from a tag that carries one token, which no author
|
|
414
|
+
// could satisfy.
|
|
415
|
+
for _, unit := range availableUnits {
|
|
416
|
+
referenceState.Population = append(referenceState.Population, unit)
|
|
417
|
+
}
|
|
418
|
+
sortUnits(referenceState.Population)
|
|
392
419
|
if len(referencePaths) != 0 &&
|
|
393
420
|
len(referenceState.Units) == 0 &&
|
|
394
421
|
referenceState.Healthy &&
|
|
395
422
|
!selectedInventoryProblem {
|
|
396
423
|
problems = append(
|
|
397
424
|
problems,
|
|
398
|
-
claimLabel(claim)+" "+referenceLabel(reference)+" matched "+decimal(len(referencePaths))+" file(s) but
|
|
425
|
+
claimLabel(claim)+" "+referenceLabel(reference)+" matched "+decimal(len(referencePaths))+" file(s) but found no selected evidence units ("+reference.Symbols.names()+"). Select symbol kinds present in those files or correct the reference globs.",
|
|
399
426
|
)
|
|
400
427
|
}
|
|
401
428
|
state.References = append(state.References, referenceState)
|
|
@@ -525,7 +552,7 @@ func evaluateEvidenceGraph(
|
|
|
525
552
|
if !declaration.valid() {
|
|
526
553
|
problems = append(
|
|
527
554
|
problems,
|
|
528
|
-
"Malformed @"+string(declaration.Tag)+" declaration at "+declaration.location()+" for "+context+": target and non-empty reason are mandatory. Write '@"+string(declaration.Tag)+" <target> <reason>'
|
|
555
|
+
"Malformed @"+string(declaration.Tag)+" declaration at "+declaration.location()+" for "+context+": target and non-empty reason are mandatory. Write '@"+string(declaration.Tag)+" <target> <reason>'.+untrueTagWarning",
|
|
529
556
|
)
|
|
530
557
|
continue
|
|
531
558
|
}
|
|
@@ -591,7 +618,7 @@ func evaluateEvidenceGraph(
|
|
|
591
618
|
}
|
|
592
619
|
problems = append(
|
|
593
620
|
problems,
|
|
594
|
-
"Unresolved evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": no configured source materializes that evidence unit. Correct the target,
|
|
621
|
+
"Unresolved evidence target '"+declaration.Target+"' at "+declaration.location()+" for "+context+": no configured source materializes that evidence unit. Correct the target, make one of the named references select the source unit this claim actually uses, or remove the tag when this host does not answer for that target."+untrueTagWarning,
|
|
595
622
|
)
|
|
596
623
|
case 1:
|
|
597
624
|
for unitID := range candidates {
|
|
@@ -628,6 +655,10 @@ func evaluateEvidenceGraph(
|
|
|
628
655
|
uncertain[declaration.ID] = true
|
|
629
656
|
}
|
|
630
657
|
}
|
|
658
|
+
// One ledger per claim, because reviews belong to the claim rather than to
|
|
659
|
+
// any one of its references, and built on first use so a claim with no
|
|
660
|
+
// reviewing reference pays nothing.
|
|
661
|
+
var reviewLedgerForClaim *reviewLedger
|
|
631
662
|
for _, reference := range state.References {
|
|
632
663
|
if !reference.Healthy {
|
|
633
664
|
for _, declaration := range state.Declarations {
|
|
@@ -668,6 +699,11 @@ func evaluateEvidenceGraph(
|
|
|
668
699
|
for _, scope := range reference.Scopes {
|
|
669
700
|
scopesByID[scope.ID] = scope
|
|
670
701
|
}
|
|
702
|
+
// Built on first use and shared by every citation of this reference. The
|
|
703
|
+
// index spans the whole materialized population, so building it per
|
|
704
|
+
// citation is quadratic, and nil until a citation actually needs it so a
|
|
705
|
+
// reference without the policy pays nothing.
|
|
706
|
+
var reviewScopes *scopeIndex
|
|
671
707
|
for _, declaration := range state.Declarations {
|
|
672
708
|
scopeID := resolved[declaration.ID]
|
|
673
709
|
covered := reference.UnitsByScope[scopeID]
|
|
@@ -707,10 +743,34 @@ func evaluateEvidenceGraph(
|
|
|
707
743
|
if declaration.Tag == tagExclude && reference.Spec.Policy.NoExclude {
|
|
708
744
|
problems = append(
|
|
709
745
|
problems,
|
|
710
|
-
"Forbidden @evidenceExclude for '"+scopesByID[scopeID].Target+"' at "+declaration.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": noEvidenceExclude requires positive @evidence for this reference. Remove the exclusion and cite the target from a selected "+string(state.Spec.Type)+" host.",
|
|
746
|
+
"Forbidden @evidenceExclude for '"+scopesByID[scopeID].Target+"' at "+declaration.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": noEvidenceExclude requires positive @evidence for this reference. Remove the exclusion and cite the target from a selected "+string(state.Spec.Type)+" host that answers for it."+untrueTagWarning,
|
|
711
747
|
)
|
|
712
748
|
continue
|
|
713
749
|
}
|
|
750
|
+
if reference.Spec.Policy.RequireReview {
|
|
751
|
+
if reviewScopes == nil {
|
|
752
|
+
// Withdrawn units are absent from Population by construction, and
|
|
753
|
+
// the scope composite still needs their identities so a member
|
|
754
|
+
// leaving the public surface moves the fingerprint.
|
|
755
|
+
reviewScopes = newScopeIndex(
|
|
756
|
+
append(
|
|
757
|
+
append([]*evidenceUnit{}, reference.Population...),
|
|
758
|
+
reference.Hidden...,
|
|
759
|
+
),
|
|
760
|
+
)
|
|
761
|
+
}
|
|
762
|
+
if reviewLedgerForClaim == nil {
|
|
763
|
+
reviewLedgerForClaim = newReviewLedger(state.Reviews)
|
|
764
|
+
}
|
|
765
|
+
problems = append(problems, reviewProblems(
|
|
766
|
+
declaration,
|
|
767
|
+
scopesByID[scopeID],
|
|
768
|
+
reference,
|
|
769
|
+
state,
|
|
770
|
+
reviewScopes,
|
|
771
|
+
reviewLedgerForClaim,
|
|
772
|
+
)...)
|
|
773
|
+
}
|
|
714
774
|
if declaration.Tag == tagEvidence && declaration.HostID != "" {
|
|
715
775
|
byScope := evidenceByHostAndScope[declaration.HostID]
|
|
716
776
|
if byScope == nil {
|
|
@@ -789,7 +849,7 @@ func evaluateEvidenceGraph(
|
|
|
789
849
|
}
|
|
790
850
|
problems = append(
|
|
791
851
|
problems,
|
|
792
|
-
"Conflicting acknowledgements for '"+conflictingUnit.Target+"' in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": @evidence at "+evidence.location()+" overlaps @evidenceExclude at "+exclusion.location()+".",
|
|
852
|
+
"Conflicting acknowledgements for '"+conflictingUnit.Target+"' in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+": @evidence at "+evidence.location()+" overlaps @evidenceExclude at "+exclusion.location()+". Delete whichever is untrue of this host."+untrueTagWarning,
|
|
793
853
|
)
|
|
794
854
|
}
|
|
795
855
|
if duplicateExclusionUnit != nil {
|
|
@@ -810,15 +870,18 @@ func evaluateEvidenceGraph(
|
|
|
810
870
|
}
|
|
811
871
|
problems = append(
|
|
812
872
|
problems,
|
|
813
|
-
"Evidence host "+host.Readable+" at "+host.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+" cites "+decimal(count)+" distinct selected evidence unit(s); singleEvidencePerSymbol requires exactly 1.
|
|
873
|
+
"Evidence host "+host.Readable+" at "+host.location()+" in "+claimLabel(state.Spec)+" "+referenceLabel(reference.Spec)+" cites "+decimal(count)+" distinct selected evidence unit(s); singleEvidencePerSymbol requires exactly 1. Split this host so each unit has one that owns it, or keep positive @evidence citations on this semantic host to exactly one distinct unit.",
|
|
814
874
|
)
|
|
815
875
|
}
|
|
816
876
|
}
|
|
817
877
|
for _, unit := range reference.Units {
|
|
818
878
|
if !acknowledged[unit.ID] {
|
|
819
|
-
|
|
879
|
+
// Building the host is named first because the other two hide it. A
|
|
880
|
+
// repair offering only the two tags frames the whole problem as which
|
|
881
|
+
// tag to write, and an unbuilt unit then leaves as an exclusion.
|
|
882
|
+
repair := "Cite the artifact that answers for this unit with @evidence on a selected " + string(state.Spec.Type) + " host, building that artifact first when none does, or write @evidenceExclude on an eligible carrier when nothing here owes it." + untrueTagWarning
|
|
820
883
|
if reference.Spec.Policy.NoExclude {
|
|
821
|
-
repair = "
|
|
884
|
+
repair = "Cite the artifact that answers for this unit with @evidence on a selected " + string(state.Spec.Type) + " host, building that artifact first when none does; this reference forbids @evidenceExclude." + untrueTagWarning
|
|
822
885
|
}
|
|
823
886
|
problems = append(
|
|
824
887
|
problems,
|
|
@@ -845,7 +908,7 @@ func evaluateEvidenceGraph(
|
|
|
845
908
|
if obligations := outsideCarrier[id]; len(obligations) != 0 {
|
|
846
909
|
problems = append(
|
|
847
910
|
problems,
|
|
848
|
-
"Misplaced @evidenceExclude at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': evidenceExcludeCarriers confines this claim's exclusions to "+outsideCarrierGlobs[id]+". Move the tag there, or delete it and
|
|
911
|
+
"Misplaced @evidenceExclude at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': evidenceExcludeCarriers confines this claim's exclusions to "+outsideCarrierGlobs[id]+". Move the tag there, or delete it and build what the target requires of this claim."+untrueTagWarning,
|
|
849
912
|
)
|
|
850
913
|
continue
|
|
851
914
|
}
|
|
@@ -863,7 +926,7 @@ func evaluateEvidenceGraph(
|
|
|
863
926
|
}
|
|
864
927
|
problems = append(
|
|
865
928
|
problems,
|
|
866
|
-
"Out-of-scope @"+string(declaration.Tag)+" host at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': host kind '"+host+"' is not selected ("+outOfScopeSelections[id].names()+") by any of these claim obligations. Move the declaration to a selected host, or widen
|
|
929
|
+
"Out-of-scope @"+string(declaration.Tag)+" host at "+declaration.location()+" for "+strings.Join(obligations, "; ")+", target '"+displayTarget(declaration.Target)+"': host kind '"+host+"' is not selected ("+outOfScopeSelections[id].names()+") by any of these claim obligations. Move the declaration to a selected host, or widen the claim symbol selector only when it genuinely owns this target."+untrueTagWarning,
|
|
867
930
|
)
|
|
868
931
|
continue
|
|
869
932
|
}
|
|
@@ -875,12 +938,181 @@ func evaluateEvidenceGraph(
|
|
|
875
938
|
}
|
|
876
939
|
problems = append(
|
|
877
940
|
problems,
|
|
878
|
-
"Non-participating @"+string(declaration.Tag)+" target '"+displayTarget(declaration.Target)+"' at "+declaration.location()+" for "+context+": the target resolves, but none of this declaration's configured references selects it. Correct the target or reference, or move the tag to an eligible host or exclusion carrier in the claim that owes it; a resolving tag must discharge at least one obligation.",
|
|
941
|
+
"Non-participating @"+string(declaration.Tag)+" target '"+displayTarget(declaration.Target)+"' at "+declaration.location()+" for "+context+": the target resolves, but none of this declaration's configured references selects it. Correct the target or reference, or move the tag to an eligible host or exclusion carrier in the claim that owes it; a resolving tag must discharge at least one obligation."+untrueTagWarning,
|
|
879
942
|
)
|
|
880
943
|
}
|
|
881
944
|
return problems
|
|
882
945
|
}
|
|
883
946
|
|
|
947
|
+
// reviewProblems judges one acknowledgement against the review it owes.
|
|
948
|
+
//
|
|
949
|
+
// The review is found by host and target rather than resolved again. A citation
|
|
950
|
+
// and its review spell one address, so resolving the review separately would let
|
|
951
|
+
// it answer a scope its citation does not name, and would report an unresolved
|
|
952
|
+
// target for a tag whose only job is to annotate one that already resolved.
|
|
953
|
+
//
|
|
954
|
+
// Three states are reported and they are mutually exclusive, because each repair
|
|
955
|
+
// subsumes the one after it: without a review there is nothing to carry a
|
|
956
|
+
// fingerprint, and without a fingerprint there is nothing to compare. Every
|
|
957
|
+
// message states the expected value, because completions cannot: the host
|
|
958
|
+
// publishes a rule's corpus only on a cycle where the rule reports nothing, and
|
|
959
|
+
// a stale fingerprint is a report.
|
|
960
|
+
//
|
|
961
|
+
// An empty expected value means this population's loader reported no content
|
|
962
|
+
// for the cited scope. Every bridge digests what it parsed, so that is a loader
|
|
963
|
+
// gap rather than a configuration one, and reporting a mismatch against nothing
|
|
964
|
+
// would name a repair no author can perform. It is silence by design and the
|
|
965
|
+
// only silence here: a bridge that fails reports its own failure, and an
|
|
966
|
+
// unhealthy population is skipped before this runs.
|
|
967
|
+
func reviewProblems(
|
|
968
|
+
declaration *evidenceDeclaration,
|
|
969
|
+
scope *evidenceUnit,
|
|
970
|
+
reference referenceState,
|
|
971
|
+
state claimState,
|
|
972
|
+
scopes *scopeIndex,
|
|
973
|
+
reviews *reviewLedger,
|
|
974
|
+
) []string {
|
|
975
|
+
if declaration == nil || scope == nil {
|
|
976
|
+
return nil
|
|
977
|
+
}
|
|
978
|
+
// The complete population is what the digest walks, never Units or Scopes.
|
|
979
|
+
// Both of those are narrowed by this reference's `symbol` selector, so an
|
|
980
|
+
// unselected descendant is absent from each: a Markdown reference selecting
|
|
981
|
+
// only `h2` would fingerprint a cited section without the H3 bodies inside it,
|
|
982
|
+
// and a review of it would never expire however much of that subtree was
|
|
983
|
+
// rewritten. Worse, two references over one scope under different selectors
|
|
984
|
+
// would then expect two different digests from a tag that carries exactly one
|
|
985
|
+
// fingerprint token, and no value an author could write would satisfy both.
|
|
986
|
+
expected := scopes.fingerprint(scope.ID)
|
|
987
|
+
if expected == "" {
|
|
988
|
+
return nil
|
|
989
|
+
}
|
|
990
|
+
where := " at " + declaration.location() + " in " +
|
|
991
|
+
claimLabel(state.Spec) + " " + referenceLabel(reference.Spec)
|
|
992
|
+
// Every message names the review tag that answers for *this* acknowledgement.
|
|
993
|
+
// A generic `@evidenceReview` in the repair would send the author of an
|
|
994
|
+
// exclusion to write the tag that does not answer it, which is the one mistake
|
|
995
|
+
// the split into two tags exists to prevent.
|
|
996
|
+
marker := reviewMarkerFor(declaration.Tag)
|
|
997
|
+
review := reviews.find(declaration)
|
|
998
|
+
if review == nil {
|
|
999
|
+
return []string{
|
|
1000
|
+
"Unreviewed @" + string(declaration.Tag) + " for '" + displayTarget(declaration.Target) + "'" + where +
|
|
1001
|
+
": requireReview asks what was verified, which the reason does not answer. Add '" + marker + " " +
|
|
1002
|
+
displayTarget(declaration.Target) + " #" + expected + " <what you checked>' to the same documentation block, or correct this host when what you checked did not hold. " + reviewExample(declaration.Tag) + untrueReviewWarning,
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
if review.Fingerprint == "" {
|
|
1006
|
+
return []string{
|
|
1007
|
+
"Unfingerprinted " + marker + " for '" + displayTarget(declaration.Target) + "'" + where +
|
|
1008
|
+
": the review states what was checked and does not name the fingerprint of what it checked, so it can never expire. Write '" +
|
|
1009
|
+
marker + " " + displayTarget(declaration.Target) + " #" + expected + " " + firstReviewWords(review.Description) + "'." + untrueReviewWarning,
|
|
1010
|
+
}
|
|
1011
|
+
}
|
|
1012
|
+
if review.Fingerprint != expected {
|
|
1013
|
+
return []string{
|
|
1014
|
+
"Stale " + marker + " for '" + displayTarget(declaration.Target) + "'" + where +
|
|
1015
|
+
": the review names '#" + review.Fingerprint + "' and that scope now digests to '#" + expected +
|
|
1016
|
+
"'. The cited content changed after this review was written. Read it again and replace the review, including the new fingerprint, or correct this host when the change means it no longer answers for that target." + untrueReviewWarning,
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
return nil
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
// reviewLedger indexes a claim's reviews by the identity they were written on.
|
|
1023
|
+
//
|
|
1024
|
+
// Two properties are load-bearing and neither is obvious.
|
|
1025
|
+
//
|
|
1026
|
+
// The key is a *semantic* host identity, never `HostID`. `HostID` is a source
|
|
1027
|
+
// position, so the two halves of a merged identity carry different ones: keying on
|
|
1028
|
+
// it refused a review written on `namespace I` for a citation on `interface I`,
|
|
1029
|
+
// which is placement the graph elsewhere calls not worth a diagnostic and which
|
|
1030
|
+
// `evidence/review` accepts. The two rules then disagreed about the same file.
|
|
1031
|
+
// `model.go` says as much where it defines the field.
|
|
1032
|
+
//
|
|
1033
|
+
// It is built once per claim rather than scanned per citation. A linear search
|
|
1034
|
+
// over every review, for every declaration, for every reference, is cubic in the
|
|
1035
|
+
// three things a large project has most of.
|
|
1036
|
+
type reviewLedger struct {
|
|
1037
|
+
byHostAndTarget map[string]*evidenceReview
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
func newReviewLedger(reviews []*evidenceReview) *reviewLedger {
|
|
1041
|
+
ledger := &reviewLedger{
|
|
1042
|
+
byHostAndTarget: make(map[string]*evidenceReview, len(reviews)),
|
|
1043
|
+
}
|
|
1044
|
+
for _, review := range reviews {
|
|
1045
|
+
if review == nil {
|
|
1046
|
+
continue
|
|
1047
|
+
}
|
|
1048
|
+
for _, hostID := range reviewLedgerHostIDs(review) {
|
|
1049
|
+
key := reviewLedgerKey(hostID, review.Reviews, review.Target)
|
|
1050
|
+
if ledger.byHostAndTarget[key] == nil {
|
|
1051
|
+
ledger.byHostAndTarget[key] = review
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
}
|
|
1055
|
+
return ledger
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
// reviewLedgerHostIDs is every identity a review answers on.
|
|
1059
|
+
//
|
|
1060
|
+
// The position identity is kept as a fallback for a carrier that has no semantic
|
|
1061
|
+
// host at all, such as an unattached Prisma documentation run, whose declarations
|
|
1062
|
+
// are keyed the same way.
|
|
1063
|
+
func reviewLedgerHostIDs(review *evidenceReview) []string {
|
|
1064
|
+
if len(review.SemanticHostIDs) != 0 {
|
|
1065
|
+
return review.SemanticHostIDs
|
|
1066
|
+
}
|
|
1067
|
+
if review.HostID == "" {
|
|
1068
|
+
return nil
|
|
1069
|
+
}
|
|
1070
|
+
return []string{review.HostID}
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// find locates the review bound to one declaration's identity and target.
|
|
1074
|
+
func (ledger *reviewLedger) find(
|
|
1075
|
+
declaration *evidenceDeclaration,
|
|
1076
|
+
) *evidenceReview {
|
|
1077
|
+
if ledger == nil {
|
|
1078
|
+
return nil
|
|
1079
|
+
}
|
|
1080
|
+
hostIDs := declaration.SemanticHostIDs
|
|
1081
|
+
if len(hostIDs) == 0 && declaration.HostID != "" {
|
|
1082
|
+
hostIDs = []string{declaration.HostID}
|
|
1083
|
+
}
|
|
1084
|
+
for _, hostID := range hostIDs {
|
|
1085
|
+
key := reviewLedgerKey(hostID, declaration.Tag, declaration.Target)
|
|
1086
|
+
if review := ledger.byHostAndTarget[key]; review != nil {
|
|
1087
|
+
return review
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
return nil
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
// reviewLedgerKey composes the one key both sides of the ledger use.
|
|
1094
|
+
//
|
|
1095
|
+
// Spelled once rather than at each site. The acknowledgement kind is part of it,
|
|
1096
|
+
// because verifying a citation and verifying an exclusion are opposite questions
|
|
1097
|
+
// and a review of one must never be found for the other; two independent
|
|
1098
|
+
// three-part spellings would agree only by inspection, and the day they stop
|
|
1099
|
+
// agreeing every review silently stops matching.
|
|
1100
|
+
func reviewLedgerKey(hostID string, tag tagKind, target string) string {
|
|
1101
|
+
return hostID + "\x00" + reviewKey(tag, target)
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// firstReviewWords echoes enough of a description to show where it goes.
|
|
1105
|
+
func firstReviewWords(description string) string {
|
|
1106
|
+
words := strings.Fields(description)
|
|
1107
|
+
if len(words) == 0 {
|
|
1108
|
+
return "<what you checked>"
|
|
1109
|
+
}
|
|
1110
|
+
if len(words) > 6 {
|
|
1111
|
+
words = append(words[:6], "...")
|
|
1112
|
+
}
|
|
1113
|
+
return strings.Join(words, " ")
|
|
1114
|
+
}
|
|
1115
|
+
|
|
884
1116
|
// declarationEligibleForClaim keeps ownership evidence on the selected host
|
|
885
1117
|
// while allowing an intentional exclusion to live on a claim-file carrier.
|
|
886
1118
|
func declarationEligibleForClaim(
|
|
@@ -904,7 +1136,7 @@ func hiddenTargetProblem(
|
|
|
904
1136
|
hidden *evidenceUnit,
|
|
905
1137
|
context string,
|
|
906
1138
|
) string {
|
|
907
|
-
return "Hidden evidence target '" + displayTarget(declaration.Target) + "' at " + declaration.location() + " for " + context + ": " + hidden.Readable + " at " + hidden.location() + " carries '" + hidden.Hidden + "' in its documentation comment, which
|
|
1139
|
+
return "Hidden evidence target '" + displayTarget(declaration.Target) + "' at " + declaration.location() + " for " + context + ": " + hidden.Readable + " at " + hidden.location() + " carries '" + hidden.Hidden + "' in its documentation comment, which removes it from the evidence population along with everything nested inside it. Remove the tag if the declaration is public contract, or drop this citation if it is not. Do not remove '@hidden' from the target to make this citation legal." + untrueTagWarning
|
|
908
1140
|
}
|
|
909
1141
|
|
|
910
1142
|
func declarationResolutionUncertain(owners []claimState) bool {
|
|
@@ -987,6 +1219,10 @@ func applyTraversedScopes(state *referenceState, reached []*evidenceUnit) {
|
|
|
987
1219
|
}
|
|
988
1220
|
}
|
|
989
1221
|
sortUnits(state.Scopes)
|
|
1222
|
+
// Every reached declaration, selected or not, so a review fingerprint over a
|
|
1223
|
+
// cited scope walks the structure rather than this reference's selection.
|
|
1224
|
+
state.Population = append(state.Population, reached...)
|
|
1225
|
+
sortUnits(state.Population)
|
|
990
1226
|
}
|
|
991
1227
|
|
|
992
1228
|
// materializeLocalTypeScriptReference selects modules of the active project
|
|
@@ -1037,7 +1273,7 @@ func materializeLocalTypeScriptReference(
|
|
|
1037
1273
|
}
|
|
1038
1274
|
if len(state.Units) == 0 && !selectedInventoryProblem {
|
|
1039
1275
|
return state, []string{
|
|
1040
|
-
claimLabel(claim) + " " + referenceLabel(reference) + " matched " + decimal(len(paths)) + " file(s) but
|
|
1276
|
+
claimLabel(claim) + " " + referenceLabel(reference) + " matched " + decimal(len(paths)) + " file(s) but found no selected evidence units (" + reference.Symbols.names() + "). Select symbol kinds present in those files or correct the reference globs.",
|
|
1041
1277
|
}
|
|
1042
1278
|
}
|
|
1043
1279
|
return state, nil
|
|
@@ -1093,7 +1329,7 @@ func materializePackageGlobReference(
|
|
|
1093
1329
|
return state, problems
|
|
1094
1330
|
}
|
|
1095
1331
|
return state, []string{
|
|
1096
|
-
claimLabel(claim) + " " + referenceLabel(reference) + " matched no files inside package '" + reference.Package + "' for " + describePatterns(reference.Files) + ". The globs resolve against the package root, not the project root. Check that the installed package actually contains those paths before changing the reference: an empty population
|
|
1332
|
+
claimLabel(claim) + " " + referenceLabel(reference) + " matched no files inside package '" + reference.Package + "' for " + describePatterns(reference.Files) + ". The globs resolve against the package root, not the project root. Check that the installed package actually contains those paths before changing the reference: an empty population contains no units, so this claim reports full coverage without checking anything.",
|
|
1097
1333
|
}
|
|
1098
1334
|
}
|
|
1099
1335
|
// The glob decides membership, the entry decides addresses. A matched module
|
|
@@ -1122,7 +1358,7 @@ func materializePackageGlobReference(
|
|
|
1122
1358
|
}
|
|
1123
1359
|
if len(state.Units) == 0 {
|
|
1124
1360
|
return state, []string{
|
|
1125
|
-
claimLabel(claim) + " " + referenceLabel(reference) + " matched " + decimal(len(state.Paths)) + " file(s) inside package '" + reference.Package + "' but
|
|
1361
|
+
claimLabel(claim) + " " + referenceLabel(reference) + " matched " + decimal(len(state.Paths)) + " file(s) inside package '" + reference.Package + "' but found no selected evidence units (" + reference.Symbols.names() + ") reachable from the package entry. Select symbol kinds present in those files, correct the globs, or narrow to modules the entry publishes.",
|
|
1126
1362
|
}
|
|
1127
1363
|
}
|
|
1128
1364
|
return state, nil
|