1 // Copyright 2020 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
5 // Package copyright checks that files have the correct copyright notices.
19 func checkCopyright(dir string) ([]string, error) {
21 err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
26 // Skip directories like ".git".
27 if strings.HasPrefix(info.Name(), ".") {
28 return filepath.SkipDir
32 needsCopyright, err := checkFile(dir, path)
37 files = append(files, path)
44 var copyrightRe = regexp.MustCompile(`Copyright \d{4} The Go Authors. All rights reserved.
45 Use of this source code is governed by a BSD-style
46 license that can be found in the LICENSE file.`)
48 func checkFile(toolsDir, filename string) (bool, error) {
49 // Only check Go files.
50 if !strings.HasSuffix(filename, "go") {
53 // Don't check testdata files.
54 normalized := strings.TrimPrefix(filepath.ToSlash(filename), filepath.ToSlash(toolsDir))
55 if strings.Contains(normalized, "/testdata/") {
58 // goyacc is the only file with a different copyright header.
59 if strings.HasSuffix(normalized, "cmd/goyacc/yacc.go") {
62 content, err := ioutil.ReadFile(filename)
66 fset := token.NewFileSet()
67 parsed, err := parser.ParseFile(fset, filename, content, parser.ParseComments)
71 // Don't require headers on generated files.
72 if isGenerated(fset, parsed) {
75 shouldAddCopyright := true
76 for _, c := range parsed.Comments {
77 // The copyright should appear before the package declaration.
78 if c.Pos() > parsed.Package {
81 if copyrightRe.MatchString(c.Text()) {
82 shouldAddCopyright = false
86 return shouldAddCopyright, nil
89 // Copied from golang.org/x/tools/internal/lsp/source/util.go.
90 // Matches cgo generated comment as well as the proposed standard:
91 // https://golang.org/s/generatedcode
92 var generatedRx = regexp.MustCompile(`// .*DO NOT EDIT\.?`)
94 func isGenerated(fset *token.FileSet, file *ast.File) bool {
95 for _, commentGroup := range file.Comments {
96 for _, comment := range commentGroup.List {
97 if matched := generatedRx.MatchString(comment.Text); !matched {
100 // Check if comment is at the beginning of the line in source.
101 if pos := fset.Position(comment.Slash); pos.Column == 1 {