mirror of
https://github.com/golang/go
synced 2024-11-11 17:51:49 -07:00
go/ast: add IsGenerated(*File) predicate
See https://go.dev/s/generatedcode for spec. Fixes #28089 Change-Id: Ic9bb138bdd180f136f9e8e74e187319acca5dbac Reviewed-on: https://go-review.googlesource.com/c/go/+/487935 Run-TryBot: Alan Donovan <adonovan@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Reviewed-by: Robert Findley <rfindley@google.com> Reviewed-by: Dmitri Shuralyov <dmitshur@golang.org>
This commit is contained in:
parent
22d94dfdc8
commit
a1284d0185
1
api/next/28089.txt
Normal file
1
api/next/28089.txt
Normal file
@ -0,0 +1 @@
|
||||
pkg go/ast, func IsGenerated(*File) bool #28089
|
@ -1073,3 +1073,40 @@ type Package struct {
|
||||
|
||||
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
|
||||
}
|
||||
|
@ -40,3 +40,100 @@ func TestIssue33649(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user