Project item conflict diagnostics through enclosing scopes
This commit is contained in:
@@ -214,6 +214,9 @@ func loadImportedWithAccessTrackedAndProcessingPolicy(access *filesystemAccess,
|
||||
|
||||
closeErr := access.close()
|
||||
if len(diagnostics) != 0 {
|
||||
if mainIsItem {
|
||||
diagnostics = prependCompositionDiagnosticPaths(diagnostics, destination.Index(0))
|
||||
}
|
||||
return nil, cloneSourceInfos(state.sources), diagnostics
|
||||
}
|
||||
if closeErr != nil {
|
||||
@@ -227,7 +230,7 @@ func loadImportedWithAccessTrackedAndProcessingPolicy(access *filesystemAccess,
|
||||
}
|
||||
completed, failures := composeDocuments(documents...)
|
||||
if len(failures) != 0 {
|
||||
return nil, cloneSourceInfos(state.sources), prependDiagnosticPaths(failures, destination.Index(0))
|
||||
return nil, cloneSourceInfos(state.sources), prependCompositionDiagnosticPaths(failures, destination.Index(0))
|
||||
}
|
||||
if versionState != nil {
|
||||
protectionRule := versionState.localRule
|
||||
@@ -525,7 +528,7 @@ func (s *importTraversal) visitItemSource(canonical string, declaration importDe
|
||||
itemScope := s.scope
|
||||
s.scope, s.itemScope, s.itemPrefix, s.itemFallback = parentScope, parentItemScope, parentPrefix, parentFallback
|
||||
if len(diagnostics) != 0 {
|
||||
return diagnostics
|
||||
return projectItemDiagnosticPaths(diagnostics, parentScope, destination)
|
||||
}
|
||||
documents := make([]*document.Node, 0, len(itemScope.contributions))
|
||||
for _, contribution := range itemScope.contributions {
|
||||
@@ -533,11 +536,7 @@ func (s *importTraversal) visitItemSource(canonical string, declaration importDe
|
||||
}
|
||||
completed, diagnostics := composeDocuments(documents...)
|
||||
if len(diagnostics) != 0 {
|
||||
prefix := destination.Index(0)
|
||||
if parentItemScope {
|
||||
prefix = appendDestination(parentPrefix, prefix)
|
||||
}
|
||||
return prependDiagnosticPaths(diagnostics, prefix)
|
||||
return projectItemDiagnosticPaths(diagnostics, parentScope, destination)
|
||||
}
|
||||
itemScope.destination = Path{}
|
||||
completed.acquisition = []acquisitionScope{cloneAcquisitionScope(itemScope)}
|
||||
@@ -580,9 +579,12 @@ func importedVersionClaim(documents []*document.Node, composed *composedDocument
|
||||
return nil
|
||||
}
|
||||
|
||||
func prependDiagnosticPaths(diagnostics []Diagnostic, prefix Path) []Diagnostic {
|
||||
func prependCompositionDiagnosticPaths(diagnostics []Diagnostic, prefix Path) []Diagnostic {
|
||||
cloned := append([]Diagnostic(nil), diagnostics...)
|
||||
for index := range cloned {
|
||||
if !isCompositionDiagnostic(cloned[index]) {
|
||||
continue
|
||||
}
|
||||
cloned[index].Path = appendDestination(prefix, cloned[index].Path)
|
||||
cloned[index].Primary = cloneLocation(cloned[index].Primary)
|
||||
cloned[index].Related = cloneLocations(cloned[index].Related)
|
||||
@@ -590,6 +592,56 @@ func prependDiagnosticPaths(diagnostics []Diagnostic, prefix Path) []Diagnostic
|
||||
return cloned
|
||||
}
|
||||
|
||||
func isCompositionDiagnostic(diagnostic Diagnostic) bool {
|
||||
return diagnostic.Code == CodeComposeConflict || diagnostic.Code == CodeComposeInvalid
|
||||
}
|
||||
|
||||
// On a composition failure only the earlier contributions are needed to locate
|
||||
// this item. Project one enclosing scope at a time as traversal unwinds; syntax
|
||||
// and access diagnostics keep their original source-local coordinates.
|
||||
func projectItemDiagnosticPaths(diagnostics []Diagnostic, parent acquisitionScope, destination Path) []Diagnostic {
|
||||
for _, diagnostic := range diagnostics {
|
||||
if isCompositionDiagnostic(diagnostic) {
|
||||
index := priorSequenceLength([]acquisitionScope{parent}, destination)
|
||||
return prependCompositionDiagnosticPaths(diagnostics, destination.Index(index))
|
||||
}
|
||||
}
|
||||
return diagnostics
|
||||
}
|
||||
|
||||
// This scan is confined to failed loads, not the successful construction path.
|
||||
func priorSequenceLength(scopes []acquisitionScope, path Path) int {
|
||||
length := 0
|
||||
for _, scope := range scopes {
|
||||
for _, contribution := range scope.contributions {
|
||||
if node, present := documentAtPath(contribution.document, path); present && node.Kind == document.Sequence {
|
||||
length += len(node.Sequence)
|
||||
}
|
||||
}
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
func projectPriorInputDiagnosticPaths(diagnostics []Diagnostic, scopes []acquisitionScope) []Diagnostic {
|
||||
cloned := cloneDiagnostics(diagnostics)
|
||||
for index := range cloned {
|
||||
if !isCompositionDiagnostic(cloned[index]) {
|
||||
continue
|
||||
}
|
||||
for position, segment := range cloned[index].Path.segments {
|
||||
if segment.kind != pathSegmentIndex {
|
||||
continue
|
||||
}
|
||||
container := Path{segments: cloned[index].Path.segments[:position]}
|
||||
cloned[index].Path.segments[position].index += priorSequenceLength(scopes, container)
|
||||
// Registration does not address paths through item indexes. A prior
|
||||
// input shifts the outer collection, not its item-local sequences.
|
||||
break
|
||||
}
|
||||
}
|
||||
return cloned
|
||||
}
|
||||
|
||||
func projectNestedEvidence(composed *composedDocument, scope acquisitionScope) {
|
||||
if composed == nil {
|
||||
return
|
||||
|
||||
@@ -175,7 +175,7 @@ func acquireUnderlayGroupWithPolicyVersion(group loadGroup, index int, rule *Ver
|
||||
}
|
||||
mergeResourceContexts(&contexts, acquired.contexts)
|
||||
if len(diagnostics) != 0 {
|
||||
return nil, sources, contexts, diagnostics
|
||||
return nil, sources, contexts, projectPriorInputDiagnosticPaths(diagnostics, scopes)
|
||||
}
|
||||
if acquired.document == nil {
|
||||
continue
|
||||
|
||||
Reference in New Issue
Block a user