10 // Validate reports an error if any of the analyzers are misconfigured.
12 // that the name is a valid identifier;
13 // that the Requires graph is acyclic;
14 // that analyzer fact types are unique;
15 // that each fact type is a pointer.
16 func Validate(analyzers []*Analyzer) error {
17 // Map each fact type to its sole generating analyzer.
18 factTypes := make(map[reflect.Type]*Analyzer)
20 // Traverse the Requires graph, depth first.
27 color := make(map[*Analyzer]uint8)
28 var visit func(a *Analyzer) error
29 visit = func(a *Analyzer) error {
31 return fmt.Errorf("nil *Analyzer")
33 if color[a] == white {
37 if !validIdent(a.Name) {
38 return fmt.Errorf("invalid analyzer name %q", a)
42 return fmt.Errorf("analyzer %q is undocumented", a)
46 for _, f := range a.FactTypes {
48 return fmt.Errorf("analyzer %s has nil FactType", a)
50 t := reflect.TypeOf(f)
51 if prev := factTypes[t]; prev != nil {
52 return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
55 if t.Kind() != reflect.Ptr {
56 return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
62 for _, req := range a.Requires {
63 if err := visit(req); err != nil {
71 stack := []*Analyzer{a}
72 inCycle := map[string]bool{}
74 current := stack[len(stack)-1]
75 stack = stack[:len(stack)-1]
76 if color[current] == grey && !inCycle[current.Name] {
77 inCycle[current.Name] = true
78 stack = append(stack, current.Requires...)
81 return &CycleInRequiresGraphError{AnalyzerNames: inCycle}
86 for _, a := range analyzers {
87 if err := visit(a); err != nil {
92 // Reject duplicates among analyzers.
93 // Precondition: color[a] == black.
94 // Postcondition: color[a] == finished.
95 for _, a := range analyzers {
96 if color[a] == finished {
97 return fmt.Errorf("duplicate analyzer: %s", a.Name)
105 func validIdent(name string) bool {
106 for i, r := range name {
107 if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
114 type CycleInRequiresGraphError struct {
115 AnalyzerNames map[string]bool
118 func (e *CycleInRequiresGraphError) Error() string {
119 var b strings.Builder
120 b.WriteString("cycle detected involving the following analyzers:")
121 for n := range e.AnalyzerNames {