generated.go 1.9 KB

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