mirror of
https://github.com/tailscale/tailscale.git
synced 2026-05-09 15:44:33 -04:00
Add a new vet checker that rejects variables, parameters, named return values, receivers, range/type-switch bindings, type parameters, struct fields, and constants named "l" (lowercase ell) or "I" (uppercase i). Both are hard to distinguish from the digit "1" and from each other in too many fonts. Rename the two pre-existing struct fields named "l" (both of type net.Listener) in drive/driveimpl/drive_test.go to "ln", matching the convention used elsewhere for net.Listener locals. Rename the test-fixture struct fields "I" (single int label) to "Int" in metrics/multilabelmap_test.go and util/deephash/deephash_test.go, preserving the "first letters of types" convention used alongside neighboring fields like I8/I16/U/U8. Also teach pkgdoc_test.go to skip testdata/ directories, which the go tool ignores; they are not real packages. Fixes #19631 Change-Id: I71ad2fa990705f7a070406ebcdb8cefa7487d849 Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
// Copyright (c) Tailscale Inc & contributors
|
|
// SPDX-License-Identifier: BSD-3-Clause
|
|
|
|
package tailscaleroot
|
|
|
|
import (
|
|
"go/ast"
|
|
"go/parser"
|
|
"go/token"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func hasIgnoreBuildTag(f *ast.File) bool {
|
|
for _, cg := range f.Comments {
|
|
for _, c := range cg.List {
|
|
if c.Text == "//go:build ignore" {
|
|
return true
|
|
}
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func TestPackageDocs(t *testing.T) {
|
|
switch runtime.GOOS {
|
|
case "darwin", "linux":
|
|
// Enough coverage for CI+devs.
|
|
default:
|
|
t.Skipf("skipping on %s", runtime.GOOS)
|
|
}
|
|
|
|
var goFiles []string
|
|
err := filepath.Walk(".", func(path string, fi os.FileInfo, err error) error {
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if fi.Mode().IsDir() && path != "." && strings.HasPrefix(filepath.Base(path), ".") {
|
|
return filepath.SkipDir // No documentation lives in dot directories (.git, .claude, etc)
|
|
}
|
|
if fi.Mode().IsDir() && filepath.Base(path) == "testdata" {
|
|
return filepath.SkipDir // testdata is ignored by the go tool; not real packages
|
|
}
|
|
if fi.Mode().IsRegular() && strings.HasSuffix(path, ".go") {
|
|
if strings.HasSuffix(path, "_test.go") {
|
|
return nil
|
|
}
|
|
goFiles = append(goFiles, path)
|
|
}
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
byDir := map[string][]string{} // dir => files
|
|
for _, fileName := range goFiles {
|
|
fset := token.NewFileSet()
|
|
f, err := parser.ParseFile(fset, fileName, nil, parser.PackageClauseOnly|parser.ParseComments)
|
|
if err != nil {
|
|
t.Fatalf("failed to ParseFile %q: %v", fileName, err)
|
|
}
|
|
if hasIgnoreBuildTag(f) {
|
|
continue
|
|
}
|
|
dir := filepath.Dir(fileName)
|
|
if _, ok := byDir[dir]; !ok {
|
|
byDir[dir] = nil
|
|
}
|
|
if f.Doc != nil {
|
|
byDir[dir] = append(byDir[dir], fileName)
|
|
txt := f.Doc.Text()
|
|
if strings.Contains(txt, "SPDX-License-Identifier") {
|
|
t.Errorf("the copyright header for %s became its package doc due to missing blank line", fileName)
|
|
}
|
|
}
|
|
}
|
|
for dir, ff := range byDir {
|
|
if len(ff) > 1 {
|
|
t.Errorf("multiple files with package doc in %s: %q", dir, ff)
|
|
}
|
|
if len(ff) == 0 {
|
|
if strings.HasPrefix(dir, "gokrazy/") {
|
|
// Ignore gokrazy appliances. Their *.go file is only for deps.
|
|
continue
|
|
}
|
|
t.Errorf("no package doc in %s", dir)
|
|
}
|
|
}
|
|
t.Logf("parsed %d files", len(goFiles))
|
|
}
|