Detect inheritance directives without paths
This commit is contained in:
@@ -120,7 +120,7 @@ Roadmap candidate 1. Owner: `internal/engine/metadata.go`.
|
|||||||
Validate: `go test ./internal/engine -run 'Metadata|Presence|Explicit|Inherited'`,
|
Validate: `go test ./internal/engine -run 'Metadata|Presence|Explicit|Inherited'`,
|
||||||
relevant root facade tests, and the everyday check. No allocation assertion.
|
relevant root facade tests, and the everyday check. No allocation assertion.
|
||||||
|
|
||||||
## Stage 3: Detect inheritance without constructing paths
|
## Stage 3: Detect inheritance without constructing paths — Complete
|
||||||
|
|
||||||
Roadmap candidate 9. Owner: `internal/engine/inheritance_load.go`.
|
Roadmap candidate 9. Owner: `internal/engine/inheritance_load.go`.
|
||||||
|
|
||||||
|
|||||||
@@ -3,14 +3,28 @@ package engine
|
|||||||
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
import "gitea.maximumdirect.net/eric/ConfigKit/internal/document"
|
||||||
|
|
||||||
func containsInheritanceDirective(root *document.Node) bool {
|
func containsInheritanceDirective(root *document.Node) bool {
|
||||||
found := false
|
var visit func(*document.Node) bool
|
||||||
walkDocument(root, Path{}, func(path Path, _ *document.Node) {
|
visit = func(node *document.Node) bool {
|
||||||
if len(path.segments) != 0 && path.segments[len(path.segments)-1].kind == pathSegmentKey &&
|
if node == nil {
|
||||||
path.segments[len(path.segments)-1].key == inheritanceKey {
|
return false
|
||||||
found = true
|
|
||||||
}
|
}
|
||||||
})
|
switch node.Kind {
|
||||||
return found
|
case document.Mapping:
|
||||||
|
for _, entry := range node.Mapping {
|
||||||
|
if entry.Key == inheritanceKey || visit(entry.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case document.Sequence:
|
||||||
|
for _, child := range node.Sequence {
|
||||||
|
if visit(child) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return visit(root)
|
||||||
}
|
}
|
||||||
|
|
||||||
func inheritanceVersionDiagnostic(rule *VersionRule, expanded *inheritanceScope) *Diagnostic {
|
func inheritanceVersionDiagnostic(rule *VersionRule, expanded *inheritanceScope) *Diagnostic {
|
||||||
|
|||||||
@@ -121,6 +121,24 @@ func TestSiblingInheritanceRejectsInvalidRelationships(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestLoadDetectsInvalidInheritanceInsideSequence(t *testing.T) {
|
||||||
|
input := []byte(`items:
|
||||||
|
- child: {extends: true}
|
||||||
|
`)
|
||||||
|
result, err := testLoad[map[string]any](Bytes("nested-inheritance.yml", input), Options[map[string]any]{})
|
||||||
|
if result != nil || err == nil {
|
||||||
|
t.Fatalf("result, err = %#v, %v; want invalid nested inheritance", result, err)
|
||||||
|
}
|
||||||
|
loadError, ok := err.(*LoadError)
|
||||||
|
if !ok {
|
||||||
|
t.Fatalf("error type = %T, want *LoadError", err)
|
||||||
|
}
|
||||||
|
diagnostics := loadError.Diagnostics()
|
||||||
|
if len(diagnostics) != 1 || diagnostics[0].Code != CodeInheritanceInvalid || diagnostics[0].Path.String() != "/items/0/child/extends" {
|
||||||
|
t.Fatalf("diagnostics = %#v", diagnostics)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestSiblingInheritanceDetectsDeepCycleWithoutRecursiveDependencyWalk(t *testing.T) {
|
func TestSiblingInheritanceDetectsDeepCycleWithoutRecursiveDependencyWalk(t *testing.T) {
|
||||||
const count = 2048
|
const count = 2048
|
||||||
entries := make([]document.Entry, count)
|
entries := make([]document.Entry, count)
|
||||||
|
|||||||
Reference in New Issue
Block a user