func (p *Package) Pos() token.Pos { return token.NoPos }
func (p *Package) End() token.Pos { return token.NoPos }
+
+// IsGenerated reports whether the file was generated by a program,
+// not handwritten, by detecting the special comment described
+// at https://go.dev/s/generatedcode.
+//
+// The syntax tree must have been parsed with the ParseComments flag.
+// Example:
+//
+// f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly)
+// if err != nil { ... }
+// gen := ast.IsGenerated(f)
+func IsGenerated(file *File) bool {
+ _, ok := generator(file)
+ return ok
+}
+
+func generator(file *File) (string, bool) {
+ for _, group := range file.Comments {
+ for _, comment := range group.List {
+ if comment.Pos() > file.Package {
+ break // after package declaration
+ }
+ // opt: check Contains first to avoid unnecessary array allocation in Split.
+ const prefix = "// Code generated "
+ if strings.Contains(comment.Text, prefix) {
+ for _, line := range strings.Split(comment.Text, "\n") {
+ if rest, ok := strings.CutPrefix(line, prefix); ok {
+ if gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok {
+ return gen, true
+ }
+ }
+ }
+ }
+ }
+ }
+ return "", false
+}
}
}
}
+
+// TestIssue28089 exercises the IsGenerated function.
+func TestIssue28089(t *testing.T) {
+ for i, test := range []struct {
+ src string
+ want bool
+ }{
+ // No file comments.
+ {`package p`, false},
+ // Irrelevant file comments.
+ {`// Package p doc.
+package p`, false},
+ // Special comment misplaced after package decl.
+ {`// Package p doc.
+package p
+// Code generated by gen. DO NOT EDIT.
+`, false},
+ // Special comment appears inside string literal.
+ {`// Package p doc.
+package p
+const c = "` + "`" + `
+// Code generated by gen. DO NOT EDIT.
+` + "`" + `
+`, false},
+ // Special comment appears properly.
+ {`// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package p doc comment goes here.
+//
+// Code generated by gen. DO NOT EDIT.
+package p
+
+... `, true},
+ // Special comment is indented.
+ //
+ // Strictly, the indent should cause IsGenerated to
+ // yield false, but we cannot detect the indent
+ // without either source text or a token.File.
+ // In other words, the function signature cannot
+ // implement the spec. Let's brush this under the
+ // rug since well-formatted code has no indent.
+ {`// Package p doc comment goes here.
+//
+ // Code generated by gen. DO NOT EDIT.
+package p
+
+... `, true},
+ // Special comment has unwanted spaces after "DO NOT EDIT."
+ {`// Copyright 2019 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Package p doc comment goes here.
+//
+// Code generated by gen. DO NOT EDIT.
+package p
+
+... `, false},
+ // Special comment has rogue interior space.
+ {`// Code generated by gen. DO NOT EDIT.
+package p
+`, false},
+ // Special comment lacks the middle portion.
+ {`// Code generated DO NOT EDIT.
+package p
+`, false},
+ // Special comment (incl. "//") appears within a /* block */ comment,
+ // an obscure corner case of the spec.
+ {`/* start of a general comment
+
+// Code generated by tool; DO NOT EDIT.
+
+end of a general comment */
+
+// +build !dev
+
+// Package comment.
+package p
+
+// Does match even though it's inside general comment (/*-style).
+`, true},
+ } {
+ fset := token.NewFileSet()
+ f, err := parser.ParseFile(fset, "", test.src, parser.PackageClauseOnly|parser.ParseComments)
+ if f == nil {
+ t.Fatalf("parse %d failed to return AST: %v", i, err)
+ }
+
+ got := ast.IsGenerated(f)
+ if got != test.want {
+ t.Errorf("%d: IsGenerated on <<%s>> returned %t", i, test.src, got)
+ }
+ }
+
+}