github.com/powerman/golang-tools@v0.1.11-0.20220410185822-5ad214d8d803/go/analysis/validate.go (about)

     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  
     5  package analysis
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  	"unicode"
    12  )
    13  
    14  // Validate reports an error if any of the analyzers are misconfigured.
    15  // Checks include:
    16  // that the name is a valid identifier;
    17  // that the Doc is not empty;
    18  // that the Run is non-nil;
    19  // that the Requires graph is acyclic;
    20  // that analyzer fact types are unique;
    21  // that each fact type is a pointer.
    22  func Validate(analyzers []*Analyzer) error {
    23  	// Map each fact type to its sole generating analyzer.
    24  	factTypes := make(map[reflect.Type]*Analyzer)
    25  
    26  	// Traverse the Requires graph, depth first.
    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  			// names
    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  			// fact types
    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  			// recursion
    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  	// Reject duplicates among analyzers.
   102  	// Precondition:  color[a] == black.
   103  	// Postcondition: color[a] == finished.
   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  }