1 // Copyright 2018 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 // Validate reports an error if any of the analyzers are misconfigured.
16 // that the name is a valid identifier;
17 // that the Requires graph is acyclic;
18 // that analyzer fact types are unique;
19 // that each fact type is a pointer.
20 func Validate(analyzers []*Analyzer) error {
21 // Map each fact type to its sole generating analyzer.
22 factTypes := make(map[reflect.Type]*Analyzer)
24 // Traverse the Requires graph, depth first.
31 color := make(map[*Analyzer]uint8)
32 var visit func(a *Analyzer) error
33 visit = func(a *Analyzer) error {
35 return fmt.Errorf("nil *Analyzer")
37 if color[a] == white {
41 if !validIdent(a.Name) {
42 return fmt.Errorf("invalid analyzer name %q", a)
46 return fmt.Errorf("analyzer %q is undocumented", a)
50 for _, f := range a.FactTypes {
52 return fmt.Errorf("analyzer %s has nil FactType", a)
54 t := reflect.TypeOf(f)
55 if prev := factTypes[t]; prev != nil {
56 return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
59 if t.Kind() != reflect.Ptr {
60 return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
66 for _, req := range a.Requires {
67 if err := visit(req); err != nil {
75 stack := []*Analyzer{a}
76 inCycle := map[string]bool{}
78 current := stack[len(stack)-1]
79 stack = stack[:len(stack)-1]
80 if color[current] == grey && !inCycle[current.Name] {
81 inCycle[current.Name] = true
82 stack = append(stack, current.Requires...)
85 return &CycleInRequiresGraphError{AnalyzerNames: inCycle}
90 for _, a := range analyzers {
91 if err := visit(a); err != nil {
96 // Reject duplicates among analyzers.
97 // Precondition: color[a] == black.
98 // Postcondition: color[a] == finished.
99 for _, a := range analyzers {
100 if color[a] == finished {
101 return fmt.Errorf("duplicate analyzer: %s", a.Name)
109 func validIdent(name string) bool {
110 for i, r := range name {
111 if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
118 type CycleInRequiresGraphError struct {
119 AnalyzerNames map[string]bool
122 func (e *CycleInRequiresGraphError) Error() string {
123 var b strings.Builder
124 b.WriteString("cycle detected involving the following analyzers:")
125 for n := range e.AnalyzerNames {