1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
|
package main
import (
"flag"
"fmt"
"go/doc"
"go/parser"
"go/token"
"io/fs"
"os"
"path/filepath"
"strings"
)
var isMGMT = false
func filter(f fs.FileInfo) bool {
return !strings.HasSuffix(f.Name(), "_test.go")
}
func findAllSubDirectories(root string) []string {
var ret []string
filepath.Walk(root, func(path string, info fs.FileInfo, err error) error {
if err != nil {
panic(err)
}
if strings.Contains(path, "resourcemanager") {
isMGMT = true
}
if strings.Contains(path, "eng/tools") {
return filepath.SkipDir
}
if info.IsDir() && strings.HasSuffix(path, "internal") {
return filepath.SkipDir
} else if info.IsDir() {
ret = append(ret, path)
}
return nil
})
return ret
}
// validateDirectory counts the number of missing doc comments
func validateDirectory(directory string) int {
missingDocCount := 0
fset := token.NewFileSet() // positions are relative to fset
d, err := parser.ParseDir(fset, directory, filter, parser.ParseComments)
if err != nil {
panic(fmt.Errorf("could not parse directory: %w", err))
}
for k, f := range d {
if strings.Contains("_test", k) {
continue
}
fmt.Println("package", k)
p := doc.New(f, "./", 2)
for _, t := range p.Types {
if !strings.HasPrefix(t.Doc, t.Name) {
fmt.Println("missing or invalid doc comment. all docs should start with the type they are documenting")
fmt.Printf("type: '%s' docs: '%s'\n", t.Name, t.Doc)
missingDocCount += 1
}
for _, m := range t.Methods {
if !strings.HasPrefix(m.Doc, m.Name) {
fmt.Println("missing or invalid doc comment. all docs should start with the function they are documenting")
fmt.Printf("type: '%s' method: '%s' docs: '%s'\n", t.Name, m.Name, m.Doc)
missingDocCount += 1
}
}
}
for _, f := range p.Funcs {
if strings.HasPrefix(f.Name, "Example") {
continue
}
if !strings.HasPrefix(f.Doc, f.Name) {
fmt.Println("missing or invalid doc comment. all docs should start with the function they are documenting")
if f.Recv != "" {
fmt.Printf("type: %s receiver: %s docs: %s\n", f.Name, f.Recv, f.Doc)
} else {
fmt.Printf("type: %s docs: %s\n", f.Name, f.Doc)
}
missingDocCount += 1
}
}
}
return missingDocCount
}
func main() {
var root string
flag.StringVar(&root, "directory", ".", "directory to check docs for")
flag.Parse()
fmt.Printf("checking documentation in %s\n", root)
var totalMissing = 0
for _, dir := range findAllSubDirectories(root) {
totalMissing += validateDirectory(dir)
}
if totalMissing > 0 {
fmt.Printf("Found %d missing doc comments\n", totalMissing)
if !isMGMT {
os.Exit(1)
}
} else {
fmt.Println("There are no public methods/functions/types with missing documentation")
}
}
|