generated.go raw

   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  }
  98