Resolve nested inheritance in destination scopes

This commit is contained in:
2026-09-29 22:31:07 +00:00
parent fdbc306cfb
commit 4207110e2f
4 changed files with 478 additions and 0 deletions

View File

@@ -40,6 +40,10 @@ type siblingFrame struct {
// Nested instructions remain in the returned templates for destination-local
// resolution. This private entry point does not participate in public loading.
func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanceScope, []Diagnostic) {
return expandSiblingDefinitionsWithLimits(scope, scopePath, defaultProjectionLimits())
}
func expandSiblingDefinitionsWithLimits(scope *document.Node, scopePath Path, limits projectionLimits) (*inheritanceScope, []Diagnostic) {
if scope == nil || scope.Kind != document.Mapping {
return nil, []Diagnostic{{
Code: CodeInheritanceInvalid, Severity: SeverityError, Path: scopePath,
@@ -58,6 +62,21 @@ func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanc
resolved := make(map[string]*document.Node, len(byName))
state := make(map[string]uint8, len(byName))
result := &inheritanceScope{}
retained := 1 // scope mapping node
for _, entry := range scope.Mapping {
if entry.Value.Kind == document.Mapping {
continue
}
remaining := limits.nodes - retained - 1
if limits.nodes >= 0 && remaining < 0 {
return nil, []Diagnostic{inheritanceBudgetDiagnostic(scopePath.Key(entry.Key), entry.Value.Location)}
}
count, within := inheritanceLowerBoundCount(entry.Value, remaining)
if !within {
return nil, []Diagnostic{inheritanceBudgetDiagnostic(scopePath.Key(entry.Key), entry.Value.Location)}
}
retained += 1 + count // key and value subtree
}
for _, name := range names {
if byName[name].Value.Kind != document.Mapping || state[name] == 2 {
continue
@@ -109,6 +128,18 @@ func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanc
}
payload := withoutInheritanceDirective(entry.Value)
if limits.nodes >= 0 {
remaining := limits.nodes - retained - 1 // the sibling key
within := false
if frame.hasParent {
within = inheritanceOverlayLowerBoundWithin(resolved[frame.parent], payload, remaining)
} else {
within = inheritanceLowerBoundWithin(payload, remaining)
}
if !within {
return nil, []Diagnostic{inheritanceBudgetDiagnostic(path, entry.Value.Location)}
}
}
if frame.hasParent {
parent := resolved[frame.parent]
merged := document.Clone(parent)
@@ -124,6 +155,8 @@ func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanc
} else {
resolved[frame.name] = document.Clone(payload)
}
count, _ := inheritanceLowerBoundCount(resolved[frame.name], limits.nodes)
retained += 1 + count
state[frame.name] = 2
stack = stack[:len(stack)-1]
}
@@ -140,6 +173,9 @@ func expandSiblingDefinitions(scope *document.Node, scopePath Path) (*inheritanc
}
}
result.root = &root
if !inheritanceLowerBoundWithin(result.root, limits.nodes) {
return nil, []Diagnostic{inheritanceBudgetDiagnostic(scopePath, scope.Location)}
}
return result, nil
}