]> Cypherpunks.ru repositories - gostls13.git/commitdiff
go/ast: add IsGenerated(*File) predicate
authorAlan Donovan <adonovan@google.com>
Sun, 23 Apr 2023 17:45:08 +0000 (13:45 -0400)
committerAlan Donovan <adonovan@google.com>
Tue, 25 Apr 2023 13:57:33 +0000 (13:57 +0000)
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>
api/next/28089.txt [new file with mode: 0644]
src/go/ast/ast.go
src/go/ast/issues_test.go

diff --git a/api/next/28089.txt b/api/next/28089.txt
new file mode 100644 (file)
index 0000000..1b55914
--- /dev/null
@@ -0,0 +1 @@
+pkg go/ast, func IsGenerated(*File) bool #28089
index b509ef1a70bec9df5b424ed904429536a93dfee6..c43905261050a179e23509160a219be80cf87393 100644 (file)
@@ -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
+}
index 788c5578b878701bce43dd8f7d0ea8bde7d7860a..28d6a30fbb328f579a1740ed698990b2ff473997 100644 (file)
@@ -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)
+               }
+       }
+
+}