liyujie
2025-08-28 d9927380ed7c8366f762049be9f3fee225860833
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
// Copyright 2011 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.
 
// +build ignore
 
/*
   The headscan command extracts comment headings from package files;
   it is used to detect false positives which may require an adjustment
   to the comment formatting heuristics in comment.go.
 
   Usage: headscan [-root root_directory]
 
   By default, the $GOROOT/src directory is scanned.
*/
package main
 
import (
   "bytes"
   "flag"
   "fmt"
   "go/doc"
   "go/parser"
   "go/token"
   "os"
   "path/filepath"
   "regexp"
   "runtime"
   "strings"
)
 
var (
   root    = flag.String("root", filepath.Join(runtime.GOROOT(), "src"), "root of filesystem tree to scan")
   verbose = flag.Bool("v", false, "verbose mode")
)
 
// ToHTML in comment.go assigns a (possibly blank) ID to each heading
var html_h = regexp.MustCompile(`<h3 id="[^"]*">`)
 
const html_endh = "</h3>\n"
 
func isGoFile(fi os.FileInfo) bool {
   return strings.HasSuffix(fi.Name(), ".go") &&
       !strings.HasSuffix(fi.Name(), "_test.go")
}
 
func appendHeadings(list []string, comment string) []string {
   var buf bytes.Buffer
   doc.ToHTML(&buf, comment, nil)
   for s := buf.String(); ; {
       loc := html_h.FindStringIndex(s)
       if len(loc) == 0 {
           break
       }
       i := loc[1]
       j := strings.Index(s, html_endh)
       if j < 0 {
           list = append(list, s[i:]) // incorrect HTML
           break
       }
       list = append(list, s[i:j])
       s = s[j+len(html_endh):]
   }
   return list
}
 
func main() {
   flag.Parse()
   fset := token.NewFileSet()
   nheadings := 0
   err := filepath.Walk(*root, func(path string, fi os.FileInfo, err error) error {
       if !fi.IsDir() {
           return nil
       }
       pkgs, err := parser.ParseDir(fset, path, isGoFile, parser.ParseComments)
       if err != nil {
           if *verbose {
               fmt.Fprintln(os.Stderr, err)
           }
           return nil
       }
       for _, pkg := range pkgs {
           d := doc.New(pkg, path, doc.Mode(0))
           list := appendHeadings(nil, d.Doc)
           for _, d := range d.Consts {
               list = appendHeadings(list, d.Doc)
           }
           for _, d := range d.Types {
               list = appendHeadings(list, d.Doc)
           }
           for _, d := range d.Vars {
               list = appendHeadings(list, d.Doc)
           }
           for _, d := range d.Funcs {
               list = appendHeadings(list, d.Doc)
           }
           if len(list) > 0 {
               // directories may contain multiple packages;
               // print path and package name
               fmt.Printf("%s (package %s)\n", path, pkg.Name)
               for _, h := range list {
                   fmt.Printf("\t%s\n", h)
               }
               nheadings += len(list)
           }
       }
       return nil
   })
   if err != nil {
       fmt.Fprintln(os.Stderr, err)
       os.Exit(1)
   }
   fmt.Println(nheadings, "headings found")
}