aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/internal
diff options
context:
space:
mode:
authorMichael Matloob <matloob@golang.org>2025-07-23 11:25:14 -0400
committerMichael Matloob <matloob@google.com>2025-07-24 09:59:40 -0700
commitbd446662ddb749e35314dd3828f2bf06f4d48518 (patch)
tree57aaec9beae42cb53b6dad8a2a516dbd8b080514 /src/cmd/internal
parentda8b50c83087897e2a8abef2d8909253f1fb4f23 (diff)
downloadgo-bd446662ddb749e35314dd3828f2bf06f4d48518.tar.xz
cmd/internal/doc: merge with cmd/go/internal/doc
Now that cmd/doc has been removed, cmd/go/internal/doc is the only user of the cmd/internal/doc code. Merge cmd/internal/doc into cmd/go/internal/doc. For #74667 Change-Id: I16bbe7b1f418b54ee411c8d6e6609c0973e40b3e Reviewed-on: https://go-review.googlesource.com/c/go/+/689836 Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Michael Matloob <matloob@golang.org> Reviewed-by: Michael Matloob <matloob@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/cmd/internal')
-rw-r--r--src/cmd/internal/doc/dirs.go320
-rw-r--r--src/cmd/internal/doc/doc_test.go1103
-rw-r--r--src/cmd/internal/doc/main.go532
-rw-r--r--src/cmd/internal/doc/pkg.go1167
-rw-r--r--src/cmd/internal/doc/signal_notunix.go13
-rw-r--r--src/cmd/internal/doc/signal_unix.go14
-rw-r--r--src/cmd/internal/doc/testdata/merge/aa.go7
-rw-r--r--src/cmd/internal/doc/testdata/merge/bb.go7
-rw-r--r--src/cmd/internal/doc/testdata/nested/empty/empty.go1
-rw-r--r--src/cmd/internal/doc/testdata/nested/ignore.go5
-rw-r--r--src/cmd/internal/doc/testdata/nested/nested/real.go4
-rw-r--r--src/cmd/internal/doc/testdata/pkg.go254
12 files changed, 0 insertions, 3427 deletions
diff --git a/src/cmd/internal/doc/dirs.go b/src/cmd/internal/doc/dirs.go
deleted file mode 100644
index 8b1670f61c..0000000000
--- a/src/cmd/internal/doc/dirs.go
+++ /dev/null
@@ -1,320 +0,0 @@
-// Copyright 2015 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 doc
-
-import (
- "bytes"
- "fmt"
- "log"
- "os"
- "os/exec"
- "path/filepath"
- "regexp"
- "strings"
- "sync"
-
- "golang.org/x/mod/semver"
-)
-
-// A Dir describes a directory holding code by specifying
-// the expected import path and the file system directory.
-type Dir struct {
- importPath string // import path for that dir
- dir string // file system directory
- inModule bool
-}
-
-// Dirs is a structure for scanning the directory tree.
-// Its Next method returns the next Go source directory it finds.
-// Although it can be used to scan the tree multiple times, it
-// only walks the tree once, caching the data it finds.
-type Dirs struct {
- scan chan Dir // Directories generated by walk.
- hist []Dir // History of reported Dirs.
- offset int // Counter for Next.
-}
-
-var dirs Dirs
-
-// dirsInit starts the scanning of package directories in GOROOT and GOPATH. Any
-// extra paths passed to it are included in the channel.
-func dirsInit(extra ...Dir) {
- if buildCtx.GOROOT == "" {
- stdout, err := exec.Command("go", "env", "GOROOT").Output()
- if err != nil {
- if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 {
- log.Fatalf("failed to determine GOROOT: $GOROOT is not set and 'go env GOROOT' failed:\n%s", ee.Stderr)
- }
- log.Fatalf("failed to determine GOROOT: $GOROOT is not set and could not run 'go env GOROOT':\n\t%s", err)
- }
- buildCtx.GOROOT = string(bytes.TrimSpace(stdout))
- }
-
- dirs.hist = make([]Dir, 0, 1000)
- dirs.hist = append(dirs.hist, extra...)
- dirs.scan = make(chan Dir)
- go dirs.walk(codeRoots())
-}
-
-// goCmd returns the "go" command path corresponding to buildCtx.GOROOT.
-func goCmd() string {
- if buildCtx.GOROOT == "" {
- return "go"
- }
- return filepath.Join(buildCtx.GOROOT, "bin", "go")
-}
-
-// Reset puts the scan back at the beginning.
-func (d *Dirs) Reset() {
- d.offset = 0
-}
-
-// Next returns the next directory in the scan. The boolean
-// is false when the scan is done.
-func (d *Dirs) Next() (Dir, bool) {
- if d.offset < len(d.hist) {
- dir := d.hist[d.offset]
- d.offset++
- return dir, true
- }
- dir, ok := <-d.scan
- if !ok {
- return Dir{}, false
- }
- d.hist = append(d.hist, dir)
- d.offset++
- return dir, ok
-}
-
-// walk walks the trees in GOROOT and GOPATH.
-func (d *Dirs) walk(roots []Dir) {
- for _, root := range roots {
- d.bfsWalkRoot(root)
- }
- close(d.scan)
-}
-
-// bfsWalkRoot walks a single directory hierarchy in breadth-first lexical order.
-// Each Go source directory it finds is delivered on d.scan.
-func (d *Dirs) bfsWalkRoot(root Dir) {
- root.dir = filepath.Clean(root.dir) // because filepath.Join will do it anyway
-
- // this is the queue of directories to examine in this pass.
- this := []string{}
- // next is the queue of directories to examine in the next pass.
- next := []string{root.dir}
-
- for len(next) > 0 {
- this, next = next, this[0:0]
- for _, dir := range this {
- fd, err := os.Open(dir)
- if err != nil {
- log.Print(err)
- continue
- }
- entries, err := fd.Readdir(0)
- fd.Close()
- if err != nil {
- log.Print(err)
- continue
- }
- hasGoFiles := false
- for _, entry := range entries {
- name := entry.Name()
- // For plain files, remember if this directory contains any .go
- // source files, but ignore them otherwise.
- if !entry.IsDir() {
- if !hasGoFiles && strings.HasSuffix(name, ".go") {
- hasGoFiles = true
- }
- continue
- }
- // Entry is a directory.
-
- // The go tool ignores directories starting with ., _, or named "testdata".
- if name[0] == '.' || name[0] == '_' || name == "testdata" {
- continue
- }
- // When in a module, ignore vendor directories and stop at module boundaries.
- if root.inModule {
- if name == "vendor" {
- continue
- }
- if fi, err := os.Stat(filepath.Join(dir, name, "go.mod")); err == nil && !fi.IsDir() {
- continue
- }
- }
- // Remember this (fully qualified) directory for the next pass.
- next = append(next, filepath.Join(dir, name))
- }
- if hasGoFiles {
- // It's a candidate.
- importPath := root.importPath
- if len(dir) > len(root.dir) {
- if importPath != "" {
- importPath += "/"
- }
- importPath += filepath.ToSlash(dir[len(root.dir)+1:])
- }
- d.scan <- Dir{importPath, dir, root.inModule}
- }
- }
-
- }
-}
-
-var testGOPATH = false // force GOPATH use for testing
-
-// codeRoots returns the code roots to search for packages.
-// In GOPATH mode this is GOROOT/src and GOPATH/src, with empty import paths.
-// In module mode, this is each module root, with an import path set to its module path.
-func codeRoots() []Dir {
- codeRootsCache.once.Do(func() {
- codeRootsCache.roots = findCodeRoots()
- })
- return codeRootsCache.roots
-}
-
-var codeRootsCache struct {
- once sync.Once
- roots []Dir
-}
-
-var usingModules bool
-
-func findCodeRoots() []Dir {
- var list []Dir
- if !testGOPATH {
- // Check for use of modules by 'go env GOMOD',
- // which reports a go.mod file path if modules are enabled.
- stdout, _ := exec.Command(goCmd(), "env", "GOMOD").Output()
- gomod := string(bytes.TrimSpace(stdout))
-
- usingModules = len(gomod) > 0
- if usingModules && buildCtx.GOROOT != "" {
- list = append(list,
- Dir{dir: filepath.Join(buildCtx.GOROOT, "src"), inModule: true},
- Dir{importPath: "cmd", dir: filepath.Join(buildCtx.GOROOT, "src", "cmd"), inModule: true})
- }
-
- if gomod == os.DevNull {
- // Modules are enabled, but the working directory is outside any module.
- // We can still access std, cmd, and packages specified as source files
- // on the command line, but there are no module roots.
- // Avoid 'go list -m all' below, since it will not work.
- return list
- }
- }
-
- if !usingModules {
- if buildCtx.GOROOT != "" {
- list = append(list, Dir{dir: filepath.Join(buildCtx.GOROOT, "src")})
- }
- for _, root := range splitGopath() {
- list = append(list, Dir{dir: filepath.Join(root, "src")})
- }
- return list
- }
-
- // Find module root directories from go list.
- // Eventually we want golang.org/x/tools/go/packages
- // to handle the entire file system search and become go/packages,
- // but for now enumerating the module roots lets us fit modules
- // into the current code with as few changes as possible.
- mainMod, vendorEnabled, err := vendorEnabled()
- if err != nil {
- return list
- }
- if vendorEnabled {
- // Add the vendor directory to the search path ahead of "std".
- // That way, if the main module *is* "std", we will identify the path
- // without the "vendor/" prefix before the one with that prefix.
- list = append([]Dir{{dir: filepath.Join(mainMod.Dir, "vendor"), inModule: false}}, list...)
- if mainMod.Path != "std" {
- list = append(list, Dir{importPath: mainMod.Path, dir: mainMod.Dir, inModule: true})
- }
- return list
- }
-
- cmd := exec.Command(goCmd(), "list", "-m", "-f={{.Path}}\t{{.Dir}}", "all")
- cmd.Stderr = os.Stderr
- out, _ := cmd.Output()
- for _, line := range strings.Split(string(out), "\n") {
- path, dir, _ := strings.Cut(line, "\t")
- if dir != "" {
- list = append(list, Dir{importPath: path, dir: dir, inModule: true})
- }
- }
-
- return list
-}
-
-// The functions below are derived from x/tools/internal/imports at CL 203017.
-
-type moduleJSON struct {
- Path, Dir, GoVersion string
-}
-
-var modFlagRegexp = regexp.MustCompile(`-mod[ =](\w+)`)
-
-// vendorEnabled indicates if vendoring is enabled.
-// Inspired by setDefaultBuildMod in modload/init.go
-func vendorEnabled() (*moduleJSON, bool, error) {
- mainMod, go114, err := getMainModuleAnd114()
- if err != nil {
- return nil, false, err
- }
-
- stdout, _ := exec.Command(goCmd(), "env", "GOFLAGS").Output()
- goflags := string(bytes.TrimSpace(stdout))
- matches := modFlagRegexp.FindStringSubmatch(goflags)
- var modFlag string
- if len(matches) != 0 {
- modFlag = matches[1]
- }
- if modFlag != "" {
- // Don't override an explicit '-mod=' argument.
- return mainMod, modFlag == "vendor", nil
- }
- if mainMod == nil || !go114 {
- return mainMod, false, nil
- }
- // Check 1.14's automatic vendor mode.
- if fi, err := os.Stat(filepath.Join(mainMod.Dir, "vendor")); err == nil && fi.IsDir() {
- if mainMod.GoVersion != "" && semver.Compare("v"+mainMod.GoVersion, "v1.14") >= 0 {
- // The Go version is at least 1.14, and a vendor directory exists.
- // Set -mod=vendor by default.
- return mainMod, true, nil
- }
- }
- return mainMod, false, nil
-}
-
-// getMainModuleAnd114 gets the main module's information and whether the
-// go command in use is 1.14+. This is the information needed to figure out
-// if vendoring should be enabled.
-func getMainModuleAnd114() (*moduleJSON, bool, error) {
- const format = `{{.Path}}
-{{.Dir}}
-{{.GoVersion}}
-{{range context.ReleaseTags}}{{if eq . "go1.14"}}{{.}}{{end}}{{end}}
-`
- cmd := exec.Command(goCmd(), "list", "-m", "-f", format)
- cmd.Stderr = os.Stderr
- stdout, err := cmd.Output()
- if err != nil {
- return nil, false, nil
- }
- lines := strings.Split(string(stdout), "\n")
- if len(lines) < 5 {
- return nil, false, fmt.Errorf("unexpected stdout: %q", stdout)
- }
- mod := &moduleJSON{
- Path: lines[0],
- Dir: lines[1],
- GoVersion: lines[2],
- }
- return mod, lines[3] == "go1.14", nil
-}
diff --git a/src/cmd/internal/doc/doc_test.go b/src/cmd/internal/doc/doc_test.go
deleted file mode 100644
index bccace40c0..0000000000
--- a/src/cmd/internal/doc/doc_test.go
+++ /dev/null
@@ -1,1103 +0,0 @@
-// Copyright 2015 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 doc
-
-import (
- "bytes"
- "flag"
- "go/build"
- "internal/testenv"
- "log"
- "os"
- "path/filepath"
- "regexp"
- "runtime"
- "strings"
- "testing"
-)
-
-func TestMain(m *testing.M) {
- // Clear GOPATH so we don't access the user's own packages in the test.
- buildCtx.GOPATH = ""
- testGOPATH = true // force GOPATH mode; module test is in cmd/go/testdata/script/mod_doc.txt
-
- // Set GOROOT in case runtime.GOROOT is wrong (for example, if the test was
- // built with -trimpath). dirsInit would identify it using 'go env GOROOT',
- // but we can't be sure that the 'go' in $PATH is the right one either.
- buildCtx.GOROOT = testenv.GOROOT(nil)
- build.Default.GOROOT = testenv.GOROOT(nil)
-
- // Add $GOROOT/src/cmd/doc/testdata explicitly so we can access its contents in the test.
- // Normally testdata directories are ignored, but sending it to dirs.scan directly is
- // a hack that works around the check.
- testdataDir, err := filepath.Abs("testdata")
- if err != nil {
- panic(err)
- }
- dirsInit(
- Dir{importPath: "testdata", dir: testdataDir},
- Dir{importPath: "testdata/nested", dir: filepath.Join(testdataDir, "nested")},
- Dir{importPath: "testdata/nested/nested", dir: filepath.Join(testdataDir, "nested", "nested")})
-
- os.Exit(m.Run())
-}
-
-func maybeSkip(t *testing.T) {
- if runtime.GOOS == "ios" {
- t.Skip("iOS does not have a full file tree")
- }
-}
-
-type isDotSlashTest struct {
- str string
- result bool
-}
-
-var isDotSlashTests = []isDotSlashTest{
- {``, false},
- {`x`, false},
- {`...`, false},
- {`.../`, false},
- {`...\`, false},
-
- {`.`, true},
- {`./`, true},
- {`.\`, true},
- {`./x`, true},
- {`.\x`, true},
-
- {`..`, true},
- {`../`, true},
- {`..\`, true},
- {`../x`, true},
- {`..\x`, true},
-}
-
-func TestIsDotSlashPath(t *testing.T) {
- for _, test := range isDotSlashTests {
- if result := isDotSlash(test.str); result != test.result {
- t.Errorf("isDotSlash(%q) = %t; expected %t", test.str, result, test.result)
- }
- }
-}
-
-type test struct {
- name string
- args []string // Arguments to "[go] doc".
- yes []string // Regular expressions that should match.
- no []string // Regular expressions that should not match.
-}
-
-const p = "cmd/internal/doc/testdata"
-
-var tests = []test{
- // Sanity check.
- {
- "sanity check",
- []string{p},
- []string{`type ExportedType struct`},
- nil,
- },
-
- // Package dump includes import, package statement.
- {
- "package clause",
- []string{p},
- []string{`package pkg.*cmd/internal/doc/testdata`},
- nil,
- },
-
- // Constants.
- // Package dump
- {
- "full package",
- []string{p},
- []string{
- `Package comment`,
- `const ExportedConstant = 1`, // Simple constant.
- `const ConstOne = 1`, // First entry in constant block.
- `const ConstFive ...`, // From block starting with unexported constant.
- `var ExportedVariable = 1`, // Simple variable.
- `var VarOne = 1`, // First entry in variable block.
- `func ExportedFunc\(a int\) bool`, // Function.
- `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
- `type ExportedType struct{ ... }`, // Exported type.
- `const ExportedTypedConstant ExportedType = iota`, // Typed constant.
- `const ExportedTypedConstant_unexported unexportedType`, // Typed constant, exported for unexported type.
- `const ConstLeft2 uint64 ...`, // Typed constant using unexported iota.
- `const ConstGroup1 unexportedType = iota ...`, // Typed constant using unexported type.
- `const ConstGroup4 ExportedType = ExportedType{}`, // Typed constant using exported type.
- `const MultiLineConst = ...`, // Multi line constant.
- `var MultiLineVar = map\[struct{ ... }\]struct{ ... }{ ... }`, // Multi line variable.
- `func MultiLineFunc\(x interface{ ... }\) \(r struct{ ... }\)`, // Multi line function.
- `var LongLine = newLongLine\(("someArgument[1-4]", ){4}...\)`, // Long list of arguments.
- `type T1 = T2`, // Type alias
- `type SimpleConstraint interface{ ... }`,
- `type TildeConstraint interface{ ... }`,
- `type StructConstraint interface{ ... }`,
- },
- []string{
- `const internalConstant = 2`, // No internal constants.
- `var internalVariable = 2`, // No internal variables.
- `func internalFunc(a int) bool`, // No internal functions.
- `Comment about exported constant`, // No comment for single constant.
- `Comment about exported variable`, // No comment for single variable.
- `Comment about block of constants`, // No comment for constant block.
- `Comment about block of variables`, // No comment for variable block.
- `Comment before ConstOne`, // No comment for first entry in constant block.
- `Comment before VarOne`, // No comment for first entry in variable block.
- `ConstTwo = 2`, // No second entry in constant block.
- `VarTwo = 2`, // No second entry in variable block.
- `VarFive = 5`, // From block starting with unexported variable.
- `type unexportedType`, // No unexported type.
- `unexportedTypedConstant`, // No unexported typed constant.
- `\bField`, // No fields.
- `Method`, // No methods.
- `someArgument[5-8]`, // No truncated arguments.
- `type T1 T2`, // Type alias does not display as type declaration.
- `ignore:directive`, // Directives should be dropped.
- },
- },
- // Package dump -all
- {
- "full package",
- []string{"-all", p},
- []string{
- `package pkg .*import`,
- `Package comment`,
- `CONSTANTS`,
- `Comment before ConstOne`,
- `ConstOne = 1`,
- `ConstTwo = 2 // Comment on line with ConstTwo`,
- `ConstFive`,
- `ConstSix`,
- `Const block where first entry is unexported`,
- `ConstLeft2, constRight2 uint64`,
- `constLeft3, ConstRight3`,
- `ConstLeft4, ConstRight4`,
- `Duplicate = iota`,
- `const CaseMatch = 1`,
- `const Casematch = 2`,
- `const ExportedConstant = 1`,
- `const MultiLineConst = `,
- `MultiLineString1`,
- `VARIABLES`,
- `Comment before VarOne`,
- `VarOne = 1`,
- `Comment about block of variables`,
- `VarFive = 5`,
- `var ExportedVariable = 1`,
- `var ExportedVarOfUnExported unexportedType`,
- `var LongLine = newLongLine\(`,
- `var MultiLineVar = map\[struct {`,
- `FUNCTIONS`,
- `func ExportedFunc\(a int\) bool`,
- `Comment about exported function`,
- `func MultiLineFunc\(x interface`,
- `func ReturnUnexported\(\) unexportedType`,
- `TYPES`,
- `type ExportedInterface interface`,
- `type ExportedStructOneField struct`,
- `type ExportedType struct`,
- `Comment about exported type`,
- `const ConstGroup4 ExportedType = ExportedType`,
- `ExportedTypedConstant ExportedType = iota`,
- `Constants tied to ExportedType`,
- `func ExportedTypeConstructor\(\) \*ExportedType`,
- `Comment about constructor for exported type`,
- `func ReturnExported\(\) ExportedType`,
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `Comment about exported method`,
- `type T1 = T2`,
- `type T2 int`,
- `type SimpleConstraint interface {`,
- `type TildeConstraint interface {`,
- `type StructConstraint interface {`,
- `BUG: function body note`,
- },
- []string{
- `constThree`,
- `_, _ uint64 = 2 \* iota, 1 << iota`,
- `constLeft1, constRight1`,
- `duplicate`,
- `varFour`,
- `func internalFunc`,
- `unexportedField`,
- `func \(unexportedType\)`,
- `ignore:directive`,
- },
- },
- // Package with just the package declaration. Issue 31457.
- {
- "only package declaration",
- []string{"-all", p + "/nested/empty"},
- []string{`package empty .*import`},
- nil,
- },
- // Package dump -short
- {
- "full package with -short",
- []string{`-short`, p},
- []string{
- `const ExportedConstant = 1`, // Simple constant.
- `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
- },
- []string{
- `MultiLine(String|Method|Field)`, // No data from multi line portions.
- },
- },
- // Package dump -u
- {
- "full package with u",
- []string{`-u`, p},
- []string{
- `const ExportedConstant = 1`, // Simple constant.
- `const internalConstant = 2`, // Internal constants.
- `func internalFunc\(a int\) bool`, // Internal functions.
- `func ReturnUnexported\(\) unexportedType`, // Function with unexported return type.
- },
- []string{
- `Comment about exported constant`, // No comment for simple constant.
- `Comment about block of constants`, // No comment for constant block.
- `Comment about internal function`, // No comment for internal function.
- `MultiLine(String|Method|Field)`, // No data from multi line portions.
- `ignore:directive`,
- },
- },
- // Package dump -u -all
- {
- "full package",
- []string{"-u", "-all", p},
- []string{
- `package pkg .*import`,
- `Package comment`,
- `CONSTANTS`,
- `Comment before ConstOne`,
- `ConstOne += 1`,
- `ConstTwo += 2 // Comment on line with ConstTwo`,
- `constThree = 3 // Comment on line with constThree`,
- `ConstFive`,
- `const internalConstant += 2`,
- `Comment about internal constant`,
- `VARIABLES`,
- `Comment before VarOne`,
- `VarOne += 1`,
- `Comment about block of variables`,
- `varFour += 4`,
- `VarFive += 5`,
- `varSix += 6`,
- `var ExportedVariable = 1`,
- `var LongLine = newLongLine\(`,
- `var MultiLineVar = map\[struct {`,
- `var internalVariable = 2`,
- `Comment about internal variable`,
- `FUNCTIONS`,
- `func ExportedFunc\(a int\) bool`,
- `Comment about exported function`,
- `func MultiLineFunc\(x interface`,
- `func internalFunc\(a int\) bool`,
- `Comment about internal function`,
- `func newLongLine\(ss .*string\)`,
- `TYPES`,
- `type ExportedType struct`,
- `type T1 = T2`,
- `type T2 int`,
- `type unexportedType int`,
- `Comment about unexported type`,
- `ConstGroup1 unexportedType = iota`,
- `ConstGroup2`,
- `ConstGroup3`,
- `ExportedTypedConstant_unexported unexportedType = iota`,
- `Constants tied to unexportedType`,
- `const unexportedTypedConstant unexportedType = 1`,
- `func ReturnUnexported\(\) unexportedType`,
- `func \(unexportedType\) ExportedMethod\(\) bool`,
- `func \(unexportedType\) unexportedMethod\(\) bool`,
- },
- []string{
- `ignore:directive`,
- },
- },
-
- // Single constant.
- {
- "single constant",
- []string{p, `ExportedConstant`},
- []string{
- `Comment about exported constant`, // Include comment.
- `const ExportedConstant = 1`,
- },
- nil,
- },
- // Single constant -u.
- {
- "single constant with -u",
- []string{`-u`, p, `internalConstant`},
- []string{
- `Comment about internal constant`, // Include comment.
- `const internalConstant = 2`,
- },
- nil,
- },
- // Block of constants.
- {
- "block of constants",
- []string{p, `ConstTwo`},
- []string{
- `Comment before ConstOne.\n.*ConstOne = 1`, // First...
- `ConstTwo = 2.*Comment on line with ConstTwo`, // And second show up.
- `Comment about block of constants`, // Comment does too.
- },
- []string{
- `constThree`, // No unexported constant.
- },
- },
- // Block of constants -u.
- {
- "block of constants with -u",
- []string{"-u", p, `constThree`},
- []string{
- `constThree = 3.*Comment on line with constThree`,
- },
- nil,
- },
- // Block of constants -src.
- {
- "block of constants with -src",
- []string{"-src", p, `ConstTwo`},
- []string{
- `Comment about block of constants`, // Top comment.
- `ConstOne.*=.*1`, // Each constant seen.
- `ConstTwo.*=.*2.*Comment on line with ConstTwo`,
- `constThree`, // Even unexported constants.
- },
- nil,
- },
- // Block of constants with carryover type from unexported field.
- {
- "block of constants with carryover type",
- []string{p, `ConstLeft2`},
- []string{
- `ConstLeft2, constRight2 uint64`,
- `constLeft3, ConstRight3`,
- `ConstLeft4, ConstRight4`,
- },
- nil,
- },
- // Block of constants -u with carryover type from unexported field.
- {
- "block of constants with carryover type",
- []string{"-u", p, `ConstLeft2`},
- []string{
- `_, _ uint64 = 2 \* iota, 1 << iota`,
- `constLeft1, constRight1`,
- `ConstLeft2, constRight2`,
- `constLeft3, ConstRight3`,
- `ConstLeft4, ConstRight4`,
- },
- nil,
- },
-
- // Single variable.
- {
- "single variable",
- []string{p, `ExportedVariable`},
- []string{
- `ExportedVariable`, // Include comment.
- `var ExportedVariable = 1`,
- },
- nil,
- },
- // Single variable -u.
- {
- "single variable with -u",
- []string{`-u`, p, `internalVariable`},
- []string{
- `Comment about internal variable`, // Include comment.
- `var internalVariable = 2`,
- },
- nil,
- },
- // Block of variables.
- {
- "block of variables",
- []string{p, `VarTwo`},
- []string{
- `Comment before VarOne.\n.*VarOne = 1`, // First...
- `VarTwo = 2.*Comment on line with VarTwo`, // And second show up.
- `Comment about block of variables`, // Comment does too.
- },
- []string{
- `varThree= 3`, // No unexported variable.
- },
- },
- // Block of variables -u.
- {
- "block of variables with -u",
- []string{"-u", p, `varThree`},
- []string{
- `varThree = 3.*Comment on line with varThree`,
- },
- nil,
- },
-
- // Function.
- {
- "function",
- []string{p, `ExportedFunc`},
- []string{
- `Comment about exported function`, // Include comment.
- `func ExportedFunc\(a int\) bool`,
- },
- nil,
- },
- // Function -u.
- {
- "function with -u",
- []string{"-u", p, `internalFunc`},
- []string{
- `Comment about internal function`, // Include comment.
- `func internalFunc\(a int\) bool`,
- },
- nil,
- },
- // Function with -src.
- {
- "function with -src",
- []string{"-src", p, `ExportedFunc`},
- []string{
- `Comment about exported function`, // Include comment.
- `func ExportedFunc\(a int\) bool`,
- `return true != false`, // Include body.
- },
- nil,
- },
-
- // Type.
- {
- "type",
- []string{p, `ExportedType`},
- []string{
- `Comment about exported type`, // Include comment.
- `type ExportedType struct`, // Type definition.
- `Comment before exported field.*\n.*ExportedField +int` +
- `.*Comment on line with exported field`,
- `ExportedEmbeddedType.*Comment on line with exported embedded field`,
- `Has unexported fields`,
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
- `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
- `io.Reader.*Comment on line with embedded Reader`,
- },
- []string{
- `unexportedField`, // No unexported field.
- `int.*embedded`, // No unexported embedded field.
- `Comment about exported method`, // No comment about exported method.
- `unexportedMethod`, // No unexported method.
- `unexportedTypedConstant`, // No unexported constant.
- `error`, // No embedded error.
- },
- },
- // Type with -src. Will see unexported fields.
- {
- "type",
- []string{"-src", p, `ExportedType`},
- []string{
- `Comment about exported type`, // Include comment.
- `type ExportedType struct`, // Type definition.
- `Comment before exported field`,
- `ExportedField.*Comment on line with exported field`,
- `ExportedEmbeddedType.*Comment on line with exported embedded field`,
- `unexportedType.*Comment on line with unexported embedded field`,
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `const ExportedTypedConstant ExportedType = iota`, // Must include associated constant.
- `func ExportedTypeConstructor\(\) \*ExportedType`, // Must include constructor.
- `io.Reader.*Comment on line with embedded Reader`,
- },
- []string{
- `Comment about exported method`, // No comment about exported method.
- `unexportedMethod`, // No unexported method.
- `unexportedTypedConstant`, // No unexported constant.
- },
- },
- // Type -all.
- {
- "type",
- []string{"-all", p, `ExportedType`},
- []string{
- `type ExportedType struct {`, // Type definition as source.
- `Comment about exported type`, // Include comment afterwards.
- `const ConstGroup4 ExportedType = ExportedType\{\}`, // Related constants.
- `ExportedTypedConstant ExportedType = iota`,
- `Constants tied to ExportedType`,
- `func ExportedTypeConstructor\(\) \*ExportedType`,
- `Comment about constructor for exported type.`,
- `func ReturnExported\(\) ExportedType`,
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `Comment about exported method.`,
- `func \(ExportedType\) Uncommented\(a int\) bool\n\n`, // Ensure line gap after method with no comment
- },
- []string{
- `unexportedType`,
- },
- },
- // Type T1 dump (alias).
- {
- "type T1",
- []string{p + ".T1"},
- []string{
- `type T1 = T2`,
- },
- []string{
- `type T1 T2`,
- `type ExportedType`,
- },
- },
- // Type -u with unexported fields.
- {
- "type with unexported fields and -u",
- []string{"-u", p, `ExportedType`},
- []string{
- `Comment about exported type`, // Include comment.
- `type ExportedType struct`, // Type definition.
- `Comment before exported field.*\n.*ExportedField +int`,
- `unexportedField.*int.*Comment on line with unexported field`,
- `ExportedEmbeddedType.*Comment on line with exported embedded field`,
- `\*ExportedEmbeddedType.*Comment on line with exported embedded \*field`,
- `\*qualified.ExportedEmbeddedType.*Comment on line with exported embedded \*selector.field`,
- `unexportedType.*Comment on line with unexported embedded field`,
- `\*unexportedType.*Comment on line with unexported embedded \*field`,
- `io.Reader.*Comment on line with embedded Reader`,
- `error.*Comment on line with embedded error`,
- `func \(ExportedType\) unexportedMethod\(a int\) bool`,
- `unexportedTypedConstant`,
- },
- []string{
- `Has unexported fields`,
- },
- },
- // Unexported type with -u.
- {
- "unexported type with -u",
- []string{"-u", p, `unexportedType`},
- []string{
- `Comment about unexported type`, // Include comment.
- `type unexportedType int`, // Type definition.
- `func \(unexportedType\) ExportedMethod\(\) bool`,
- `func \(unexportedType\) unexportedMethod\(\) bool`,
- `ExportedTypedConstant_unexported unexportedType = iota`,
- `const unexportedTypedConstant unexportedType = 1`,
- },
- nil,
- },
-
- // Interface.
- {
- "interface type",
- []string{p, `ExportedInterface`},
- []string{
- `Comment about exported interface`, // Include comment.
- `type ExportedInterface interface`, // Interface definition.
- `Comment before exported method.\n.*//\n.*// // Code block showing how to use ExportedMethod\n.*// func DoSomething\(\) error {\n.*// ExportedMethod\(\)\n.*// return nil\n.*// }\n.*//.*\n.*ExportedMethod\(\)` +
- `.*Comment on line with exported method`,
- `io.Reader.*Comment on line with embedded Reader`,
- `error.*Comment on line with embedded error`,
- `Has unexported methods`,
- },
- []string{
- `unexportedField`, // No unexported field.
- `Comment about exported method`, // No comment about exported method.
- `unexportedMethod`, // No unexported method.
- `unexportedTypedConstant`, // No unexported constant.
- },
- },
- // Interface -u with unexported methods.
- {
- "interface type with unexported methods and -u",
- []string{"-u", p, `ExportedInterface`},
- []string{
- `Comment about exported interface`, // Include comment.
- `type ExportedInterface interface`, // Interface definition.
- `Comment before exported method.\n.*//\n.*// // Code block showing how to use ExportedMethod\n.*// func DoSomething\(\) error {\n.*// ExportedMethod\(\)\n.*// return nil\n.*// }\n.*//.*\n.*ExportedMethod\(\)` + `.*Comment on line with exported method`,
- `unexportedMethod\(\).*Comment on line with unexported method`,
- `io.Reader.*Comment on line with embedded Reader`,
- `error.*Comment on line with embedded error`,
- },
- []string{
- `Has unexported methods`,
- },
- },
-
- // Interface method.
- {
- "interface method",
- []string{p, `ExportedInterface.ExportedMethod`},
- []string{
- `Comment before exported method.\n.*//\n.*// // Code block showing how to use ExportedMethod\n.*// func DoSomething\(\) error {\n.*// ExportedMethod\(\)\n.*// return nil\n.*// }\n.*//.*\n.*ExportedMethod\(\)` +
- `.*Comment on line with exported method`,
- },
- []string{
- `Comment about exported interface`,
- },
- },
- // Interface method at package level.
- {
- "interface method at package level",
- []string{p, `ExportedMethod`},
- []string{
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `Comment about exported method`,
- },
- []string{
- `Comment before exported method.*\n.*ExportedMethod\(\)` +
- `.*Comment on line with exported method`,
- },
- },
-
- // Method.
- {
- "method",
- []string{p, `ExportedType.ExportedMethod`},
- []string{
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `Comment about exported method`,
- },
- nil,
- },
- // Method with -u.
- {
- "method with -u",
- []string{"-u", p, `ExportedType.unexportedMethod`},
- []string{
- `func \(ExportedType\) unexportedMethod\(a int\) bool`,
- `Comment about unexported method`,
- },
- nil,
- },
- // Method with -src.
- {
- "method with -src",
- []string{"-src", p, `ExportedType.ExportedMethod`},
- []string{
- `func \(ExportedType\) ExportedMethod\(a int\) bool`,
- `Comment about exported method`,
- `return true != true`,
- },
- nil,
- },
-
- // Field.
- {
- "field",
- []string{p, `ExportedType.ExportedField`},
- []string{
- `type ExportedType struct`,
- `ExportedField int`,
- `Comment before exported field`,
- `Comment on line with exported field`,
- `other fields elided`,
- },
- nil,
- },
-
- // Field with -u.
- {
- "method with -u",
- []string{"-u", p, `ExportedType.unexportedField`},
- []string{
- `unexportedField int`,
- `Comment on line with unexported field`,
- },
- nil,
- },
-
- // Field of struct with only one field.
- {
- "single-field struct",
- []string{p, `ExportedStructOneField.OnlyField`},
- []string{`the only field`},
- []string{`other fields elided`},
- },
-
- // Case matching off.
- {
- "case matching off",
- []string{p, `casematch`},
- []string{
- `CaseMatch`,
- `Casematch`,
- },
- nil,
- },
-
- // Case matching on.
- {
- "case matching on",
- []string{"-c", p, `Casematch`},
- []string{
- `Casematch`,
- },
- []string{
- `CaseMatch`,
- },
- },
-
- // Merging comments with -src.
- {
- "merge comments with -src A",
- []string{"-src", p + "/merge", `A`},
- []string{
- `A doc`,
- `func A`,
- `A comment`,
- },
- []string{
- `Package A doc`,
- `Package B doc`,
- `B doc`,
- `B comment`,
- `B doc`,
- },
- },
- {
- "merge comments with -src B",
- []string{"-src", p + "/merge", `B`},
- []string{
- `B doc`,
- `func B`,
- `B comment`,
- },
- []string{
- `Package A doc`,
- `Package B doc`,
- `A doc`,
- `A comment`,
- `A doc`,
- },
- },
-
- // No dups with -u. Issue 21797.
- {
- "case matching on, no dups",
- []string{"-u", p, `duplicate`},
- []string{
- `Duplicate`,
- `duplicate`,
- },
- []string{
- "\\)\n+const", // This will appear if the const decl appears twice.
- },
- },
- {
- "non-imported: pkg.sym",
- []string{"nested.Foo"},
- []string{"Foo struct"},
- nil,
- },
- {
- "non-imported: pkg only",
- []string{"nested"},
- []string{"Foo struct"},
- nil,
- },
- {
- "non-imported: pkg sym",
- []string{"nested", "Foo"},
- []string{"Foo struct"},
- nil,
- },
- {
- "formatted doc on function",
- []string{p, "ExportedFormattedDoc"},
- []string{
- `func ExportedFormattedDoc\(a int\) bool`,
- ` Comment about exported function with formatting\.
-
- Example
-
- fmt\.Println\(FormattedDoc\(\)\)
-
- Text after pre-formatted block\.`,
- },
- nil,
- },
- {
- "formatted doc on type field",
- []string{p, "ExportedFormattedType.ExportedField"},
- []string{
- `type ExportedFormattedType struct`,
- ` // Comment before exported field with formatting\.
- //[ ]
- // Example
- //[ ]
- // a\.ExportedField = 123
- //[ ]
- // Text after pre-formatted block\.`,
- `ExportedField int`,
- },
- []string{"ignore:directive"},
- },
- {
- "formatted doc on entire type",
- []string{p, "ExportedFormattedType"},
- []string{
- `type ExportedFormattedType struct`,
- ` // Comment before exported field with formatting\.
- //
- // Example
- //
- // a\.ExportedField = 123
- //
- // Text after pre-formatted block\.`,
- `ExportedField int`,
- },
- []string{"ignore:directive"},
- },
- {
- "formatted doc on entire type with -all",
- []string{"-all", p, "ExportedFormattedType"},
- []string{
- `type ExportedFormattedType struct`,
- ` // Comment before exported field with formatting\.
- //
- // Example
- //
- // a\.ExportedField = 123
- //
- // Text after pre-formatted block\.`,
- `ExportedField int`,
- },
- []string{"ignore:directive"},
- },
-}
-
-func TestDoc(t *testing.T) {
- maybeSkip(t)
- defer log.SetOutput(log.Writer())
- for _, test := range tests {
- var b bytes.Buffer
- var flagSet flag.FlagSet
- var logbuf bytes.Buffer
- log.SetOutput(&logbuf)
- err := do(&b, &flagSet, test.args)
- if err != nil {
- t.Fatalf("%s %v: %s\n", test.name, test.args, err)
- }
- if logbuf.Len() > 0 {
- t.Errorf("%s %v: unexpected log messages:\n%s", test.name, test.args, logbuf.Bytes())
- }
- output := b.Bytes()
- failed := false
- for j, yes := range test.yes {
- re, err := regexp.Compile(yes)
- if err != nil {
- t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, yes, err)
- }
- if !re.Match(output) {
- t.Errorf("%s.%d: no match for %s %#q", test.name, j, test.args, yes)
- failed = true
- }
- }
- for j, no := range test.no {
- re, err := regexp.Compile(no)
- if err != nil {
- t.Fatalf("%s.%d: compiling %#q: %s", test.name, j, no, err)
- }
- if re.Match(output) {
- t.Errorf("%s.%d: incorrect match for %s %#q", test.name, j, test.args, no)
- failed = true
- }
- }
- if bytes.Count(output, []byte("TYPES\n")) > 1 {
- t.Fatalf("%s: repeating headers", test.name)
- }
- if failed {
- t.Logf("\n%s", output)
- }
- }
-}
-
-// Test the code to try multiple packages. Our test case is
-//
-// go doc rand.Float64
-//
-// This needs to find math/rand.Float64; however crypto/rand, which doesn't
-// have the symbol, usually appears first in the directory listing.
-func TestMultiplePackages(t *testing.T) {
- if testing.Short() {
- t.Skip("scanning file system takes too long")
- }
- maybeSkip(t)
- var b bytes.Buffer // We don't care about the output.
- // Make sure crypto/rand does not have the symbol.
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"crypto/rand.float64"})
- if err == nil {
- t.Errorf("expected error from crypto/rand.float64")
- } else if !strings.Contains(err.Error(), "no symbol float64") {
- t.Errorf("unexpected error %q from crypto/rand.float64", err)
- }
- }
- // Make sure math/rand does have the symbol.
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"math/rand.float64"})
- if err != nil {
- t.Errorf("unexpected error %q from math/rand.float64", err)
- }
- }
- // Try the shorthand.
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"rand.float64"})
- if err != nil {
- t.Errorf("unexpected error %q from rand.float64", err)
- }
- }
- // Now try a missing symbol. We should see both packages in the error.
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"rand.doesnotexit"})
- if err == nil {
- t.Errorf("expected error from rand.doesnotexit")
- } else {
- errStr := err.Error()
- if !strings.Contains(errStr, "no symbol") {
- t.Errorf("error %q should contain 'no symbol", errStr)
- }
- if !strings.Contains(errStr, "crypto/rand") {
- t.Errorf("error %q should contain crypto/rand", errStr)
- }
- if !strings.Contains(errStr, "math/rand") {
- t.Errorf("error %q should contain math/rand", errStr)
- }
- }
- }
-}
-
-// Test the code to look up packages when given two args. First test case is
-//
-// go doc binary BigEndian
-//
-// This needs to find encoding/binary.BigEndian, which means
-// finding the package encoding/binary given only "binary".
-// Second case is
-//
-// go doc rand Float64
-//
-// which again needs to find math/rand and not give up after crypto/rand,
-// which has no such function.
-func TestTwoArgLookup(t *testing.T) {
- if testing.Short() {
- t.Skip("scanning file system takes too long")
- }
- maybeSkip(t)
- var b bytes.Buffer // We don't care about the output.
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"binary", "BigEndian"})
- if err != nil {
- t.Errorf("unexpected error %q from binary BigEndian", err)
- }
- }
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"rand", "Float64"})
- if err != nil {
- t.Errorf("unexpected error %q from rand Float64", err)
- }
- }
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"bytes", "Foo"})
- if err == nil {
- t.Errorf("expected error from bytes Foo")
- } else if !strings.Contains(err.Error(), "no symbol Foo") {
- t.Errorf("unexpected error %q from bytes Foo", err)
- }
- }
- {
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"nosuchpackage", "Foo"})
- if err == nil {
- // actually present in the user's filesystem
- } else if !strings.Contains(err.Error(), "no such package") {
- t.Errorf("unexpected error %q from nosuchpackage Foo", err)
- }
- }
-}
-
-// Test the code to look up packages when the first argument starts with "./".
-// Our test case is in effect "cd src/text; doc ./template". This should get
-// text/template but before Issue 23383 was fixed would give html/template.
-func TestDotSlashLookup(t *testing.T) {
- if testing.Short() {
- t.Skip("scanning file system takes too long")
- }
- maybeSkip(t)
- t.Chdir(filepath.Join(buildCtx.GOROOT, "src", "text"))
-
- var b strings.Builder
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"./template"})
- if err != nil {
- t.Errorf("unexpected error %q from ./template", err)
- }
- // The output should contain information about the text/template package.
- const want = `package template // import "text/template"`
- output := b.String()
- if !strings.HasPrefix(output, want) {
- t.Fatalf("wrong package: %.*q...", len(want), output)
- }
-}
-
-// Test that we don't print spurious package clauses
-// when there should be no output at all. Issue 37969.
-func TestNoPackageClauseWhenNoMatch(t *testing.T) {
- maybeSkip(t)
- var b strings.Builder
- var flagSet flag.FlagSet
- err := do(&b, &flagSet, []string{"template.ZZZ"})
- // Expect an error.
- if err == nil {
- t.Error("expect an error for template.zzz")
- }
- // And the output should not contain any package clauses.
- const dontWant = `package template // import `
- output := b.String()
- if strings.Contains(output, dontWant) {
- t.Fatalf("improper package clause printed:\n%s", output)
- }
-}
-
-type trimTest struct {
- path string
- prefix string
- result string
- ok bool
-}
-
-var trimTests = []trimTest{
- {"", "", "", true},
- {"/usr/gopher", "/usr/gopher", "/usr/gopher", true},
- {"/usr/gopher/bar", "/usr/gopher", "bar", true},
- {"/usr/gopherflakes", "/usr/gopher", "/usr/gopherflakes", false},
- {"/usr/gopher/bar", "/usr/zot", "/usr/gopher/bar", false},
-}
-
-func TestTrim(t *testing.T) {
- for _, test := range trimTests {
- result, ok := trim(test.path, test.prefix)
- if ok != test.ok {
- t.Errorf("%s %s expected %t got %t", test.path, test.prefix, test.ok, ok)
- continue
- }
- if result != test.result {
- t.Errorf("%s %s expected %q got %q", test.path, test.prefix, test.result, result)
- continue
- }
- }
-}
diff --git a/src/cmd/internal/doc/main.go b/src/cmd/internal/doc/main.go
deleted file mode 100644
index d61f4379bd..0000000000
--- a/src/cmd/internal/doc/main.go
+++ /dev/null
@@ -1,532 +0,0 @@
-// Copyright 2015 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 doc provides the implementation of the "go doc" subcommand and cmd/doc.
-package doc
-
-import (
- "bytes"
- "errors"
- "flag"
- "fmt"
- "go/build"
- "go/token"
- "io"
- "log"
- "net"
- "net/url"
- "os"
- "os/exec"
- "os/signal"
- "path"
- "path/filepath"
- "strings"
-
- "cmd/internal/telemetry/counter"
-)
-
-var (
- unexported bool // -u flag
- matchCase bool // -c flag
- chdir string // -C flag
- showAll bool // -all flag
- showCmd bool // -cmd flag
- showSrc bool // -src flag
- short bool // -short flag
- serveHTTP bool // -http flag
-)
-
-// usage is a replacement usage function for the flags package.
-func usage(flagSet *flag.FlagSet) {
- fmt.Fprintf(os.Stderr, "Usage of [go] doc:\n")
- fmt.Fprintf(os.Stderr, "\tgo doc\n")
- fmt.Fprintf(os.Stderr, "\tgo doc <pkg>\n")
- fmt.Fprintf(os.Stderr, "\tgo doc <sym>[.<methodOrField>]\n")
- fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>.]<sym>[.<methodOrField>]\n")
- fmt.Fprintf(os.Stderr, "\tgo doc [<pkg>.][<sym>.]<methodOrField>\n")
- fmt.Fprintf(os.Stderr, "\tgo doc <pkg> <sym>[.<methodOrField>]\n")
- fmt.Fprintf(os.Stderr, "For more information run\n")
- fmt.Fprintf(os.Stderr, "\tgo help doc\n\n")
- fmt.Fprintf(os.Stderr, "Flags:\n")
- flagSet.PrintDefaults()
- os.Exit(2)
-}
-
-// Main is the entry point, invoked both by go doc and cmd/doc.
-func Main(args []string) {
- log.SetFlags(0)
- log.SetPrefix("doc: ")
- dirsInit()
- var flagSet flag.FlagSet
- err := do(os.Stdout, &flagSet, args)
- if err != nil {
- log.Fatal(err)
- }
-}
-
-// do is the workhorse, broken out of main to make testing easier.
-func do(writer io.Writer, flagSet *flag.FlagSet, args []string) (err error) {
- flagSet.Usage = func() { usage(flagSet) }
- unexported = false
- matchCase = false
- flagSet.StringVar(&chdir, "C", "", "change to `dir` before running command")
- flagSet.BoolVar(&unexported, "u", false, "show unexported symbols as well as exported")
- flagSet.BoolVar(&matchCase, "c", false, "symbol matching honors case (paths not affected)")
- flagSet.BoolVar(&showAll, "all", false, "show all documentation for package")
- flagSet.BoolVar(&showCmd, "cmd", false, "show symbols with package docs even if package is a command")
- flagSet.BoolVar(&showSrc, "src", false, "show source code for symbol")
- flagSet.BoolVar(&short, "short", false, "one-line representation for each symbol")
- flagSet.BoolVar(&serveHTTP, "http", false, "serve HTML docs over HTTP")
- flagSet.Parse(args)
- counter.CountFlags("doc/flag:", *flag.CommandLine)
- if chdir != "" {
- if err := os.Chdir(chdir); err != nil {
- return err
- }
- }
- if serveHTTP {
- // Special case: if there are no arguments, try to go to an appropriate page
- // depending on whether we're in a module or workspace. The pkgsite homepage
- // is often not the most useful page.
- if len(flagSet.Args()) == 0 {
- mod, err := runCmd(append(os.Environ(), "GOWORK=off"), "go", "list", "-m")
- if err == nil && mod != "" && mod != "command-line-arguments" {
- // If there's a module, go to the module's doc page.
- return doPkgsite(mod)
- }
- gowork, err := runCmd(nil, "go", "env", "GOWORK")
- if err == nil && gowork != "" {
- // Outside a module, but in a workspace, go to the home page
- // with links to each of the modules' pages.
- return doPkgsite("")
- }
- // Outside a module or workspace, go to the documentation for the standard library.
- return doPkgsite("std")
- }
-
- // If args are provided, we need to figure out which page to open on the pkgsite
- // instance. Run the logic below to determine a match for a symbol, method,
- // or field, but don't actually print the documentation to the output.
- writer = io.Discard
- }
- var paths []string
- var symbol, method string
- // Loop until something is printed.
- dirs.Reset()
- for i := 0; ; i++ {
- buildPackage, userPath, sym, more := parseArgs(flagSet, flagSet.Args())
- if i > 0 && !more { // Ignore the "more" bit on the first iteration.
- return failMessage(paths, symbol, method)
- }
- if buildPackage == nil {
- return fmt.Errorf("no such package: %s", userPath)
- }
-
- // The builtin package needs special treatment: its symbols are lower
- // case but we want to see them, always.
- if buildPackage.ImportPath == "builtin" {
- unexported = true
- }
-
- symbol, method = parseSymbol(flagSet, sym)
- pkg := parsePackage(writer, buildPackage, userPath)
- paths = append(paths, pkg.prettyPath())
-
- defer func() {
- pkg.flush()
- e := recover()
- if e == nil {
- return
- }
- pkgError, ok := e.(PackageError)
- if ok {
- err = pkgError
- return
- }
- panic(e)
- }()
-
- var found bool
- switch {
- case symbol == "":
- pkg.packageDoc() // The package exists, so we got some output.
- found = true
- case method == "":
- if pkg.symbolDoc(symbol) {
- found = true
- }
- case pkg.printMethodDoc(symbol, method):
- found = true
- case pkg.printFieldDoc(symbol, method):
- found = true
- }
- if found {
- if serveHTTP {
- path, err := objectPath(userPath, pkg, symbol, method)
- if err != nil {
- return err
- }
- return doPkgsite(path)
- }
- return nil
- }
- }
-}
-
-func runCmd(env []string, cmdline ...string) (string, error) {
- var stdout, stderr strings.Builder
- cmd := exec.Command(cmdline[0], cmdline[1:]...)
- cmd.Env = env
- cmd.Stdout = &stdout
- cmd.Stderr = &stderr
- if err := cmd.Run(); err != nil {
- return "", fmt.Errorf("go doc: %s: %v\n%s\n", strings.Join(cmdline, " "), err, stderr.String())
- }
- return strings.TrimSpace(stdout.String()), nil
-}
-
-func objectPath(userPath string, pkg *Package, symbol, method string) (string, error) {
- var err error
- path := pkg.build.ImportPath
- if path == "." {
- // go/build couldn't determine the import path, probably
- // because this was a relative path into a module. Use
- // go list to get the import path.
- path, err = runCmd(nil, "go", "list", userPath)
- if err != nil {
- return "", err
- }
- }
-
- object := symbol
- if symbol != "" && method != "" {
- object = symbol + "." + method
- }
- if object != "" {
- path = path + "#" + object
- }
- return path, nil
-}
-
-func doPkgsite(urlPath string) error {
- port, err := pickUnusedPort()
- if err != nil {
- return fmt.Errorf("failed to find port for documentation server: %v", err)
- }
- addr := fmt.Sprintf("localhost:%d", port)
- path, err := url.JoinPath("http://"+addr, urlPath)
- if err != nil {
- return fmt.Errorf("internal error: failed to construct url: %v", err)
- }
-
- // Turn off the default signal handler for SIGINT (and SIGQUIT on Unix)
- // and instead wait for the child process to handle the signal and
- // exit before exiting ourselves.
- signal.Ignore(signalsToIgnore...)
-
- // Prepend the local download cache to GOPROXY to get around deprecation checks.
- env := os.Environ()
- vars, err := runCmd(env, goCmd(), "env", "GOPROXY", "GOMODCACHE")
- fields := strings.Fields(vars)
- if err == nil && len(fields) == 2 {
- goproxy, gomodcache := fields[0], fields[1]
- gomodcache = filepath.Join(gomodcache, "cache", "download")
- // Convert absolute path to file URL. pkgsite will not accept
- // Windows absolute paths because they look like a host:path remote.
- // TODO(golang.org/issue/32456): use url.FromFilePath when implemented.
- if strings.HasPrefix(gomodcache, "/") {
- gomodcache = "file://" + gomodcache
- } else {
- gomodcache = "file:///" + filepath.ToSlash(gomodcache)
- }
- env = append(env, "GOPROXY="+gomodcache+","+goproxy)
- }
-
- const version = "v0.0.0-20250714212547-01b046e81fe7"
- cmd := exec.Command(goCmd(), "run", "golang.org/x/pkgsite/cmd/internal/doc@"+version,
- "-gorepo", buildCtx.GOROOT,
- "-http", addr,
- "-open", path)
- cmd.Env = env
- cmd.Stdout = os.Stderr
- cmd.Stderr = os.Stderr
-
- if err := cmd.Run(); err != nil {
- var ee *exec.ExitError
- if errors.As(err, &ee) {
- // Exit with the same exit status as pkgsite to avoid
- // printing of "exit status" error messages.
- // Any relevant messages have already been printed
- // to stdout or stderr.
- os.Exit(ee.ExitCode())
- }
- return err
- }
-
- return nil
-}
-
-// pickUnusedPort finds an unused port by trying to listen on port 0
-// and letting the OS pick a port, then closing that connection and
-// returning that port number.
-// This is inherently racy.
-func pickUnusedPort() (int, error) {
- l, err := net.Listen("tcp", "localhost:0")
- if err != nil {
- return 0, err
- }
- port := l.Addr().(*net.TCPAddr).Port
- if err := l.Close(); err != nil {
- return 0, err
- }
- return port, nil
-}
-
-// failMessage creates a nicely formatted error message when there is no result to show.
-func failMessage(paths []string, symbol, method string) error {
- var b bytes.Buffer
- if len(paths) > 1 {
- b.WriteString("s")
- }
- b.WriteString(" ")
- for i, path := range paths {
- if i > 0 {
- b.WriteString(", ")
- }
- b.WriteString(path)
- }
- if method == "" {
- return fmt.Errorf("no symbol %s in package%s", symbol, &b)
- }
- return fmt.Errorf("no method or field %s.%s in package%s", symbol, method, &b)
-}
-
-// parseArgs analyzes the arguments (if any) and returns the package
-// it represents, the part of the argument the user used to identify
-// the path (or "" if it's the current package) and the symbol
-// (possibly with a .method) within that package.
-// parseSymbol is used to analyze the symbol itself.
-// The boolean final argument reports whether it is possible that
-// there may be more directories worth looking at. It will only
-// be true if the package path is a partial match for some directory
-// and there may be more matches. For example, if the argument
-// is rand.Float64, we must scan both crypto/rand and math/rand
-// to find the symbol, and the first call will return crypto/rand, true.
-func parseArgs(flagSet *flag.FlagSet, args []string) (pkg *build.Package, path, symbol string, more bool) {
- wd, err := os.Getwd()
- if err != nil {
- log.Fatal(err)
- }
- if len(args) == 0 {
- // Easy: current directory.
- return importDir(wd), "", "", false
- }
- arg := args[0]
- // We have an argument. If it is a directory name beginning with . or ..,
- // use the absolute path name. This discriminates "./errors" from "errors"
- // if the current directory contains a non-standard errors package.
- if isDotSlash(arg) {
- arg = filepath.Join(wd, arg)
- }
- switch len(args) {
- default:
- usage(flagSet)
- case 1:
- // Done below.
- case 2:
- // Package must be findable and importable.
- pkg, err := build.Import(args[0], wd, build.ImportComment)
- if err == nil {
- return pkg, args[0], args[1], false
- }
- for {
- packagePath, ok := findNextPackage(arg)
- if !ok {
- break
- }
- if pkg, err := build.ImportDir(packagePath, build.ImportComment); err == nil {
- return pkg, arg, args[1], true
- }
- }
- return nil, args[0], args[1], false
- }
- // Usual case: one argument.
- // If it contains slashes, it begins with either a package path
- // or an absolute directory.
- // First, is it a complete package path as it is? If so, we are done.
- // This avoids confusion over package paths that have other
- // package paths as their prefix.
- var importErr error
- if filepath.IsAbs(arg) {
- pkg, importErr = build.ImportDir(arg, build.ImportComment)
- if importErr == nil {
- return pkg, arg, "", false
- }
- } else {
- pkg, importErr = build.Import(arg, wd, build.ImportComment)
- if importErr == nil {
- return pkg, arg, "", false
- }
- }
- // Another disambiguator: If the argument starts with an upper
- // case letter, it can only be a symbol in the current directory.
- // Kills the problem caused by case-insensitive file systems
- // matching an upper case name as a package name.
- if !strings.ContainsAny(arg, `/\`) && token.IsExported(arg) {
- pkg, err := build.ImportDir(".", build.ImportComment)
- if err == nil {
- return pkg, "", arg, false
- }
- }
- // If it has a slash, it must be a package path but there is a symbol.
- // It's the last package path we care about.
- slash := strings.LastIndex(arg, "/")
- // There may be periods in the package path before or after the slash
- // and between a symbol and method.
- // Split the string at various periods to see what we find.
- // In general there may be ambiguities but this should almost always
- // work.
- var period int
- // slash+1: if there's no slash, the value is -1 and start is 0; otherwise
- // start is the byte after the slash.
- for start := slash + 1; start < len(arg); start = period + 1 {
- period = strings.Index(arg[start:], ".")
- symbol := ""
- if period < 0 {
- period = len(arg)
- } else {
- period += start
- symbol = arg[period+1:]
- }
- // Have we identified a package already?
- pkg, err := build.Import(arg[0:period], wd, build.ImportComment)
- if err == nil {
- return pkg, arg[0:period], symbol, false
- }
- // See if we have the basename or tail of a package, as in json for encoding/json
- // or ivy/value for robpike.io/ivy/value.
- pkgName := arg[:period]
- for {
- path, ok := findNextPackage(pkgName)
- if !ok {
- break
- }
- if pkg, err = build.ImportDir(path, build.ImportComment); err == nil {
- return pkg, arg[0:period], symbol, true
- }
- }
- dirs.Reset() // Next iteration of for loop must scan all the directories again.
- }
- // If it has a slash, we've failed.
- if slash >= 0 {
- // build.Import should always include the path in its error message,
- // and we should avoid repeating it. Unfortunately, build.Import doesn't
- // return a structured error. That can't easily be fixed, since it
- // invokes 'go list' and returns the error text from the loaded package.
- // TODO(golang.org/issue/34750): load using golang.org/x/tools/go/packages
- // instead of go/build.
- importErrStr := importErr.Error()
- if strings.Contains(importErrStr, arg[:period]) {
- log.Fatal(importErrStr)
- } else {
- log.Fatalf("no such package %s: %s", arg[:period], importErrStr)
- }
- }
- // Guess it's a symbol in the current directory.
- return importDir(wd), "", arg, false
-}
-
-// dotPaths lists all the dotted paths legal on Unix-like and
-// Windows-like file systems. We check them all, as the chance
-// of error is minute and even on Windows people will use ./
-// sometimes.
-var dotPaths = []string{
- `./`,
- `../`,
- `.\`,
- `..\`,
-}
-
-// isDotSlash reports whether the path begins with a reference
-// to the local . or .. directory.
-func isDotSlash(arg string) bool {
- if arg == "." || arg == ".." {
- return true
- }
- for _, dotPath := range dotPaths {
- if strings.HasPrefix(arg, dotPath) {
- return true
- }
- }
- return false
-}
-
-// importDir is just an error-catching wrapper for build.ImportDir.
-func importDir(dir string) *build.Package {
- pkg, err := build.ImportDir(dir, build.ImportComment)
- if err != nil {
- log.Fatal(err)
- }
- return pkg
-}
-
-// parseSymbol breaks str apart into a symbol and method.
-// Both may be missing or the method may be missing.
-// If present, each must be a valid Go identifier.
-func parseSymbol(flagSet *flag.FlagSet, str string) (symbol, method string) {
- if str == "" {
- return
- }
- elem := strings.Split(str, ".")
- switch len(elem) {
- case 1:
- case 2:
- method = elem[1]
- default:
- log.Printf("too many periods in symbol specification")
- usage(flagSet)
- }
- symbol = elem[0]
- return
-}
-
-// isExported reports whether the name is an exported identifier.
-// If the unexported flag (-u) is true, isExported returns true because
-// it means that we treat the name as if it is exported.
-func isExported(name string) bool {
- return unexported || token.IsExported(name)
-}
-
-// findNextPackage returns the next full file name path that matches the
-// (perhaps partial) package path pkg. The boolean reports if any match was found.
-func findNextPackage(pkg string) (string, bool) {
- if filepath.IsAbs(pkg) {
- if dirs.offset == 0 {
- dirs.offset = -1
- return pkg, true
- }
- return "", false
- }
- if pkg == "" || token.IsExported(pkg) { // Upper case symbol cannot be a package name.
- return "", false
- }
- pkg = path.Clean(pkg)
- pkgSuffix := "/" + pkg
- for {
- d, ok := dirs.Next()
- if !ok {
- return "", false
- }
- if d.importPath == pkg || strings.HasSuffix(d.importPath, pkgSuffix) {
- return d.dir, true
- }
- }
-}
-
-var buildCtx = build.Default
-
-// splitGopath splits $GOPATH into a list of roots.
-func splitGopath() []string {
- return filepath.SplitList(buildCtx.GOPATH)
-}
diff --git a/src/cmd/internal/doc/pkg.go b/src/cmd/internal/doc/pkg.go
deleted file mode 100644
index 953b0d9a28..0000000000
--- a/src/cmd/internal/doc/pkg.go
+++ /dev/null
@@ -1,1167 +0,0 @@
-// Copyright 2015 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 doc
-
-import (
- "bufio"
- "bytes"
- "fmt"
- "go/ast"
- "go/build"
- "go/doc"
- "go/format"
- "go/parser"
- "go/printer"
- "go/token"
- "io"
- "io/fs"
- "log"
- "path/filepath"
- "strings"
- "unicode"
- "unicode/utf8"
-)
-
-const (
- punchedCardWidth = 80
- indent = " "
-)
-
-type Package struct {
- writer io.Writer // Destination for output.
- name string // Package name, json for encoding/json.
- userPath string // String the user used to find this package.
- pkg *ast.Package // Parsed package.
- file *ast.File // Merged from all files in the package
- doc *doc.Package
- build *build.Package
- typedValue map[*doc.Value]bool // Consts and vars related to types.
- constructor map[*doc.Func]bool // Constructors.
- fs *token.FileSet // Needed for printing.
- buf pkgBuffer
-}
-
-func (pkg *Package) ToText(w io.Writer, text, prefix, codePrefix string) {
- d := pkg.doc.Parser().Parse(text)
- pr := pkg.doc.Printer()
- pr.TextPrefix = prefix
- pr.TextCodePrefix = codePrefix
- w.Write(pr.Text(d))
-}
-
-// pkgBuffer is a wrapper for bytes.Buffer that prints a package clause the
-// first time Write is called.
-type pkgBuffer struct {
- pkg *Package
- printed bool // Prevent repeated package clauses.
- bytes.Buffer
-}
-
-func (pb *pkgBuffer) Write(p []byte) (int, error) {
- pb.packageClause()
- return pb.Buffer.Write(p)
-}
-
-func (pb *pkgBuffer) packageClause() {
- if !pb.printed {
- pb.printed = true
- // Only show package clause for commands if requested explicitly.
- if pb.pkg.pkg.Name != "main" || showCmd {
- pb.pkg.packageClause()
- }
- }
-}
-
-type PackageError string // type returned by pkg.Fatalf.
-
-func (p PackageError) Error() string {
- return string(p)
-}
-
-// prettyPath returns a version of the package path that is suitable for an
-// error message. It obeys the import comment if present. Also, since
-// pkg.build.ImportPath is sometimes the unhelpful "" or ".", it looks for a
-// directory name in GOROOT or GOPATH if that happens.
-func (pkg *Package) prettyPath() string {
- path := pkg.build.ImportComment
- if path == "" {
- path = pkg.build.ImportPath
- }
- if path != "." && path != "" {
- return path
- }
- // Convert the source directory into a more useful path.
- // Also convert everything to slash-separated paths for uniform handling.
- path = filepath.Clean(filepath.ToSlash(pkg.build.Dir))
- // Can we find a decent prefix?
- if buildCtx.GOROOT != "" {
- goroot := filepath.Join(buildCtx.GOROOT, "src")
- if p, ok := trim(path, filepath.ToSlash(goroot)); ok {
- return p
- }
- }
- for _, gopath := range splitGopath() {
- if p, ok := trim(path, filepath.ToSlash(gopath)); ok {
- return p
- }
- }
- return path
-}
-
-// trim trims the directory prefix from the path, paying attention
-// to the path separator. If they are the same string or the prefix
-// is not present the original is returned. The boolean reports whether
-// the prefix is present. That path and prefix have slashes for separators.
-func trim(path, prefix string) (string, bool) {
- if !strings.HasPrefix(path, prefix) {
- return path, false
- }
- if path == prefix {
- return path, true
- }
- if path[len(prefix)] == '/' {
- return path[len(prefix)+1:], true
- }
- return path, false // Textual prefix but not a path prefix.
-}
-
-// pkg.Fatalf is like log.Fatalf, but panics so it can be recovered in the
-// main do function, so it doesn't cause an exit. Allows testing to work
-// without running a subprocess. The log prefix will be added when
-// logged in main; it is not added here.
-func (pkg *Package) Fatalf(format string, args ...any) {
- panic(PackageError(fmt.Sprintf(format, args...)))
-}
-
-// parsePackage turns the build package we found into a parsed package
-// we can then use to generate documentation.
-func parsePackage(writer io.Writer, pkg *build.Package, userPath string) *Package {
- // include tells parser.ParseDir which files to include.
- // That means the file must be in the build package's GoFiles or CgoFiles
- // list only (no tag-ignored files, tests, swig or other non-Go files).
- include := func(info fs.FileInfo) bool {
- for _, name := range pkg.GoFiles {
- if name == info.Name() {
- return true
- }
- }
- for _, name := range pkg.CgoFiles {
- if name == info.Name() {
- return true
- }
- }
- return false
- }
- fset := token.NewFileSet()
- pkgs, err := parser.ParseDir(fset, pkg.Dir, include, parser.ParseComments)
- if err != nil {
- log.Fatal(err)
- }
- // Make sure they are all in one package.
- if len(pkgs) == 0 {
- log.Fatalf("no source-code package in directory %s", pkg.Dir)
- }
- if len(pkgs) > 1 {
- log.Fatalf("multiple packages in directory %s", pkg.Dir)
- }
- astPkg := pkgs[pkg.Name]
-
- // TODO: go/doc does not include typed constants in the constants
- // list, which is what we want. For instance, time.Sunday is of type
- // time.Weekday, so it is defined in the type but not in the
- // Consts list for the package. This prevents
- // go doc time.Sunday
- // from finding the symbol. Work around this for now, but we
- // should fix it in go/doc.
- // A similar story applies to factory functions.
- mode := doc.AllDecls
- if showSrc {
- mode |= doc.PreserveAST // See comment for Package.emit.
- }
- docPkg := doc.New(astPkg, pkg.ImportPath, mode)
- typedValue := make(map[*doc.Value]bool)
- constructor := make(map[*doc.Func]bool)
- for _, typ := range docPkg.Types {
- docPkg.Consts = append(docPkg.Consts, typ.Consts...)
- docPkg.Vars = append(docPkg.Vars, typ.Vars...)
- docPkg.Funcs = append(docPkg.Funcs, typ.Funcs...)
- if isExported(typ.Name) {
- for _, value := range typ.Consts {
- typedValue[value] = true
- }
- for _, value := range typ.Vars {
- typedValue[value] = true
- }
- for _, fun := range typ.Funcs {
- // We don't count it as a constructor bound to the type
- // if the type itself is not exported.
- constructor[fun] = true
- }
- }
- }
-
- p := &Package{
- writer: writer,
- name: pkg.Name,
- userPath: userPath,
- pkg: astPkg,
- file: ast.MergePackageFiles(astPkg, 0),
- doc: docPkg,
- typedValue: typedValue,
- constructor: constructor,
- build: pkg,
- fs: fset,
- }
- p.buf.pkg = p
- return p
-}
-
-func (pkg *Package) Printf(format string, args ...any) {
- fmt.Fprintf(&pkg.buf, format, args...)
-}
-
-func (pkg *Package) flush() {
- _, err := pkg.writer.Write(pkg.buf.Bytes())
- if err != nil {
- log.Fatal(err)
- }
- pkg.buf.Reset() // Not needed, but it's a flush.
-}
-
-var newlineBytes = []byte("\n\n") // We never ask for more than 2.
-
-// newlines guarantees there are n newlines at the end of the buffer.
-func (pkg *Package) newlines(n int) {
- for !bytes.HasSuffix(pkg.buf.Bytes(), newlineBytes[:n]) {
- pkg.buf.WriteRune('\n')
- }
-}
-
-// emit prints the node. If showSrc is true, it ignores the provided comment,
-// assuming the comment is in the node itself. Otherwise, the go/doc package
-// clears the stuff we don't want to print anyway. It's a bit of a magic trick.
-func (pkg *Package) emit(comment string, node ast.Node) {
- if node != nil {
- var arg any = node
- if showSrc {
- // Need an extra little dance to get internal comments to appear.
- arg = &printer.CommentedNode{
- Node: node,
- Comments: pkg.file.Comments,
- }
- }
- err := format.Node(&pkg.buf, pkg.fs, arg)
- if err != nil {
- log.Fatal(err)
- }
- if comment != "" && !showSrc {
- pkg.newlines(1)
- pkg.ToText(&pkg.buf, comment, indent, indent+indent)
- pkg.newlines(2) // Blank line after comment to separate from next item.
- } else {
- pkg.newlines(1)
- }
- }
-}
-
-// oneLineNode returns a one-line summary of the given input node.
-func (pkg *Package) oneLineNode(node ast.Node) string {
- const maxDepth = 10
- return pkg.oneLineNodeDepth(node, maxDepth)
-}
-
-// oneLineNodeDepth returns a one-line summary of the given input node.
-// The depth specifies the maximum depth when traversing the AST.
-func (pkg *Package) oneLineNodeDepth(node ast.Node, depth int) string {
- const dotDotDot = "..."
- if depth == 0 {
- return dotDotDot
- }
- depth--
-
- switch n := node.(type) {
- case nil:
- return ""
-
- case *ast.GenDecl:
- // Formats const and var declarations.
- trailer := ""
- if len(n.Specs) > 1 {
- trailer = " " + dotDotDot
- }
-
- // Find the first relevant spec.
- typ := ""
- for i, spec := range n.Specs {
- valueSpec := spec.(*ast.ValueSpec) // Must succeed; we can't mix types in one GenDecl.
-
- // The type name may carry over from a previous specification in the
- // case of constants and iota.
- if valueSpec.Type != nil {
- typ = fmt.Sprintf(" %s", pkg.oneLineNodeDepth(valueSpec.Type, depth))
- } else if len(valueSpec.Values) > 0 {
- typ = ""
- }
-
- if !isExported(valueSpec.Names[0].Name) {
- continue
- }
- val := ""
- if i < len(valueSpec.Values) && valueSpec.Values[i] != nil {
- val = fmt.Sprintf(" = %s", pkg.oneLineNodeDepth(valueSpec.Values[i], depth))
- }
- return fmt.Sprintf("%s %s%s%s%s", n.Tok, valueSpec.Names[0], typ, val, trailer)
- }
- return ""
-
- case *ast.FuncDecl:
- // Formats func declarations.
- name := n.Name.Name
- recv := pkg.oneLineNodeDepth(n.Recv, depth)
- if len(recv) > 0 {
- recv = "(" + recv + ") "
- }
- fnc := pkg.oneLineNodeDepth(n.Type, depth)
- fnc = strings.TrimPrefix(fnc, "func")
- return fmt.Sprintf("func %s%s%s", recv, name, fnc)
-
- case *ast.TypeSpec:
- sep := " "
- if n.Assign.IsValid() {
- sep = " = "
- }
- tparams := pkg.formatTypeParams(n.TypeParams, depth)
- return fmt.Sprintf("type %s%s%s%s", n.Name.Name, tparams, sep, pkg.oneLineNodeDepth(n.Type, depth))
-
- case *ast.FuncType:
- var params []string
- if n.Params != nil {
- for _, field := range n.Params.List {
- params = append(params, pkg.oneLineField(field, depth))
- }
- }
- needParens := false
- var results []string
- if n.Results != nil {
- needParens = needParens || len(n.Results.List) > 1
- for _, field := range n.Results.List {
- needParens = needParens || len(field.Names) > 0
- results = append(results, pkg.oneLineField(field, depth))
- }
- }
-
- tparam := pkg.formatTypeParams(n.TypeParams, depth)
- param := joinStrings(params)
- if len(results) == 0 {
- return fmt.Sprintf("func%s(%s)", tparam, param)
- }
- result := joinStrings(results)
- if !needParens {
- return fmt.Sprintf("func%s(%s) %s", tparam, param, result)
- }
- return fmt.Sprintf("func%s(%s) (%s)", tparam, param, result)
-
- case *ast.StructType:
- if n.Fields == nil || len(n.Fields.List) == 0 {
- return "struct{}"
- }
- return "struct{ ... }"
-
- case *ast.InterfaceType:
- if n.Methods == nil || len(n.Methods.List) == 0 {
- return "interface{}"
- }
- return "interface{ ... }"
-
- case *ast.FieldList:
- if n == nil || len(n.List) == 0 {
- return ""
- }
- if len(n.List) == 1 {
- return pkg.oneLineField(n.List[0], depth)
- }
- return dotDotDot
-
- case *ast.FuncLit:
- return pkg.oneLineNodeDepth(n.Type, depth) + " { ... }"
-
- case *ast.CompositeLit:
- typ := pkg.oneLineNodeDepth(n.Type, depth)
- if len(n.Elts) == 0 {
- return fmt.Sprintf("%s{}", typ)
- }
- return fmt.Sprintf("%s{ %s }", typ, dotDotDot)
-
- case *ast.ArrayType:
- length := pkg.oneLineNodeDepth(n.Len, depth)
- element := pkg.oneLineNodeDepth(n.Elt, depth)
- return fmt.Sprintf("[%s]%s", length, element)
-
- case *ast.MapType:
- key := pkg.oneLineNodeDepth(n.Key, depth)
- value := pkg.oneLineNodeDepth(n.Value, depth)
- return fmt.Sprintf("map[%s]%s", key, value)
-
- case *ast.CallExpr:
- fnc := pkg.oneLineNodeDepth(n.Fun, depth)
- var args []string
- for _, arg := range n.Args {
- args = append(args, pkg.oneLineNodeDepth(arg, depth))
- }
- return fmt.Sprintf("%s(%s)", fnc, joinStrings(args))
-
- case *ast.UnaryExpr:
- return fmt.Sprintf("%s%s", n.Op, pkg.oneLineNodeDepth(n.X, depth))
-
- case *ast.Ident:
- return n.Name
-
- default:
- // As a fallback, use default formatter for all unknown node types.
- buf := new(strings.Builder)
- format.Node(buf, pkg.fs, node)
- s := buf.String()
- if strings.Contains(s, "\n") {
- return dotDotDot
- }
- return s
- }
-}
-
-func (pkg *Package) formatTypeParams(list *ast.FieldList, depth int) string {
- if list.NumFields() == 0 {
- return ""
- }
- var tparams []string
- for _, field := range list.List {
- tparams = append(tparams, pkg.oneLineField(field, depth))
- }
- return "[" + joinStrings(tparams) + "]"
-}
-
-// oneLineField returns a one-line summary of the field.
-func (pkg *Package) oneLineField(field *ast.Field, depth int) string {
- var names []string
- for _, name := range field.Names {
- names = append(names, name.Name)
- }
- if len(names) == 0 {
- return pkg.oneLineNodeDepth(field.Type, depth)
- }
- return joinStrings(names) + " " + pkg.oneLineNodeDepth(field.Type, depth)
-}
-
-// joinStrings formats the input as a comma-separated list,
-// but truncates the list at some reasonable length if necessary.
-func joinStrings(ss []string) string {
- var n int
- for i, s := range ss {
- n += len(s) + len(", ")
- if n > punchedCardWidth {
- ss = append(ss[:i:i], "...")
- break
- }
- }
- return strings.Join(ss, ", ")
-}
-
-// printHeader prints a header for the section named s, adding a blank line on each side.
-func (pkg *Package) printHeader(s string) {
- pkg.Printf("\n%s\n\n", s)
-}
-
-// constsDoc prints all const documentation, if any, including a header.
-// The one argument is the valueDoc registry.
-func (pkg *Package) constsDoc(printed map[*ast.GenDecl]bool) {
- var header bool
- for _, value := range pkg.doc.Consts {
- // Constants and variables come in groups, and valueDoc prints
- // all the items in the group. We only need to find one exported symbol.
- for _, name := range value.Names {
- if isExported(name) && !pkg.typedValue[value] {
- if !header {
- pkg.printHeader("CONSTANTS")
- header = true
- }
- pkg.valueDoc(value, printed)
- break
- }
- }
- }
-}
-
-// varsDoc prints all var documentation, if any, including a header.
-// Printed is the valueDoc registry.
-func (pkg *Package) varsDoc(printed map[*ast.GenDecl]bool) {
- var header bool
- for _, value := range pkg.doc.Vars {
- // Constants and variables come in groups, and valueDoc prints
- // all the items in the group. We only need to find one exported symbol.
- for _, name := range value.Names {
- if isExported(name) && !pkg.typedValue[value] {
- if !header {
- pkg.printHeader("VARIABLES")
- header = true
- }
- pkg.valueDoc(value, printed)
- break
- }
- }
- }
-}
-
-// funcsDoc prints all func documentation, if any, including a header.
-func (pkg *Package) funcsDoc() {
- var header bool
- for _, fun := range pkg.doc.Funcs {
- if isExported(fun.Name) && !pkg.constructor[fun] {
- if !header {
- pkg.printHeader("FUNCTIONS")
- header = true
- }
- pkg.emit(fun.Doc, fun.Decl)
- }
- }
-}
-
-// funcsDoc prints all type documentation, if any, including a header.
-func (pkg *Package) typesDoc() {
- var header bool
- for _, typ := range pkg.doc.Types {
- if isExported(typ.Name) {
- if !header {
- pkg.printHeader("TYPES")
- header = true
- }
- pkg.typeDoc(typ)
- }
- }
-}
-
-// packageDoc prints the docs for the package.
-func (pkg *Package) packageDoc() {
- pkg.Printf("") // Trigger the package clause; we know the package exists.
- if showAll || !short {
- pkg.ToText(&pkg.buf, pkg.doc.Doc, "", indent)
- pkg.newlines(1)
- }
-
- switch {
- case showAll:
- printed := make(map[*ast.GenDecl]bool) // valueDoc registry
- pkg.constsDoc(printed)
- pkg.varsDoc(printed)
- pkg.funcsDoc()
- pkg.typesDoc()
-
- case pkg.pkg.Name == "main" && !showCmd:
- // Show only package docs for commands.
- return
-
- default:
- if !short {
- pkg.newlines(2) // Guarantee blank line before the components.
- }
- pkg.valueSummary(pkg.doc.Consts, false)
- pkg.valueSummary(pkg.doc.Vars, false)
- pkg.funcSummary(pkg.doc.Funcs, false)
- pkg.typeSummary()
- }
-
- if !short {
- pkg.bugs()
- }
-}
-
-// packageClause prints the package clause.
-func (pkg *Package) packageClause() {
- if short {
- return
- }
- importPath := pkg.build.ImportComment
- if importPath == "" {
- importPath = pkg.build.ImportPath
- }
-
- // If we're using modules, the import path derived from module code locations wins.
- // If we did a file system scan, we knew the import path when we found the directory.
- // But if we started with a directory name, we never knew the import path.
- // Either way, we don't know it now, and it's cheap to (re)compute it.
- if usingModules {
- for _, root := range codeRoots() {
- if pkg.build.Dir == root.dir {
- importPath = root.importPath
- break
- }
- if strings.HasPrefix(pkg.build.Dir, root.dir+string(filepath.Separator)) {
- suffix := filepath.ToSlash(pkg.build.Dir[len(root.dir)+1:])
- if root.importPath == "" {
- importPath = suffix
- } else {
- importPath = root.importPath + "/" + suffix
- }
- break
- }
- }
- }
-
- pkg.Printf("package %s // import %q\n\n", pkg.name, importPath)
- if !usingModules && importPath != pkg.build.ImportPath {
- pkg.Printf("WARNING: package source is installed in %q\n", pkg.build.ImportPath)
- }
-}
-
-// valueSummary prints a one-line summary for each set of values and constants.
-// If all the types in a constant or variable declaration belong to the same
-// type they can be printed by typeSummary, and so can be suppressed here.
-func (pkg *Package) valueSummary(values []*doc.Value, showGrouped bool) {
- var isGrouped map[*doc.Value]bool
- if !showGrouped {
- isGrouped = make(map[*doc.Value]bool)
- for _, typ := range pkg.doc.Types {
- if !isExported(typ.Name) {
- continue
- }
- for _, c := range typ.Consts {
- isGrouped[c] = true
- }
- for _, v := range typ.Vars {
- isGrouped[v] = true
- }
- }
- }
-
- for _, value := range values {
- if !isGrouped[value] {
- if decl := pkg.oneLineNode(value.Decl); decl != "" {
- pkg.Printf("%s\n", decl)
- }
- }
- }
-}
-
-// funcSummary prints a one-line summary for each function. Constructors
-// are printed by typeSummary, below, and so can be suppressed here.
-func (pkg *Package) funcSummary(funcs []*doc.Func, showConstructors bool) {
- for _, fun := range funcs {
- // Exported functions only. The go/doc package does not include methods here.
- if isExported(fun.Name) {
- if showConstructors || !pkg.constructor[fun] {
- pkg.Printf("%s\n", pkg.oneLineNode(fun.Decl))
- }
- }
- }
-}
-
-// typeSummary prints a one-line summary for each type, followed by its constructors.
-func (pkg *Package) typeSummary() {
- for _, typ := range pkg.doc.Types {
- for _, spec := range typ.Decl.Specs {
- typeSpec := spec.(*ast.TypeSpec) // Must succeed.
- if isExported(typeSpec.Name.Name) {
- pkg.Printf("%s\n", pkg.oneLineNode(typeSpec))
- // Now print the consts, vars, and constructors.
- for _, c := range typ.Consts {
- if decl := pkg.oneLineNode(c.Decl); decl != "" {
- pkg.Printf(indent+"%s\n", decl)
- }
- }
- for _, v := range typ.Vars {
- if decl := pkg.oneLineNode(v.Decl); decl != "" {
- pkg.Printf(indent+"%s\n", decl)
- }
- }
- for _, constructor := range typ.Funcs {
- if isExported(constructor.Name) {
- pkg.Printf(indent+"%s\n", pkg.oneLineNode(constructor.Decl))
- }
- }
- }
- }
- }
-}
-
-// bugs prints the BUGS information for the package.
-// TODO: Provide access to TODOs and NOTEs as well (very noisy so off by default)?
-func (pkg *Package) bugs() {
- if pkg.doc.Notes["BUG"] == nil {
- return
- }
- pkg.Printf("\n")
- for _, note := range pkg.doc.Notes["BUG"] {
- pkg.Printf("%s: %v\n", "BUG", note.Body)
- }
-}
-
-// findValues finds the doc.Values that describe the symbol.
-func (pkg *Package) findValues(symbol string, docValues []*doc.Value) (values []*doc.Value) {
- for _, value := range docValues {
- for _, name := range value.Names {
- if match(symbol, name) {
- values = append(values, value)
- }
- }
- }
- return
-}
-
-// findFuncs finds the doc.Funcs that describes the symbol.
-func (pkg *Package) findFuncs(symbol string) (funcs []*doc.Func) {
- for _, fun := range pkg.doc.Funcs {
- if match(symbol, fun.Name) {
- funcs = append(funcs, fun)
- }
- }
- return
-}
-
-// findTypes finds the doc.Types that describes the symbol.
-// If symbol is empty, it finds all exported types.
-func (pkg *Package) findTypes(symbol string) (types []*doc.Type) {
- for _, typ := range pkg.doc.Types {
- if symbol == "" && isExported(typ.Name) || match(symbol, typ.Name) {
- types = append(types, typ)
- }
- }
- return
-}
-
-// findTypeSpec returns the ast.TypeSpec within the declaration that defines the symbol.
-// The name must match exactly.
-func (pkg *Package) findTypeSpec(decl *ast.GenDecl, symbol string) *ast.TypeSpec {
- for _, spec := range decl.Specs {
- typeSpec := spec.(*ast.TypeSpec) // Must succeed.
- if symbol == typeSpec.Name.Name {
- return typeSpec
- }
- }
- return nil
-}
-
-// symbolDoc prints the docs for symbol. There may be multiple matches.
-// If symbol matches a type, output includes its methods factories and associated constants.
-// If there is no top-level symbol, symbolDoc looks for methods that match.
-func (pkg *Package) symbolDoc(symbol string) bool {
- found := false
- // Functions.
- for _, fun := range pkg.findFuncs(symbol) {
- // Symbol is a function.
- decl := fun.Decl
- pkg.emit(fun.Doc, decl)
- found = true
- }
- // Constants and variables behave the same.
- values := pkg.findValues(symbol, pkg.doc.Consts)
- values = append(values, pkg.findValues(symbol, pkg.doc.Vars)...)
- printed := make(map[*ast.GenDecl]bool) // valueDoc registry
- for _, value := range values {
- pkg.valueDoc(value, printed)
- found = true
- }
- // Types.
- for _, typ := range pkg.findTypes(symbol) {
- pkg.typeDoc(typ)
- found = true
- }
- if !found {
- // See if there are methods.
- if !pkg.printMethodDoc("", symbol) {
- return false
- }
- }
- return true
-}
-
-// valueDoc prints the docs for a constant or variable. The printed map records
-// which values have been printed already to avoid duplication. Otherwise, a
-// declaration like:
-//
-// const ( c = 1; C = 2 )
-//
-// … could be printed twice if the -u flag is set, as it matches twice.
-func (pkg *Package) valueDoc(value *doc.Value, printed map[*ast.GenDecl]bool) {
- if printed[value.Decl] {
- return
- }
- // Print each spec only if there is at least one exported symbol in it.
- // (See issue 11008.)
- // TODO: Should we elide unexported symbols from a single spec?
- // It's an unlikely scenario, probably not worth the trouble.
- // TODO: Would be nice if go/doc did this for us.
- specs := make([]ast.Spec, 0, len(value.Decl.Specs))
- var typ ast.Expr
- for _, spec := range value.Decl.Specs {
- vspec := spec.(*ast.ValueSpec)
-
- // The type name may carry over from a previous specification in the
- // case of constants and iota.
- if vspec.Type != nil {
- typ = vspec.Type
- }
-
- for _, ident := range vspec.Names {
- if showSrc || isExported(ident.Name) {
- if vspec.Type == nil && vspec.Values == nil && typ != nil {
- // This a standalone identifier, as in the case of iota usage.
- // Thus, assume the type comes from the previous type.
- vspec.Type = &ast.Ident{
- Name: pkg.oneLineNode(typ),
- NamePos: vspec.End() - 1,
- }
- }
-
- specs = append(specs, vspec)
- typ = nil // Only inject type on first exported identifier
- break
- }
- }
- }
- if len(specs) == 0 {
- return
- }
- value.Decl.Specs = specs
- pkg.emit(value.Doc, value.Decl)
- printed[value.Decl] = true
-}
-
-// typeDoc prints the docs for a type, including constructors and other items
-// related to it.
-func (pkg *Package) typeDoc(typ *doc.Type) {
- decl := typ.Decl
- spec := pkg.findTypeSpec(decl, typ.Name)
- trimUnexportedElems(spec)
- // If there are multiple types defined, reduce to just this one.
- if len(decl.Specs) > 1 {
- decl.Specs = []ast.Spec{spec}
- }
- pkg.emit(typ.Doc, decl)
- pkg.newlines(2)
- // Show associated methods, constants, etc.
- if showAll {
- printed := make(map[*ast.GenDecl]bool) // valueDoc registry
- // We can use append here to print consts, then vars. Ditto for funcs and methods.
- values := typ.Consts
- values = append(values, typ.Vars...)
- for _, value := range values {
- for _, name := range value.Names {
- if isExported(name) {
- pkg.valueDoc(value, printed)
- break
- }
- }
- }
- funcs := typ.Funcs
- funcs = append(funcs, typ.Methods...)
- for _, fun := range funcs {
- if isExported(fun.Name) {
- pkg.emit(fun.Doc, fun.Decl)
- if fun.Doc == "" {
- pkg.newlines(2)
- }
- }
- }
- } else {
- pkg.valueSummary(typ.Consts, true)
- pkg.valueSummary(typ.Vars, true)
- pkg.funcSummary(typ.Funcs, true)
- pkg.funcSummary(typ.Methods, true)
- }
-}
-
-// trimUnexportedElems modifies spec in place to elide unexported fields from
-// structs and methods from interfaces (unless the unexported flag is set or we
-// are asked to show the original source).
-func trimUnexportedElems(spec *ast.TypeSpec) {
- if showSrc {
- return
- }
- switch typ := spec.Type.(type) {
- case *ast.StructType:
- typ.Fields = trimUnexportedFields(typ.Fields, false)
- case *ast.InterfaceType:
- typ.Methods = trimUnexportedFields(typ.Methods, true)
- }
-}
-
-// trimUnexportedFields returns the field list trimmed of unexported fields.
-func trimUnexportedFields(fields *ast.FieldList, isInterface bool) *ast.FieldList {
- what := "methods"
- if !isInterface {
- what = "fields"
- }
-
- trimmed := false
- list := make([]*ast.Field, 0, len(fields.List))
- for _, field := range fields.List {
- // When printing fields we normally print field.Doc.
- // Here we are going to pass the AST to go/format,
- // which will print the comments from the AST,
- // not field.Doc which is from go/doc.
- // The two are similar but not identical;
- // for example, field.Doc does not include directives.
- // In order to consistently print field.Doc,
- // we replace the comment in the AST with field.Doc.
- // That will cause go/format to print what we want.
- // See issue #56592.
- if field.Doc != nil {
- doc := field.Doc
- text := doc.Text()
-
- trailingBlankLine := len(doc.List[len(doc.List)-1].Text) == 2
- if !trailingBlankLine {
- // Remove trailing newline.
- lt := len(text)
- if lt > 0 && text[lt-1] == '\n' {
- text = text[:lt-1]
- }
- }
-
- start := doc.List[0].Slash
- doc.List = doc.List[:0]
- for _, line := range strings.Split(text, "\n") {
- prefix := "// "
- if len(line) > 0 && line[0] == '\t' {
- prefix = "//"
- }
- doc.List = append(doc.List, &ast.Comment{
- Text: prefix + line,
- })
- }
- doc.List[0].Slash = start
- }
-
- names := field.Names
- if len(names) == 0 {
- // Embedded type. Use the name of the type. It must be of the form ident or
- // pkg.ident (for structs and interfaces), or *ident or *pkg.ident (structs only).
- // Or a type embedded in a constraint.
- // Nothing else is allowed.
- ty := field.Type
- if se, ok := field.Type.(*ast.StarExpr); !isInterface && ok {
- // The form *ident or *pkg.ident is only valid on
- // embedded types in structs.
- ty = se.X
- }
- constraint := false
- switch ident := ty.(type) {
- case *ast.Ident:
- if isInterface && ident.Name == "error" && ident.Obj == nil {
- // For documentation purposes, we consider the builtin error
- // type special when embedded in an interface, such that it
- // always gets shown publicly.
- list = append(list, field)
- continue
- }
- names = []*ast.Ident{ident}
- case *ast.SelectorExpr:
- // An embedded type may refer to a type in another package.
- names = []*ast.Ident{ident.Sel}
- default:
- // An approximation or union or type
- // literal in an interface.
- constraint = true
- }
- if names == nil && !constraint {
- // Can only happen if AST is incorrect. Safe to continue with a nil list.
- log.Print("invalid program: unexpected type for embedded field")
- }
- }
- // Trims if any is unexported. Good enough in practice.
- ok := true
- if !unexported {
- for _, name := range names {
- if !isExported(name.Name) {
- trimmed = true
- ok = false
- break
- }
- }
- }
- if ok {
- list = append(list, field)
- }
- }
- if !trimmed {
- return fields
- }
- unexportedField := &ast.Field{
- Type: &ast.Ident{
- // Hack: printer will treat this as a field with a named type.
- // Setting Name and NamePos to ("", fields.Closing-1) ensures that
- // when Pos and End are called on this field, they return the
- // position right before closing '}' character.
- Name: "",
- NamePos: fields.Closing - 1,
- },
- Comment: &ast.CommentGroup{
- List: []*ast.Comment{{Text: fmt.Sprintf("// Has unexported %s.\n", what)}},
- },
- }
- return &ast.FieldList{
- Opening: fields.Opening,
- List: append(list, unexportedField),
- Closing: fields.Closing,
- }
-}
-
-// printMethodDoc prints the docs for matches of symbol.method.
-// If symbol is empty, it prints all methods for any concrete type
-// that match the name. It reports whether it found any methods.
-func (pkg *Package) printMethodDoc(symbol, method string) bool {
- types := pkg.findTypes(symbol)
- if types == nil {
- if symbol == "" {
- return false
- }
- pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
- }
- found := false
- for _, typ := range types {
- if len(typ.Methods) > 0 {
- for _, meth := range typ.Methods {
- if match(method, meth.Name) {
- decl := meth.Decl
- pkg.emit(meth.Doc, decl)
- found = true
- }
- }
- continue
- }
- if symbol == "" {
- continue
- }
- // Type may be an interface. The go/doc package does not attach
- // an interface's methods to the doc.Type. We need to dig around.
- spec := pkg.findTypeSpec(typ.Decl, typ.Name)
- inter, ok := spec.Type.(*ast.InterfaceType)
- if !ok {
- // Not an interface type.
- continue
- }
-
- // Collect and print only the methods that match.
- var methods []*ast.Field
- for _, iMethod := range inter.Methods.List {
- // This is an interface, so there can be only one name.
- // TODO: Anonymous methods (embedding)
- if len(iMethod.Names) == 0 {
- continue
- }
- name := iMethod.Names[0].Name
- if match(method, name) {
- methods = append(methods, iMethod)
- found = true
- }
- }
- if found {
- pkg.Printf("type %s ", spec.Name)
- inter.Methods.List, methods = methods, inter.Methods.List
- err := format.Node(&pkg.buf, pkg.fs, inter)
- if err != nil {
- log.Fatal(err)
- }
- pkg.newlines(1)
- // Restore the original methods.
- inter.Methods.List = methods
- }
- }
- return found
-}
-
-// printFieldDoc prints the docs for matches of symbol.fieldName.
-// It reports whether it found any field.
-// Both symbol and fieldName must be non-empty or it returns false.
-func (pkg *Package) printFieldDoc(symbol, fieldName string) bool {
- if symbol == "" || fieldName == "" {
- return false
- }
- types := pkg.findTypes(symbol)
- if types == nil {
- pkg.Fatalf("symbol %s is not a type in package %s installed in %q", symbol, pkg.name, pkg.build.ImportPath)
- }
- found := false
- numUnmatched := 0
- for _, typ := range types {
- // Type must be a struct.
- spec := pkg.findTypeSpec(typ.Decl, typ.Name)
- structType, ok := spec.Type.(*ast.StructType)
- if !ok {
- // Not a struct type.
- continue
- }
- for _, field := range structType.Fields.List {
- // TODO: Anonymous fields.
- for _, name := range field.Names {
- if !match(fieldName, name.Name) {
- numUnmatched++
- continue
- }
- if !found {
- pkg.Printf("type %s struct {\n", typ.Name)
- }
- if field.Doc != nil {
- // To present indented blocks in comments correctly, process the comment as
- // a unit before adding the leading // to each line.
- docBuf := new(bytes.Buffer)
- pkg.ToText(docBuf, field.Doc.Text(), "", indent)
- scanner := bufio.NewScanner(docBuf)
- for scanner.Scan() {
- fmt.Fprintf(&pkg.buf, "%s// %s\n", indent, scanner.Bytes())
- }
- }
- s := pkg.oneLineNode(field.Type)
- lineComment := ""
- if field.Comment != nil {
- lineComment = fmt.Sprintf(" %s", field.Comment.List[0].Text)
- }
- pkg.Printf("%s%s %s%s\n", indent, name, s, lineComment)
- found = true
- }
- }
- }
- if found {
- if numUnmatched > 0 {
- pkg.Printf("\n // ... other fields elided ...\n")
- }
- pkg.Printf("}\n")
- }
- return found
-}
-
-// match reports whether the user's symbol matches the program's.
-// A lower-case character in the user's string matches either case in the program's.
-// The program string must be exported.
-func match(user, program string) bool {
- if !isExported(program) {
- return false
- }
- if matchCase {
- return user == program
- }
- for _, u := range user {
- p, w := utf8.DecodeRuneInString(program)
- program = program[w:]
- if u == p {
- continue
- }
- if unicode.IsLower(u) && simpleFold(u) == simpleFold(p) {
- continue
- }
- return false
- }
- return program == ""
-}
-
-// simpleFold returns the minimum rune equivalent to r
-// under Unicode-defined simple case folding.
-func simpleFold(r rune) rune {
- for {
- r1 := unicode.SimpleFold(r)
- if r1 <= r {
- return r1 // wrapped around, found min
- }
- r = r1
- }
-}
diff --git a/src/cmd/internal/doc/signal_notunix.go b/src/cmd/internal/doc/signal_notunix.go
deleted file mode 100644
index b91a67eb5f..0000000000
--- a/src/cmd/internal/doc/signal_notunix.go
+++ /dev/null
@@ -1,13 +0,0 @@
-// Copyright 2012 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.
-
-//go:build plan9 || windows
-
-package doc
-
-import (
- "os"
-)
-
-var signalsToIgnore = []os.Signal{os.Interrupt}
diff --git a/src/cmd/internal/doc/signal_unix.go b/src/cmd/internal/doc/signal_unix.go
deleted file mode 100644
index f30612ce9d..0000000000
--- a/src/cmd/internal/doc/signal_unix.go
+++ /dev/null
@@ -1,14 +0,0 @@
-// Copyright 2012 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.
-
-//go:build unix || js || wasip1
-
-package doc
-
-import (
- "os"
- "syscall"
-)
-
-var signalsToIgnore = []os.Signal{os.Interrupt, syscall.SIGQUIT}
diff --git a/src/cmd/internal/doc/testdata/merge/aa.go b/src/cmd/internal/doc/testdata/merge/aa.go
deleted file mode 100644
index f8ab92dfd0..0000000000
--- a/src/cmd/internal/doc/testdata/merge/aa.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package comment A.
-package merge
-
-// A doc.
-func A() {
- // A comment.
-}
diff --git a/src/cmd/internal/doc/testdata/merge/bb.go b/src/cmd/internal/doc/testdata/merge/bb.go
deleted file mode 100644
index fd8cf3c446..0000000000
--- a/src/cmd/internal/doc/testdata/merge/bb.go
+++ /dev/null
@@ -1,7 +0,0 @@
-// Package comment B.
-package merge
-
-// B doc.
-func B() {
- // B comment.
-}
diff --git a/src/cmd/internal/doc/testdata/nested/empty/empty.go b/src/cmd/internal/doc/testdata/nested/empty/empty.go
deleted file mode 100644
index 609cf0e0a0..0000000000
--- a/src/cmd/internal/doc/testdata/nested/empty/empty.go
+++ /dev/null
@@ -1 +0,0 @@
-package empty
diff --git a/src/cmd/internal/doc/testdata/nested/ignore.go b/src/cmd/internal/doc/testdata/nested/ignore.go
deleted file mode 100644
index 5fa811d0a8..0000000000
--- a/src/cmd/internal/doc/testdata/nested/ignore.go
+++ /dev/null
@@ -1,5 +0,0 @@
-//go:build ignore
-// +build ignore
-
-// Ignored package
-package nested
diff --git a/src/cmd/internal/doc/testdata/nested/nested/real.go b/src/cmd/internal/doc/testdata/nested/nested/real.go
deleted file mode 100644
index 1e5546081c..0000000000
--- a/src/cmd/internal/doc/testdata/nested/nested/real.go
+++ /dev/null
@@ -1,4 +0,0 @@
-package nested
-
-type Foo struct {
-}
diff --git a/src/cmd/internal/doc/testdata/pkg.go b/src/cmd/internal/doc/testdata/pkg.go
deleted file mode 100644
index 4d269ff0a2..0000000000
--- a/src/cmd/internal/doc/testdata/pkg.go
+++ /dev/null
@@ -1,254 +0,0 @@
-// Copyright 2015 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 comment.
-package pkg
-
-import "io"
-
-// Constants
-
-// Comment about exported constant.
-const ExportedConstant = 1
-
-// Comment about internal constant.
-const internalConstant = 2
-
-// Comment about block of constants.
-const (
- // Comment before ConstOne.
- ConstOne = 1
- ConstTwo = 2 // Comment on line with ConstTwo.
- constThree = 3 // Comment on line with constThree.
-)
-
-// Const block where first entry is unexported.
-const (
- constFour = iota
- ConstFive
- ConstSix
-)
-
-// Variables
-
-// Comment about exported variable.
-var ExportedVariable = 1
-
-var ExportedVarOfUnExported unexportedType
-
-// Comment about internal variable.
-var internalVariable = 2
-
-// Comment about block of variables.
-var (
- // Comment before VarOne.
- VarOne = 1
- VarTwo = 2 // Comment on line with VarTwo.
- varThree = 3 // Comment on line with varThree.
-)
-
-// Var block where first entry is unexported.
-var (
- varFour = 4
- VarFive = 5
- varSix = 6
-)
-
-// Comment about exported function.
-func ExportedFunc(a int) bool {
- // BUG(me): function body note
- return true != false
-}
-
-// Comment about internal function.
-func internalFunc(a int) bool
-
-// Comment about exported type.
-type ExportedType struct {
- // Comment before exported field.
- ExportedField int // Comment on line with exported field.
- unexportedField int // Comment on line with unexported field.
- ExportedEmbeddedType // Comment on line with exported embedded field.
- *ExportedEmbeddedType // Comment on line with exported embedded *field.
- *qualified.ExportedEmbeddedType // Comment on line with exported embedded *selector.field.
- unexportedType // Comment on line with unexported embedded field.
- *unexportedType // Comment on line with unexported embedded *field.
- io.Reader // Comment on line with embedded Reader.
- error // Comment on line with embedded error.
-}
-
-// Comment about exported method.
-func (ExportedType) ExportedMethod(a int) bool {
- return true != true
-}
-
-func (ExportedType) Uncommented(a int) bool {
- return true != true
-}
-
-// Comment about unexported method.
-func (ExportedType) unexportedMethod(a int) bool {
- return true
-}
-
-type ExportedStructOneField struct {
- OnlyField int // the only field
-}
-
-// Constants tied to ExportedType. (The type is a struct so this isn't valid Go,
-// but it parses and that's all we need.)
-const (
- ExportedTypedConstant ExportedType = iota
-)
-
-// Comment about constructor for exported type.
-func ExportedTypeConstructor() *ExportedType {
- return nil
-}
-
-const unexportedTypedConstant ExportedType = 1 // In a separate section to test -u.
-
-// Comment about exported interface.
-type ExportedInterface interface {
- // Comment before exported method.
- //
- // // Code block showing how to use ExportedMethod
- // func DoSomething() error {
- // ExportedMethod()
- // return nil
- // }
- //
- ExportedMethod() // Comment on line with exported method.
- unexportedMethod() // Comment on line with unexported method.
- io.Reader // Comment on line with embedded Reader.
- error // Comment on line with embedded error.
-}
-
-// Comment about unexported type.
-type unexportedType int
-
-func (unexportedType) ExportedMethod() bool {
- return true
-}
-
-func (unexportedType) unexportedMethod() bool {
- return true
-}
-
-// Constants tied to unexportedType.
-const (
- ExportedTypedConstant_unexported unexportedType = iota
-)
-
-const unexportedTypedConstant unexportedType = 1 // In a separate section to test -u.
-
-// For case matching.
-const CaseMatch = 1
-const Casematch = 2
-
-func ReturnUnexported() unexportedType { return 0 }
-func ReturnExported() ExportedType { return ExportedType{} }
-
-const MultiLineConst = `
- MultiLineString1
- MultiLineString2
- MultiLineString3
-`
-
-func MultiLineFunc(x interface {
- MultiLineMethod1() int
- MultiLineMethod2() int
- MultiLineMethod3() int
-}) (r struct {
- MultiLineField1 int
- MultiLineField2 int
- MultiLineField3 int
-}) {
- return r
-}
-
-var MultiLineVar = map[struct {
- MultiLineField1 string
- MultiLineField2 uint64
-}]struct {
- MultiLineField3 error
- MultiLineField2 error
-}{
- {"FieldVal1", 1}: {},
- {"FieldVal2", 2}: {},
- {"FieldVal3", 3}: {},
-}
-
-const (
- _, _ uint64 = 2 * iota, 1 << iota
- constLeft1, constRight1
- ConstLeft2, constRight2
- constLeft3, ConstRight3
- ConstLeft4, ConstRight4
-)
-
-const (
- ConstGroup1 unexportedType = iota
- ConstGroup2
- ConstGroup3
-)
-
-const ConstGroup4 ExportedType = ExportedType{}
-
-func newLongLine(ss ...string)
-
-var LongLine = newLongLine(
- "someArgument1",
- "someArgument2",
- "someArgument3",
- "someArgument4",
- "someArgument5",
- "someArgument6",
- "someArgument7",
- "someArgument8",
-)
-
-type T2 int
-
-type T1 = T2
-
-const (
- Duplicate = iota
- duplicate
-)
-
-// Comment about exported function with formatting.
-//
-// Example
-//
-// fmt.Println(FormattedDoc())
-//
-// Text after pre-formatted block.
-func ExportedFormattedDoc(a int) bool {
- return true
-}
-
-type ExportedFormattedType struct {
- // Comment before exported field with formatting.
- //
- // Example
- //
- // a.ExportedField = 123
- //
- // Text after pre-formatted block.
- //ignore:directive
- ExportedField int
-}
-
-type SimpleConstraint interface {
- ~int | ~float64
-}
-
-type TildeConstraint interface {
- ~int
-}
-
-type StructConstraint interface {
- struct { F int }
-}