aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
Diffstat (limited to 'cmd')
-rw-r--r--cmd/auth/cookieauth/cookieauth.go4
-rw-r--r--cmd/auth/gitauth/gitauth.go3
-rw-r--r--cmd/auth/netrcauth/netrcauth.go3
-rw-r--r--cmd/benchcmp/compare.go4
-rw-r--r--cmd/benchcmp/doc.go2
-rw-r--r--cmd/bundle/main.go39
-rw-r--r--cmd/callgraph/main.go12
-rw-r--r--cmd/callgraph/main_test.go21
-rw-r--r--cmd/compilebench/main.go172
-rw-r--r--cmd/cover/README.md3
-rw-r--r--cmd/cover/cover.go722
-rw-r--r--cmd/cover/cover_test.go108
-rw-r--r--cmd/cover/doc.go24
-rw-r--r--cmd/cover/func.go166
-rw-r--r--cmd/cover/html.go284
-rw-r--r--cmd/cover/testdata/main.go112
-rw-r--r--cmd/cover/testdata/test.go218
-rw-r--r--cmd/digraph/digraph.go49
-rw-r--r--cmd/digraph/digraph_test.go4
-rw-r--r--cmd/file2fuzz/main.go1
-rw-r--r--cmd/fiximports/main.go56
-rw-r--r--cmd/fiximports/main_test.go3
-rw-r--r--cmd/godex/doc.go1
-rw-r--r--cmd/godoc/doc.go1
-rw-r--r--cmd/godoc/godoc_test.go189
-rw-r--r--cmd/godoc/main.go14
-rw-r--r--cmd/goimports/doc.go25
-rw-r--r--cmd/gorename/main.go1
-rw-r--r--cmd/gotype/gotype.go4
-rw-r--r--cmd/goyacc/doc.go2
-rw-r--r--cmd/goyacc/yacc.go78
-rw-r--r--cmd/guru/TODO11
-rw-r--r--cmd/guru/callers.go1
-rw-r--r--cmd/guru/callstack.go1
-rw-r--r--cmd/guru/describe.go15
-rw-r--r--cmd/guru/freevars.go1
-rw-r--r--cmd/guru/guru.go20
-rw-r--r--cmd/guru/implements.go1
-rw-r--r--cmd/guru/main.go3
-rw-r--r--cmd/guru/pointsto.go3
-rw-r--r--cmd/guru/pos.go3
-rw-r--r--cmd/guru/referrers.go3
-rw-r--r--cmd/guru/serial/serial.go45
-rw-r--r--cmd/guru/what.go2
-rw-r--r--cmd/present/dir.go11
-rw-r--r--cmd/present/doc.go35
-rw-r--r--cmd/present/main.go28
-rw-r--r--cmd/present/play.go25
-rw-r--r--cmd/present/static/article.css16
-rw-r--r--cmd/present/static/styles.css19
-rw-r--r--cmd/present2md/main.go1
-rw-r--r--cmd/signature-fuzzer/fuzz-runner/runner.go2
-rw-r--r--cmd/signature-fuzzer/internal/fuzz-generator/generator.go8
-rw-r--r--cmd/splitdwarf/splitdwarf.go6
-rw-r--r--cmd/ssadump/main.go25
-rw-r--r--cmd/stress/stress.go4
-rw-r--r--cmd/stringer/endtoend_test.go59
-rw-r--r--cmd/stringer/golden_test.go10
-rw-r--r--cmd/stringer/stringer.go8
-rwxr-xr-xcmd/toolstash/buildall4
-rw-r--r--cmd/toolstash/main.go15
61 files changed, 594 insertions, 2116 deletions
diff --git a/cmd/auth/cookieauth/cookieauth.go b/cmd/auth/cookieauth/cookieauth.go
index 37e8d6e18..8b0ff1766 100644
--- a/cmd/auth/cookieauth/cookieauth.go
+++ b/cmd/auth/cookieauth/cookieauth.go
@@ -7,7 +7,8 @@
// It expects the location of the file as the first command-line argument.
//
// Example GOAUTH usage:
-// export GOAUTH="cookieauth $(git config --get http.cookieFile)"
+//
+// export GOAUTH="cookieauth $(git config --get http.cookieFile)"
//
// See http://www.cookiecentral.com/faq/#3.5 for a description of the Netscape
// cookie file format.
@@ -39,7 +40,6 @@ func main() {
f, err := os.Open(os.Args[1])
if err != nil {
log.Fatalf("failed to read cookie file: %v\n", os.Args[1])
- os.Exit(1)
}
defer f.Close()
diff --git a/cmd/auth/gitauth/gitauth.go b/cmd/auth/gitauth/gitauth.go
index 7bfca6efb..6128889f0 100644
--- a/cmd/auth/gitauth/gitauth.go
+++ b/cmd/auth/gitauth/gitauth.go
@@ -7,7 +7,8 @@
// directory for the 'git' command as the first command-line argument.
//
// Example GOAUTH usage:
-// export GOAUTH="gitauth $HOME"
+//
+// export GOAUTH="gitauth $HOME"
//
// See https://git-scm.com/docs/gitcredentials or run 'man gitcredentials' for
// information on how to configure 'git credential'.
diff --git a/cmd/auth/netrcauth/netrcauth.go b/cmd/auth/netrcauth/netrcauth.go
index 1855cfa24..7d29c9603 100644
--- a/cmd/auth/netrcauth/netrcauth.go
+++ b/cmd/auth/netrcauth/netrcauth.go
@@ -7,7 +7,8 @@
// It expects the location of the file as the first command-line argument.
//
// Example GOAUTH usage:
-// export GOAUTH="netrcauth $HOME/.netrc"
+//
+// export GOAUTH="netrcauth $HOME/.netrc"
//
// See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html
// or run 'man 5 netrc' for a description of the .netrc file format.
diff --git a/cmd/benchcmp/compare.go b/cmd/benchcmp/compare.go
index c3f5e89c7..083aa4ddb 100644
--- a/cmd/benchcmp/compare.go
+++ b/cmd/benchcmp/compare.go
@@ -109,8 +109,8 @@ func (x ByParseOrder) Swap(i, j int) { x[i], x[j] = x[j], x[i] }
func (x ByParseOrder) Less(i, j int) bool { return x[i].Before.Ord < x[j].Before.Ord }
// lessByDelta provides lexicographic ordering:
-// * largest delta by magnitude
-// * alphabetic by name
+// - largest delta by magnitude
+// - alphabetic by name
func lessByDelta(i, j BenchCmp, calcDelta func(BenchCmp) Delta) bool {
iDelta, jDelta := calcDelta(i).mag(), calcDelta(j).mag()
if iDelta != jDelta {
diff --git a/cmd/benchcmp/doc.go b/cmd/benchcmp/doc.go
index cfe9801d8..97e8d8ace 100644
--- a/cmd/benchcmp/doc.go
+++ b/cmd/benchcmp/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Deprecated: benchcmp is deprecated in favor of benchstat: golang.org/x/perf/cmd/benchstat
The benchcmp command displays performance changes between benchmarks.
@@ -34,6 +33,5 @@ in a format like this:
benchmark old bytes new bytes delta
BenchmarkConcat 80 48 -40.00%
-
*/
package main // import "golang.org/x/tools/cmd/benchcmp"
diff --git a/cmd/bundle/main.go b/cmd/bundle/main.go
index fd8b0e5a9..194797bd8 100644
--- a/cmd/bundle/main.go
+++ b/cmd/bundle/main.go
@@ -21,8 +21,8 @@
//
// By default, bundle writes the bundled code to standard output.
// If the -o argument is given, bundle writes to the named file
-// and also includes a ``//go:generate'' comment giving the exact
-// command line used, for regenerating the file with ``go generate.''
+// and also includes a “//go:generate” comment giving the exact
+// command line used, for regenerating the file with “go generate.”
//
// Bundle customizes its output for inclusion in a particular package, the destination package.
// By default bundle assumes the destination is the package in the current directory,
@@ -47,7 +47,7 @@
// process. The -import option, which may be repeated, specifies that
// an import of "old" should be rewritten to import "new" instead.
//
-// Example
+// # Example
//
// Bundle archive/zip for inclusion in cmd/dist:
//
@@ -68,7 +68,6 @@
// Update all bundles in the standard library:
//
// go generate -run bundle std
-//
package main
import (
@@ -85,6 +84,7 @@ import (
"os"
"strconv"
"strings"
+ "unicode"
"golang.org/x/tools/go/packages"
)
@@ -234,7 +234,7 @@ func bundle(src, dst, dstpkg, prefix, buildTags string) ([]byte, error) {
fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n")
if *outputFile != "" && buildTags == "" {
- fmt.Fprintf(&out, "//go:generate bundle %s\n", strings.Join(os.Args[1:], " "))
+ fmt.Fprintf(&out, "//go:generate bundle %s\n", strings.Join(quoteArgs(os.Args[1:]), " "))
} else {
fmt.Fprintf(&out, "// $ bundle %s\n", strings.Join(os.Args[1:], " "))
}
@@ -448,6 +448,35 @@ func printSameLineComment(out *bytes.Buffer, comments []*ast.CommentGroup, fset
return pos
}
+func quoteArgs(ss []string) []string {
+ // From go help generate:
+ //
+ // > The arguments to the directive are space-separated tokens or
+ // > double-quoted strings passed to the generator as individual
+ // > arguments when it is run.
+ //
+ // > Quoted strings use Go syntax and are evaluated before execution; a
+ // > quoted string appears as a single argument to the generator.
+ //
+ var qs []string
+ for _, s := range ss {
+ if s == "" || containsSpace(s) {
+ s = strconv.Quote(s)
+ }
+ qs = append(qs, s)
+ }
+ return qs
+}
+
+func containsSpace(s string) bool {
+ for _, r := range s {
+ if unicode.IsSpace(r) {
+ return true
+ }
+ }
+ return false
+}
+
type flagFunc func(string)
func (f flagFunc) Set(s string) error {
diff --git a/cmd/callgraph/main.go b/cmd/callgraph/main.go
index f83be0ea5..eb8c0d116 100644
--- a/cmd/callgraph/main.go
+++ b/cmd/callgraph/main.go
@@ -37,6 +37,7 @@ import (
"golang.org/x/tools/go/callgraph/cha"
"golang.org/x/tools/go/callgraph/rta"
"golang.org/x/tools/go/callgraph/static"
+ "golang.org/x/tools/go/callgraph/vta"
"golang.org/x/tools/go/packages"
"golang.org/x/tools/go/pointer"
"golang.org/x/tools/go/ssa"
@@ -46,7 +47,7 @@ import (
// flags
var (
algoFlag = flag.String("algo", "rta",
- `Call graph construction algorithm (static, cha, rta, pta)`)
+ `Call graph construction algorithm (static, cha, rta, vta, pta)`)
testFlag = flag.Bool("test", false,
"Loads test code (*_test.go) for imported packages")
@@ -67,7 +68,7 @@ const Usage = `callgraph: display the call graph of a Go program.
Usage:
- callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] package...
+ callgraph [-algo=static|cha|rta|vta|pta] [-test] [-format=...] package...
Flags:
@@ -76,6 +77,7 @@ Flags:
static static calls only (unsound)
cha Class Hierarchy Analysis
rta Rapid Type Analysis
+ vta Variable Type Analysis
pta inclusion-based Points-To Analysis
The algorithms are ordered by increasing precision in their
@@ -187,7 +189,8 @@ func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) er
}
// Create and build SSA-form program representation.
- prog, pkgs := ssautil.AllPackages(initial, 0)
+ mode := ssa.InstantiateGenerics // instantiate generics by default for soundness
+ prog, pkgs := ssautil.AllPackages(initial, mode)
prog.Build()
// -- call graph construction ------------------------------------------
@@ -250,6 +253,9 @@ func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) er
// NB: RTA gives us Reachable and RuntimeTypes too.
+ case "vta":
+ cg = vta.CallGraph(ssautil.AllFunctions(prog), cha.CallGraph(prog))
+
default:
return fmt.Errorf("unknown algorithm: %s", algo)
}
diff --git a/cmd/callgraph/main_test.go b/cmd/callgraph/main_test.go
index 7e838f774..c8bee87e2 100644
--- a/cmd/callgraph/main_test.go
+++ b/cmd/callgraph/main_test.go
@@ -15,6 +15,7 @@ import (
"log"
"os"
"path/filepath"
+ "runtime"
"strings"
"testing"
@@ -34,8 +35,8 @@ func init() {
}
func TestCallgraph(t *testing.T) {
- if b := os.Getenv("GO_BUILDER_NAME"); b == "windows-arm64-10" {
- t.Skipf("skipping due to suspected file corruption bug on %s builder (https://go.dev/issue/50706)", b)
+ if runtime.GOOS == "windows" && runtime.GOARCH == "arm64" {
+ t.Skipf("skipping due to suspected file corruption bug on windows/arm64 (https://go.dev/issue/50706)")
}
testenv.NeedsTool(t, "go")
@@ -58,6 +59,12 @@ func TestCallgraph(t *testing.T) {
`pkg.main2 --> (pkg.C).f`,
`pkg.main2 --> (pkg.D).f`,
}},
+ {"vta", false, []string{
+ // vta distinguishes main->C, main2->D.
+ "pkg.main --> (pkg.C).f",
+ "pkg.main --> pkg.main2",
+ "pkg.main2 --> (pkg.D).f",
+ }},
{"pta", false, []string{
// pta distinguishes main->C, main2->D. Also has a root node.
`<root> --> pkg.init`,
@@ -74,6 +81,12 @@ func TestCallgraph(t *testing.T) {
`pkg.Example --> (pkg.C).f`,
`pkg.main --> (pkg.C).f`,
}},
+ {"vta", true, []string{
+ `pkg.test.main --> testing.MainStart`,
+ `testing.runExample --> pkg.Example`,
+ `pkg.Example --> (pkg.C).f`,
+ `pkg.main --> (pkg.C).f`,
+ }},
{"pta", true, []string{
`<root> --> pkg.test.main`,
`<root> --> pkg.main`,
@@ -94,13 +107,15 @@ func TestCallgraph(t *testing.T) {
for _, line := range strings.Split(fmt.Sprint(stdout), "\n") {
edges[line] = true
}
+ ok := true
for _, edge := range test.want {
if !edges[edge] {
+ ok = false
t.Errorf("callgraph(%q, %t): missing edge: %s",
test.algo, test.tests, edge)
}
}
- if t.Failed() {
+ if !ok {
t.Log("got:\n", stdout)
}
}
diff --git a/cmd/compilebench/main.go b/cmd/compilebench/main.go
index d7da6d51b..754acdca0 100644
--- a/cmd/compilebench/main.go
+++ b/cmd/compilebench/main.go
@@ -60,21 +60,20 @@
// today they write only the profile for the last benchmark executed.
//
// The default memory profiling rate is one profile sample per 512 kB
-// allocated (see ``go doc runtime.MemProfileRate'').
+// allocated (see “go doc runtime.MemProfileRate”).
// Lowering the rate (for example, -memprofilerate 64000) produces
// a more fine-grained and therefore accurate profile, but it also incurs
// execution cost. For benchmark comparisons, never use timings
// obtained with a low -memprofilerate option.
//
-// Example
+// # Example
//
// Assuming the base version of the compiler has been saved with
-// ``toolstash save,'' this sequence compares the old and new compiler:
+// “toolstash save,” this sequence compares the old and new compiler:
//
// compilebench -count 10 -compile $(toolstash -n compile) >old.txt
// compilebench -count 10 >new.txt
// benchstat old.txt new.txt
-//
package main
import (
@@ -82,23 +81,26 @@ import (
"encoding/json"
"flag"
"fmt"
- exec "golang.org/x/sys/execabs"
"io/ioutil"
"log"
"os"
"path/filepath"
"regexp"
+ "runtime"
"strconv"
"strings"
"time"
+
+ exec "golang.org/x/sys/execabs"
)
var (
- goroot string
- compiler string
- linker string
- runRE *regexp.Regexp
- is6g bool
+ goroot string
+ compiler string
+ assembler string
+ linker string
+ runRE *regexp.Regexp
+ is6g bool
)
var (
@@ -106,6 +108,7 @@ var (
flagAlloc = flag.Bool("alloc", false, "report allocations")
flagObj = flag.Bool("obj", false, "report object file stats")
flagCompiler = flag.String("compile", "", "use `exe` as the cmd/compile binary")
+ flagAssembler = flag.String("asm", "", "use `exe` as the cmd/asm binary")
flagCompilerFlags = flag.String("compileflags", "", "additional `flags` to pass to compile")
flagLinker = flag.String("link", "", "use `exe` as the cmd/link binary")
flagLinkerFlags = flag.String("linkflags", "", "additional `flags` to pass to link")
@@ -116,6 +119,7 @@ var (
flagMemprofilerate = flag.Int64("memprofilerate", -1, "set memory profile `rate`")
flagPackage = flag.String("pkg", "", "if set, benchmark the package at path `pkg`")
flagShort = flag.Bool("short", false, "skip long-running benchmarks")
+ flagTrace = flag.Bool("trace", false, "debug tracing of builds")
)
type test struct {
@@ -178,6 +182,10 @@ func main() {
is6g = true
}
}
+ assembler = *flagAssembler
+ if assembler == "" {
+ _, assembler = toolPath("asm")
+ }
linker = *flagLinker
if linker == "" && !is6g { // TODO: Support 6l
@@ -238,8 +246,10 @@ func toolPath(names ...string) (found, path string) {
}
type Pkg struct {
- Dir string
- GoFiles []string
+ ImportPath string
+ Dir string
+ GoFiles []string
+ SFiles []string
}
func goList(dir string) (*Pkg, error) {
@@ -325,10 +335,10 @@ type compile struct{ dir string }
func (compile) long() bool { return false }
func (c compile) run(name string, count int) error {
- // Make sure dependencies needed by go tool compile are installed to GOROOT/pkg.
- out, err := exec.Command(*flagGoCmd, "build", "-i", c.dir).CombinedOutput()
+ // Make sure dependencies needed by go tool compile are built.
+ out, err := exec.Command(*flagGoCmd, "build", c.dir).CombinedOutput()
if err != nil {
- return fmt.Errorf("go build -i %s: %v\n%s", c.dir, err, out)
+ return fmt.Errorf("go build %s: %v\n%s", c.dir, err, out)
}
// Find dir and source file list.
@@ -337,8 +347,39 @@ func (c compile) run(name string, count int) error {
return err
}
- args := []string{"-o", "_compilebench_.o"}
+ importcfg, err := genImportcfgFile(c.dir, false)
+ if err != nil {
+ return err
+ }
+
+ // If this package has assembly files, we'll need to pass a symabis
+ // file to the compiler; call a helper to invoke the assembler
+ // to do that.
+ var symAbisFile string
+ var asmIncFile string
+ if len(pkg.SFiles) != 0 {
+ symAbisFile = filepath.Join(pkg.Dir, "symabis")
+ asmIncFile = filepath.Join(pkg.Dir, "go_asm.h")
+ content := "\n"
+ if err := os.WriteFile(asmIncFile, []byte(content), 0666); err != nil {
+ return fmt.Errorf("os.WriteFile(%s) failed: %v", asmIncFile, err)
+ }
+ defer os.Remove(symAbisFile)
+ defer os.Remove(asmIncFile)
+ if err := genSymAbisFile(pkg, symAbisFile, pkg.Dir); err != nil {
+ return err
+ }
+ }
+
+ args := []string{"-o", "_compilebench_.o", "-p", pkg.ImportPath}
args = append(args, strings.Fields(*flagCompilerFlags)...)
+ if symAbisFile != "" {
+ args = append(args, "-symabis", symAbisFile)
+ }
+ if importcfg != "" {
+ args = append(args, "-importcfg", importcfg)
+ defer os.Remove(importcfg)
+ }
args = append(args, pkg.GoFiles...)
if err := runBuildCmd(name, count, pkg.Dir, compiler, args); err != nil {
return err
@@ -374,18 +415,28 @@ func (r link) run(name string, count int) error {
}
// Build dependencies.
- out, err := exec.Command(*flagGoCmd, "build", "-i", "-o", "/dev/null", r.dir).CombinedOutput()
+ out, err := exec.Command(*flagGoCmd, "build", "-o", "/dev/null", r.dir).CombinedOutput()
+ if err != nil {
+ return fmt.Errorf("go build -a %s: %v\n%s", r.dir, err, out)
+ }
+
+ importcfg, err := genImportcfgFile(r.dir, true)
if err != nil {
- return fmt.Errorf("go build -i %s: %v\n%s", r.dir, err, out)
+ return err
}
+ defer os.Remove(importcfg)
// Build the main package.
pkg, err := goList(r.dir)
if err != nil {
return err
}
- args := []string{"-o", "_compilebench_.o"}
+ args := []string{"-o", "_compilebench_.o", "-importcfg", importcfg}
args = append(args, pkg.GoFiles...)
+ if *flagTrace {
+ fmt.Fprintf(os.Stderr, "running: %s %+v\n",
+ compiler, args)
+ }
cmd := exec.Command(compiler, args...)
cmd.Dir = pkg.Dir
cmd.Stdout = os.Stderr
@@ -397,7 +448,7 @@ func (r link) run(name string, count int) error {
defer os.Remove(pkg.Dir + "/_compilebench_.o")
// Link the main package.
- args = []string{"-o", "_compilebench_.exe"}
+ args = []string{"-o", "_compilebench_.exe", "-importcfg", importcfg}
args = append(args, strings.Fields(*flagLinkerFlags)...)
args = append(args, strings.Fields(r.flags)...)
args = append(args, "_compilebench_.o")
@@ -429,6 +480,10 @@ func runBuildCmd(name string, count int, dir, tool string, args []string) error
preArgs = append(preArgs, "-cpuprofile", "_compilebench_.cpuprof")
}
}
+ if *flagTrace {
+ fmt.Fprintf(os.Stderr, "running: %s %+v\n",
+ tool, append(preArgs, args...))
+ }
cmd := exec.Command(tool, append(preArgs, args...)...)
cmd.Dir = dir
cmd.Stdout = os.Stderr
@@ -511,3 +566,80 @@ func runBuildCmd(name string, count int, dir, tool string, args []string) error
return nil
}
+
+// genSymAbisFile runs the assembler on the target packge asm files
+// with "-gensymabis" to produce a symabis file that will feed into
+// the Go source compilation. This is fairly hacky in that if the
+// asm invocation convenion changes it will need to be updated
+// (hopefully that will not be needed too frequently).
+func genSymAbisFile(pkg *Pkg, symAbisFile, incdir string) error {
+ args := []string{"-gensymabis", "-o", symAbisFile,
+ "-p", pkg.ImportPath,
+ "-I", filepath.Join(goroot, "pkg", "include"),
+ "-I", incdir,
+ "-D", "GOOS_" + runtime.GOOS,
+ "-D", "GOARCH_" + runtime.GOARCH}
+ if pkg.ImportPath == "reflect" {
+ args = append(args, "-compiling-runtime")
+ }
+ args = append(args, pkg.SFiles...)
+ if *flagTrace {
+ fmt.Fprintf(os.Stderr, "running: %s %+v\n",
+ assembler, args)
+ }
+ cmd := exec.Command(assembler, args...)
+ cmd.Dir = pkg.Dir
+ cmd.Stdout = os.Stderr
+ cmd.Stderr = os.Stderr
+ err := cmd.Run()
+ if err != nil {
+ return fmt.Errorf("assembling to produce symabis file: %v", err)
+ }
+ return nil
+}
+
+// genImportcfgFile generates an importcfg file for building package
+// dir. Returns the generated importcfg file path (or empty string
+// if the package has no dependency).
+func genImportcfgFile(dir string, full bool) (string, error) {
+ need := "{{.Imports}}"
+ if full {
+ // for linking, we need transitive dependencies
+ need = "{{.Deps}}"
+ }
+
+ // find imported/dependent packages
+ cmd := exec.Command(*flagGoCmd, "list", "-f", need, dir)
+ cmd.Stderr = os.Stderr
+ out, err := cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("go list -f %s %s: %v", need, dir, err)
+ }
+ // trim [ ]\n
+ if len(out) < 3 || out[0] != '[' || out[len(out)-2] != ']' || out[len(out)-1] != '\n' {
+ return "", fmt.Errorf("unexpected output from go list -f %s %s: %s", need, dir, out)
+ }
+ out = out[1 : len(out)-2]
+ if len(out) == 0 {
+ return "", nil
+ }
+
+ // build importcfg for imported packages
+ cmd = exec.Command(*flagGoCmd, "list", "-export", "-f", "{{if .Export}}packagefile {{.ImportPath}}={{.Export}}{{end}}")
+ cmd.Args = append(cmd.Args, strings.Fields(string(out))...)
+ cmd.Stderr = os.Stderr
+ out, err = cmd.Output()
+ if err != nil {
+ return "", fmt.Errorf("generating importcfg for %s: %s: %v", dir, cmd, err)
+ }
+
+ f, err := os.CreateTemp("", "importcfg")
+ if err != nil {
+ return "", fmt.Errorf("creating tmp importcfg file failed: %v", err)
+ }
+ defer f.Close()
+ if _, err := f.Write(out); err != nil {
+ return "", fmt.Errorf("writing importcfg file %s failed: %v", f.Name(), err)
+ }
+ return f.Name(), nil
+}
diff --git a/cmd/cover/README.md b/cmd/cover/README.md
deleted file mode 100644
index 62e60279a..000000000
--- a/cmd/cover/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# Deprecated
-
-NOTE: For Go releases 1.5 and later, this tool lives in the standard repository. The code here is not maintained.
diff --git a/cmd/cover/cover.go b/cmd/cover/cover.go
deleted file mode 100644
index 42a7e37aa..000000000
--- a/cmd/cover/cover.go
+++ /dev/null
@@ -1,722 +0,0 @@
-// Copyright 2013 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 main
-
-import (
- "bytes"
- "flag"
- "fmt"
- "go/ast"
- "go/parser"
- "go/printer"
- "go/token"
- "io"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "sort"
- "strconv"
- "strings"
-)
-
-const usageMessage = "" +
- `Usage of 'go tool cover':
-Given a coverage profile produced by 'go test':
- go test -coverprofile=c.out
-
-Open a web browser displaying annotated source code:
- go tool cover -html=c.out
-
-Write out an HTML file instead of launching a web browser:
- go tool cover -html=c.out -o coverage.html
-
-Display coverage percentages to stdout for each function:
- go tool cover -func=c.out
-
-Finally, to generate modified source code with coverage annotations
-(what go test -cover does):
- go tool cover -mode=set -var=CoverageVariableName program.go
-`
-
-func usage() {
- fmt.Fprint(os.Stderr, usageMessage)
- fmt.Fprintln(os.Stderr, "\nFlags:")
- flag.PrintDefaults()
- fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.")
- os.Exit(2)
-}
-
-var (
- mode = flag.String("mode", "", "coverage mode: set, count, atomic")
- varVar = flag.String("var", "GoCover", "name of coverage variable to generate")
- output = flag.String("o", "", "file for output; default: stdout")
- htmlOut = flag.String("html", "", "generate HTML representation of coverage profile")
- funcOut = flag.String("func", "", "output coverage profile information for each function")
-)
-
-var profile string // The profile to read; the value of -html or -func
-
-var counterStmt func(*File, ast.Expr) ast.Stmt
-
-const (
- atomicPackagePath = "sync/atomic"
- atomicPackageName = "_cover_atomic_"
-)
-
-func main() {
- flag.Usage = usage
- flag.Parse()
-
- // Usage information when no arguments.
- if flag.NFlag() == 0 && flag.NArg() == 0 {
- flag.Usage()
- }
-
- err := parseFlags()
- if err != nil {
- fmt.Fprintln(os.Stderr, err)
- fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`)
- os.Exit(2)
- }
-
- // Generate coverage-annotated source.
- if *mode != "" {
- annotate(flag.Arg(0))
- return
- }
-
- // Output HTML or function coverage information.
- if *htmlOut != "" {
- err = htmlOutput(profile, *output)
- } else {
- err = funcOutput(profile, *output)
- }
-
- if err != nil {
- fmt.Fprintf(os.Stderr, "cover: %v\n", err)
- os.Exit(2)
- }
-}
-
-// parseFlags sets the profile and counterStmt globals and performs validations.
-func parseFlags() error {
- profile = *htmlOut
- if *funcOut != "" {
- if profile != "" {
- return fmt.Errorf("too many options")
- }
- profile = *funcOut
- }
-
- // Must either display a profile or rewrite Go source.
- if (profile == "") == (*mode == "") {
- return fmt.Errorf("too many options")
- }
-
- if *mode != "" {
- switch *mode {
- case "set":
- counterStmt = setCounterStmt
- case "count":
- counterStmt = incCounterStmt
- case "atomic":
- counterStmt = atomicCounterStmt
- default:
- return fmt.Errorf("unknown -mode %v", *mode)
- }
-
- if flag.NArg() == 0 {
- return fmt.Errorf("missing source file")
- } else if flag.NArg() == 1 {
- return nil
- }
- } else if flag.NArg() == 0 {
- return nil
- }
- return fmt.Errorf("too many arguments")
-}
-
-// Block represents the information about a basic block to be recorded in the analysis.
-// Note: Our definition of basic block is based on control structures; we don't break
-// apart && and ||. We could but it doesn't seem important enough to bother.
-type Block struct {
- startByte token.Pos
- endByte token.Pos
- numStmt int
-}
-
-// File is a wrapper for the state of a file used in the parser.
-// The basic parse tree walker is a method of this type.
-type File struct {
- fset *token.FileSet
- name string // Name of file.
- astFile *ast.File
- blocks []Block
- atomicPkg string // Package name for "sync/atomic" in this file.
-}
-
-// Visit implements the ast.Visitor interface.
-func (f *File) Visit(node ast.Node) ast.Visitor {
- switch n := node.(type) {
- case *ast.BlockStmt:
- // If it's a switch or select, the body is a list of case clauses; don't tag the block itself.
- if len(n.List) > 0 {
- switch n.List[0].(type) {
- case *ast.CaseClause: // switch
- for _, n := range n.List {
- clause := n.(*ast.CaseClause)
- clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
- }
- return f
- case *ast.CommClause: // select
- for _, n := range n.List {
- clause := n.(*ast.CommClause)
- clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false)
- }
- return f
- }
- }
- n.List = f.addCounters(n.Lbrace, n.Rbrace+1, n.List, true) // +1 to step past closing brace.
- case *ast.IfStmt:
- ast.Walk(f, n.Body)
- if n.Else == nil {
- return nil
- }
- // The elses are special, because if we have
- // if x {
- // } else if y {
- // }
- // we want to cover the "if y". To do this, we need a place to drop the counter,
- // so we add a hidden block:
- // if x {
- // } else {
- // if y {
- // }
- // }
- switch stmt := n.Else.(type) {
- case *ast.IfStmt:
- block := &ast.BlockStmt{
- Lbrace: n.Body.End(), // Start at end of the "if" block so the covered part looks like it starts at the "else".
- List: []ast.Stmt{stmt},
- Rbrace: stmt.End(),
- }
- n.Else = block
- case *ast.BlockStmt:
- stmt.Lbrace = n.Body.End() // Start at end of the "if" block so the covered part looks like it starts at the "else".
- default:
- panic("unexpected node type in if")
- }
- ast.Walk(f, n.Else)
- return nil
- case *ast.SelectStmt:
- // Don't annotate an empty select - creates a syntax error.
- if n.Body == nil || len(n.Body.List) == 0 {
- return nil
- }
- case *ast.SwitchStmt:
- // Don't annotate an empty switch - creates a syntax error.
- if n.Body == nil || len(n.Body.List) == 0 {
- return nil
- }
- case *ast.TypeSwitchStmt:
- // Don't annotate an empty type switch - creates a syntax error.
- if n.Body == nil || len(n.Body.List) == 0 {
- return nil
- }
- }
- return f
-}
-
-// unquote returns the unquoted string.
-func unquote(s string) string {
- t, err := strconv.Unquote(s)
- if err != nil {
- log.Fatalf("cover: improperly quoted string %q\n", s)
- }
- return t
-}
-
-// addImport adds an import for the specified path, if one does not already exist, and returns
-// the local package name.
-func (f *File) addImport(path string) string {
- // Does the package already import it?
- for _, s := range f.astFile.Imports {
- if unquote(s.Path.Value) == path {
- if s.Name != nil {
- return s.Name.Name
- }
- return filepath.Base(path)
- }
- }
- newImport := &ast.ImportSpec{
- Name: ast.NewIdent(atomicPackageName),
- Path: &ast.BasicLit{
- Kind: token.STRING,
- Value: fmt.Sprintf("%q", path),
- },
- }
- impDecl := &ast.GenDecl{
- Tok: token.IMPORT,
- Specs: []ast.Spec{
- newImport,
- },
- }
- // Make the new import the first Decl in the file.
- astFile := f.astFile
- astFile.Decls = append(astFile.Decls, nil)
- copy(astFile.Decls[1:], astFile.Decls[0:])
- astFile.Decls[0] = impDecl
- astFile.Imports = append(astFile.Imports, newImport)
-
- // Now refer to the package, just in case it ends up unused.
- // That is, append to the end of the file the declaration
- // var _ = _cover_atomic_.AddUint32
- reference := &ast.GenDecl{
- Tok: token.VAR,
- Specs: []ast.Spec{
- &ast.ValueSpec{
- Names: []*ast.Ident{
- ast.NewIdent("_"),
- },
- Values: []ast.Expr{
- &ast.SelectorExpr{
- X: ast.NewIdent(atomicPackageName),
- Sel: ast.NewIdent("AddUint32"),
- },
- },
- },
- },
- }
- astFile.Decls = append(astFile.Decls, reference)
- return atomicPackageName
-}
-
-var slashslash = []byte("//")
-
-// initialComments returns the prefix of content containing only
-// whitespace and line comments. Any +build directives must appear
-// within this region. This approach is more reliable than using
-// go/printer to print a modified AST containing comments.
-//
-func initialComments(content []byte) []byte {
- // Derived from go/build.Context.shouldBuild.
- end := 0
- p := content
- for len(p) > 0 {
- line := p
- if i := bytes.IndexByte(line, '\n'); i >= 0 {
- line, p = line[:i], p[i+1:]
- } else {
- p = p[len(p):]
- }
- line = bytes.TrimSpace(line)
- if len(line) == 0 { // Blank line.
- end = len(content) - len(p)
- continue
- }
- if !bytes.HasPrefix(line, slashslash) { // Not comment line.
- break
- }
- }
- return content[:end]
-}
-
-func annotate(name string) {
- fset := token.NewFileSet()
- content, err := ioutil.ReadFile(name)
- if err != nil {
- log.Fatalf("cover: %s: %s", name, err)
- }
- parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments)
- if err != nil {
- log.Fatalf("cover: %s: %s", name, err)
- }
- parsedFile.Comments = trimComments(parsedFile, fset)
-
- file := &File{
- fset: fset,
- name: name,
- astFile: parsedFile,
- }
- if *mode == "atomic" {
- file.atomicPkg = file.addImport(atomicPackagePath)
- }
- ast.Walk(file, file.astFile)
- fd := os.Stdout
- if *output != "" {
- var err error
- fd, err = os.Create(*output)
- if err != nil {
- log.Fatalf("cover: %s", err)
- }
- }
- fd.Write(initialComments(content)) // Retain '// +build' directives.
- file.print(fd)
- // After printing the source tree, add some declarations for the counters etc.
- // We could do this by adding to the tree, but it's easier just to print the text.
- file.addVariables(fd)
-}
-
-// trimComments drops all but the //go: comments, some of which are semantically important.
-// We drop all others because they can appear in places that cause our counters
-// to appear in syntactically incorrect places. //go: appears at the beginning of
-// the line and is syntactically safe.
-func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup {
- var comments []*ast.CommentGroup
- for _, group := range file.Comments {
- var list []*ast.Comment
- for _, comment := range group.List {
- if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 {
- list = append(list, comment)
- }
- }
- if list != nil {
- comments = append(comments, &ast.CommentGroup{List: list})
- }
- }
- return comments
-}
-
-func (f *File) print(w io.Writer) {
- printer.Fprint(w, f.fset, f.astFile)
-}
-
-// intLiteral returns an ast.BasicLit representing the integer value.
-func (f *File) intLiteral(i int) *ast.BasicLit {
- node := &ast.BasicLit{
- Kind: token.INT,
- Value: fmt.Sprint(i),
- }
- return node
-}
-
-// index returns an ast.BasicLit representing the number of counters present.
-func (f *File) index() *ast.BasicLit {
- return f.intLiteral(len(f.blocks))
-}
-
-// setCounterStmt returns the expression: __count[23] = 1.
-func setCounterStmt(f *File, counter ast.Expr) ast.Stmt {
- return &ast.AssignStmt{
- Lhs: []ast.Expr{counter},
- Tok: token.ASSIGN,
- Rhs: []ast.Expr{f.intLiteral(1)},
- }
-}
-
-// incCounterStmt returns the expression: __count[23]++.
-func incCounterStmt(f *File, counter ast.Expr) ast.Stmt {
- return &ast.IncDecStmt{
- X: counter,
- Tok: token.INC,
- }
-}
-
-// atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1)
-func atomicCounterStmt(f *File, counter ast.Expr) ast.Stmt {
- return &ast.ExprStmt{
- X: &ast.CallExpr{
- Fun: &ast.SelectorExpr{
- X: ast.NewIdent(f.atomicPkg),
- Sel: ast.NewIdent("AddUint32"),
- },
- Args: []ast.Expr{&ast.UnaryExpr{
- Op: token.AND,
- X: counter,
- },
- f.intLiteral(1),
- },
- },
- }
-}
-
-// newCounter creates a new counter expression of the appropriate form.
-func (f *File) newCounter(start, end token.Pos, numStmt int) ast.Stmt {
- counter := &ast.IndexExpr{
- X: &ast.SelectorExpr{
- X: ast.NewIdent(*varVar),
- Sel: ast.NewIdent("Count"),
- },
- Index: f.index(),
- }
- stmt := counterStmt(f, counter)
- f.blocks = append(f.blocks, Block{start, end, numStmt})
- return stmt
-}
-
-// addCounters takes a list of statements and adds counters to the beginning of
-// each basic block at the top level of that list. For instance, given
-//
-// S1
-// if cond {
-// S2
-// }
-// S3
-//
-// counters will be added before S1 and before S3. The block containing S2
-// will be visited in a separate call.
-// TODO: Nested simple blocks get unnecessary (but correct) counters
-func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) []ast.Stmt {
- // Special case: make sure we add a counter to an empty block. Can't do this below
- // or we will add a counter to an empty statement list after, say, a return statement.
- if len(list) == 0 {
- return []ast.Stmt{f.newCounter(pos, blockEnd, 0)}
- }
- // We have a block (statement list), but it may have several basic blocks due to the
- // appearance of statements that affect the flow of control.
- var newList []ast.Stmt
- for {
- // Find first statement that affects flow of control (break, continue, if, etc.).
- // It will be the last statement of this basic block.
- var last int
- end := blockEnd
- for last = 0; last < len(list); last++ {
- end = f.statementBoundary(list[last])
- if f.endsBasicSourceBlock(list[last]) {
- extendToClosingBrace = false // Block is broken up now.
- last++
- break
- }
- }
- if extendToClosingBrace {
- end = blockEnd
- }
- if pos != end { // Can have no source to cover if e.g. blocks abut.
- newList = append(newList, f.newCounter(pos, end, last))
- }
- newList = append(newList, list[0:last]...)
- list = list[last:]
- if len(list) == 0 {
- break
- }
- pos = list[0].Pos()
- }
- return newList
-}
-
-// hasFuncLiteral reports the existence and position of the first func literal
-// in the node, if any. If a func literal appears, it usually marks the termination
-// of a basic block because the function body is itself a block.
-// Therefore we draw a line at the start of the body of the first function literal we find.
-// TODO: what if there's more than one? Probably doesn't matter much.
-func hasFuncLiteral(n ast.Node) (bool, token.Pos) {
- if n == nil {
- return false, 0
- }
- var literal funcLitFinder
- ast.Walk(&literal, n)
- return literal.found(), token.Pos(literal)
-}
-
-// statementBoundary finds the location in s that terminates the current basic
-// block in the source.
-func (f *File) statementBoundary(s ast.Stmt) token.Pos {
- // Control flow statements are easy.
- switch s := s.(type) {
- case *ast.BlockStmt:
- // Treat blocks like basic blocks to avoid overlapping counters.
- return s.Lbrace
- case *ast.IfStmt:
- found, pos := hasFuncLiteral(s.Init)
- if found {
- return pos
- }
- found, pos = hasFuncLiteral(s.Cond)
- if found {
- return pos
- }
- return s.Body.Lbrace
- case *ast.ForStmt:
- found, pos := hasFuncLiteral(s.Init)
- if found {
- return pos
- }
- found, pos = hasFuncLiteral(s.Cond)
- if found {
- return pos
- }
- found, pos = hasFuncLiteral(s.Post)
- if found {
- return pos
- }
- return s.Body.Lbrace
- case *ast.LabeledStmt:
- return f.statementBoundary(s.Stmt)
- case *ast.RangeStmt:
- found, pos := hasFuncLiteral(s.X)
- if found {
- return pos
- }
- return s.Body.Lbrace
- case *ast.SwitchStmt:
- found, pos := hasFuncLiteral(s.Init)
- if found {
- return pos
- }
- found, pos = hasFuncLiteral(s.Tag)
- if found {
- return pos
- }
- return s.Body.Lbrace
- case *ast.SelectStmt:
- return s.Body.Lbrace
- case *ast.TypeSwitchStmt:
- found, pos := hasFuncLiteral(s.Init)
- if found {
- return pos
- }
- return s.Body.Lbrace
- }
- // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal.
- // If it does, that's tricky because we want to exclude the body of the function from this block.
- // Draw a line at the start of the body of the first function literal we find.
- // TODO: what if there's more than one? Probably doesn't matter much.
- found, pos := hasFuncLiteral(s)
- if found {
- return pos
- }
- return s.End()
-}
-
-// endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc.,
-// or if it's just problematic, for instance contains a function literal, which will complicate
-// accounting due to the block-within-an expression.
-func (f *File) endsBasicSourceBlock(s ast.Stmt) bool {
- switch s := s.(type) {
- case *ast.BlockStmt:
- // Treat blocks like basic blocks to avoid overlapping counters.
- return true
- case *ast.BranchStmt:
- return true
- case *ast.ForStmt:
- return true
- case *ast.IfStmt:
- return true
- case *ast.LabeledStmt:
- return f.endsBasicSourceBlock(s.Stmt)
- case *ast.RangeStmt:
- return true
- case *ast.SwitchStmt:
- return true
- case *ast.SelectStmt:
- return true
- case *ast.TypeSwitchStmt:
- return true
- case *ast.ExprStmt:
- // Calls to panic change the flow.
- // We really should verify that "panic" is the predefined function,
- // but without type checking we can't and the likelihood of it being
- // an actual problem is vanishingly small.
- if call, ok := s.X.(*ast.CallExpr); ok {
- if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 {
- return true
- }
- }
- }
- found, _ := hasFuncLiteral(s)
- return found
-}
-
-// funcLitFinder implements the ast.Visitor pattern to find the location of any
-// function literal in a subtree.
-type funcLitFinder token.Pos
-
-func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) {
- if f.found() {
- return nil // Prune search.
- }
- switch n := node.(type) {
- case *ast.FuncLit:
- *f = funcLitFinder(n.Body.Lbrace)
- return nil // Prune search.
- }
- return f
-}
-
-func (f *funcLitFinder) found() bool {
- return token.Pos(*f) != token.NoPos
-}
-
-// Sort interface for []block1; used for self-check in addVariables.
-
-type block1 struct {
- Block
- index int
-}
-
-type blockSlice []block1
-
-func (b blockSlice) Len() int { return len(b) }
-func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte }
-func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] }
-
-// offset translates a token position into a 0-indexed byte offset.
-func (f *File) offset(pos token.Pos) int {
- return f.fset.Position(pos).Offset
-}
-
-// addVariables adds to the end of the file the declarations to set up the counter and position variables.
-func (f *File) addVariables(w io.Writer) {
- // Self-check: Verify that the instrumented basic blocks are disjoint.
- t := make([]block1, len(f.blocks))
- for i := range f.blocks {
- t[i].Block = f.blocks[i]
- t[i].index = i
- }
- sort.Sort(blockSlice(t))
- for i := 1; i < len(t); i++ {
- if t[i-1].endByte > t[i].startByte {
- fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index)
- // Note: error message is in byte positions, not token positions.
- fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n",
- f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte),
- f.name, f.offset(t[i].startByte), f.offset(t[i].endByte))
- }
- }
-
- // Declare the coverage struct as a package-level variable.
- fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar)
- fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks))
- fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks))
- fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks))
- fmt.Fprintf(w, "} {\n")
-
- // Initialize the position array field.
- fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks))
-
- // A nice long list of positions. Each position is encoded as follows to reduce size:
- // - 32-bit starting line number
- // - 32-bit ending line number
- // - (16 bit ending column number << 16) | (16-bit starting column number).
- for i, block := range f.blocks {
- start := f.fset.Position(block.startByte)
- end := f.fset.Position(block.endByte)
- fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i)
- }
-
- // Close the position array.
- fmt.Fprintf(w, "\t},\n")
-
- // Initialize the position array field.
- fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks))
-
- // A nice long list of statements-per-block, so we can give a conventional
- // valuation of "percent covered". To save space, it's a 16-bit number, so we
- // clamp it if it overflows - won't matter in practice.
- for i, block := range f.blocks {
- n := block.numStmt
- if n > 1<<16-1 {
- n = 1<<16 - 1
- }
- fmt.Fprintf(w, "\t\t%d, // %d\n", n, i)
- }
-
- // Close the statements-per-block array.
- fmt.Fprintf(w, "\t},\n")
-
- // Close the struct initialization.
- fmt.Fprintf(w, "}\n")
-}
diff --git a/cmd/cover/cover_test.go b/cmd/cover/cover_test.go
deleted file mode 100644
index 228c91144..000000000
--- a/cmd/cover/cover_test.go
+++ /dev/null
@@ -1,108 +0,0 @@
-// Copyright 2013 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.
-
-// No testdata on Android.
-
-//go:build !android
-// +build !android
-
-package main_test
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "os"
- "os/exec"
- "path/filepath"
- "testing"
-
- "golang.org/x/tools/internal/testenv"
-)
-
-const (
- // Data directory, also the package directory for the test.
- testdata = "testdata"
-)
-
-var debug = false // Keeps the rewritten files around if set.
-
-// Run this shell script, but do it in Go so it can be run by "go test".
-//
-// replace the word LINE with the line number < testdata/test.go > testdata/test_line.go
-// go build -o ./testcover
-// ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go
-// go run ./testdata/main.go ./testdata/test.go
-//
-func TestCover(t *testing.T) {
- testenv.NeedsTool(t, "go")
-
- tmpdir, err := ioutil.TempDir("", "TestCover")
- if err != nil {
- t.Fatal(err)
- }
- defer func() {
- if debug {
- fmt.Printf("test files left in %s\n", tmpdir)
- } else {
- os.RemoveAll(tmpdir)
- }
- }()
-
- testcover := filepath.Join(tmpdir, "testcover.exe")
- testMain := filepath.Join(tmpdir, "main.go")
- testTest := filepath.Join(tmpdir, "test.go")
- coverInput := filepath.Join(tmpdir, "test_line.go")
- coverOutput := filepath.Join(tmpdir, "test_cover.go")
-
- for _, f := range []string{testMain, testTest} {
- data, err := ioutil.ReadFile(filepath.Join(testdata, filepath.Base(f)))
- if err != nil {
- t.Fatal(err)
- }
- if err := ioutil.WriteFile(f, data, 0644); err != nil {
- t.Fatal(err)
- }
- }
-
- // Read in the test file (testTest) and write it, with LINEs specified, to coverInput.
- file, err := ioutil.ReadFile(testTest)
- if err != nil {
- t.Fatal(err)
- }
- lines := bytes.Split(file, []byte("\n"))
- for i, line := range lines {
- lines[i] = bytes.Replace(line, []byte("LINE"), []byte(fmt.Sprint(i+1)), -1)
- }
- err = ioutil.WriteFile(coverInput, bytes.Join(lines, []byte("\n")), 0666)
- if err != nil {
- t.Fatal(err)
- }
-
- // go build -o testcover
- cmd := exec.Command("go", "build", "-o", testcover)
- run(cmd, t)
-
- // ./testcover -mode=count -var=coverTest -o ./testdata/test_cover.go testdata/test_line.go
- cmd = exec.Command(testcover, "-mode=count", "-var=coverTest", "-o", coverOutput, coverInput)
- run(cmd, t)
-
- // defer removal of ./testdata/test_cover.go
- if !debug {
- defer os.Remove(coverOutput)
- }
-
- // go run ./testdata/main.go ./testdata/test.go
- cmd = exec.Command("go", "run", testMain, coverOutput)
- run(cmd, t)
-}
-
-func run(c *exec.Cmd, t *testing.T) {
- c.Stdout = os.Stdout
- c.Stderr = os.Stderr
- err := c.Run()
- if err != nil {
- t.Fatal(err)
- }
-}
diff --git a/cmd/cover/doc.go b/cmd/cover/doc.go
deleted file mode 100644
index f903d8508..000000000
--- a/cmd/cover/doc.go
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2013 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.
-
-/*
-Cover is a program for analyzing the coverage profiles generated by
-'go test -coverprofile=cover.out'.
-
-Deprecated: For Go releases 1.5 and later, this tool lives in the
-standard repository. The code here is not maintained.
-
-Cover is also used by 'go test -cover' to rewrite the source code with
-annotations to track which parts of each function are executed.
-It operates on one Go source file at a time, computing approximate
-basic block information by studying the source. It is thus more portable
-than binary-rewriting coverage tools, but also a little less capable.
-For instance, it does not probe inside && and || expressions, and can
-be mildly confused by single statements with multiple function literals.
-
-For usage information, please see:
- go help testflag
- go tool cover -help
-*/
-package main // import "golang.org/x/tools/cmd/cover"
diff --git a/cmd/cover/func.go b/cmd/cover/func.go
deleted file mode 100644
index 41d9fceca..000000000
--- a/cmd/cover/func.go
+++ /dev/null
@@ -1,166 +0,0 @@
-// Copyright 2013 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.
-
-// This file implements the visitor that computes the (line, column)-(line-column) range for each function.
-
-package main
-
-import (
- "bufio"
- "fmt"
- "go/ast"
- "go/build"
- "go/parser"
- "go/token"
- "os"
- "path/filepath"
- "text/tabwriter"
-
- "golang.org/x/tools/cover"
-)
-
-// funcOutput takes two file names as arguments, a coverage profile to read as input and an output
-// file to write ("" means to write to standard output). The function reads the profile and produces
-// as output the coverage data broken down by function, like this:
-//
-// fmt/format.go:30: init 100.0%
-// fmt/format.go:57: clearflags 100.0%
-// ...
-// fmt/scan.go:1046: doScan 100.0%
-// fmt/scan.go:1075: advance 96.2%
-// fmt/scan.go:1119: doScanf 96.8%
-// total: (statements) 91.9%
-
-func funcOutput(profile, outputFile string) error {
- profiles, err := cover.ParseProfiles(profile)
- if err != nil {
- return err
- }
-
- var out *bufio.Writer
- if outputFile == "" {
- out = bufio.NewWriter(os.Stdout)
- } else {
- fd, err := os.Create(outputFile)
- if err != nil {
- return err
- }
- defer fd.Close()
- out = bufio.NewWriter(fd)
- }
- defer out.Flush()
-
- tabber := tabwriter.NewWriter(out, 1, 8, 1, '\t', 0)
- defer tabber.Flush()
-
- var total, covered int64
- for _, profile := range profiles {
- fn := profile.FileName
- file, err := findFile(fn)
- if err != nil {
- return err
- }
- funcs, err := findFuncs(file)
- if err != nil {
- return err
- }
- // Now match up functions and profile blocks.
- for _, f := range funcs {
- c, t := f.coverage(profile)
- fmt.Fprintf(tabber, "%s:%d:\t%s\t%.1f%%\n", fn, f.startLine, f.name, 100.0*float64(c)/float64(t))
- total += t
- covered += c
- }
- }
- fmt.Fprintf(tabber, "total:\t(statements)\t%.1f%%\n", 100.0*float64(covered)/float64(total))
-
- return nil
-}
-
-// findFuncs parses the file and returns a slice of FuncExtent descriptors.
-func findFuncs(name string) ([]*FuncExtent, error) {
- fset := token.NewFileSet()
- parsedFile, err := parser.ParseFile(fset, name, nil, 0)
- if err != nil {
- return nil, err
- }
- visitor := &FuncVisitor{
- fset: fset,
- name: name,
- astFile: parsedFile,
- }
- ast.Walk(visitor, visitor.astFile)
- return visitor.funcs, nil
-}
-
-// FuncExtent describes a function's extent in the source by file and position.
-type FuncExtent struct {
- name string
- startLine int
- startCol int
- endLine int
- endCol int
-}
-
-// FuncVisitor implements the visitor that builds the function position list for a file.
-type FuncVisitor struct {
- fset *token.FileSet
- name string // Name of file.
- astFile *ast.File
- funcs []*FuncExtent
-}
-
-// Visit implements the ast.Visitor interface.
-func (v *FuncVisitor) Visit(node ast.Node) ast.Visitor {
- switch n := node.(type) {
- case *ast.FuncDecl:
- start := v.fset.Position(n.Pos())
- end := v.fset.Position(n.End())
- fe := &FuncExtent{
- name: n.Name.Name,
- startLine: start.Line,
- startCol: start.Column,
- endLine: end.Line,
- endCol: end.Column,
- }
- v.funcs = append(v.funcs, fe)
- }
- return v
-}
-
-// coverage returns the fraction of the statements in the function that were covered, as a numerator and denominator.
-func (f *FuncExtent) coverage(profile *cover.Profile) (num, den int64) {
- // We could avoid making this n^2 overall by doing a single scan and annotating the functions,
- // but the sizes of the data structures is never very large and the scan is almost instantaneous.
- var covered, total int64
- // The blocks are sorted, so we can stop counting as soon as we reach the end of the relevant block.
- for _, b := range profile.Blocks {
- if b.StartLine > f.endLine || (b.StartLine == f.endLine && b.StartCol >= f.endCol) {
- // Past the end of the function.
- break
- }
- if b.EndLine < f.startLine || (b.EndLine == f.startLine && b.EndCol <= f.startCol) {
- // Before the beginning of the function
- continue
- }
- total += int64(b.NumStmt)
- if b.Count > 0 {
- covered += int64(b.NumStmt)
- }
- }
- if total == 0 {
- total = 1 // Avoid zero denominator.
- }
- return covered, total
-}
-
-// findFile finds the location of the named file in GOROOT, GOPATH etc.
-func findFile(file string) (string, error) {
- dir, file := filepath.Split(file)
- pkg, err := build.Import(dir, ".", build.FindOnly)
- if err != nil {
- return "", fmt.Errorf("can't find %q: %v", file, err)
- }
- return filepath.Join(pkg.Dir, file), nil
-}
diff --git a/cmd/cover/html.go b/cmd/cover/html.go
deleted file mode 100644
index 0f8c72542..000000000
--- a/cmd/cover/html.go
+++ /dev/null
@@ -1,284 +0,0 @@
-// Copyright 2013 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 main
-
-import (
- "bufio"
- "bytes"
- "fmt"
- exec "golang.org/x/sys/execabs"
- "html/template"
- "io"
- "io/ioutil"
- "math"
- "os"
- "path/filepath"
- "runtime"
-
- "golang.org/x/tools/cover"
-)
-
-// htmlOutput reads the profile data from profile and generates an HTML
-// coverage report, writing it to outfile. If outfile is empty,
-// it writes the report to a temporary file and opens it in a web browser.
-func htmlOutput(profile, outfile string) error {
- profiles, err := cover.ParseProfiles(profile)
- if err != nil {
- return err
- }
-
- var d templateData
-
- for _, profile := range profiles {
- fn := profile.FileName
- if profile.Mode == "set" {
- d.Set = true
- }
- file, err := findFile(fn)
- if err != nil {
- return err
- }
- src, err := ioutil.ReadFile(file)
- if err != nil {
- return fmt.Errorf("can't read %q: %v", fn, err)
- }
- var buf bytes.Buffer
- err = htmlGen(&buf, src, profile.Boundaries(src))
- if err != nil {
- return err
- }
- d.Files = append(d.Files, &templateFile{
- Name: fn,
- Body: template.HTML(buf.String()),
- Coverage: percentCovered(profile),
- })
- }
-
- var out *os.File
- if outfile == "" {
- var dir string
- dir, err = ioutil.TempDir("", "cover")
- if err != nil {
- return err
- }
- out, err = os.Create(filepath.Join(dir, "coverage.html"))
- } else {
- out, err = os.Create(outfile)
- }
- if err != nil {
- return err
- }
- err = htmlTemplate.Execute(out, d)
- if err == nil {
- err = out.Close()
- }
- if err != nil {
- return err
- }
-
- if outfile == "" {
- if !startBrowser("file://" + out.Name()) {
- fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name())
- }
- }
-
- return nil
-}
-
-// percentCovered returns, as a percentage, the fraction of the statements in
-// the profile covered by the test run.
-// In effect, it reports the coverage of a given source file.
-func percentCovered(p *cover.Profile) float64 {
- var total, covered int64
- for _, b := range p.Blocks {
- total += int64(b.NumStmt)
- if b.Count > 0 {
- covered += int64(b.NumStmt)
- }
- }
- if total == 0 {
- return 0
- }
- return float64(covered) / float64(total) * 100
-}
-
-// htmlGen generates an HTML coverage report with the provided filename,
-// source code, and tokens, and writes it to the given Writer.
-func htmlGen(w io.Writer, src []byte, boundaries []cover.Boundary) error {
- dst := bufio.NewWriter(w)
- for i := range src {
- for len(boundaries) > 0 && boundaries[0].Offset == i {
- b := boundaries[0]
- if b.Start {
- n := 0
- if b.Count > 0 {
- n = int(math.Floor(b.Norm*9)) + 1
- }
- fmt.Fprintf(dst, `<span class="cov%v" title="%v">`, n, b.Count)
- } else {
- dst.WriteString("</span>")
- }
- boundaries = boundaries[1:]
- }
- switch b := src[i]; b {
- case '>':
- dst.WriteString("&gt;")
- case '<':
- dst.WriteString("&lt;")
- case '&':
- dst.WriteString("&amp;")
- case '\t':
- dst.WriteString(" ")
- default:
- dst.WriteByte(b)
- }
- }
- return dst.Flush()
-}
-
-// startBrowser tries to open the URL in a browser
-// and reports whether it succeeds.
-func startBrowser(url string) bool {
- // try to start the browser
- var args []string
- switch runtime.GOOS {
- case "darwin":
- args = []string{"open"}
- case "windows":
- args = []string{"cmd", "/c", "start"}
- default:
- args = []string{"xdg-open"}
- }
- cmd := exec.Command(args[0], append(args[1:], url)...)
- return cmd.Start() == nil
-}
-
-// rgb returns an rgb value for the specified coverage value
-// between 0 (no coverage) and 10 (max coverage).
-func rgb(n int) string {
- if n == 0 {
- return "rgb(192, 0, 0)" // Red
- }
- // Gradient from gray to green.
- r := 128 - 12*(n-1)
- g := 128 + 12*(n-1)
- b := 128 + 3*(n-1)
- return fmt.Sprintf("rgb(%v, %v, %v)", r, g, b)
-}
-
-// colors generates the CSS rules for coverage colors.
-func colors() template.CSS {
- var buf bytes.Buffer
- for i := 0; i < 11; i++ {
- fmt.Fprintf(&buf, ".cov%v { color: %v }\n", i, rgb(i))
- }
- return template.CSS(buf.String())
-}
-
-var htmlTemplate = template.Must(template.New("html").Funcs(template.FuncMap{
- "colors": colors,
-}).Parse(tmplHTML))
-
-type templateData struct {
- Files []*templateFile
- Set bool
-}
-
-type templateFile struct {
- Name string
- Body template.HTML
- Coverage float64
-}
-
-const tmplHTML = `
-<!DOCTYPE html>
-<html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <style>
- body {
- background: black;
- color: rgb(80, 80, 80);
- }
- body, pre, #legend span {
- font-family: Menlo, monospace;
- font-weight: bold;
- }
- #topbar {
- background: black;
- position: fixed;
- top: 0; left: 0; right: 0;
- height: 42px;
- border-bottom: 1px solid rgb(80, 80, 80);
- }
- #content {
- margin-top: 50px;
- }
- #nav, #legend {
- float: left;
- margin-left: 10px;
- }
- #legend {
- margin-top: 12px;
- }
- #nav {
- margin-top: 10px;
- }
- #legend span {
- margin: 0 5px;
- }
- {{colors}}
- </style>
- </head>
- <body>
- <div id="topbar">
- <div id="nav">
- <select id="files">
- {{range $i, $f := .Files}}
- <option value="file{{$i}}">{{$f.Name}} ({{printf "%.1f" $f.Coverage}}%)</option>
- {{end}}
- </select>
- </div>
- <div id="legend">
- <span>not tracked</span>
- {{if .Set}}
- <span class="cov0">not covered</span>
- <span class="cov8">covered</span>
- {{else}}
- <span class="cov0">no coverage</span>
- <span class="cov1">low coverage</span>
- <span class="cov2">*</span>
- <span class="cov3">*</span>
- <span class="cov4">*</span>
- <span class="cov5">*</span>
- <span class="cov6">*</span>
- <span class="cov7">*</span>
- <span class="cov8">*</span>
- <span class="cov9">*</span>
- <span class="cov10">high coverage</span>
- {{end}}
- </div>
- </div>
- <div id="content">
- {{range $i, $f := .Files}}
- <pre class="file" id="file{{$i}}" {{if $i}}style="display: none"{{end}}>{{$f.Body}}</pre>
- {{end}}
- </div>
- </body>
- <script>
- (function() {
- var files = document.getElementById('files');
- var visible = document.getElementById('file0');
- files.addEventListener('change', onChange, false);
- function onChange() {
- visible.style.display = 'none';
- visible = document.getElementById(files.value);
- visible.style.display = 'block';
- window.scrollTo(0, 0);
- }
- })();
- </script>
-</html>
-`
diff --git a/cmd/cover/testdata/main.go b/cmd/cover/testdata/main.go
deleted file mode 100644
index 6ed39c4f2..000000000
--- a/cmd/cover/testdata/main.go
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2013 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.
-
-// Test runner for coverage test. This file is not coverage-annotated; test.go is.
-// It knows the coverage counter is called "coverTest".
-
-package main
-
-import (
- "fmt"
- "os"
-)
-
-func main() {
- testAll()
- verify()
-}
-
-type block struct {
- count uint32
- line uint32
-}
-
-var counters = make(map[block]bool)
-
-// check records the location and expected value for a counter.
-func check(line, count uint32) {
- b := block{
- count,
- line,
- }
- counters[b] = true
-}
-
-// checkVal is a version of check that returns its extra argument,
-// so it can be used in conditionals.
-func checkVal(line, count uint32, val int) int {
- b := block{
- count,
- line,
- }
- counters[b] = true
- return val
-}
-
-var PASS = true
-
-// verify checks the expected counts against the actual. It runs after the test has completed.
-func verify() {
- for b := range counters {
- got, index := count(b.line)
- if b.count == anything && got != 0 {
- got = anything
- }
- if got != b.count {
- fmt.Fprintf(os.Stderr, "test_go:%d expected count %d got %d [counter %d]\n", b.line, b.count, got, index)
- PASS = false
- }
- }
- verifyPanic()
- if !PASS {
- fmt.Fprintf(os.Stderr, "FAIL\n")
- os.Exit(2)
- }
-}
-
-// verifyPanic is a special check for the known counter that should be
-// after the panic call in testPanic.
-func verifyPanic() {
- if coverTest.Count[panicIndex-1] != 1 {
- // Sanity check for test before panic.
- fmt.Fprintf(os.Stderr, "bad before panic")
- PASS = false
- }
- if coverTest.Count[panicIndex] != 0 {
- fmt.Fprintf(os.Stderr, "bad at panic: %d should be 0\n", coverTest.Count[panicIndex])
- PASS = false
- }
- if coverTest.Count[panicIndex+1] != 1 {
- fmt.Fprintf(os.Stderr, "bad after panic")
- PASS = false
- }
-}
-
-// count returns the count and index for the counter at the specified line.
-func count(line uint32) (uint32, int) {
- // Linear search is fine. Choose perfect fit over approximate.
- // We can have a closing brace for a range on the same line as a condition for an "else if"
- // and we don't want that brace to steal the count for the condition on the "if".
- // Therefore we test for a perfect (lo==line && hi==line) match, but if we can't
- // find that we take the first imperfect match.
- index := -1
- indexLo := uint32(1e9)
- for i := range coverTest.Count {
- lo, hi := coverTest.Pos[3*i], coverTest.Pos[3*i+1]
- if lo == line && line == hi {
- return coverTest.Count[i], i
- }
- // Choose the earliest match (the counters are in unpredictable order).
- if lo <= line && line <= hi && indexLo > lo {
- index = i
- indexLo = lo
- }
- }
- if index == -1 {
- fmt.Fprintln(os.Stderr, "cover_test: no counter for line", line)
- PASS = false
- return 0, 0
- }
- return coverTest.Count[index], index
-}
diff --git a/cmd/cover/testdata/test.go b/cmd/cover/testdata/test.go
deleted file mode 100644
index 9013950a2..000000000
--- a/cmd/cover/testdata/test.go
+++ /dev/null
@@ -1,218 +0,0 @@
-// Copyright 2013 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.
-
-// This program is processed by the cover command, and then testAll is called.
-// The test driver in main.go can then compare the coverage statistics with expectation.
-
-// The word LINE is replaced by the line number in this file. When the file is executed,
-// the coverage processing has changed the line numbers, so we can't use runtime.Caller.
-
-package main
-
-const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often"
-
-func testAll() {
- testSimple()
- testBlockRun()
- testIf()
- testFor()
- testRange()
- testSwitch()
- testTypeSwitch()
- testSelect1()
- testSelect2()
- testPanic()
- testEmptySwitches()
-}
-
-// The indexes of the counters in testPanic are known to main.go
-const panicIndex = 3
-
-// This test appears first because the index of its counters is known to main.go
-func testPanic() {
- defer func() {
- recover()
- }()
- check(LINE, 1)
- panic("should not get next line")
- check(LINE, 0) // this is GoCover.Count[panicIndex]
- // The next counter is in testSimple and it will be non-zero.
- // If the panic above does not trigger a counter, the test will fail
- // because GoCover.Count[panicIndex] will be the one in testSimple.
-}
-
-func testSimple() {
- check(LINE, 1)
-}
-
-func testIf() {
- if true {
- check(LINE, 1)
- } else {
- check(LINE, 0)
- }
- if false {
- check(LINE, 0)
- } else {
- check(LINE, 1)
- }
- for i := 0; i < 3; i++ {
- if checkVal(LINE, 3, i) <= 2 {
- check(LINE, 3)
- }
- if checkVal(LINE, 3, i) <= 1 {
- check(LINE, 2)
- }
- if checkVal(LINE, 3, i) <= 0 {
- check(LINE, 1)
- }
- }
- for i := 0; i < 3; i++ {
- if checkVal(LINE, 3, i) <= 1 {
- check(LINE, 2)
- } else {
- check(LINE, 1)
- }
- }
- for i := 0; i < 3; i++ {
- if checkVal(LINE, 3, i) <= 0 {
- check(LINE, 1)
- } else if checkVal(LINE, 2, i) <= 1 {
- check(LINE, 1)
- } else if checkVal(LINE, 1, i) <= 2 {
- check(LINE, 1)
- } else if checkVal(LINE, 0, i) <= 3 {
- check(LINE, 0)
- }
- }
- if func(a, b int) bool { return a < b }(3, 4) {
- check(LINE, 1)
- }
-}
-
-func testFor() {
- for i := 0; i < 10; func() { i++; check(LINE, 10) }() {
- check(LINE, 10)
- }
-}
-
-func testRange() {
- for _, f := range []func(){
- func() { check(LINE, 1) },
- } {
- f()
- check(LINE, 1)
- }
-}
-
-func testBlockRun() {
- check(LINE, 1)
- {
- check(LINE, 1)
- }
- {
- check(LINE, 1)
- }
- check(LINE, 1)
- {
- check(LINE, 1)
- }
- {
- check(LINE, 1)
- }
- check(LINE, 1)
-}
-
-func testSwitch() {
- for i := 0; i < 5; func() { i++; check(LINE, 5) }() {
- switch i {
- case 0:
- check(LINE, 1)
- case 1:
- check(LINE, 1)
- case 2:
- check(LINE, 1)
- default:
- check(LINE, 2)
- }
- }
-}
-
-func testTypeSwitch() {
- var x = []interface{}{1, 2.0, "hi"}
- for _, v := range x {
- switch func() { check(LINE, 3) }(); v.(type) {
- case int:
- check(LINE, 1)
- case float64:
- check(LINE, 1)
- case string:
- check(LINE, 1)
- case complex128:
- check(LINE, 0)
- default:
- check(LINE, 0)
- }
- }
-}
-
-func testSelect1() {
- c := make(chan int)
- go func() {
- for i := 0; i < 1000; i++ {
- c <- i
- }
- }()
- for {
- select {
- case <-c:
- check(LINE, anything)
- case <-c:
- check(LINE, anything)
- default:
- check(LINE, 1)
- return
- }
- }
-}
-
-func testSelect2() {
- c1 := make(chan int, 1000)
- c2 := make(chan int, 1000)
- for i := 0; i < 1000; i++ {
- c1 <- i
- c2 <- i
- }
- for {
- select {
- case <-c1:
- check(LINE, 1000)
- case <-c2:
- check(LINE, 1000)
- default:
- check(LINE, 1)
- return
- }
- }
-}
-
-// Empty control statements created syntax errors. This function
-// is here just to be sure that those are handled correctly now.
-func testEmptySwitches() {
- check(LINE, 1)
- switch 3 {
- }
- check(LINE, 1)
- switch i := (interface{})(3).(int); i {
- }
- check(LINE, 1)
- c := make(chan int)
- go func() {
- check(LINE, 1)
- c <- 1
- select {}
- }()
- <-c
- check(LINE, 1)
-}
diff --git a/cmd/digraph/digraph.go b/cmd/digraph/digraph.go
index 88eb05bf1..0e50ad18d 100644
--- a/cmd/digraph/digraph.go
+++ b/cmd/digraph/digraph.go
@@ -34,7 +34,7 @@ The support commands are:
sccs
all strongly connected components (one per line)
scc <node>
- the set of nodes nodes strongly connected to the specified one
+ the set of nodes strongly connected to the specified one
focus <node>
the subgraph containing all directed paths that pass through the specified node
@@ -69,11 +69,12 @@ Using digraph with existing Go tools:
$ go list -m all | digraph nodes # Operate on the Go package graph.
Show the transitive closure of imports of the digraph tool itself:
+
$ go list -f '{{.ImportPath}} {{join .Imports " "}}' ... | digraph forward golang.org/x/tools/cmd/digraph
Show which clothes (see above) must be donned before a jacket:
- $ digraph reverse jacket
+ $ digraph reverse jacket
*/
package main // import "golang.org/x/tools/cmd/digraph"
@@ -121,7 +122,8 @@ The support commands are:
allpaths <node> <node>
the set of nodes on all paths from the first node to the second
sccs
- all strongly connected components (one per line)
+ all non-trivial strongly connected components, one per line
+ (single-node components are only printed for nodes with self-loops)
scc <node>
the set of nodes nodes strongly connected to the specified one
focus <node>
@@ -157,7 +159,7 @@ func (l nodelist) println(sep string) {
fmt.Fprintln(stdout)
}
-type nodeset map[string]bool // TODO(deklerk): change bool to struct to reduce memory footprint
+type nodeset map[string]bool
func (s nodeset) sort() nodelist {
nodes := make(nodelist, len(s))
@@ -265,6 +267,9 @@ func (g graph) sccs() []nodeset {
if !seen[top] {
scc = make(nodeset)
rvisit(top)
+ if len(scc) == 1 && !g[top][top] {
+ continue
+ }
sccs = append(sccs, scc)
}
}
@@ -346,25 +351,34 @@ func parse(rd io.Reader) (graph, error) {
g := make(graph)
var linenum int
- in := bufio.NewScanner(rd)
- for in.Scan() {
+ // We avoid bufio.Scanner as it imposes a (configurable) limit
+ // on line length, whereas Reader.ReadString does not.
+ in := bufio.NewReader(rd)
+ for {
linenum++
+ line, err := in.ReadString('\n')
+ eof := false
+ if err == io.EOF {
+ eof = true
+ } else if err != nil {
+ return nil, err
+ }
// Split into words, honoring double-quotes per Go spec.
- words, err := split(in.Text())
+ words, err := split(line)
if err != nil {
return nil, fmt.Errorf("at line %d: %v", linenum, err)
}
if len(words) > 0 {
g.addEdges(words[0], words[1:]...)
}
- }
- if err := in.Err(); err != nil {
- return nil, err
+ if eof {
+ break
+ }
}
return g, nil
}
-// Overridable for testing purposes.
+// Overridable for redirection.
var stdin io.Reader = os.Stdin
var stdout io.Writer = os.Stdout
@@ -484,9 +498,16 @@ func digraph(cmd string, args []string) error {
if len(args) != 0 {
return fmt.Errorf("usage: digraph sccs")
}
+ buf := new(bytes.Buffer)
+ oldStdout := stdout
+ stdout = buf
for _, scc := range g.sccs() {
scc.sort().println(" ")
}
+ lines := strings.SplitAfter(buf.String(), "\n")
+ sort.Strings(lines)
+ stdout = oldStdout
+ io.WriteString(stdout, strings.Join(lines, ""))
case "scc":
if len(args) != 1 {
@@ -546,9 +567,8 @@ func digraph(cmd string, args []string) error {
// spaces, but Go-style double-quoted string literals are also supported.
// (This approximates the behaviour of the Bourne shell.)
//
-// `one "two three"` -> ["one" "two three"]
-// `a"\n"b` -> ["a\nb"]
-//
+// `one "two three"` -> ["one" "two three"]
+// `a"\n"b` -> ["a\nb"]
func split(line string) ([]string, error) {
var (
words []string
@@ -605,7 +625,6 @@ func split(line string) ([]string, error) {
// its length is returned.
//
// TODO(adonovan): move this into a strconv-like utility package.
-//
func quotedLength(input string) (n int, ok bool) {
var offset int
diff --git a/cmd/digraph/digraph_test.go b/cmd/digraph/digraph_test.go
index 1746fcaa6..60b8e75eb 100644
--- a/cmd/digraph/digraph_test.go
+++ b/cmd/digraph/digraph_test.go
@@ -27,6 +27,7 @@ a b c
b d
c d
d c
+e e
`
for _, test := range []struct {
@@ -41,9 +42,10 @@ d c
{"transpose", g1, "transpose", nil, "belt pants\njacket sweater\npants shorts\nshoes pants\nshoes socks\nsweater shirt\ntie shirt\n"},
{"forward", g1, "forward", []string{"socks"}, "shoes\nsocks\n"},
{"forward multiple args", g1, "forward", []string{"socks", "sweater"}, "jacket\nshoes\nsocks\nsweater\n"},
- {"scss", g2, "sccs", nil, "a\nb\nc d\n"},
+ {"scss", g2, "sccs", nil, "c d\ne\n"},
{"scc", g2, "scc", []string{"d"}, "c\nd\n"},
{"succs", g2, "succs", []string{"a"}, "b\nc\n"},
+ {"succs-long-token", g2 + "x " + strings.Repeat("x", 96*1024), "succs", []string{"x"}, strings.Repeat("x", 96*1024) + "\n"},
{"preds", g2, "preds", []string{"c"}, "a\nd\n"},
{"preds multiple args", g2, "preds", []string{"c", "d"}, "a\nb\nc\nd\n"},
} {
diff --git a/cmd/file2fuzz/main.go b/cmd/file2fuzz/main.go
index 350ed0af6..ed212cb9d 100644
--- a/cmd/file2fuzz/main.go
+++ b/cmd/file2fuzz/main.go
@@ -17,7 +17,6 @@
// argument is specified it may be a file path or an existing directory, if there are
// multiple inputs specified it must be a directory. If a directory is provided
// the name of the file will be the SHA-256 hash of its contents.
-//
package main
import (
diff --git a/cmd/fiximports/main.go b/cmd/fiximports/main.go
index f572a15c5..8eeacd1ed 100644
--- a/cmd/fiximports/main.go
+++ b/cmd/fiximports/main.go
@@ -6,8 +6,7 @@
// import path for packages that have an "import comment" as defined by
// https://golang.org/s/go14customimport.
//
-//
-// Background
+// # Background
//
// The Go 1 custom import path mechanism lets the maintainer of a
// package give it a stable name by which clients may import and "go
@@ -28,15 +27,14 @@
// does not match the path of the enclosing package relative to
// GOPATH/src:
//
-// $ grep ^package $GOPATH/src/github.com/bob/vanity/foo/foo.go
-// package foo // import "vanity.com/foo"
+// $ grep ^package $GOPATH/src/github.com/bob/vanity/foo/foo.go
+// package foo // import "vanity.com/foo"
//
// The error from "go build" indicates that the package canonically
// known as "vanity.com/foo" is locally installed under the
// non-canonical name "github.com/bob/vanity/foo".
//
-//
-// Usage
+// # Usage
//
// When a package that you depend on introduces a custom import comment,
// and your workspace imports it by the non-canonical name, your build
@@ -66,7 +64,6 @@
//
// To see the changes fiximports would make without applying them, use
// the -n flag.
-//
package main
import (
@@ -75,11 +72,9 @@ import (
"flag"
"fmt"
"go/ast"
- "go/build"
"go/format"
"go/parser"
"go/token"
- exec "golang.org/x/sys/execabs"
"io"
"io/ioutil"
"log"
@@ -89,6 +84,8 @@ import (
"sort"
"strconv"
"strings"
+
+ exec "golang.org/x/sys/execabs"
)
// flags
@@ -140,16 +137,16 @@ type canonicalName struct{ path, name string }
// Invariant: a false result implies an error was already printed.
func fiximports(packages ...string) bool {
// importedBy is the transpose of the package import graph.
- importedBy := make(map[string]map[*build.Package]bool)
+ importedBy := make(map[string]map[*listPackage]bool)
// addEdge adds an edge to the import graph.
- addEdge := func(from *build.Package, to string) {
+ addEdge := func(from *listPackage, to string) {
if to == "C" || to == "unsafe" {
return // fake
}
pkgs := importedBy[to]
if pkgs == nil {
- pkgs = make(map[*build.Package]bool)
+ pkgs = make(map[*listPackage]bool)
importedBy[to] = pkgs
}
pkgs[from] = true
@@ -165,7 +162,7 @@ func fiximports(packages ...string) bool {
// packageName maps each package's path to its name.
packageName := make(map[string]string)
for _, p := range pkgs {
- packageName[p.ImportPath] = p.Package.Name
+ packageName[p.ImportPath] = p.Name
}
// canonical maps each non-canonical package path to
@@ -210,21 +207,21 @@ func fiximports(packages ...string) bool {
}
for _, imp := range p.Imports {
- addEdge(&p.Package, imp)
+ addEdge(p, imp)
}
for _, imp := range p.TestImports {
- addEdge(&p.Package, imp)
+ addEdge(p, imp)
}
for _, imp := range p.XTestImports {
- addEdge(&p.Package, imp)
+ addEdge(p, imp)
}
// Does package have an explicit import comment?
if p.ImportComment != "" {
if p.ImportComment != p.ImportPath {
canonical[p.ImportPath] = canonicalName{
- path: p.Package.ImportComment,
- name: p.Package.Name,
+ path: p.ImportComment,
+ name: p.Name,
}
}
} else {
@@ -276,7 +273,7 @@ func fiximports(packages ...string) bool {
// Find all clients (direct importers) of canonical packages.
// These are the packages that need fixing up.
- clients := make(map[*build.Package]bool)
+ clients := make(map[*listPackage]bool)
for path := range canonical {
for client := range importedBy[path] {
clients[client] = true
@@ -353,7 +350,7 @@ func fiximports(packages ...string) bool {
}
// Invariant: false result => error already printed.
-func rewritePackage(client *build.Package, canonical map[string]canonicalName) bool {
+func rewritePackage(client *listPackage, canonical map[string]canonicalName) bool {
ok := true
used := make(map[string]bool)
@@ -392,7 +389,7 @@ func rewritePackage(client *build.Package, canonical map[string]canonicalName) b
return ok
}
-// rewrite reads, modifies, and writes filename, replacing all imports
+// rewriteFile reads, modifies, and writes filename, replacing all imports
// of packages P in canonical by canonical[P].
// It records in used which canonical packages were imported.
// used[P]=="" indicates that P was imported but its canonical path is unknown.
@@ -453,11 +450,20 @@ func rewriteFile(filename string, canonical map[string]canonicalName, used map[s
return nil
}
-// listPackage is a copy of cmd/go/list.Package.
-// It has more fields than build.Package and we need some of them.
+// listPackage corresponds to the output of go list -json,
+// but only the fields we need.
type listPackage struct {
- build.Package
- Error *packageError // error loading package
+ Name string
+ Dir string
+ ImportPath string
+ GoFiles []string
+ TestGoFiles []string
+ XTestGoFiles []string
+ Imports []string
+ TestImports []string
+ XTestImports []string
+ ImportComment string
+ Error *packageError // error loading package
}
// A packageError describes an error loading information about a package.
diff --git a/cmd/fiximports/main_test.go b/cmd/fiximports/main_test.go
index bbc4a2eb2..ebbd7520d 100644
--- a/cmd/fiximports/main_test.go
+++ b/cmd/fiximports/main_test.go
@@ -55,6 +55,9 @@ func init() {
}
func TestFixImports(t *testing.T) {
+ if os.Getenv("GO_BUILDER_NAME") == "plan9-arm" {
+ t.Skipf("skipping test that times out on plan9-arm; see https://go.dev/issue/50775")
+ }
testenv.NeedsTool(t, "go")
defer func() {
diff --git a/cmd/godex/doc.go b/cmd/godex/doc.go
index ceb7c2fe1..3c2112ebf 100644
--- a/cmd/godex/doc.go
+++ b/cmd/godex/doc.go
@@ -62,7 +62,6 @@
// (uncompiled) source code (not yet implemented)
//
// If no -s argument is provided, godex will try to find a matching source.
-//
package main // import "golang.org/x/tools/cmd/godex"
// BUG(gri): support for -s=source is not yet implemented
diff --git a/cmd/godoc/doc.go b/cmd/godoc/doc.go
index 279b2b1bb..91d015046 100644
--- a/cmd/godoc/doc.go
+++ b/cmd/godoc/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Godoc extracts and generates documentation for Go programs.
It runs as a web server and presents the documentation as a
diff --git a/cmd/godoc/godoc_test.go b/cmd/godoc/godoc_test.go
index 76568c31d..3e91ac6f9 100644
--- a/cmd/godoc/godoc_test.go
+++ b/cmd/godoc/godoc_test.go
@@ -2,10 +2,11 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-package main_test
+package main
import (
"bytes"
+ "context"
"fmt"
"go/build"
"io/ioutil"
@@ -13,10 +14,10 @@ import (
"net/http"
"os"
"os/exec"
- "path/filepath"
"regexp"
"runtime"
"strings"
+ "sync"
"testing"
"time"
@@ -24,42 +25,39 @@ import (
"golang.org/x/tools/internal/testenv"
)
-// buildGodoc builds the godoc executable.
-// It returns its path, and a cleanup function.
-//
-// TODO(adonovan): opt: do this at most once, and do the cleanup
-// exactly once. How though? There's no atexit.
-func buildGodoc(t *testing.T) (bin string, cleanup func()) {
- t.Helper()
-
- if runtime.GOARCH == "arm" {
- t.Skip("skipping test on arm platforms; too slow")
- }
- if runtime.GOOS == "android" {
- t.Skipf("the dependencies are not available on android")
+func TestMain(m *testing.M) {
+ if os.Getenv("GODOC_TEST_IS_GODOC") != "" {
+ main()
+ os.Exit(0)
}
- testenv.NeedsTool(t, "go")
- tmp, err := ioutil.TempDir("", "godoc-regtest-")
- if err != nil {
- t.Fatal(err)
- }
- defer func() {
- if cleanup == nil { // probably, go build failed.
- os.RemoveAll(tmp)
- }
- }()
+ // Inform subprocesses that they should run the cmd/godoc main instead of
+ // running tests. It's a close approximation to building and running the real
+ // command, and much less complicated and expensive to build and clean up.
+ os.Setenv("GODOC_TEST_IS_GODOC", "1")
- bin = filepath.Join(tmp, "godoc")
- if runtime.GOOS == "windows" {
- bin += ".exe"
- }
- cmd := exec.Command("go", "build", "-o", bin)
- if err := cmd.Run(); err != nil {
- t.Fatalf("Building godoc: %v", err)
+ os.Exit(m.Run())
+}
+
+var exe struct {
+ path string
+ err error
+ once sync.Once
+}
+
+func godocPath(t *testing.T) string {
+ switch runtime.GOOS {
+ case "js", "ios":
+ t.Skipf("skipping test that requires exec")
}
- return bin, func() { os.RemoveAll(tmp) }
+ exe.once.Do(func() {
+ exe.path, exe.err = os.Executable()
+ })
+ if exe.err != nil {
+ t.Fatal(exe.err)
+ }
+ return exe.path
}
func serverAddress(t *testing.T) string {
@@ -74,60 +72,42 @@ func serverAddress(t *testing.T) string {
return ln.Addr().String()
}
-func waitForServerReady(t *testing.T, cmd *exec.Cmd, addr string) {
- ch := make(chan error, 1)
- go func() { ch <- fmt.Errorf("server exited early: %v", cmd.Wait()) }()
- go waitForServer(t, ch,
+func waitForServerReady(t *testing.T, ctx context.Context, cmd *exec.Cmd, addr string) {
+ waitForServer(t, ctx,
fmt.Sprintf("http://%v/", addr),
"Go Documentation Server",
- 15*time.Second,
false)
- if err := <-ch; err != nil {
- t.Fatal(err)
- }
}
-func waitForSearchReady(t *testing.T, cmd *exec.Cmd, addr string) {
- ch := make(chan error, 1)
- go func() { ch <- fmt.Errorf("server exited early: %v", cmd.Wait()) }()
- go waitForServer(t, ch,
+func waitForSearchReady(t *testing.T, ctx context.Context, cmd *exec.Cmd, addr string) {
+ waitForServer(t, ctx,
fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr),
"The list of tokens.",
- 2*time.Minute,
false)
- if err := <-ch; err != nil {
- t.Fatal(err)
- }
}
-func waitUntilScanComplete(t *testing.T, addr string) {
- ch := make(chan error)
- go waitForServer(t, ch,
+func waitUntilScanComplete(t *testing.T, ctx context.Context, addr string) {
+ waitForServer(t, ctx,
fmt.Sprintf("http://%v/pkg", addr),
"Scan is not yet complete",
- 2*time.Minute,
// setting reverse as true, which means this waits
// until the string is not returned in the response anymore
- true,
- )
- if err := <-ch; err != nil {
- t.Fatal(err)
- }
+ true)
}
-const pollInterval = 200 * time.Millisecond
+const pollInterval = 50 * time.Millisecond
-// waitForServer waits for server to meet the required condition.
-// It sends a single error value to ch, unless the test has failed.
-// The error value is nil if the required condition was met within
-// timeout, or non-nil otherwise.
-func waitForServer(t *testing.T, ch chan<- error, url, match string, timeout time.Duration, reverse bool) {
- deadline := time.Now().Add(timeout)
- for time.Now().Before(deadline) {
- time.Sleep(pollInterval)
- if t.Failed() {
- return
+// waitForServer waits for server to meet the required condition,
+// failing the test if ctx is canceled before that occurs.
+func waitForServer(t *testing.T, ctx context.Context, url, match string, reverse bool) {
+ start := time.Now()
+ for {
+ if ctx.Err() != nil {
+ t.Helper()
+ t.Fatalf("server failed to respond in %v", time.Since(start))
}
+
+ time.Sleep(pollInterval)
res, err := http.Get(url)
if err != nil {
continue
@@ -140,11 +120,9 @@ func waitForServer(t *testing.T, ch chan<- error, url, match string, timeout tim
switch {
case !reverse && bytes.Contains(body, []byte(match)),
reverse && !bytes.Contains(body, []byte(match)):
- ch <- nil
return
}
}
- ch <- fmt.Errorf("server failed to respond in %v", timeout)
}
// hasTag checks whether a given release tag is contained in the current version
@@ -158,24 +136,18 @@ func hasTag(t string) bool {
return false
}
-func killAndWait(cmd *exec.Cmd) {
- cmd.Process.Kill()
- cmd.Process.Wait()
-}
-
func TestURL(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; fails to start up quickly enough")
}
- bin, cleanup := buildGodoc(t)
- defer cleanup()
+ bin := godocPath(t)
testcase := func(url string, contents string) func(t *testing.T) {
return func(t *testing.T) {
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
args := []string{fmt.Sprintf("-url=%s", url)}
- cmd := exec.Command(bin, args...)
+ cmd := testenv.Command(t, bin, args...)
cmd.Stdout = stdout
cmd.Stderr = stderr
cmd.Args[0] = "godoc"
@@ -205,8 +177,8 @@ func TestURL(t *testing.T) {
// Basic integration test for godoc HTTP interface.
func TestWeb(t *testing.T) {
- bin, cleanup := buildGodoc(t)
- defer cleanup()
+ bin := godocPath(t)
+
for _, x := range packagestest.All {
t.Run(x.Name(), func(t *testing.T) {
testWeb(t, x, bin, false)
@@ -217,17 +189,19 @@ func TestWeb(t *testing.T) {
// Basic integration test for godoc HTTP interface.
func TestWebIndex(t *testing.T) {
if testing.Short() {
- t.Skip("skipping test in -short mode")
+ t.Skip("skipping slow test in -short mode")
}
- bin, cleanup := buildGodoc(t)
- defer cleanup()
+ bin := godocPath(t)
testWeb(t, packagestest.GOPATH, bin, true)
}
// Basic integration test for godoc HTTP interface.
func testWeb(t *testing.T, x packagestest.Exporter, bin string, withIndex bool) {
- if runtime.GOOS == "plan9" {
- t.Skip("skipping on plan9; fails to start up quickly enough")
+ switch runtime.GOOS {
+ case "plan9":
+ t.Skip("skipping on plan9: fails to start up quickly enough")
+ case "android", "ios":
+ t.Skip("skipping on mobile: lacks GOROOT/api in test environment")
}
// Write a fake GOROOT/GOPATH with some third party packages.
@@ -256,23 +230,39 @@ package a; import _ "godoc.test/repo2/a"; const Name = "repo1a"`,
if withIndex {
args = append(args, "-index", "-index_interval=-1s")
}
- cmd := exec.Command(bin, args...)
+ cmd := testenv.Command(t, bin, args...)
cmd.Dir = e.Config.Dir
cmd.Env = e.Config.Env
- cmd.Stdout = os.Stderr
- cmd.Stderr = os.Stderr
+ cmdOut := new(strings.Builder)
+ cmd.Stdout = cmdOut
+ cmd.Stderr = cmdOut
cmd.Args[0] = "godoc"
if err := cmd.Start(); err != nil {
t.Fatalf("failed to start godoc: %s", err)
}
- defer killAndWait(cmd)
+ ctx, cancel := context.WithCancel(context.Background())
+ go func() {
+ err := cmd.Wait()
+ t.Logf("%v: %v", cmd, err)
+ cancel()
+ }()
+ defer func() {
+ // Shut down the server cleanly if possible.
+ if runtime.GOOS == "windows" {
+ cmd.Process.Kill() // Windows doesn't support os.Interrupt.
+ } else {
+ cmd.Process.Signal(os.Interrupt)
+ }
+ <-ctx.Done()
+ t.Logf("server output:\n%s", cmdOut)
+ }()
if withIndex {
- waitForSearchReady(t, cmd, addr)
+ waitForSearchReady(t, ctx, cmd, addr)
} else {
- waitForServerReady(t, cmd, addr)
- waitUntilScanComplete(t, addr)
+ waitForServerReady(t, ctx, cmd, addr)
+ waitUntilScanComplete(t, ctx, addr)
}
tests := []struct {
@@ -454,22 +444,17 @@ func TestNoMainModule(t *testing.T) {
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; for consistency with other tests that build godoc binary")
}
- bin, cleanup := buildGodoc(t)
- defer cleanup()
- tempDir, err := ioutil.TempDir("", "godoc-test-")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(tempDir)
+ bin := godocPath(t)
+ tempDir := t.TempDir()
// Run godoc in an empty directory with module mode explicitly on,
// so that 'go env GOMOD' reports os.DevNull.
- cmd := exec.Command(bin, "-url=/")
+ cmd := testenv.Command(t, bin, "-url=/")
cmd.Dir = tempDir
cmd.Env = append(os.Environ(), "GO111MODULE=on")
var stderr bytes.Buffer
cmd.Stderr = &stderr
- err = cmd.Run()
+ err := cmd.Run()
if err != nil {
t.Fatalf("godoc command failed: %v\nstderr=%q", err, stderr.String())
}
diff --git a/cmd/godoc/main.go b/cmd/godoc/main.go
index 352bb4b7a..79dcf3821 100644
--- a/cmd/godoc/main.go
+++ b/cmd/godoc/main.go
@@ -21,6 +21,7 @@ import (
"bytes"
"context"
"encoding/json"
+ "errors"
_ "expvar" // to serve /debug/vars
"flag"
"fmt"
@@ -46,7 +47,6 @@ import (
"golang.org/x/tools/godoc/vfs/mapfs"
"golang.org/x/tools/godoc/vfs/zipfs"
"golang.org/x/tools/internal/gocommand"
- "golang.org/x/xerrors"
)
const defaultAddr = "localhost:6060" // default webserver address
@@ -368,12 +368,11 @@ func main() {
//
// GOMOD is documented at https://golang.org/cmd/go/#hdr-Environment_variables:
//
-// The absolute path to the go.mod of the main module,
-// or the empty string if not using modules.
-//
+// The absolute path to the go.mod of the main module,
+// or the empty string if not using modules.
func goMod() (string, error) {
out, err := exec.Command("go", "env", "-json", "GOMOD").Output()
- if ee := (*exec.ExitError)(nil); xerrors.As(err, &ee) {
+ if ee := (*exec.ExitError)(nil); errors.As(err, &ee) {
return "", fmt.Errorf("go command exited unsuccessfully: %v\n%s", ee.ProcessState.String(), ee.Stderr)
} else if err != nil {
return "", err
@@ -406,7 +405,7 @@ func fillModuleCache(w io.Writer, goMod string) {
cmd.Stdout = &out
cmd.Stderr = w
err := cmd.Run()
- if ee := (*exec.ExitError)(nil); xerrors.As(err, &ee) && ee.ExitCode() == 1 {
+ if ee := (*exec.ExitError)(nil); errors.As(err, &ee) && ee.ExitCode() == 1 {
// Exit code 1 from this command means there were some
// non-empty Error values in the output. Print them to w.
fmt.Fprintf(w, "documentation for some packages is not shown:\n")
@@ -450,7 +449,7 @@ func buildList(goMod string) ([]mod, error) {
}
out, err := exec.Command("go", "list", "-m", "-json", "all").Output()
- if ee := (*exec.ExitError)(nil); xerrors.As(err, &ee) {
+ if ee := (*exec.ExitError)(nil); errors.As(err, &ee) {
return nil, fmt.Errorf("go command exited unsuccessfully: %v\n%s", ee.ProcessState.String(), ee.Stderr)
} else if err != nil {
return nil, err
@@ -483,7 +482,6 @@ func buildList(goMod string) ([]mod, error) {
// workspaces are bound at their roots, but scales poorly in the
// general case. It should be replaced by a more direct solution
// for determining whether a package is third party or not.
-//
type moduleFS struct{ vfs.FileSystem }
func (moduleFS) RootType(path string) vfs.RootType {
diff --git a/cmd/goimports/doc.go b/cmd/goimports/doc.go
index 5a5b9005f..18a3ad448 100644
--- a/cmd/goimports/doc.go
+++ b/cmd/goimports/doc.go
@@ -3,29 +3,33 @@
// license that can be found in the LICENSE file.
/*
-
Command goimports updates your Go import lines,
adding missing ones and removing unreferenced ones.
- $ go install golang.org/x/tools/cmd/goimports@latest
+ $ go install golang.org/x/tools/cmd/goimports@latest
In addition to fixing imports, goimports also formats
your code in the same style as gofmt so it can be used
as a replacement for your editor's gofmt-on-save hook.
For emacs, make sure you have the latest go-mode.el:
- https://github.com/dominikh/go-mode.el
+
+ https://github.com/dominikh/go-mode.el
+
Then in your .emacs file:
- (setq gofmt-command "goimports")
- (add-hook 'before-save-hook 'gofmt-before-save)
+
+ (setq gofmt-command "goimports")
+ (add-hook 'before-save-hook 'gofmt-before-save)
For vim, set "gofmt_command" to "goimports":
- https://golang.org/change/39c724dd7f252
- https://golang.org/wiki/IDEsAndTextEditorPlugins
- etc
+
+ https://golang.org/change/39c724dd7f252
+ https://golang.org/wiki/IDEsAndTextEditorPlugins
+ etc
For GoSublime, follow the steps described here:
- http://michaelwhatcott.com/gosublime-goimports/
+
+ http://michaelwhatcott.com/gosublime-goimports/
For other editors, you probably know what to do.
@@ -39,9 +43,8 @@ working and see what goimports is doing.
File bugs or feature requests at:
- https://golang.org/issues/new?title=x/tools/cmd/goimports:+
+ https://golang.org/issues/new?title=x/tools/cmd/goimports:+
Happy hacking!
-
*/
package main // import "golang.org/x/tools/cmd/goimports"
diff --git a/cmd/gorename/main.go b/cmd/gorename/main.go
index e59abd758..98625fff6 100644
--- a/cmd/gorename/main.go
+++ b/cmd/gorename/main.go
@@ -8,7 +8,6 @@
// Run with -help for usage information, or view the Usage constant in
// package golang.org/x/tools/refactor/rename, which contains most of
// the implementation.
-//
package main // import "golang.org/x/tools/cmd/gorename"
import (
diff --git a/cmd/gotype/gotype.go b/cmd/gotype/gotype.go
index 22fe4aa9d..08b52057f 100644
--- a/cmd/gotype/gotype.go
+++ b/cmd/gotype/gotype.go
@@ -41,9 +41,11 @@ checking packages containing imports with relative import paths
files to include for such packages.
Usage:
+
gotype [flags] [path...]
The flags are:
+
-t
include local test files in a directory (ignored if -x is provided)
-x
@@ -56,6 +58,7 @@ The flags are:
compiler used for installed packages (gc, gccgo, or source); default: source
Flags controlling additional output:
+
-ast
print AST (forces -seq)
-trace
@@ -81,7 +84,6 @@ cmd/compile:
To verify the output of a pipe:
echo "package foo" | gotype
-
*/
package main
diff --git a/cmd/goyacc/doc.go b/cmd/goyacc/doc.go
index 03ffee7b6..5eb27f16a 100644
--- a/cmd/goyacc/doc.go
+++ b/cmd/goyacc/doc.go
@@ -3,7 +3,6 @@
// license that can be found in the LICENSE file.
/*
-
Goyacc is a version of yacc for Go.
It is written in Go and generates parsers written in Go.
@@ -65,6 +64,5 @@ goyacc sets the prefix, by default yy, that begins the names of
symbols, including types, the parser, and the lexer, generated and
referenced by yacc's generated code. Setting it to distinct values
allows multiple grammars to be placed in a single package.
-
*/
package main
diff --git a/cmd/goyacc/yacc.go b/cmd/goyacc/yacc.go
index 70d01f0d5..948e50104 100644
--- a/cmd/goyacc/yacc.go
+++ b/cmd/goyacc/yacc.go
@@ -742,9 +742,7 @@ outer:
}
}
-//
// allocate enough room to hold another production
-//
func moreprod() {
n := len(prdptr)
if nprod >= n {
@@ -763,10 +761,8 @@ func moreprod() {
}
}
-//
// define s to be a terminal if nt==0
// or a nonterminal if nt==1
-//
func defin(nt int, s string) int {
val := 0
if nt != 0 {
@@ -1007,9 +1003,7 @@ func getword(c rune) {
ungetrune(finput, c)
}
-//
// determine the type of a symbol
-//
func fdtype(t int) int {
var v int
var s string
@@ -1049,9 +1043,7 @@ func chfind(t int, s string) int {
return defin(t, s)
}
-//
// copy the union declaration to the output, and the define file if present
-//
func cpyunion() {
if !lflag {
@@ -1086,10 +1078,8 @@ out:
fmt.Fprintf(ftable, "\n\n")
}
-//
// saves code between %{ and %}
// adds an import for __fmt__ the first time
-//
func cpycode() {
lno := lineno
@@ -1122,11 +1112,9 @@ func cpycode() {
errorf("eof before %%}")
}
-//
// emits code saved up from between %{ and %}
// called by cpycode
// adds an import for __yyfmt__ after the package clause
-//
func emitcode(code []rune, lineno int) {
for i, line := range lines(code) {
writecode(line)
@@ -1140,9 +1128,7 @@ func emitcode(code []rune, lineno int) {
}
}
-//
// does this line look like a package clause? not perfect: might be confused by early comments.
-//
func isPackageClause(line []rune) bool {
line = skipspace(line)
@@ -1184,9 +1170,7 @@ func isPackageClause(line []rune) bool {
return false
}
-//
// skip initial spaces
-//
func skipspace(line []rune) []rune {
for len(line) > 0 {
if line[0] != ' ' && line[0] != '\t' {
@@ -1197,9 +1181,7 @@ func skipspace(line []rune) []rune {
return line
}
-//
// break code into lines
-//
func lines(code []rune) [][]rune {
l := make([][]rune, 0, 100)
for len(code) > 0 {
@@ -1216,19 +1198,15 @@ func lines(code []rune) [][]rune {
return l
}
-//
// writes code to ftable
-//
func writecode(code []rune) {
for _, r := range code {
ftable.WriteRune(r)
}
}
-//
// skip over comments
// skipcom is called after reading a '/'
-//
func skipcom() int {
c := getrune(finput)
if c == '/' {
@@ -1268,9 +1246,7 @@ l1:
return nl
}
-//
// copy action to the next ; or closing }
-//
func cpyact(curprod []int, max int) {
if !lflag {
@@ -1488,9 +1464,7 @@ func openup() {
}
-//
// return a pointer to the name of symbol i
-//
func symnam(i int) string {
var s string
@@ -1502,20 +1476,16 @@ func symnam(i int) string {
return s
}
-//
// set elements 0 through n-1 to c
-//
func aryfil(v []int, n, c int) {
for i := 0; i < n; i++ {
v[i] = c
}
}
-//
// compute an array with the beginnings of productions yielding given nonterminals
// The array pres points to these lists
// the array pyield has the lists: the total size is only NPROD+1
-//
func cpres() {
pres = make([][][]int, nnonter+1)
curres := make([][]int, nprod)
@@ -1553,10 +1523,8 @@ func cpres() {
}
}
-//
// mark nonterminals which derive the empty string
// also, look for nonterminals which don't derive any token strings
-//
func cempty() {
var i, p, np int
var prd []int
@@ -1639,9 +1607,7 @@ again:
}
}
-//
// compute an array with the first of nonterminals
-//
func cpfir() {
var s, n, p, np, ch, i int
var curres [][]int
@@ -1707,9 +1673,7 @@ func cpfir() {
}
}
-//
// generate the states
-//
func stagen() {
// initialize
nstate = 0
@@ -1799,9 +1763,7 @@ func stagen() {
}
}
-//
// generate the closure of state i
-//
func closure(i int) {
zzclose++
@@ -1931,9 +1893,7 @@ func closure(i int) {
}
}
-//
// sorts last state,and sees if it equals earlier ones. returns state number
-//
func state(c int) int {
zzstate++
p1 := pstate[nstate]
@@ -2046,9 +2006,7 @@ func putitem(p Pitem, set Lkset) {
pstate[nstate+1] = j
}
-//
// creates output string for item pointed to by pp
-//
func writem(pp Pitem) string {
var i int
@@ -2082,9 +2040,7 @@ func writem(pp Pitem) string {
return q
}
-//
// pack state i from temp1 into amem
-//
func apack(p []int, n int) int {
//
// we don't need to worry about checking because
@@ -2149,9 +2105,7 @@ nextk:
return 0
}
-//
// print the output for the states
-//
func output() {
var c, u, v int
@@ -2240,12 +2194,10 @@ func output() {
fmt.Fprintf(ftable, "const %sPrivate = %v\n", prefix, PRIVATE)
}
-//
// decide a shift/reduce conflict by precedence.
// r is a rule number, t a token number
// the conflict is in state s
// temp1[t] is changed to reflect the action
-//
func precftn(r, t, s int) {
action := NOASC
@@ -2276,10 +2228,8 @@ func precftn(r, t, s int) {
}
}
-//
// output state i
// temp1 has the actions, lastred the default
-//
func addActions(act []int, i int) []int {
var p, p1 int
@@ -2368,9 +2318,7 @@ func addActions(act []int, i int) []int {
return act
}
-//
// writes state i
-//
func wrstate(i int) {
var j0, j1, u int
var pp, qq int
@@ -2440,9 +2388,7 @@ func wrstate(i int) {
}
}
-//
// output the gotos for the nontermninals
-//
func go2out() {
for i := 1; i <= nnonter; i++ {
go2gen(i)
@@ -2505,9 +2451,7 @@ func go2out() {
}
}
-//
// output the gotos for nonterminal c
-//
func go2gen(c int) {
var i, cc, p, q int
@@ -2559,12 +2503,10 @@ func go2gen(c int) {
}
}
-//
// in order to free up the mem and amem arrays for the optimizer,
// and still be able to output yyr1, etc., after the sizes of
// the action array is known, we hide the nonterminals
// derived by productions in levprd.
-//
func hideprod() {
nred := 0
levprd[0] = 0
@@ -2678,9 +2620,7 @@ func callopt() {
osummary()
}
-//
// finds the next i
-//
func nxti() int {
max := 0
maxi := 0
@@ -2817,10 +2757,8 @@ nextn:
errorf("Error; failure to place state %v", i)
}
-//
// this version is for limbo
// write out the optimized parser
-//
func aoutput() {
ftable.WriteRune('\n')
fmt.Fprintf(ftable, "const %sLast = %v\n", prefix, maxa+1)
@@ -2829,9 +2767,7 @@ func aoutput() {
arout("Pgo", pgo, nnonter+1)
}
-//
// put out other arrays, copy the parsers
-//
func others() {
var i, j int
@@ -3066,9 +3002,7 @@ func arout(s string, v []int, n int) {
arrayOutColumns(s, v[:n], 10, true)
}
-//
// output the summary on y.output
-//
func summary() {
if foutput != nil {
fmt.Fprintf(foutput, "\n%v terminals, %v nonterminals\n", ntokens, nnonter+1)
@@ -3096,9 +3030,7 @@ func summary() {
}
}
-//
// write optimizer summary
-//
func osummary() {
if foutput == nil {
return
@@ -3115,9 +3047,7 @@ func osummary() {
fmt.Fprintf(foutput, "maximum spread: %v, maximum offset: %v\n", maxspr, maxoff)
}
-//
// copies and protects "'s in q
-//
func chcopy(q string) string {
s := ""
i := 0
@@ -3142,10 +3072,8 @@ func setbit(set Lkset, bit int) { set[bit>>5] |= (1 << uint(bit&31)) }
func mkset() Lkset { return make([]int, tbitset) }
-//
// set a to the union of a and b
// return 1 if b is not a subset of a, 0 otherwise
-//
func setunion(a, b []int) int {
sub := 0
for i := 0; i < tbitset; i++ {
@@ -3173,9 +3101,7 @@ func prlook(p Lkset) {
fmt.Fprintf(foutput, "}")
}
-//
// utility routines
-//
var peekrune rune
func isdigit(c rune) bool { return c >= '0' && c <= '9' }
@@ -3184,10 +3110,8 @@ func isword(c rune) bool {
return c >= 0xa0 || c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
-//
// return 1 if 2 arrays are equal
// return 0 if not equal
-//
func aryeq(a []int, b []int) int {
n := len(a)
if len(b) != n {
@@ -3252,9 +3176,7 @@ func create(s string) *bufio.Writer {
return bufio.NewWriter(fo)
}
-//
// write out error comment
-//
func lerrorf(lineno int, s string, v ...interface{}) {
nerrors++
fmt.Fprintf(stderr, s, v...)
diff --git a/cmd/guru/TODO b/cmd/guru/TODO
new file mode 100644
index 000000000..61bf1519e
--- /dev/null
+++ b/cmd/guru/TODO
@@ -0,0 +1,11 @@
+-*- text -*-
+
+Guru to-do list
+===========================
+
+Generics:
+- decide on whether to support generics in guru
+- decide on whether to instantiate generics in ssa (go.dev/issue/52503)
+
+MISC:
+- test support for *ssa.SliceToArrayPointer instructions (go.dev/issue/47326) \ No newline at end of file
diff --git a/cmd/guru/callers.go b/cmd/guru/callers.go
index b39b07869..8afefba33 100644
--- a/cmd/guru/callers.go
+++ b/cmd/guru/callers.go
@@ -18,7 +18,6 @@ import (
// The callers function reports the possible callers of the function
// immediately enclosing the specified source location.
-//
func callers(q *Query) error {
lconf := loader.Config{Build: q.Build}
diff --git a/cmd/guru/callstack.go b/cmd/guru/callstack.go
index 10939ddfb..c3d6d6ee7 100644
--- a/cmd/guru/callstack.go
+++ b/cmd/guru/callstack.go
@@ -25,7 +25,6 @@ import (
//
// TODO(adonovan): permit user to specify a starting point other than
// the analysis root.
-//
func callstack(q *Query) error {
fset := token.NewFileSet()
lconf := loader.Config{Fset: fset, Build: q.Build}
diff --git a/cmd/guru/describe.go b/cmd/guru/describe.go
index 41189f662..0e4964428 100644
--- a/cmd/guru/describe.go
+++ b/cmd/guru/describe.go
@@ -26,7 +26,6 @@ import (
// - its syntactic category
// - the definition of its referent (for identifiers) [now redundant]
// - its type, fields, and methods (for an expression or type expression)
-//
func describe(q *Query) error {
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
@@ -106,15 +105,15 @@ const (
)
// findInterestingNode classifies the syntax node denoted by path as one of:
-// - an expression, part of an expression or a reference to a constant
-// or variable;
-// - a type, part of a type, or a reference to a named type;
-// - a statement, part of a statement, or a label referring to a statement;
-// - part of a package declaration or import spec.
-// - none of the above.
+// - an expression, part of an expression or a reference to a constant
+// or variable;
+// - a type, part of a type, or a reference to a named type;
+// - a statement, part of a statement, or a label referring to a statement;
+// - part of a package declaration or import spec.
+// - none of the above.
+//
// and returns the most "interesting" associated node, which may be
// the same node, an ancestor or a descendent.
-//
func findInterestingNode(pkginfo *loader.PackageInfo, path []ast.Node) ([]ast.Node, action) {
// TODO(adonovan): integrate with go/types/stdlib_test.go and
// apply this to every AST node we can find to make sure it
diff --git a/cmd/guru/freevars.go b/cmd/guru/freevars.go
index a36d1f80b..b079a3ef4 100644
--- a/cmd/guru/freevars.go
+++ b/cmd/guru/freevars.go
@@ -28,7 +28,6 @@ import (
// Depending on where the resulting function abstraction will go,
// these might be interesting. Perhaps group the results into three
// bands.
-//
func freevars(q *Query) error {
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
diff --git a/cmd/guru/guru.go b/cmd/guru/guru.go
index 2eafca654..7a42aaa3a 100644
--- a/cmd/guru/guru.go
+++ b/cmd/guru/guru.go
@@ -55,12 +55,12 @@ type queryPos struct {
info *loader.PackageInfo // type info for the queried package (nil for fastQueryPos)
}
-// TypeString prints type T relative to the query position.
+// typeString prints type T relative to the query position.
func (qpos *queryPos) typeString(T types.Type) string {
return types.TypeString(T, types.RelativeTo(qpos.info.Pkg))
}
-// ObjectString prints object obj relative to the query position.
+// objectString prints object obj relative to the query position.
func (qpos *queryPos) objectString(obj types.Object) string {
return types.ObjectString(obj, types.RelativeTo(qpos.info.Pkg))
}
@@ -207,12 +207,11 @@ func pkgContainsFile(bp *build.Package, filename string) byte {
return 0 // not found
}
-// ParseQueryPos parses the source query position pos and returns the
+// parseQueryPos parses the source query position pos and returns the
// AST node of the loaded program lprog that it identifies.
// If needExact, it must identify a single AST subtree;
// this is appropriate for queries that allow fairly arbitrary syntax,
// e.g. "describe".
-//
func parseQueryPos(lprog *loader.Program, pos string, needExact bool) (*queryPos, error) {
filename, startOffset, endOffset, err := parsePos(pos)
if err != nil {
@@ -331,16 +330,15 @@ func deref(typ types.Type) types.Type {
// where location is derived from pos.
//
// pos must be one of:
-// - a token.Pos, denoting a position
-// - an ast.Node, denoting an interval
-// - anything with a Pos() method:
-// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
-// - a QueryPos, denoting the extent of the user's query.
-// - nil, meaning no position at all.
+// - a token.Pos, denoting a position
+// - an ast.Node, denoting an interval
+// - anything with a Pos() method:
+// ssa.Member, ssa.Value, ssa.Instruction, types.Object, pointer.Label, etc.
+// - a QueryPos, denoting the extent of the user's query.
+// - nil, meaning no position at all.
//
// The output format is is compatible with the 'gnu'
// compilation-error-regexp in Emacs' compilation mode.
-//
func fprintf(w io.Writer, fset *token.FileSet, pos interface{}, format string, args ...interface{}) {
var start, end token.Pos
switch pos := pos.(type) {
diff --git a/cmd/guru/implements.go b/cmd/guru/implements.go
index dbdba0412..527e88bd7 100644
--- a/cmd/guru/implements.go
+++ b/cmd/guru/implements.go
@@ -24,7 +24,6 @@ import (
// If the selection is a method, 'implements' displays
// the corresponding methods of the types that would have been reported
// by an implements query on the receiver type.
-//
func implements(q *Query) error {
lconf := loader.Config{Build: q.Build}
allowErrors(&lconf)
diff --git a/cmd/guru/main.go b/cmd/guru/main.go
index 4fde4d2d2..7ad083e45 100644
--- a/cmd/guru/main.go
+++ b/cmd/guru/main.go
@@ -4,10 +4,9 @@
// guru: a tool for answering questions about Go source code.
//
-// http://golang.org/s/using-guru
+// http://golang.org/s/using-guru
//
// Run with -help flag or help subcommand for usage information.
-//
package main // import "golang.org/x/tools/cmd/guru"
import (
diff --git a/cmd/guru/pointsto.go b/cmd/guru/pointsto.go
index 782277f37..e7608442c 100644
--- a/cmd/guru/pointsto.go
+++ b/cmd/guru/pointsto.go
@@ -25,7 +25,6 @@ import (
// reflect.Type expression) and their points-to sets.
//
// All printed sets are sorted to ensure determinism.
-//
func pointsto(q *Query) error {
lconf := loader.Config{Build: q.Build}
@@ -113,7 +112,6 @@ func pointsto(q *Query) error {
// ssaValueForIdent returns the ssa.Value for the ast.Ident whose path
// to the root of the AST is path. isAddr reports whether the
// ssa.Value is the address denoted by the ast.Ident, not its value.
-//
func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Object, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
switch obj := obj.(type) {
case *types.Var:
@@ -138,7 +136,6 @@ func ssaValueForIdent(prog *ssa.Program, qinfo *loader.PackageInfo, obj types.Ob
// ssaValueForExpr returns the ssa.Value of the non-ast.Ident
// expression whose path to the root of the AST is path.
-//
func ssaValueForExpr(prog *ssa.Program, qinfo *loader.PackageInfo, path []ast.Node) (value ssa.Value, isAddr bool, err error) {
pkg := prog.Package(qinfo.Pkg)
pkg.SetDebugMode(true)
diff --git a/cmd/guru/pos.go b/cmd/guru/pos.go
index 2e659fe42..9ae4d16b6 100644
--- a/cmd/guru/pos.go
+++ b/cmd/guru/pos.go
@@ -37,7 +37,6 @@ func parseOctothorpDecimal(s string) int {
//
// (Numbers without a '#' prefix are reserved for future use,
// e.g. to indicate line/column positions.)
-//
func parsePos(pos string) (filename string, startOffset, endOffset int, err error) {
if pos == "" {
err = fmt.Errorf("no source position specified")
@@ -71,7 +70,6 @@ func parsePos(pos string) (filename string, startOffset, endOffset int, err erro
// fileOffsetToPos translates the specified file-relative byte offsets
// into token.Pos form. It returns an error if the file was not found
// or the offsets were out of bounds.
-//
func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end token.Pos, err error) {
// Range check [start..end], inclusive of both end-points.
@@ -94,7 +92,6 @@ func fileOffsetToPos(file *token.File, startOffset, endOffset int) (start, end t
// sameFile returns true if x and y have the same basename and denote
// the same file.
-//
func sameFile(x, y string) bool {
if filepath.Base(x) == filepath.Base(y) { // (optimisation)
if xi, err := os.Stat(x); err == nil {
diff --git a/cmd/guru/referrers.go b/cmd/guru/referrers.go
index 9d1507157..d75196bf9 100644
--- a/cmd/guru/referrers.go
+++ b/cmd/guru/referrers.go
@@ -617,7 +617,6 @@ func findObject(fset *token.FileSet, info *types.Info, objposn token.Position) t
// same reports whether x and y are identical, or both are PkgNames
// that import the same Package.
-//
func sameObj(x, y types.Object) bool {
if x == y {
return true
@@ -704,7 +703,7 @@ type referrersPackageResult struct {
refs []*ast.Ident // set of all other references to it
}
-// forEachRef calls f(id, text) for id in r.refs, in order.
+// foreachRef calls f(id, text) for id in r.refs, in order.
// Text is the text of the line on which id appears.
func (r *referrersPackageResult) foreachRef(f func(id *ast.Ident, text string)) {
// Show referring lines, like grep.
diff --git a/cmd/guru/serial/serial.go b/cmd/guru/serial/serial.go
index 5f097c51a..082e6cf0d 100644
--- a/cmd/guru/serial/serial.go
+++ b/cmd/guru/serial/serial.go
@@ -8,20 +8,20 @@
// This table shows the types of objects in the result stream for each
// query type.
//
-// Query Result stream
-// ----- -------------
-// callees Callees
-// callers Caller ...
-// callstack CallStack
-// definition Definition
-// describe Describe
-// freevars FreeVar ...
-// implements Implements
-// peers Peers
-// pointsto PointsTo ...
-// referrers ReferrersInitial ReferrersPackage ...
-// what What
-// whicherrs WhichErrs
+// Query Result stream
+// ----- -------------
+// callees Callees
+// callers Caller ...
+// callstack CallStack
+// definition Definition
+// describe Describe
+// freevars FreeVar ...
+// implements Implements
+// peers Peers
+// pointsto PointsTo ...
+// referrers ReferrersInitial ReferrersPackage ...
+// what What
+// whicherrs WhichErrs
//
// All 'pos' strings in the output are of the form "file:line:col",
// where line is the 1-based line number and col is the 1-based byte index.
@@ -113,7 +113,6 @@ type FreeVar struct {
// It describes the queried type, the set of named non-empty interface
// types to which it is assignable, and the set of named/*named types
// (concrete or non-empty interface) which may be assigned to it.
-//
type Implements struct {
T ImplementsType `json:"type,omitempty"` // the queried type
AssignableTo []ImplementsType `json:"to,omitempty"` // types assignable to T
@@ -161,14 +160,13 @@ type What struct {
//
// A "label" is an object that may be pointed to by a pointer, map,
// channel, 'func', slice or interface. Labels include:
-// - functions
-// - globals
-// - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s))
-// - stack- and heap-allocated variables (including composite literals)
-// - arrays allocated by append()
-// - channels, maps and arrays created by make()
-// - and their subelements, e.g. "alloc.y[*].z"
-//
+// - functions
+// - globals
+// - arrays created by literals (e.g. []byte("foo")) and conversions ([]byte(s))
+// - stack- and heap-allocated variables (including composite literals)
+// - arrays allocated by append()
+// - channels, maps and arrays created by make()
+// - and their subelements, e.g. "alloc.y[*].z"
type PointsToLabel struct {
Pos string `json:"pos"` // location of syntax that allocated the object
Desc string `json:"desc"` // description of the label
@@ -183,7 +181,6 @@ type PointsToLabel struct {
// concrete type that is a pointer, the PTS entry describes the labels
// it may point to. The same is true for reflect.Values, except the
// dynamic types needn't be concrete.
-//
type PointsTo struct {
Type string `json:"type"` // (concrete) type of the pointer
NamePos string `json:"namepos,omitempty"` // location of type defn, if Named
diff --git a/cmd/guru/what.go b/cmd/guru/what.go
index 82495b4f8..7ebabbd82 100644
--- a/cmd/guru/what.go
+++ b/cmd/guru/what.go
@@ -24,7 +24,6 @@ import (
// It is intended to be a very low-latency query callable from GUI
// tools, e.g. to populate a menu of options of slower queries about
// the selected location.
-//
func what(q *Query) error {
qpos, err := fastQueryPos(q.Build, q.Pos)
if err != nil {
@@ -170,7 +169,6 @@ func what(q *Query) error {
//
// TODO(adonovan): what about _test.go files that are not part of the
// package?
-//
func guessImportPath(filename string, buildContext *build.Context) (srcdir, importPath string, err error) {
absFile, err := filepath.Abs(filename)
if err != nil {
diff --git a/cmd/present/dir.go b/cmd/present/dir.go
index 17736ec14..93db12bf4 100644
--- a/cmd/present/dir.go
+++ b/cmd/present/dir.go
@@ -7,6 +7,7 @@ package main
import (
"html/template"
"io"
+ "io/fs"
"log"
"net"
"net/http"
@@ -65,9 +66,9 @@ var (
contentTemplate map[string]*template.Template
)
-func initTemplates(base string) error {
+func initTemplates(fsys fs.FS) error {
// Locate the template file.
- actionTmpl := filepath.Join(base, "templates/action.tmpl")
+ actionTmpl := "templates/action.tmpl"
contentTemplate = make(map[string]*template.Template)
@@ -75,19 +76,19 @@ func initTemplates(base string) error {
".slide": "slides.tmpl",
".article": "article.tmpl",
} {
- contentTmpl = filepath.Join(base, "templates", contentTmpl)
+ contentTmpl = "templates/" + contentTmpl
// Read and parse the input.
tmpl := present.Template()
tmpl = tmpl.Funcs(template.FuncMap{"playable": playable})
- if _, err := tmpl.ParseFiles(actionTmpl, contentTmpl); err != nil {
+ if _, err := tmpl.ParseFS(fsys, actionTmpl, contentTmpl); err != nil {
return err
}
contentTemplate[ext] = tmpl
}
var err error
- dirListTemplate, err = template.ParseFiles(filepath.Join(base, "templates/dir.tmpl"))
+ dirListTemplate, err = template.ParseFS(fsys, "templates/dir.tmpl")
return err
}
diff --git a/cmd/present/doc.go b/cmd/present/doc.go
index e66984edb..654553507 100644
--- a/cmd/present/doc.go
+++ b/cmd/present/doc.go
@@ -14,35 +14,36 @@ https://golang.org/wiki/NativeClient
To use with App Engine, copy the files in the tools/cmd/present directory to the
root of your application and create an app.yaml file similar to this:
- runtime: go111
-
- handlers:
- - url: /favicon.ico
- static_files: static/favicon.ico
- upload: static/favicon.ico
- - url: /static
- static_dir: static
- - url: /.*
- script: auto
-
- # nobuild_files is a regexp that identifies which files to not build. It
- # is useful for embedding static assets like code snippets and preventing
- # them from producing build errors for your project.
- nobuild_files: [path regexp for talk materials]
+ runtime: go111
+
+ handlers:
+ - url: /favicon.ico
+ static_files: static/favicon.ico
+ upload: static/favicon.ico
+ - url: /static
+ static_dir: static
+ - url: /.*
+ script: auto
+
+ # nobuild_files is a regexp that identifies which files to not build. It
+ # is useful for embedding static assets like code snippets and preventing
+ # them from producing build errors for your project.
+ nobuild_files: [path regexp for talk materials]
When running on App Engine, content will be served from the ./content/
subdirectory.
Present then can be tested in a local App Engine environment with
- GAE_ENV=standard go run .
+ GAE_ENV=standard go run .
And deployed using
- gcloud app deploy
+ gcloud app deploy
Input files are named foo.extension, where "extension" defines the format of
the generated output. The supported formats are:
+
.slide // HTML5 slide presentation
.article // article format, such as a blog post
diff --git a/cmd/present/main.go b/cmd/present/main.go
index b89e11fe5..340025276 100644
--- a/cmd/present/main.go
+++ b/cmd/present/main.go
@@ -5,9 +5,10 @@
package main
import (
+ "embed"
"flag"
"fmt"
- "go/build"
+ "io/fs"
"log"
"net"
"net/http"
@@ -18,17 +19,17 @@ import (
"golang.org/x/tools/present"
)
-const basePkg = "golang.org/x/tools/cmd/present"
-
var (
httpAddr = flag.String("http", "127.0.0.1:3999", "HTTP service address (e.g., '127.0.0.1:3999')")
originHost = flag.String("orighost", "", "host component of web origin URL (e.g., 'localhost')")
basePath = flag.String("base", "", "base path for slide template and static resources")
contentPath = flag.String("content", ".", "base path for presentation content")
usePlayground = flag.Bool("use_playground", false, "run code snippets using play.golang.org; if false, run them locally and deliver results by WebSocket transport")
- nativeClient = flag.Bool("nacl", false, "use Native Client environment playground (prevents non-Go code execution) when using local WebSocket transport")
)
+//go:embed static templates
+var embedFS embed.FS
+
func main() {
flag.BoolVar(&present.PlayEnabled, "play", true, "enable playground (permit execution of arbitrary user code)")
flag.BoolVar(&present.NotesEnabled, "notes", false, "enable presenter notes (press 'N' from the browser to display them)")
@@ -50,16 +51,11 @@ func main() {
*contentPath = "./content/"
}
- if *basePath == "" {
- p, err := build.Default.Import(basePkg, "", build.FindOnly)
- if err != nil {
- fmt.Fprintf(os.Stderr, "Couldn't find gopresent files: %v\n", err)
- fmt.Fprintf(os.Stderr, basePathMessage, basePkg)
- os.Exit(1)
- }
- *basePath = p.Dir
+ var fsys fs.FS = embedFS
+ if *basePath != "" {
+ fsys = os.DirFS(*basePath)
}
- err := initTemplates(*basePath)
+ err := initTemplates(fsys)
if err != nil {
log.Fatalf("Failed to parse templates: %v", err)
}
@@ -98,11 +94,11 @@ func main() {
}
}
- initPlayground(*basePath, origin)
- http.Handle("/static/", http.FileServer(http.Dir(*basePath)))
+ initPlayground(fsys, origin)
+ http.Handle("/static/", http.FileServer(http.FS(fsys)))
if !ln.Addr().(*net.TCPAddr).IP.IsLoopback() &&
- present.PlayEnabled && !*nativeClient && !*usePlayground {
+ present.PlayEnabled && !*usePlayground {
log.Print(localhostWarning)
}
diff --git a/cmd/present/play.go b/cmd/present/play.go
index 2e53f1474..fb24fabfc 100644
--- a/cmd/present/play.go
+++ b/cmd/present/play.go
@@ -7,11 +7,9 @@ package main
import (
"bytes"
"fmt"
- "io/ioutil"
+ "io/fs"
"net/http"
"net/url"
- "path/filepath"
- "runtime"
"time"
"golang.org/x/tools/godoc/static"
@@ -31,7 +29,7 @@ var scripts = []string{"jquery.js", "jquery-ui.js", "playground.js", "play.js"}
// playScript registers an HTTP handler at /play.js that serves all the
// scripts specified by the variable above, and appends a line that
// initializes the playground with the specified transport.
-func playScript(root, transport string) {
+func playScript(fsys fs.FS, transport string) {
modTime := time.Now()
var buf bytes.Buffer
for _, p := range scripts {
@@ -39,7 +37,7 @@ func playScript(root, transport string) {
buf.WriteString(s)
continue
}
- b, err := ioutil.ReadFile(filepath.Join(root, "static", p))
+ b, err := fs.ReadFile(fsys, "static/"+p)
if err != nil {
panic(err)
}
@@ -53,27 +51,16 @@ func playScript(root, transport string) {
})
}
-func initPlayground(basepath string, origin *url.URL) {
+func initPlayground(fsys fs.FS, origin *url.URL) {
if !present.PlayEnabled {
return
}
if *usePlayground {
- playScript(basepath, "HTTPTransport")
+ playScript(fsys, "HTTPTransport")
return
}
- if *nativeClient {
- // When specifying nativeClient, non-Go code cannot be executed
- // because the NaCl setup doesn't support doing so.
- socket.RunScripts = false
- socket.Environ = func() []string {
- if runtime.GOARCH == "amd64" {
- return environ("GOOS=nacl", "GOARCH=amd64p32")
- }
- return environ("GOOS=nacl")
- }
- }
- playScript(basepath, "SocketTransport")
+ playScript(fsys, "SocketTransport")
http.Handle("/socket", socket.NewHandler(origin))
}
diff --git a/cmd/present/static/article.css b/cmd/present/static/article.css
index 52fd73737..b577aaf2e 100644
--- a/cmd/present/static/article.css
+++ b/cmd/present/static/article.css
@@ -102,29 +102,33 @@ div#footer {
div.code,
div.output {
+ margin: 0;
+}
+
+pre {
margin: 20px 20px 20px 40px;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
-div.output {
+div.output pre {
padding: 10px;
}
-div.code {
+pre {
background: white;
}
-div.output {
+div.output pre {
background: black;
}
-div.output .stdout {
+div.output .stdout pre {
color: #e6e6e6;
}
-div.output .stderr {
+div.output .stderr pre {
color: rgb(244, 74, 63);
}
-div.output .system {
+div.output .system pre {
color: rgb(255, 209, 77);
}
diff --git a/cmd/present/static/styles.css b/cmd/present/static/styles.css
index 5edfde934..47c9f196d 100644
--- a/cmd/present/static/styles.css
+++ b/cmd/present/static/styles.css
@@ -242,7 +242,7 @@
margin-bottom: 100px !important;
}
- div.code {
+ pre {
background: rgb(240, 240, 240);
}
@@ -359,7 +359,12 @@ li {
margin: 0 0 0.5em 0;
}
-div.code {
+div.code, div.output {
+ margin: 0;
+ padding: 0;
+}
+
+pre {
padding: 5px 10px;
margin-top: 20px;
margin-bottom: 20px;
@@ -367,10 +372,6 @@ div.code {
background: rgb(240, 240, 240);
border: 1px solid rgb(224, 224, 224);
-}
-pre {
- margin: 0;
- padding: 0;
font-family: 'Droid Sans Mono', 'Courier New', monospace;
font-size: 18px;
@@ -393,6 +394,10 @@ code {
color: black;
}
+pre code {
+ font-size: 100%;
+}
+
article > .image,
article > .video {
text-align: center;
@@ -433,7 +438,7 @@ p.link {
}
/* Code */
-div.code {
+pre {
outline: 0px solid transparent;
}
div.playground {
diff --git a/cmd/present2md/main.go b/cmd/present2md/main.go
index 64be64b97..748b041e4 100644
--- a/cmd/present2md/main.go
+++ b/cmd/present2md/main.go
@@ -18,7 +18,6 @@
//
// present2md your.article
// present2md -w *.article
-//
package main
import (
diff --git a/cmd/signature-fuzzer/fuzz-runner/runner.go b/cmd/signature-fuzzer/fuzz-runner/runner.go
index 4e5b413f3..b77b218f5 100644
--- a/cmd/signature-fuzzer/fuzz-runner/runner.go
+++ b/cmd/signature-fuzzer/fuzz-runner/runner.go
@@ -107,7 +107,7 @@ func docmd(cmd []string, dir string) int {
return st
}
-// docodmout forks and execs command 'cmd' in dir 'dir', redirecting
+// docmdout forks and execs command 'cmd' in dir 'dir', redirecting
// stderr and stdout from the execution to file 'outfile'.
func docmdout(cmd []string, dir string, outfile string) int {
of, err := os.OpenFile(outfile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
diff --git a/cmd/signature-fuzzer/internal/fuzz-generator/generator.go b/cmd/signature-fuzzer/internal/fuzz-generator/generator.go
index bbe53fb10..ba5f05525 100644
--- a/cmd/signature-fuzzer/internal/fuzz-generator/generator.go
+++ b/cmd/signature-fuzzer/internal/fuzz-generator/generator.go
@@ -1564,10 +1564,10 @@ func (s *genstate) emitParamChecks(f *funcdef, b *bytes.Buffer, pidx int, value
// emitDeferChecks creates code like
//
-// defer func(...args...) {
-// check arg
-// check param
-// }(...)
+// defer func(...args...) {
+// check arg
+// check param
+// }(...)
//
// where we randomly choose to either pass a param through to the
// function literal, or have the param captured by the closure, then
diff --git a/cmd/splitdwarf/splitdwarf.go b/cmd/splitdwarf/splitdwarf.go
index a13b9f316..9729b0b7a 100644
--- a/cmd/splitdwarf/splitdwarf.go
+++ b/cmd/splitdwarf/splitdwarf.go
@@ -6,7 +6,6 @@
// +build !js,!nacl,!plan9,!solaris,!windows
/*
-
Splitdwarf uncompresses and copies the DWARF segment of a Mach-O
executable into the "dSYM" file expected by lldb and ports of gdb
on OSX.
@@ -17,7 +16,6 @@ Unless a dSYM file name is provided on the command line,
splitdwarf will place it where the OSX tools expect it, in
"<osxMachoFile>.dSYM/Contents/Resources/DWARF/<osxMachoFile>",
creating directories as necessary.
-
*/
package main // import "golang.org/x/tools/cmd/splitdwarf"
@@ -94,7 +92,7 @@ for input_exe need to allow writing.
// IndSym Offset = file offset (within link edit section) of 4-byte indices within symtab.
//
// Section __TEXT.__symbol_stub1.
- // Offset and size (Reserved2) locate and describe a table for thios section.
+ // Offset and size (Reserved2) locate and describe a table for this section.
// Symbols beginning at IndirectSymIndex (Reserved1) (see LC_DYSYMTAB.IndSymOffset) refer to this table.
// (These table entries are apparently PLTs [Procedure Linkage Table/Trampoline])
//
@@ -184,7 +182,7 @@ for input_exe need to allow writing.
oldsym := symtab.Syms[ii]
newsymtab.Syms = append(newsymtab.Syms, oldsym)
- linkeditsyms = append(linkeditsyms, macho.Nlist64{Name: uint32(linkeditstringcur),
+ linkeditsyms = append(linkeditsyms, macho.Nlist64{Name: linkeditstringcur,
Type: oldsym.Type, Sect: oldsym.Sect, Desc: oldsym.Desc, Value: oldsym.Value})
linkeditstringcur += uint32(len(oldsym.Name)) + 1
linkeditstrings = append(linkeditstrings, oldsym.Name)
diff --git a/cmd/ssadump/main.go b/cmd/ssadump/main.go
index fee931b1c..cfb9122b2 100644
--- a/cmd/ssadump/main.go
+++ b/cmd/ssadump/main.go
@@ -47,7 +47,7 @@ func init() {
}
const usage = `SSA builder and interpreter.
-Usage: ssadump [-build=[DBCSNFL]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
+Usage: ssadump [-build=[DBCSNFLG]] [-test] [-run] [-interp=[TR]] [-arg=...] package...
Use -help flag to display options.
Examples:
@@ -55,7 +55,8 @@ Examples:
% ssadump -build=F -test fmt # dump SSA form of a package and its tests
% ssadump -run -interp=T hello.go # interpret a program, with tracing
-The -run flag causes ssadump to run the first package named main.
+The -run flag causes ssadump to build the code in a runnable form and run the first
+package named main.
Interpretation of the standard "testing" package is no longer supported.
`
@@ -130,6 +131,11 @@ func doMain() error {
return fmt.Errorf("packages contain errors")
}
+ // Turn on instantiating generics during build if the program will be run.
+ if *runFlag {
+ mode |= ssa.InstantiateGenerics
+ }
+
// Create SSA-form program representation.
prog, pkgs := ssautil.AllPackages(initial, mode)
@@ -151,12 +157,15 @@ func doMain() error {
// Build SSA for all packages.
prog.Build()
- // The interpreter needs the runtime package.
- // It is a limitation of go/packages that
- // we cannot add "runtime" to its initial set,
- // we can only check that it is present.
- if prog.ImportedPackage("runtime") == nil {
- return fmt.Errorf("-run: program does not depend on runtime")
+ // Earlier versions of the interpreter needed the runtime
+ // package; however, interp cannot handle unsafe constructs
+ // used during runtime's package initialization at the moment.
+ // The key construct blocking support is:
+ // *((*T)(unsafe.Pointer(p)))
+ // Unfortunately, this means only trivial programs can be
+ // interpreted by ssadump.
+ if prog.ImportedPackage("runtime") != nil {
+ return fmt.Errorf("-run: program depends on runtime package (interpreter can run only trivial programs)")
}
if runtime.GOARCH != build.Default.GOARCH {
diff --git a/cmd/stress/stress.go b/cmd/stress/stress.go
index 9ba6ef35f..c4a187212 100644
--- a/cmd/stress/stress.go
+++ b/cmd/stress/stress.go
@@ -8,7 +8,9 @@
// The stress utility is intended for catching sporadic failures.
// It runs a given process in parallel in a loop and collects any failures.
// Usage:
-// $ stress ./fmt.test -test.run=TestSometing -test.cpu=10
+//
+// $ stress ./fmt.test -test.run=TestSometing -test.cpu=10
+//
// You can also specify a number of parallel processes with -p flag;
// instruct the utility to not kill hanged processes for gdb attach;
// or specify the failure output you are looking for (if you want to
diff --git a/cmd/stringer/endtoend_test.go b/cmd/stringer/endtoend_test.go
index 5b969a52e..29eb91860 100644
--- a/cmd/stringer/endtoend_test.go
+++ b/cmd/stringer/endtoend_test.go
@@ -14,15 +14,14 @@ import (
"fmt"
"go/build"
"io"
- "io/ioutil"
"os"
"os/exec"
"path"
"path/filepath"
"strings"
+ "sync"
"testing"
- "golang.org/x/tools/internal/testenv"
"golang.org/x/tools/internal/typeparams"
)
@@ -31,9 +30,22 @@ import (
// we run stringer -type X and then compile and run the program. The resulting
// binary panics if the String method for X is not correct, including for error cases.
+func TestMain(m *testing.M) {
+ if os.Getenv("STRINGER_TEST_IS_STRINGER") != "" {
+ main()
+ os.Exit(0)
+ }
+
+ // Inform subprocesses that they should run the cmd/stringer main instead of
+ // running tests. It's a close approximation to building and running the real
+ // command, and much less complicated and expensive to build and clean up.
+ os.Setenv("STRINGER_TEST_IS_STRINGER", "1")
+
+ os.Exit(m.Run())
+}
+
func TestEndToEnd(t *testing.T) {
- dir, stringer := buildStringer(t)
- defer os.RemoveAll(dir)
+ stringer := stringerPath(t)
// Read the testdata directory.
fd, err := os.Open("testdata")
if err != nil {
@@ -65,7 +77,7 @@ func TestEndToEnd(t *testing.T) {
t.Logf("cgo is not enabled for %s", name)
continue
}
- stringerCompileAndRun(t, dir, stringer, typeName(name), name)
+ stringerCompileAndRun(t, t.TempDir(), stringer, typeName(name), name)
}
}
@@ -92,8 +104,8 @@ func moreTests(t *testing.T, dirname, prefix string) []string {
// TestTags verifies that the -tags flag works as advertised.
func TestTags(t *testing.T) {
- dir, stringer := buildStringer(t)
- defer os.RemoveAll(dir)
+ stringer := stringerPath(t)
+ dir := t.TempDir()
var (
protectedConst = []byte("TagProtected")
output = filepath.Join(dir, "const_string.go")
@@ -113,7 +125,7 @@ func TestTags(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- result, err := ioutil.ReadFile(output)
+ result, err := os.ReadFile(output)
if err != nil {
t.Fatal(err)
}
@@ -128,7 +140,7 @@ func TestTags(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- result, err = ioutil.ReadFile(output)
+ result, err = os.ReadFile(output)
if err != nil {
t.Fatal(err)
}
@@ -140,8 +152,8 @@ func TestTags(t *testing.T) {
// TestConstValueChange verifies that if a constant value changes and
// the stringer code is not regenerated, we'll get a compiler error.
func TestConstValueChange(t *testing.T) {
- dir, stringer := buildStringer(t)
- defer os.RemoveAll(dir)
+ stringer := stringerPath(t)
+ dir := t.TempDir()
source := filepath.Join(dir, "day.go")
err := copy(source, filepath.Join("testdata", "day.go"))
if err != nil {
@@ -179,21 +191,20 @@ func TestConstValueChange(t *testing.T) {
}
}
-// buildStringer creates a temporary directory and installs stringer there.
-func buildStringer(t *testing.T) (dir string, stringer string) {
- t.Helper()
- testenv.NeedsTool(t, "go")
+var exe struct {
+ path string
+ err error
+ once sync.Once
+}
- dir, err := ioutil.TempDir("", "stringer")
- if err != nil {
- t.Fatal(err)
- }
- stringer = filepath.Join(dir, "stringer.exe")
- err = run("go", "build", "-o", stringer)
- if err != nil {
- t.Fatalf("building stringer: %s", err)
+func stringerPath(t *testing.T) string {
+ exe.once.Do(func() {
+ exe.path, exe.err = os.Executable()
+ })
+ if exe.err != nil {
+ t.Fatal(exe.err)
}
- return dir, stringer
+ return exe.path
}
// stringerCompileAndRun runs stringer for the named file and compiles and
diff --git a/cmd/stringer/golden_test.go b/cmd/stringer/golden_test.go
index b29763174..250af05f9 100644
--- a/cmd/stringer/golden_test.go
+++ b/cmd/stringer/golden_test.go
@@ -10,7 +10,6 @@
package main
import (
- "io/ioutil"
"os"
"path/filepath"
"strings"
@@ -452,12 +451,7 @@ func (i Token) String() string {
func TestGolden(t *testing.T) {
testenv.NeedsTool(t, "go")
- dir, err := ioutil.TempDir("", "stringer")
- if err != nil {
- t.Error(err)
- }
- defer os.RemoveAll(dir)
-
+ dir := t.TempDir()
for _, test := range golden {
g := Generator{
trimPrefix: test.trimPrefix,
@@ -466,7 +460,7 @@ func TestGolden(t *testing.T) {
input := "package test\n" + test.input
file := test.name + ".go"
absFile := filepath.Join(dir, file)
- err := ioutil.WriteFile(absFile, []byte(input), 0644)
+ err := os.WriteFile(absFile, []byte(input), 0644)
if err != nil {
t.Error(err)
}
diff --git a/cmd/stringer/stringer.go b/cmd/stringer/stringer.go
index 558a234d6..998d1a51b 100644
--- a/cmd/stringer/stringer.go
+++ b/cmd/stringer/stringer.go
@@ -5,7 +5,9 @@
// Stringer is a tool to automate the creation of methods that satisfy the fmt.Stringer
// interface. Given the name of a (signed or unsigned) integer type T that has constants
// defined, stringer will create a new self-contained Go source file implementing
+//
// func (t T) String() string
+//
// The file is created in the same package and directory as the package that defines T.
// It has helpful defaults designed for use with go generate.
//
@@ -74,7 +76,6 @@ import (
"go/format"
"go/token"
"go/types"
- "io/ioutil"
"log"
"os"
"path/filepath"
@@ -164,7 +165,7 @@ func main() {
baseName := fmt.Sprintf("%s_string.go", types[0])
outputName = filepath.Join(dir, strings.ToLower(baseName))
}
- err := ioutil.WriteFile(outputName, src, 0644)
+ err := os.WriteFile(outputName, src, 0644)
if err != nil {
log.Fatalf("writing output: %s", err)
}
@@ -215,7 +216,7 @@ type Package struct {
// parsePackage exits if there is an error.
func (g *Generator) parsePackage(patterns []string, tags []string) {
cfg := &packages.Config{
- Mode: packages.LoadSyntax,
+ Mode: packages.NeedName | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedSyntax,
// TODO: Need to think about constants in test files. Maybe write type_string_test.go
// in a separate pass? For later.
Tests: false,
@@ -570,6 +571,7 @@ func (g *Generator) buildOneRun(runs [][]Value, typeName string) {
}
// Arguments to format are:
+//
// [1]: type name
// [2]: size of index element (8 for uint8 etc.)
// [3]: less than zero check (for signed types)
diff --git a/cmd/toolstash/buildall b/cmd/toolstash/buildall
index 0c6492c9e..4fc22f7f8 100755
--- a/cmd/toolstash/buildall
+++ b/cmd/toolstash/buildall
@@ -38,10 +38,10 @@ if [ "$pattern" = "" ]; then
fi
targets="$(go tool dist list; echo linux/386/softfloat)"
-targets="$(echo "$targets" | tr '/' '-' | sort | egrep "$pattern" | egrep -v 'android-arm|darwin-arm')"
+targets="$(echo "$targets" | tr '/' '-' | sort | grep -E "$pattern" | grep -E -v 'android-arm|darwin-arm')"
# put linux first in the target list to get all the architectures up front.
-targets="$(echo "$targets" | egrep 'linux') $(echo "$targets" | egrep -v 'linux')"
+targets="$(echo "$targets" | grep -E 'linux') $(echo "$targets" | grep -E -v 'linux')"
if [ "$sete" = true ]; then
set -e
diff --git a/cmd/toolstash/main.go b/cmd/toolstash/main.go
index 4c3494201..ddb1905ae 100644
--- a/cmd/toolstash/main.go
+++ b/cmd/toolstash/main.go
@@ -12,14 +12,14 @@
// toolstash [-n] [-v] [-t] go run x.go
// toolstash [-n] [-v] [-t] [-cmp] compile x.go
//
-// The toolstash command manages a ``stashed'' copy of the Go toolchain
+// The toolstash command manages a “stashed” copy of the Go toolchain
// kept in $GOROOT/pkg/toolstash. In this case, the toolchain means the
// tools available with the 'go tool' command as well as the go, godoc, and gofmt
// binaries.
//
-// The command ``toolstash save'', typically run when the toolchain is known to be working,
+// The command “toolstash save”, typically run when the toolchain is known to be working,
// copies the toolchain from its installed location to the toolstash directory.
-// Its inverse, ``toolchain restore'', typically run when the toolchain is known to be broken,
+// Its inverse, “toolchain restore”, typically run when the toolchain is known to be broken,
// copies the toolchain from the toolstash directory back to the installed locations.
// If additional arguments are given, the save or restore applies only to the named tools.
// Otherwise, it applies to all tools.
@@ -39,7 +39,7 @@
// The -t flag causes toolstash to print the time elapsed during while the
// command ran.
//
-// Comparing
+// # Comparing
//
// The -cmp flag causes toolstash to run both the installed and the stashed
// copy of an assembler or compiler and check that they produce identical
@@ -65,7 +65,7 @@
// go tool dist install cmd/compile # install compiler only
// toolstash -cmp compile x.go
//
-// Go Command Integration
+// # Go Command Integration
//
// The go command accepts a -toolexec flag that specifies a program
// to use to run the build tools.
@@ -97,7 +97,7 @@
// # If not, restore, in order to keep working on Go code.
// toolstash restore
//
-// Version Skew
+// # Version Skew
//
// The Go tools write the current Go version to object files, and (outside
// release branches) that version includes the hash and time stamp
@@ -118,9 +118,8 @@
// echo devel >$GOROOT/VERSION
//
// The version can be arbitrary text, but to pass all.bash's API check, it must
-// contain the substring ``devel''. The VERSION file must be created before
+// contain the substring “devel”. The VERSION file must be created before
// building either version of the toolchain.
-//
package main // import "golang.org/x/tools/cmd/toolstash"
import (