From 8c0d6637fa72104f691f9c9835a7d79240e234a5 Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Thu, 1 Oct 2026 22:53:24 +0800 Subject: [PATCH 1/2] Limit generated marker detection to file header --- analysis/facts/generated/generated.go | 8 +++- analysis/facts/generated/generated_test.go | 44 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 analysis/facts/generated/generated_test.go diff --git a/analysis/facts/generated/generated.go b/analysis/facts/generated/generated.go index 301ad0e3..d2f03a8d 100644 --- a/analysis/facts/generated/generated.go +++ b/analysis/facts/generated/generated.go @@ -38,13 +38,19 @@ func isGenerated(path string) (Generator, bool) { } defer f.Close() br := bufio.NewReader(f) - for { + // Generated-file markers are only meaningful in the file header. In + // particular, don't treat comments in function bodies as a file-wide + // declaration that the file was generated. + for i := 0; i < 10; i++ { 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, []byte("package ")) { + return 0, false + } if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) { if len(s)-len(suffix) < len(prefix) { return Unknown, true diff --git a/analysis/facts/generated/generated_test.go b/analysis/facts/generated/generated_test.go new file mode 100644 index 00000000..1312bed7 --- /dev/null +++ b/analysis/facts/generated/generated_test.go @@ -0,0 +1,44 @@ +package generated + +import ( + "os" + "path/filepath" + "testing" +) + +func TestIsGeneratedOnlyChecksFileHeader(t *testing.T) { + tests := []struct { + name string + text string + want bool + }{ + { + name: "header", + text: "// Code generated by protoc-gen-go. DO NOT EDIT.\n\npackage p\n", + want: true, + }, + { + name: "body comment", + text: "package p\n\nfunc f() {\n\t// Code generated by protoc-gen-go. DO NOT EDIT.\n}\n", + want: false, + }, + { + name: "late comment", + text: "package p\n\n// Code generated by protoc-gen-go. DO NOT EDIT.\n", + want: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + path := filepath.Join(t.TempDir(), "generated.go") + if err := os.WriteFile(path, []byte(tt.text), 0o600); err != nil { + t.Fatal(err) + } + _, got := isGenerated(path) + if got != tt.want { + t.Fatalf("isGenerated() = %v, want %v", got, tt.want) + } + }) + } +} From 9354a7b5e25f829ab4019a6ea8fc527caaec56ce Mon Sep 17 00:00:00 2001 From: dajiaohuang Date: Fri, 2 Oct 2026 14:36:25 +0800 Subject: [PATCH 2/2] Parse generated markers in the complete file header --- analysis/facts/generated/generated.go | 76 +++++++++------------- analysis/facts/generated/generated_test.go | 21 ++++++ 2 files changed, 52 insertions(+), 45 deletions(-) diff --git a/analysis/facts/generated/generated.go b/analysis/facts/generated/generated.go index d2f03a8d..b665582a 100644 --- a/analysis/facts/generated/generated.go +++ b/analysis/facts/generated/generated.go @@ -1,10 +1,9 @@ package generated import ( - "bufio" "bytes" - "io" - "os" + "go/parser" + "go/token" "reflect" "strings" @@ -27,58 +26,45 @@ var ( 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) + f, err := parser.ParseFile(token.NewFileSet(), path, nil, parser.ParseComments|parser.PackageClauseOnly|parser.SkipObjectResolution) if err != nil { return 0, false } - defer f.Close() - br := bufio.NewReader(f) - // Generated-file markers are only meaningful in the file header. In - // particular, don't treat comments in function bodies as a file-wide - // declaration that the file was generated. - for i := 0; i < 10; i++ { - 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, []byte("package ")) { - return 0, false - } - if bytes.HasPrefix(s, prefix) && bytes.HasSuffix(s, suffix) { - if len(s)-len(suffix) < len(prefix) { - return Unknown, true + for _, group := range f.Comments { + for _, comment := range group.List { + if comment.Pos() >= f.Package { + continue } + s := []byte(comment.Text) + 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 + 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 strings.HasPrefix(text, `by goyacc `) { - return Goyacc, true + if bytes.Equal(s, oldCgo) { + return Cgo, true } - - return Unknown, true - } - if bytes.Equal(s, oldCgo) { - return Cgo, true - } - if err == io.EOF { - break } } return 0, false diff --git a/analysis/facts/generated/generated_test.go b/analysis/facts/generated/generated_test.go index 1312bed7..d7adfa36 100644 --- a/analysis/facts/generated/generated_test.go +++ b/analysis/facts/generated/generated_test.go @@ -3,6 +3,7 @@ package generated import ( "os" "path/filepath" + "strings" "testing" ) @@ -12,6 +13,26 @@ func TestIsGeneratedOnlyChecksFileHeader(t *testing.T) { text string want bool }{ + { + name: "long license header", + text: strings.Repeat("// license\n", 20) + "// Code generated by protoc-gen-go. DO NOT EDIT.\npackage p\n", + want: true, + }, + { + name: "indented package clause", + text: " package p\n// Code generated by protoc-gen-go. DO NOT EDIT.\n", + want: false, + }, + { + name: "marker inside block comment", + text: "/*\n// Code generated by protoc-gen-go. DO NOT EDIT.\n*/\npackage p\n", + want: false, + }, + { + name: "old cgo header", + text: "// Created by cgo - DO NOT EDIT\npackage p\n", + want: true, + }, { name: "header", text: "// Code generated by protoc-gen-go. DO NOT EDIT.\n\npackage p\n",