...
1
2
3
4
5 package analysis
6
7 import (
8 "fmt"
9 "reflect"
10 "strings"
11 "unicode"
12 )
13
14
15
16
17
18
19
20
21
22 func Validate(analyzers []*Analyzer) error {
23
24 factTypes := make(map[reflect.Type]*Analyzer)
25
26
27 const (
28 white = iota
29 grey
30 black
31 finished
32 )
33 color := make(map[*Analyzer]uint8)
34 var visit func(a *Analyzer) error
35 visit = func(a *Analyzer) error {
36 if a == nil {
37 return fmt.Errorf("nil *Analyzer")
38 }
39 if color[a] == white {
40 color[a] = grey
41
42
43 if !validIdent(a.Name) {
44 return fmt.Errorf("invalid analyzer name %q", a)
45 }
46
47 if a.Doc == "" {
48 return fmt.Errorf("analyzer %q is undocumented", a)
49 }
50
51 if a.Run == nil {
52 return fmt.Errorf("analyzer %q has nil Run", a)
53 }
54
55 for _, f := range a.FactTypes {
56 if f == nil {
57 return fmt.Errorf("analyzer %s has nil FactType", a)
58 }
59 t := reflect.TypeOf(f)
60 if prev := factTypes[t]; prev != nil {
61 return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
62 t, a, prev)
63 }
64 if t.Kind() != reflect.Ptr {
65 return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
66 }
67 factTypes[t] = a
68 }
69
70
71 for _, req := range a.Requires {
72 if err := visit(req); err != nil {
73 return err
74 }
75 }
76 color[a] = black
77 }
78
79 if color[a] == grey {
80 stack := []*Analyzer{a}
81 inCycle := map[string]bool{}
82 for len(stack) > 0 {
83 current := stack[len(stack)-1]
84 stack = stack[:len(stack)-1]
85 if color[current] == grey && !inCycle[current.Name] {
86 inCycle[current.Name] = true
87 stack = append(stack, current.Requires...)
88 }
89 }
90 return &CycleInRequiresGraphError{AnalyzerNames: inCycle}
91 }
92
93 return nil
94 }
95 for _, a := range analyzers {
96 if err := visit(a); err != nil {
97 return err
98 }
99 }
100
101
102
103
104 for _, a := range analyzers {
105 if color[a] == finished {
106 return fmt.Errorf("duplicate analyzer: %s", a.Name)
107 }
108 color[a] = finished
109 }
110
111 return nil
112 }
113
114 func validIdent(name string) bool {
115 for i, r := range name {
116 if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
117 return false
118 }
119 }
120 return name != ""
121 }
122
123 type CycleInRequiresGraphError struct {
124 AnalyzerNames map[string]bool
125 }
126
127 func (e *CycleInRequiresGraphError) Error() string {
128 var b strings.Builder
129 b.WriteString("cycle detected involving the following analyzers:")
130 for n := range e.AnalyzerNames {
131 b.WriteByte(' ')
132 b.WriteString(n)
133 }
134 return b.String()
135 }
136
View as plain text