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