98 lines
1.9 KiB
Go
98 lines
1.9 KiB
Go
|
package facts
|
||
|
|
||
|
import (
|
||
|
"bufio"
|
||
|
"bytes"
|
||
|
"io"
|
||
|
"os"
|
||
|
"reflect"
|
||
|
"strings"
|
||
|
|
||
|
"golang.org/x/tools/go/analysis"
|
||
|
)
|
||
|
|
||
|
type Generator int
|
||
|
|
||
|
// A list of known generators we can detect
|
||
|
const (
|
||
|
Unknown Generator = iota
|
||
|
Goyacc
|
||
|
Cgo
|
||
|
Stringer
|
||
|
ProtocGenGo
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
// used by cgo before Go 1.11
|
||
|
oldCgo = []byte("// Created by cgo - DO NOT EDIT")
|
||
|
prefix = []byte("// Code generated ")
|
||
|
suffix = []byte(" DO NOT EDIT.")
|
||
|
nl = []byte("\n")
|
||
|
crnl = []byte("\r\n")
|
||
|
)
|
||
|
|
||
|
func isGenerated(path string) (Generator, bool) {
|
||
|
f, err := os.Open(path)
|
||
|
if err != nil {
|
||
|
return 0, false
|
||
|
}
|
||
|
defer f.Close()
|
||
|
br := bufio.NewReader(f)
|
||
|
for {
|
||
|
s, err := br.ReadBytes('\n')
|
||
|
if err != nil && err != io.EOF {
|
||
|
return 0, false
|
||
|
}
|
||
|
s = bytes.TrimSuffix(s, crnl)
|
||
|
s = bytes.TrimSuffix(s, nl)
|
||
|
if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) {
|
||
|
if len(s)-len(suffix) < len(prefix) {
|
||
|
return Unknown, true
|
||
|
}
|
||
|
|
||
|
text := string(s[len(prefix) : len(s)-len(suffix)])
|
||
|
switch text {
|
||
|
case "by goyacc.":
|
||
|
return Goyacc, true
|
||
|
case "by cmd/cgo;":
|
||
|
return Cgo, true
|
||
|
case "by protoc-gen-go.":
|
||
|
return ProtocGenGo, true
|
||
|
}
|
||
|
if strings.HasPrefix(text, `by "stringer `) {
|
||
|
return Stringer, true
|
||
|
}
|
||
|
if strings.HasPrefix(text, `by goyacc `) {
|
||
|
return Goyacc, true
|
||
|
}
|
||
|
|
||
|
return Unknown, true
|
||
|
}
|
||
|
if bytes.Equal(s, oldCgo) {
|
||
|
return Cgo, true
|
||
|
}
|
||
|
if err == io.EOF {
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
return 0, false
|
||
|
}
|
||
|
|
||
|
var Generated = &analysis.Analyzer{
|
||
|
Name: "isgenerated",
|
||
|
Doc: "annotate file names that have been code generated",
|
||
|
Run: func(pass *analysis.Pass) (interface{}, error) {
|
||
|
m := map[string]Generator{}
|
||
|
for _, f := range pass.Files {
|
||
|
path := pass.Fset.PositionFor(f.Pos(), false).Filename
|
||
|
g, ok := isGenerated(path)
|
||
|
if ok {
|
||
|
m[path] = g
|
||
|
}
|
||
|
}
|
||
|
return m, nil
|
||
|
},
|
||
|
RunDespiteErrors: true,
|
||
|
ResultType: reflect.TypeOf(map[string]Generator{}),
|
||
|
}
|