validate.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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.
  4. package analysis
  5. import (
  6. "fmt"
  7. "reflect"
  8. "strings"
  9. "unicode"
  10. )
  11. // Validate reports an error if any of the analyzers are misconfigured.
  12. // Checks include:
  13. // that the name is a valid identifier;
  14. // that the Doc is not empty;
  15. // that the Run is non-nil;
  16. // that the Requires graph is acyclic;
  17. // that analyzer fact types are unique;
  18. // that each fact type is a pointer.
  19. func Validate(analyzers []*Analyzer) error {
  20. // Map each fact type to its sole generating analyzer.
  21. factTypes := make(map[reflect.Type]*Analyzer)
  22. // Traverse the Requires graph, depth first.
  23. const (
  24. white = iota
  25. grey
  26. black
  27. finished
  28. )
  29. color := make(map[*Analyzer]uint8)
  30. var visit func(a *Analyzer) error
  31. visit = func(a *Analyzer) error {
  32. if a == nil {
  33. return fmt.Errorf("nil *Analyzer")
  34. }
  35. if color[a] == white {
  36. color[a] = grey
  37. // names
  38. if !validIdent(a.Name) {
  39. return fmt.Errorf("invalid analyzer name %q", a)
  40. }
  41. if a.Doc == "" {
  42. return fmt.Errorf("analyzer %q is undocumented", a)
  43. }
  44. if a.Run == nil {
  45. return fmt.Errorf("analyzer %q has nil Run", a)
  46. }
  47. // fact types
  48. for _, f := range a.FactTypes {
  49. if f == nil {
  50. return fmt.Errorf("analyzer %s has nil FactType", a)
  51. }
  52. t := reflect.TypeOf(f)
  53. if prev := factTypes[t]; prev != nil {
  54. return fmt.Errorf("fact type %s registered by two analyzers: %v, %v",
  55. t, a, prev)
  56. }
  57. if t.Kind() != reflect.Ptr {
  58. return fmt.Errorf("%s: fact type %s is not a pointer", a, t)
  59. }
  60. factTypes[t] = a
  61. }
  62. // recursion
  63. for _, req := range a.Requires {
  64. if err := visit(req); err != nil {
  65. return err
  66. }
  67. }
  68. color[a] = black
  69. }
  70. if color[a] == grey {
  71. stack := []*Analyzer{a}
  72. inCycle := map[string]bool{}
  73. for len(stack) > 0 {
  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...)
  79. }
  80. }
  81. return &CycleInRequiresGraphError{AnalyzerNames: inCycle}
  82. }
  83. return nil
  84. }
  85. for _, a := range analyzers {
  86. if err := visit(a); err != nil {
  87. return err
  88. }
  89. }
  90. // Reject duplicates among analyzers.
  91. // Precondition: color[a] == black.
  92. // Postcondition: color[a] == finished.
  93. for _, a := range analyzers {
  94. if color[a] == finished {
  95. return fmt.Errorf("duplicate analyzer: %s", a.Name)
  96. }
  97. color[a] = finished
  98. }
  99. return nil
  100. }
  101. func validIdent(name string) bool {
  102. for i, r := range name {
  103. if !(r == '_' || unicode.IsLetter(r) || i > 0 && unicode.IsDigit(r)) {
  104. return false
  105. }
  106. }
  107. return name != ""
  108. }
  109. type CycleInRequiresGraphError struct {
  110. AnalyzerNames map[string]bool
  111. }
  112. func (e *CycleInRequiresGraphError) Error() string {
  113. var b strings.Builder
  114. b.WriteString("cycle detected involving the following analyzers:")
  115. for n := range e.AnalyzerNames {
  116. b.WriteByte(' ')
  117. b.WriteString(n)
  118. }
  119. return b.String()
  120. }