autogenerated.go

  1package server
  2
  3import (
  4	"bytes"
  5	"go/parser"
  6	"go/token"
  7	"path/filepath"
  8	"strings"
  9)
 10
 11// IsAutogeneratedPath reports whether a file path suggests it's autogenerated.
 12// This checks for common autogenerated file patterns based on path alone.
 13func IsAutogeneratedPath(path string) bool {
 14	base := filepath.Base(path)
 15
 16	// Convert path separators to forward slashes for consistent matching
 17	normPath := filepath.ToSlash(path)
 18
 19	// Check directory patterns first
 20	// We check if any path component matches the autogenerated directory name
 21	for _, d := range autogeneratedDirs {
 22		// Match at start of path or after a /
 23		if strings.HasPrefix(normPath, d) || strings.Contains(normPath, "/"+d) {
 24			return true
 25		}
 26	}
 27
 28	// Check file extension patterns
 29	for _, ext := range autogeneratedExtensions {
 30		if strings.HasSuffix(base, ext) {
 31			return true
 32		}
 33	}
 34
 35	// Check exact filename matches
 36	for _, name := range autogeneratedFilenames {
 37		if base == name {
 38			return true
 39		}
 40	}
 41
 42	return false
 43}
 44
 45// IsAutogeneratedFile reports whether a file is autogenerated based on its path and content.
 46// For Go files, it also analyzes the content for autogeneration markers.
 47func IsAutogeneratedFile(path string, content []byte) bool {
 48	if IsAutogeneratedPath(path) {
 49		return true
 50	}
 51
 52	// For Go files, check content for autogeneration markers
 53	if strings.HasSuffix(path, ".go") && content != nil {
 54		return isAutogeneratedGoContent(content)
 55	}
 56
 57	return false
 58}
 59
 60// isAutogeneratedGoContent reports whether a Go file has markers indicating it was autogenerated.
 61func isAutogeneratedGoContent(buf []byte) bool {
 62	for _, sig := range autogeneratedSignals {
 63		if bytes.Contains(buf, sig) {
 64			return true
 65		}
 66	}
 67
 68	// https://pkg.go.dev/cmd/go#hdr-Generate_Go_files_by_processing_source
 69	// "This line must appear before the first non-comment, non-blank text in the file."
 70	// Approximate that by looking for it at the top of the file, before the last of the imports.
 71	fset := token.NewFileSet()
 72	f, err := parser.ParseFile(fset, "x.go", buf, parser.ImportsOnly|parser.ParseComments)
 73	if err == nil {
 74		for _, cg := range f.Comments {
 75			t := strings.ToLower(cg.Text())
 76			for _, sig := range autogeneratedHeaderSignals {
 77				if strings.Contains(t, sig) {
 78					return true
 79				}
 80			}
 81		}
 82	}
 83
 84	return false
 85}
 86
 87// autogeneratedDirs are directory names that typically contain generated files.
 88var autogeneratedDirs = []string{
 89	"vendor/",
 90	"node_modules/",
 91	".git/",
 92	"__pycache__/",
 93	".next/",
 94	"dist/",
 95	"build/",
 96	"generated/",
 97	"gen/",
 98}
 99
100// autogeneratedExtensions are file suffixes that indicate autogenerated files.
101var autogeneratedExtensions = []string{
102	".pb.go",        // Protocol buffers
103	".pb.gw.go",     // gRPC gateway
104	"_string.go",    // stringer
105	".gen.go",       // general generated Go
106	".generated.go", // general generated Go
107	"_generated.go", // general generated Go
108	".mock.go",      // mocks
109	"_mock.go",      // mocks
110	".mocks.go",     // mocks
111	"_mocks.go",     // mocks
112	".min.js",       // minified JS
113	".min.css",      // minified CSS
114	".d.ts",         // TypeScript declarations
115	".pb.ts",        // Protocol buffers TypeScript
116	".generated.ts", // general generated TypeScript
117	"_generated.ts", // general generated TypeScript
118	".sql.go",       // sqlc generated
119	".enumer.go",    // enumer generated
120	"_easyjson.go",  // easyjson generated
121	".deepcopy.go",  // Kubernetes deepcopy
122}
123
124// autogeneratedFilenames are exact filenames that are typically autogenerated.
125var autogeneratedFilenames = []string{
126	"go.sum",
127	"package-lock.json",
128	"yarn.lock",
129	"pnpm-lock.yaml",
130	"Cargo.lock",
131	"Gemfile.lock",
132	"composer.lock",
133	"poetry.lock",
134	"uv.lock",
135}
136
137// autogeneratedSignals are signals that a Go file is autogenerated, when present anywhere in the file.
138var autogeneratedSignals = [][]byte{
139	[]byte("\nfunc bindataRead("), // pre-embed bindata packed file
140}
141
142// autogeneratedHeaderSignals are signals that a file is autogenerated, when present at the top of the file.
143var autogeneratedHeaderSignals = []string{
144	// canonical would be `(?m)^// Code generated .* DO NOT EDIT\.$`
145	// but people screw it up, a lot, so be more lenient
146	strings.ToLower("generate"),
147	strings.ToLower("DO NOT EDIT"),
148	strings.ToLower("export by"),
149}