honnef.co/go/tools@v0.5.0-0.dev.0.20240520180541-dcae280a5e87/analysis/facts/generated/generated.go (about)

     1  package generated
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  	"os"
     8  	"reflect"
     9  	"strings"
    10  
    11  	"golang.org/x/tools/go/analysis"
    12  )
    13  
    14  type Generator int
    15  
    16  // A list of known generators we can detect
    17  const (
    18  	Unknown Generator = iota
    19  	Goyacc
    20  	Cgo
    21  	Stringer
    22  	ProtocGenGo
    23  )
    24  
    25  var (
    26  	// used by cgo before Go 1.11
    27  	oldCgo = []byte("// Created by cgo - DO NOT EDIT")
    28  	prefix = []byte("// Code generated ")
    29  	suffix = []byte(" DO NOT EDIT.")
    30  	nl     = []byte("\n")
    31  	crnl   = []byte("\r\n")
    32  )
    33  
    34  func isGenerated(path string) (Generator, bool) {
    35  	f, err := os.Open(path)
    36  	if err != nil {
    37  		return 0, false
    38  	}
    39  	defer f.Close()
    40  	br := bufio.NewReader(f)
    41  	for {
    42  		s, err := br.ReadBytes('\n')
    43  		if err != nil && err != io.EOF {
    44  			return 0, false
    45  		}
    46  		s = bytes.TrimSuffix(s, crnl)
    47  		s = bytes.TrimSuffix(s, nl)
    48  		if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) {
    49  			if len(s)-len(suffix) < len(prefix) {
    50  				return Unknown, true
    51  			}
    52  
    53  			text := string(s[len(prefix) : len(s)-len(suffix)])
    54  			switch text {
    55  			case "by goyacc.":
    56  				return Goyacc, true
    57  			case "by cmd/cgo;":
    58  				return Cgo, true
    59  			case "by protoc-gen-go.":
    60  				return ProtocGenGo, true
    61  			}
    62  			if strings.HasPrefix(text, `by "stringer `) {
    63  				return Stringer, true
    64  			}
    65  			if strings.HasPrefix(text, `by goyacc `) {
    66  				return Goyacc, true
    67  			}
    68  
    69  			return Unknown, true
    70  		}
    71  		if bytes.Equal(s, oldCgo) {
    72  			return Cgo, true
    73  		}
    74  		if err == io.EOF {
    75  			break
    76  		}
    77  	}
    78  	return 0, false
    79  }
    80  
    81  var Analyzer = &analysis.Analyzer{
    82  	Name: "isgenerated",
    83  	Doc:  "annotate file names that have been code generated",
    84  	Run: func(pass *analysis.Pass) (interface{}, error) {
    85  		m := map[string]Generator{}
    86  		for _, f := range pass.Files {
    87  			path := pass.Fset.PositionFor(f.Pos(), false).Filename
    88  			g, ok := isGenerated(path)
    89  			if ok {
    90  				m[path] = g
    91  			}
    92  		}
    93  		return m, nil
    94  	},
    95  	RunDespiteErrors: true,
    96  	ResultType:       reflect.TypeOf(map[string]Generator{}),
    97  }