pax_global_header00006660000000000000000000000064147610317230014516gustar00rootroot0000000000000052 comment=cdc6a283840c4b2d762ccefc7b4197aeaf5e6c9d golang-golang-x-tools-0.25.0+ds/000077500000000000000000000000001476103172300163235ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/.gitattributes000066400000000000000000000005311476103172300212150ustar00rootroot00000000000000# Treat all files in this repo as binary, with no git magic updating # line endings. Windows users contributing to Go will need to use a # modern version of git and editors capable of LF line endings. # # We'll prevent accidental CRLF line endings from entering the repo # via the git-review gofmt checks. # # See golang.org/issue/9281 * -text golang-golang-x-tools-0.25.0+ds/.gitignore000066400000000000000000000001251476103172300203110ustar00rootroot00000000000000# Add no patterns to .gitignore except for files generated by the build. last-change golang-golang-x-tools-0.25.0+ds/.prettierrc000066400000000000000000000000631476103172300205060ustar00rootroot00000000000000{ "singleQuote": true, "trailingComma": "es5" }golang-golang-x-tools-0.25.0+ds/CONTRIBUTING.md000066400000000000000000000016211476103172300205540ustar00rootroot00000000000000# Contributing to Go Go is an open source project. It is the work of hundreds of contributors. We appreciate your help! ## Filing issues When [filing an issue](https://golang.org/issue/new), make sure to answer these five questions: 1. What version of Go are you using (`go version`)? 2. What operating system and processor architecture are you using? 3. What did you do? 4. What did you expect to see? 5. What did you see instead? General questions should go to the [golang-nuts mailing list](https://groups.google.com/group/golang-nuts) instead of the issue tracker. The gophers there will answer or ask you to file an issue if you've tripped over a bug. ## Contributing code Please read the [Contribution Guidelines](https://golang.org/doc/contribute.html) before sending patches. Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file. golang-golang-x-tools-0.25.0+ds/LICENSE000066400000000000000000000026551476103172300173400ustar00rootroot00000000000000Copyright 2009 The Go Authors. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of Google LLC nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. golang-golang-x-tools-0.25.0+ds/PATENTS000066400000000000000000000024271476103172300173710ustar00rootroot00000000000000Additional IP Rights Grant (Patents) "This implementation" means the copyrightable works distributed by Google as part of the Go project. Google hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, transfer and otherwise run, modify and propagate the contents of this implementation of Go, where such license applies only to those patent claims, both currently owned or controlled by Google and acquired in the future, licensable by Google that are necessarily infringed by this implementation of Go. This grant does not include claims that would be infringed only as a consequence of further modification of this implementation. If you or your agent or exclusive licensee institute or order or agree to the institution of patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that this implementation of Go or any code incorporated within this implementation of Go constitutes direct or contributory patent infringement, or inducement of patent infringement, then any patent rights granted to you under this License for this implementation of Go shall terminate as of the date such litigation is filed. golang-golang-x-tools-0.25.0+ds/README.md000066400000000000000000000067101476103172300176060ustar00rootroot00000000000000# Go Tools [![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/golang.org/x/tools) This repository provides the `golang.org/x/tools` module, comprising various tools and packages mostly for static analysis of Go programs, some of which are listed below. Use the "Go reference" link above for more information about any package. It also contains the [`golang.org/x/tools/gopls`](https://pkg.go.dev/golang.org/x/tools/gopls) module, whose root package is a language-server protocol (LSP) server for Go. An LSP server analyses the source code of a project and responds to requests from a wide range of editors such as VSCode and Vim, allowing them to support IDE-like functionality. Selected commands: - `cmd/goimports` formats a Go program like `go fmt` and additionally inserts import statements for any packages required by the file after it is edited. - `cmd/callgraph` prints the call graph of a Go program. - `cmd/digraph` is a utility for manipulating directed graphs in textual notation. - `cmd/stringer` generates declarations (including a `String` method) for "enum" types. - `cmd/toolstash` is a utility to simplify working with multiple versions of the Go toolchain. These commands may be fetched with a command such as ``` go install golang.org/x/tools/cmd/goimports@latest ``` Selected packages: - `go/ssa` provides a static single-assignment form (SSA) intermediate representation (IR) for Go programs, similar to a typical compiler, for use by analysis tools. - `go/packages` provides a simple interface for loading, parsing, and type checking a complete Go program from source code. - `go/analysis` provides a framework for modular static analysis of Go programs. - `go/callgraph` provides call graphs of Go programs using a variety of algorithms with different trade-offs. - `go/ast/inspector` provides an optimized means of traversing a Go parse tree for use in analysis tools. - `go/cfg` provides a simple control-flow graph (CFG) for a Go function. - `go/expect` reads Go source files used as test inputs and interprets special comments within them as queries or assertions for testing. - `go/gcexportdata` and `go/gccgoexportdata` read and write the binary files containing type information used by the standard and `gccgo` compilers. - `go/types/objectpath` provides a stable naming scheme for named entities ("objects") in the `go/types` API. Numerous other packages provide more esoteric functionality. ## Contributing This repository uses Gerrit for code changes. To learn how to submit changes, see https://golang.org/doc/contribute.html. The main issue tracker for the tools repository is located at https://github.com/golang/go/issues. Prefix your issue with "x/tools/(your subdir):" in the subject line, so it is easy to find. ### JavaScript and CSS Formatting This repository uses [prettier](https://prettier.io/) to format JS and CSS files. The version of `prettier` used is 1.18.2. It is encouraged that all JS and CSS code be run through this before submitting a change. However, it is not a strict requirement enforced by CI. golang-golang-x-tools-0.25.0+ds/benchmark/000077500000000000000000000000001476103172300202555ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/benchmark/parse/000077500000000000000000000000001476103172300213675ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/benchmark/parse/parse.go000066400000000000000000000066131476103172300230360ustar00rootroot00000000000000// Copyright 2014 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 parse provides support for parsing benchmark results as // generated by 'go test -bench'. package parse // import "golang.org/x/tools/benchmark/parse" import ( "bufio" "bytes" "fmt" "io" "strconv" "strings" ) // Flags used by Benchmark.Measured to indicate // which measurements a Benchmark contains. const ( NsPerOp = 1 << iota MBPerS AllocedBytesPerOp AllocsPerOp ) // Benchmark is one run of a single benchmark. type Benchmark struct { Name string // benchmark name N int // number of iterations NsPerOp float64 // nanoseconds per iteration AllocedBytesPerOp uint64 // bytes allocated per iteration AllocsPerOp uint64 // allocs per iteration MBPerS float64 // MB processed per second Measured int // which measurements were recorded Ord int // ordinal position within a benchmark run } // ParseLine extracts a Benchmark from a single line of testing.B // output. func ParseLine(line string) (*Benchmark, error) { fields := strings.Fields(line) // Two required, positional fields: Name and iterations. if len(fields) < 2 { return nil, fmt.Errorf("two fields required, have %d", len(fields)) } if !strings.HasPrefix(fields[0], "Benchmark") { return nil, fmt.Errorf(`first field does not start with "Benchmark"`) } n, err := strconv.Atoi(fields[1]) if err != nil { return nil, err } b := &Benchmark{Name: fields[0], N: n} // Parse any remaining pairs of fields; we've parsed one pair already. for i := 1; i < len(fields)/2; i++ { b.parseMeasurement(fields[i*2], fields[i*2+1]) } return b, nil } func (b *Benchmark) parseMeasurement(quant string, unit string) { switch unit { case "ns/op": if f, err := strconv.ParseFloat(quant, 64); err == nil { b.NsPerOp = f b.Measured |= NsPerOp } case "MB/s": if f, err := strconv.ParseFloat(quant, 64); err == nil { b.MBPerS = f b.Measured |= MBPerS } case "B/op": if i, err := strconv.ParseUint(quant, 10, 64); err == nil { b.AllocedBytesPerOp = i b.Measured |= AllocedBytesPerOp } case "allocs/op": if i, err := strconv.ParseUint(quant, 10, 64); err == nil { b.AllocsPerOp = i b.Measured |= AllocsPerOp } } } func (b *Benchmark) String() string { buf := new(bytes.Buffer) fmt.Fprintf(buf, "%s %d", b.Name, b.N) if (b.Measured & NsPerOp) != 0 { fmt.Fprintf(buf, " %.2f ns/op", b.NsPerOp) } if (b.Measured & MBPerS) != 0 { fmt.Fprintf(buf, " %.2f MB/s", b.MBPerS) } if (b.Measured & AllocedBytesPerOp) != 0 { fmt.Fprintf(buf, " %d B/op", b.AllocedBytesPerOp) } if (b.Measured & AllocsPerOp) != 0 { fmt.Fprintf(buf, " %d allocs/op", b.AllocsPerOp) } return buf.String() } // Set is a collection of benchmarks from one // testing.B run, keyed by name to facilitate comparison. type Set map[string][]*Benchmark // ParseSet extracts a Set from testing.B output. // ParseSet preserves the order of benchmarks that have identical // names. func ParseSet(r io.Reader) (Set, error) { bb := make(Set) scan := bufio.NewScanner(r) ord := 0 for scan.Scan() { if b, err := ParseLine(scan.Text()); err == nil { b.Ord = ord ord++ bb[b.Name] = append(bb[b.Name], b) } } if err := scan.Err(); err != nil { return nil, err } return bb, nil } golang-golang-x-tools-0.25.0+ds/benchmark/parse/parse_test.go000066400000000000000000000121541476103172300240720ustar00rootroot00000000000000// Copyright 2014 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 parse import ( "reflect" "strings" "testing" ) func TestParseLine(t *testing.T) { cases := []struct { line string want *Benchmark err bool // expect an error }{ { line: "BenchmarkEncrypt 100000000 19.6 ns/op", want: &Benchmark{ Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, Measured: NsPerOp, }, }, { line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s", want: &Benchmark{ Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, MBPerS: 817.77, Measured: NsPerOp | MBPerS, }, }, { line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77", want: &Benchmark{ Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, Measured: NsPerOp, }, }, { line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s 5 allocs/op", want: &Benchmark{ Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, MBPerS: 817.77, AllocsPerOp: 5, Measured: NsPerOp | MBPerS | AllocsPerOp, }, }, { line: "BenchmarkEncrypt 100000000 19.6 ns/op 817.77 MB/s 3 B/op 5 allocs/op", want: &Benchmark{ Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, MBPerS: 817.77, AllocedBytesPerOp: 3, AllocsPerOp: 5, Measured: NsPerOp | MBPerS | AllocedBytesPerOp | AllocsPerOp, }, }, // error handling cases { line: "BenchPress 100 19.6 ns/op", // non-benchmark err: true, }, { line: "BenchmarkEncrypt lots 19.6 ns/op", // non-int iterations err: true, }, { line: "BenchmarkBridge 100000000 19.6 smoots", // unknown unit want: &Benchmark{ Name: "BenchmarkBridge", N: 100000000, }, }, { line: "PASS", err: true, }, } for _, tt := range cases { have, err := ParseLine(tt.line) if tt.err && err == nil { t.Errorf("parsing line %q should have failed", tt.line) continue } if !reflect.DeepEqual(have, tt.want) { t.Errorf("parsed line %q incorrectly, want %v have %v", tt.line, tt.want, have) } } } func TestParseSet(t *testing.T) { // Test two things: // 1. The noise that can accompany testing.B output gets ignored. // 2. Benchmarks with the same name have their order preserved. in := ` ? crypto [no test files] PASS pem_decrypt_test.go:17: test 4. %!s(x509.PEMCipher=5) ... [output truncated] BenchmarkEncrypt 100000000 19.6 ns/op BenchmarkEncrypt 5000000 517 ns/op === RUN TestChunk --- PASS: TestChunk (0.00 seconds) --- SKIP: TestLinuxSendfile (0.00 seconds) fs_test.go:716: skipping; linux-only test BenchmarkReadRequestApachebench 1000000 2960 ns/op 27.70 MB/s 839 B/op 9 allocs/op BenchmarkClientServerParallel64 50000 59192 ns/op 7028 B/op 60 allocs/op ok net/http 95.783s ` want := Set{ "BenchmarkReadRequestApachebench": []*Benchmark{ { Name: "BenchmarkReadRequestApachebench", N: 1000000, NsPerOp: 2960, MBPerS: 27.70, AllocedBytesPerOp: 839, AllocsPerOp: 9, Measured: NsPerOp | MBPerS | AllocedBytesPerOp | AllocsPerOp, Ord: 2, }, }, "BenchmarkClientServerParallel64": []*Benchmark{ { Name: "BenchmarkClientServerParallel64", N: 50000, NsPerOp: 59192, AllocedBytesPerOp: 7028, AllocsPerOp: 60, Measured: NsPerOp | AllocedBytesPerOp | AllocsPerOp, Ord: 3, }, }, "BenchmarkEncrypt": []*Benchmark{ { Name: "BenchmarkEncrypt", N: 100000000, NsPerOp: 19.6, Measured: NsPerOp, Ord: 0, }, { Name: "BenchmarkEncrypt", N: 5000000, NsPerOp: 517, Measured: NsPerOp, Ord: 1, }, }, } have, err := ParseSet(strings.NewReader(in)) if err != nil { t.Fatalf("unexpected err during ParseSet: %v", err) } if !reflect.DeepEqual(want, have) { t.Errorf("parsed bench set incorrectly, want %v have %v", want, have) } } func TestString(t *testing.T) { tests := []struct { name string input *Benchmark wanted string }{ { name: "nsTest", input: &Benchmark{ Name: "BenchmarkTest", N: 100000000, NsPerOp: 19.6, Measured: NsPerOp, }, wanted: "BenchmarkTest 100000000 19.60 ns/op", }, { name: "mbTest", input: &Benchmark{ Name: "BenchmarkTest", N: 100000000, MBPerS: 19.6, Measured: MBPerS, }, wanted: "BenchmarkTest 100000000 19.60 MB/s", }, { name: "allocatedBytesTest", input: &Benchmark{ Name: "BenchmarkTest", N: 100000000, AllocedBytesPerOp: 5, Measured: AllocedBytesPerOp, }, wanted: "BenchmarkTest 100000000 5 B/op", }, { name: "allocsTest", input: &Benchmark{ Name: "BenchmarkTest", N: 100000000, AllocsPerOp: 5, Measured: AllocsPerOp, }, wanted: "BenchmarkTest 100000000 5 allocs/op", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := tt.input.String() if result != tt.wanted { t.Errorf("String() is called, want %q, have %q", tt.wanted, result) } }) } } golang-golang-x-tools-0.25.0+ds/blog/000077500000000000000000000000001476103172300172465ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/blog/atom/000077500000000000000000000000001476103172300202065ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/blog/atom/atom.go000066400000000000000000000030471476103172300215010ustar00rootroot00000000000000// Copyright 2009 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. // Adapted from encoding/xml/read_test.go. // Package atom defines XML data structures for an Atom feed. package atom // import "golang.org/x/tools/blog/atom" import ( "encoding/xml" "time" ) type Feed struct { XMLName xml.Name `xml:"http://www.w3.org/2005/Atom feed"` Title string `xml:"title"` ID string `xml:"id"` Link []Link `xml:"link"` Updated TimeStr `xml:"updated"` Author *Person `xml:"author"` Entry []*Entry `xml:"entry"` } type Entry struct { Title string `xml:"title"` ID string `xml:"id"` Link []Link `xml:"link"` Published TimeStr `xml:"published"` Updated TimeStr `xml:"updated"` Author *Person `xml:"author"` Summary *Text `xml:"summary"` Content *Text `xml:"content"` } type Link struct { Rel string `xml:"rel,attr,omitempty"` Href string `xml:"href,attr"` Type string `xml:"type,attr,omitempty"` HrefLang string `xml:"hreflang,attr,omitempty"` Title string `xml:"title,attr,omitempty"` Length uint `xml:"length,attr,omitempty"` } type Person struct { Name string `xml:"name"` URI string `xml:"uri,omitempty"` Email string `xml:"email,omitempty"` InnerXML string `xml:",innerxml"` } type Text struct { Type string `xml:"type,attr"` Body string `xml:",chardata"` } type TimeStr string func Time(t time.Time) TimeStr { return TimeStr(t.Format("2006-01-02T15:04:05-07:00")) } golang-golang-x-tools-0.25.0+ds/blog/blog.go000066400000000000000000000303561476103172300205270ustar00rootroot00000000000000// 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 blog implements a web server for articles written in present format. package blog // import "golang.org/x/tools/blog" import ( "bytes" "encoding/json" "encoding/xml" "fmt" "html/template" "log" "net/http" "os" "path/filepath" "regexp" "sort" "strings" "time" "golang.org/x/tools/blog/atom" "golang.org/x/tools/present" ) var ( validJSONPFunc = regexp.MustCompile(`(?i)^[a-z_][a-z0-9_.]*$`) // used to serve relative paths when ServeLocalLinks is enabled. golangOrgAbsLinkReplacer = strings.NewReplacer( `href="https://golang.org/pkg`, `href="/pkg`, `href="https://golang.org/cmd`, `href="/cmd`, ) ) // Config specifies Server configuration values. type Config struct { ContentPath string // Relative or absolute location of article files and related content. TemplatePath string // Relative or absolute location of template files. BaseURL string // Absolute base URL (for permalinks; no trailing slash). BasePath string // Base URL path relative to server root (no trailing slash). GodocURL string // The base URL of godoc (for menu bar; no trailing slash). Hostname string // Server host name, used for rendering ATOM feeds. AnalyticsHTML template.HTML // Optional analytics HTML to insert at the beginning of . HomeArticles int // Articles to display on the home page. FeedArticles int // Articles to include in Atom and JSON feeds. FeedTitle string // The title of the Atom XML feed PlayEnabled bool ServeLocalLinks bool // rewrite golang.org/{pkg,cmd} links to host-less, relative paths. } // Doc represents an article adorned with presentation data. type Doc struct { *present.Doc Permalink string // Canonical URL for this document. Path string // Path relative to server root (including base). HTML template.HTML // rendered article Related []*Doc Newer, Older *Doc } // Server implements an http.Handler that serves blog articles. type Server struct { cfg Config docs []*Doc redirects map[string]string tags []string docPaths map[string]*Doc // key is path without BasePath. docTags map[string][]*Doc template struct { home, index, article, doc *template.Template } atomFeed []byte // pre-rendered Atom feed jsonFeed []byte // pre-rendered JSON feed content http.Handler } // NewServer constructs a new Server using the specified config. func NewServer(cfg Config) (*Server, error) { present.PlayEnabled = cfg.PlayEnabled if notExist(cfg.TemplatePath) { return nil, fmt.Errorf("template directory not found: %s", cfg.TemplatePath) } root := filepath.Join(cfg.TemplatePath, "root.tmpl") parse := func(name string) (*template.Template, error) { path := filepath.Join(cfg.TemplatePath, name) if notExist(path) { return nil, fmt.Errorf("template %s was not found in %s", name, cfg.TemplatePath) } t := template.New("").Funcs(funcMap) return t.ParseFiles(root, path) } s := &Server{cfg: cfg} // Parse templates. var err error s.template.home, err = parse("home.tmpl") if err != nil { return nil, err } s.template.index, err = parse("index.tmpl") if err != nil { return nil, err } s.template.article, err = parse("article.tmpl") if err != nil { return nil, err } p := present.Template().Funcs(funcMap) s.template.doc, err = p.ParseFiles(filepath.Join(cfg.TemplatePath, "doc.tmpl")) if err != nil { return nil, err } // Load content. content := filepath.Clean(cfg.ContentPath) err = s.loadDocs(content) if err != nil { return nil, err } err = s.renderAtomFeed() if err != nil { return nil, err } err = s.renderJSONFeed() if err != nil { return nil, err } // Set up content file server. s.content = http.StripPrefix(s.cfg.BasePath, http.FileServer(http.Dir(cfg.ContentPath))) return s, nil } var funcMap = template.FuncMap{ "sectioned": sectioned, "authors": authors, } // sectioned returns true if the provided Doc contains more than one section. // This is used to control whether to display the table of contents and headings. func sectioned(d *present.Doc) bool { return len(d.Sections) > 1 } // authors returns a comma-separated list of author names. func authors(authors []present.Author) string { var b bytes.Buffer last := len(authors) - 1 for i, a := range authors { if i > 0 { if i == last { if len(authors) > 2 { b.WriteString(",") } b.WriteString(" and ") } else { b.WriteString(", ") } } b.WriteString(authorName(a)) } return b.String() } // authorName returns the first line of the Author text: the author's name. func authorName(a present.Author) string { el := a.TextElem() if len(el) == 0 { return "" } text, ok := el[0].(present.Text) if !ok || len(text.Lines) == 0 { return "" } return text.Lines[0] } // loadDocs reads all content from the provided file system root, renders all // the articles it finds, adds them to the Server's docs field, computes the // denormalized docPaths, docTags, and tags fields, and populates the various // helper fields (Next, Previous, Related) for each Doc. func (s *Server) loadDocs(root string) error { // Read content into docs field. const ext = ".article" fn := func(p string, info os.FileInfo, err error) error { if err != nil { return err } if filepath.Ext(p) != ext { return nil } f, err := os.Open(p) if err != nil { return err } defer f.Close() d, err := present.Parse(f, p, 0) if err != nil { return err } var html bytes.Buffer err = d.Render(&html, s.template.doc) if err != nil { return err } p = p[len(root) : len(p)-len(ext)] // trim root and extension p = filepath.ToSlash(p) s.docs = append(s.docs, &Doc{ Doc: d, Path: s.cfg.BasePath + p, Permalink: s.cfg.BaseURL + p, HTML: template.HTML(html.String()), }) return nil } err := filepath.Walk(root, fn) if err != nil { return err } sort.Sort(docsByTime(s.docs)) // Pull out doc paths and tags and put in reverse-associating maps. s.docPaths = make(map[string]*Doc) s.docTags = make(map[string][]*Doc) s.redirects = make(map[string]string) for _, d := range s.docs { s.docPaths[strings.TrimPrefix(d.Path, s.cfg.BasePath)] = d for _, t := range d.Tags { s.docTags[t] = append(s.docTags[t], d) } } for _, d := range s.docs { for _, old := range d.OldURL { if !strings.HasPrefix(old, "/") { old = "/" + old } if _, ok := s.docPaths[old]; ok { return fmt.Errorf("redirect %s -> %s conflicts with document %s", old, d.Path, old) } if new, ok := s.redirects[old]; ok { return fmt.Errorf("redirect %s -> %s conflicts with redirect %s -> %s", old, d.Path, old, new) } s.redirects[old] = d.Path } } // Pull out unique sorted list of tags. for t := range s.docTags { s.tags = append(s.tags, t) } sort.Strings(s.tags) // Set up presentation-related fields, Newer, Older, and Related. for _, doc := range s.docs { // Newer, Older: docs adjacent to doc for i := range s.docs { if s.docs[i] != doc { continue } if i > 0 { doc.Newer = s.docs[i-1] } if i+1 < len(s.docs) { doc.Older = s.docs[i+1] } break } // Related: all docs that share tags with doc. related := make(map[*Doc]bool) for _, t := range doc.Tags { for _, d := range s.docTags[t] { if d != doc { related[d] = true } } } for d := range related { doc.Related = append(doc.Related, d) } sort.Sort(docsByTime(doc.Related)) } return nil } // renderAtomFeed generates an XML Atom feed and stores it in the Server's // atomFeed field. func (s *Server) renderAtomFeed() error { var updated time.Time if len(s.docs) > 0 { updated = s.docs[0].Time } feed := atom.Feed{ Title: s.cfg.FeedTitle, ID: "tag:" + s.cfg.Hostname + ",2013:" + s.cfg.Hostname, Updated: atom.Time(updated), Link: []atom.Link{{ Rel: "self", Href: s.cfg.BaseURL + "/feed.atom", }}, } for i, doc := range s.docs { if i >= s.cfg.FeedArticles { break } // Use original article path as ID in atom feed // to avoid articles being treated as new when renamed. idPath := doc.Path if len(doc.OldURL) > 0 { old := doc.OldURL[0] if !strings.HasPrefix(old, "/") { old = "/" + old } idPath = old } e := &atom.Entry{ Title: doc.Title, ID: feed.ID + idPath, Link: []atom.Link{{ Rel: "alternate", Href: doc.Permalink, }}, Published: atom.Time(doc.Time), Updated: atom.Time(doc.Time), Summary: &atom.Text{ Type: "html", Body: summary(doc), }, Content: &atom.Text{ Type: "html", Body: string(doc.HTML), }, Author: &atom.Person{ Name: authors(doc.Authors), }, } feed.Entry = append(feed.Entry, e) } data, err := xml.Marshal(&feed) if err != nil { return err } s.atomFeed = data return nil } type jsonItem struct { Title string Link string Time time.Time Summary string Content string Author string } // renderJSONFeed generates a JSON feed and stores it in the Server's jsonFeed // field. func (s *Server) renderJSONFeed() error { var feed []jsonItem for i, doc := range s.docs { if i >= s.cfg.FeedArticles { break } item := jsonItem{ Title: doc.Title, Link: doc.Permalink, Time: doc.Time, Summary: summary(doc), Content: string(doc.HTML), Author: authors(doc.Authors), } feed = append(feed, item) } data, err := json.Marshal(feed) if err != nil { return err } s.jsonFeed = data return nil } // summary returns the first paragraph of text from the provided Doc. func summary(d *Doc) string { if len(d.Sections) == 0 { return "" } for _, elem := range d.Sections[0].Elem { text, ok := elem.(present.Text) if !ok || text.Pre { // skip everything but non-text elements continue } var buf bytes.Buffer for _, s := range text.Lines { buf.WriteString(string(present.Style(s))) buf.WriteByte('\n') } return buf.String() } return "" } // rootData encapsulates data destined for the root template. type rootData struct { Doc *Doc BasePath string GodocURL string AnalyticsHTML template.HTML Data interface{} } // ServeHTTP serves the front, index, and article pages // as well as the ATOM and JSON feeds. func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { var ( d = rootData{ BasePath: s.cfg.BasePath, GodocURL: s.cfg.GodocURL, AnalyticsHTML: s.cfg.AnalyticsHTML, } t *template.Template ) switch p := strings.TrimPrefix(r.URL.Path, s.cfg.BasePath); p { case "/": d.Data = s.docs if len(s.docs) > s.cfg.HomeArticles { d.Data = s.docs[:s.cfg.HomeArticles] } t = s.template.home case "/index": d.Data = s.docs t = s.template.index case "/feed.atom", "/feeds/posts/default": w.Header().Set("Content-type", "application/atom+xml; charset=utf-8") w.Write(s.atomFeed) return case "/.json": if p := r.FormValue("jsonp"); validJSONPFunc.MatchString(p) { w.Header().Set("Content-type", "application/javascript; charset=utf-8") fmt.Fprintf(w, "%v(%s)", p, s.jsonFeed) return } w.Header().Set("Content-type", "application/json; charset=utf-8") w.Write(s.jsonFeed) return default: if redir, ok := s.redirects[p]; ok { http.Redirect(w, r, redir, http.StatusMovedPermanently) return } doc, ok := s.docPaths[p] if !ok { // Not a doc; try to just serve static content. s.content.ServeHTTP(w, r) return } d.Doc = doc t = s.template.article } var err error if s.cfg.ServeLocalLinks { var buf bytes.Buffer err = t.ExecuteTemplate(&buf, "root", d) if err != nil { log.Println(err) return } _, err = golangOrgAbsLinkReplacer.WriteString(w, buf.String()) } else { err = t.ExecuteTemplate(w, "root", d) } if err != nil { log.Println(err) } } // docsByTime implements sort.Interface, sorting Docs by their Time field. type docsByTime []*Doc func (s docsByTime) Len() int { return len(s) } func (s docsByTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] } func (s docsByTime) Less(i, j int) bool { return s[i].Time.After(s[j].Time) } // notExist reports whether the path exists or not. func notExist(path string) bool { _, err := os.Stat(path) return os.IsNotExist(err) } golang-golang-x-tools-0.25.0+ds/blog/blog_test.go000066400000000000000000000036741476103172300215710ustar00rootroot00000000000000// Copyright 2018 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 blog import ( "bytes" "testing" ) func TestLinkRewrite(t *testing.T) { tests := []struct { input string output string }{ { `For instance, the bytes package from the standard library exports the Buffer type.`, `For instance, the bytes package from the standard library exports the Buffer type.`}, { `(The gofmt command has a -r flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`, `(The gofmt command has a -r flag that provides a syntax-aware search and replace, making large-scale refactoring easier.)`, }, { `BSD license.
Terms of Service `, `BSD license.
Terms of Service `, }, { `For instance, the websocket package from the go.net sub-repository has an import path of "golang.org/x/net/websocket".`, `For instance, the websocket package from the go.net sub-repository has an import path of "golang.org/x/net/websocket".`, }, } for _, test := range tests { var buf bytes.Buffer _, err := golangOrgAbsLinkReplacer.WriteString(&buf, test.input) if err != nil { t.Errorf("unexpected error during replacing links. Got: %#v, Want: nil.\n", err) continue } if got, want := buf.String(), test.output; got != want { t.Errorf("WriteString(%q) = %q. Expected: %q", test.input, got, want) } } } golang-golang-x-tools-0.25.0+ds/cmd/000077500000000000000000000000001476103172300170665ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/000077500000000000000000000000001476103172300200275ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/authtest/000077500000000000000000000000001476103172300216705ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/authtest/authtest.go000066400000000000000000000123161476103172300240630ustar00rootroot00000000000000// Copyright 2019 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. // authtest is a diagnostic tool for implementations of the GOAUTH protocol // described in https://golang.org/issue/26232. // // It accepts a single URL as an argument, and executes the GOAUTH protocol to // fetch and display the headers for that URL. // // CAUTION: authtest logs the GOAUTH responses, which may include user // credentials, to stderr. Do not post its output unless you are certain that // all of the credentials involved are fake! package main import ( "bufio" "bytes" "flag" "fmt" "io" "log" "net/http" "net/textproto" "net/url" "os" "os/exec" "path/filepath" "strings" ) var v = flag.Bool("v", false, "if true, log GOAUTH responses to stderr") func main() { log.SetFlags(log.LstdFlags | log.Lshortfile) flag.Parse() args := flag.Args() if len(args) != 1 { log.Fatalf("usage: [GOAUTH=CMD...] %s URL", filepath.Base(os.Args[0])) } resp := try(args[0], nil) if resp.StatusCode == http.StatusOK { return } resp = try(args[0], resp) if resp.StatusCode != http.StatusOK { os.Exit(1) } } func try(url string, prev *http.Response) *http.Response { req := new(http.Request) if prev != nil { *req = *prev.Request } else { var err error req, err = http.NewRequest("HEAD", os.Args[1], nil) if err != nil { log.Fatal(err) } } goauth: for _, argList := range strings.Split(os.Getenv("GOAUTH"), ";") { // TODO(golang.org/issue/26849): If we escape quoted strings in GOFLAGS, use // the same quoting here. args := strings.Split(argList, " ") if len(args) == 0 || args[0] == "" { log.Fatalf("invalid or empty command in GOAUTH") } creds, err := getCreds(args, prev) if err != nil { log.Fatal(err) } for _, c := range creds { if c.Apply(req) { fmt.Fprintf(os.Stderr, "# request to %s\n", req.URL) fmt.Fprintf(os.Stderr, "%s %s %s\n", req.Method, req.URL, req.Proto) req.Header.Write(os.Stderr) fmt.Fprintln(os.Stderr) break goauth } } } resp, err := http.DefaultClient.Do(req) if err != nil { log.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK && resp.StatusCode < 400 || resp.StatusCode > 500 { log.Fatalf("unexpected status: %v", resp.Status) } fmt.Fprintf(os.Stderr, "# response from %s\n", resp.Request.URL) formatHead(os.Stderr, resp) return resp } func formatHead(out io.Writer, resp *http.Response) { fmt.Fprintf(out, "%s %s\n", resp.Proto, resp.Status) if err := resp.Header.Write(out); err != nil { log.Fatal(err) } fmt.Fprintln(out) } type Cred struct { URLPrefixes []*url.URL Header http.Header } func (c Cred) Apply(req *http.Request) bool { if req.URL == nil { return false } ok := false for _, prefix := range c.URLPrefixes { if prefix.Host == req.URL.Host && (req.URL.Path == prefix.Path || (strings.HasPrefix(req.URL.Path, prefix.Path) && (strings.HasSuffix(prefix.Path, "/") || req.URL.Path[len(prefix.Path)] == '/'))) { ok = true break } } if !ok { return false } for k, vs := range c.Header { req.Header.Del(k) for _, v := range vs { req.Header.Add(k, v) } } return true } func (c Cred) String() string { var buf strings.Builder for _, u := range c.URLPrefixes { fmt.Fprintln(&buf, u) } buf.WriteString("\n") c.Header.Write(&buf) buf.WriteString("\n") return buf.String() } func getCreds(args []string, resp *http.Response) ([]Cred, error) { cmd := exec.Command(args[0], args[1:]...) cmd.Stderr = os.Stderr if resp != nil { u := *resp.Request.URL u.RawQuery = "" cmd.Args = append(cmd.Args, u.String()) } var head strings.Builder if resp != nil { formatHead(&head, resp) } cmd.Stdin = strings.NewReader(head.String()) fmt.Fprintf(os.Stderr, "# %s\n", strings.Join(cmd.Args, " ")) out, err := cmd.Output() if err != nil { return nil, fmt.Errorf("%s: %v", strings.Join(cmd.Args, " "), err) } os.Stderr.Write(out) os.Stderr.WriteString("\n") var creds []Cred r := textproto.NewReader(bufio.NewReader(bytes.NewReader(out))) line := 0 readLoop: for { var prefixes []*url.URL for { prefix, err := r.ReadLine() if err == io.EOF { if len(prefixes) > 0 { return nil, fmt.Errorf("line %d: %v", line, io.ErrUnexpectedEOF) } break readLoop } line++ if prefix == "" { if len(prefixes) == 0 { return nil, fmt.Errorf("line %d: unexpected newline", line) } break } u, err := url.Parse(prefix) if err != nil { return nil, fmt.Errorf("line %d: malformed URL: %v", line, err) } if u.Scheme != "https" { return nil, fmt.Errorf("line %d: non-HTTPS URL %q", line, prefix) } if len(u.RawQuery) > 0 { return nil, fmt.Errorf("line %d: unexpected query string in URL %q", line, prefix) } if len(u.Fragment) > 0 { return nil, fmt.Errorf("line %d: unexpected fragment in URL %q", line, prefix) } prefixes = append(prefixes, u) } header, err := r.ReadMIMEHeader() if err != nil { return nil, fmt.Errorf("headers at line %d: %v", line, err) } if len(header) > 0 { creds = append(creds, Cred{ URLPrefixes: prefixes, Header: http.Header(header), }) } } return creds, nil } golang-golang-x-tools-0.25.0+ds/cmd/auth/cookieauth/000077500000000000000000000000001476103172300221625ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/cookieauth/cookieauth.go000066400000000000000000000071231476103172300246470ustar00rootroot00000000000000// Copyright 2019 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. // cookieauth uses a “Netscape cookie file” to implement the GOAUTH protocol // described in https://golang.org/issue/26232. // It expects the location of the file as the first command-line argument. // // Example GOAUTH usage: // // 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. package main import ( "bufio" "fmt" "io" "log" "net/http" "net/http/cookiejar" "net/url" "os" "strconv" "strings" "time" "unicode" ) func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "usage: %s COOKIEFILE [URL]\n", os.Args[0]) os.Exit(2) } log.SetPrefix("cookieauth: ") f, err := os.Open(os.Args[1]) if err != nil { log.Fatalf("failed to read cookie file: %v\n", os.Args[1]) } defer f.Close() var ( targetURL *url.URL targetURLs = map[string]*url.URL{} ) if len(os.Args) == 3 { targetURL, err = url.ParseRequestURI(os.Args[2]) if err != nil { log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[2]) } targetURLs[targetURL.String()] = targetURL } else if len(os.Args) > 3 { // Extra arguments were passed: maybe the protocol was expanded? // We don't know how to interpret the request, so ignore it. return } entries, err := parseCookieFile(f.Name(), f) if err != nil { log.Fatalf("error reading cookie file: %v\n", f.Name()) } jar, err := cookiejar.New(nil) if err != nil { log.Fatalf("failed to initialize cookie jar: %v\n", err) } for _, e := range entries { u := &url.URL{ Scheme: "https", Host: e.Host, Path: e.Cookie.Path, } if targetURL == nil { targetURLs[u.String()] = u } jar.SetCookies(u, []*http.Cookie{&e.Cookie}) } for _, u := range targetURLs { req := &http.Request{URL: u, Header: make(http.Header)} for _, c := range jar.Cookies(req.URL) { req.AddCookie(c) } fmt.Printf("%s\n\n", u) req.Header.Write(os.Stdout) fmt.Println() } } type Entry struct { Host string Cookie http.Cookie } // parseCookieFile parses a Netscape cookie file as described in // http://www.cookiecentral.com/faq/#3.5. func parseCookieFile(name string, r io.Reader) ([]*Entry, error) { var entries []*Entry s := bufio.NewScanner(r) line := 0 for s.Scan() { line++ text := strings.TrimSpace(s.Text()) if len(text) < 2 || (text[0] == '#' && unicode.IsSpace(rune(text[1]))) { continue } e, err := parseCookieLine(text) if err != nil { log.Printf("%s:%d: %v\n", name, line, err) continue } entries = append(entries, e) } return entries, s.Err() } func parseCookieLine(line string) (*Entry, error) { f := strings.Fields(line) if len(f) < 7 { return nil, fmt.Errorf("found %d columns; want 7", len(f)) } e := new(Entry) c := &e.Cookie if domain := f[0]; strings.HasPrefix(domain, "#HttpOnly_") { c.HttpOnly = true e.Host = strings.TrimPrefix(domain[10:], ".") } else { e.Host = strings.TrimPrefix(domain, ".") } isDomain, err := strconv.ParseBool(f[1]) if err != nil { return nil, fmt.Errorf("non-boolean domain flag: %v", err) } if isDomain { c.Domain = e.Host } c.Path = f[2] c.Secure, err = strconv.ParseBool(f[3]) if err != nil { return nil, fmt.Errorf("non-boolean secure flag: %v", err) } expiration, err := strconv.ParseInt(f[4], 10, 64) if err != nil { return nil, fmt.Errorf("malformed expiration: %v", err) } c.Expires = time.Unix(expiration, 0) c.Name = f[5] c.Value = f[6] return e, nil } golang-golang-x-tools-0.25.0+ds/cmd/auth/gitauth/000077500000000000000000000000001476103172300214745ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/gitauth/gitauth.go000066400000000000000000000112571476103172300234760ustar00rootroot00000000000000// Copyright 2019 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. // gitauth uses 'git credential' to implement the GOAUTH protocol described in // https://golang.org/issue/26232. It expects an absolute path to the working // directory for the 'git' command as the first command-line argument. // // Example GOAUTH usage: // // export GOAUTH="gitauth $HOME" // // See https://git-scm.com/docs/gitcredentials or run 'man gitcredentials' for // information on how to configure 'git credential'. package main import ( "bytes" "fmt" "log" "net/http" "net/url" "os" "os/exec" "path/filepath" "strings" ) func main() { if len(os.Args) < 2 || !filepath.IsAbs(os.Args[1]) { fmt.Fprintf(os.Stderr, "usage: %s WORKDIR [URL]", os.Args[0]) os.Exit(2) } log.SetPrefix("gitauth: ") if len(os.Args) != 3 { // No explicit URL was passed on the command line, but 'git credential' // provides no way to enumerate existing credentials. // Wait for a request for a specific URL. return } u, err := url.ParseRequestURI(os.Args[2]) if err != nil { log.Fatalf("invalid request URI (%v): %q\n", err, os.Args[1]) } var ( prefix *url.URL lastHeader http.Header lastStatus = http.StatusUnauthorized ) for lastStatus == http.StatusUnauthorized { cmd := exec.Command("git", "credential", "fill") // We don't want to execute a 'git' command in an arbitrary directory, since // that opens up a number of config-injection attacks (for example, // https://golang.org/issue/29230). Instead, we have the user configure a // directory explicitly on the command line. cmd.Dir = os.Args[1] cmd.Stdin = strings.NewReader(fmt.Sprintf("url=%s\n", u)) cmd.Stderr = os.Stderr out, err := cmd.Output() if err != nil { log.Fatalf("'git credential fill' failed: %v\n", err) } prefix = new(url.URL) var username, password string lines := strings.Split(string(out), "\n") for _, line := range lines { frags := strings.SplitN(line, "=", 2) if len(frags) != 2 { continue // Ignore unrecognized response lines. } switch strings.TrimSpace(frags[0]) { case "protocol": prefix.Scheme = frags[1] case "host": prefix.Host = frags[1] case "path": prefix.Path = frags[1] case "username": username = frags[1] case "password": password = frags[1] case "url": // Write to a local variable instead of updating prefix directly: // if the url field is malformed, we don't want to invalidate // information parsed from the protocol, host, and path fields. u, err := url.ParseRequestURI(frags[1]) if err == nil { prefix = u } else { log.Printf("malformed URL from 'git credential fill' (%v): %q\n", err, frags[1]) // Proceed anyway: we might be able to parse the prefix from other fields of the response. } } } // Double-check that the URL Git gave us is a prefix of the one we requested. if !strings.HasPrefix(u.String(), prefix.String()) { log.Fatalf("requested a credential for %q, but 'git credential fill' provided one for %q\n", u, prefix) } // Send a HEAD request to try to detect whether the credential is valid. // If the user just typed in a correct password and has caching enabled, // we don't want to nag them for it again the next time they run a 'go' command. req, err := http.NewRequest("HEAD", u.String(), nil) if err != nil { log.Fatalf("internal error constructing HTTP HEAD request: %v\n", err) } req.SetBasicAuth(username, password) lastHeader = req.Header resp, err := http.DefaultClient.Do(req) if err != nil { log.Printf("HTTPS HEAD request failed to connect: %v\n", err) // Couldn't verify the credential, but we have no evidence that it is invalid either. // Proceed, but don't update git's credential cache. break } lastStatus = resp.StatusCode if resp.StatusCode != http.StatusOK { log.Printf("%s: %v %s\n", u, resp.StatusCode, http.StatusText(resp.StatusCode)) } if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusUnauthorized { // We learned something about the credential: it either worked or it was invalid. // Approve or reject the credential (on a best-effort basis) // so that the git credential helper can update its cache as appropriate. action := "approve" if resp.StatusCode != http.StatusOK { action = "reject" } cmd = exec.Command("git", "credential", action) cmd.Stderr = os.Stderr cmd.Stdout = os.Stderr cmd.Stdin = bytes.NewReader(out) _ = cmd.Run() } } // Write out the credential in the format expected by the 'go' command. fmt.Printf("%s\n\n", prefix) lastHeader.Write(os.Stdout) fmt.Println() } golang-golang-x-tools-0.25.0+ds/cmd/auth/netrcauth/000077500000000000000000000000001476103172300220245ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/auth/netrcauth/netrcauth.go000066400000000000000000000060711476103172300243540ustar00rootroot00000000000000// Copyright 2018 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. // netrcauth uses a .netrc file (or _netrc file on Windows) to implement the // GOAUTH protocol described in https://golang.org/issue/26232. // It expects the location of the file as the first command-line argument. // // Example GOAUTH usage: // // 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. package main import ( "fmt" "log" "net/http" "net/url" "os" "strings" ) func main() { if len(os.Args) < 2 { fmt.Fprintf(os.Stderr, "usage: %s NETRCFILE [URL]", os.Args[0]) os.Exit(2) } log.SetPrefix("netrcauth: ") if len(os.Args) != 2 { // An explicit URL was passed on the command line, but netrcauth does not // have any URL-specific output: it dumps the entire .netrc file at the // first call. return } path := os.Args[1] data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return } log.Fatalf("failed to read %s: %v\n", path, err) } u := &url.URL{Scheme: "https"} lines := parseNetrc(string(data)) for _, l := range lines { u.Host = l.machine fmt.Printf("%s\n\n", u) req := &http.Request{Header: make(http.Header)} req.SetBasicAuth(l.login, l.password) req.Header.Write(os.Stdout) fmt.Println() } } // The following functions were extracted from src/cmd/go/internal/web2/web.go // as of https://golang.org/cl/161698. type netrcLine struct { machine string login string password string } func parseNetrc(data string) []netrcLine { // See https://www.gnu.org/software/inetutils/manual/html_node/The-_002enetrc-file.html // for documentation on the .netrc format. var nrc []netrcLine var l netrcLine inMacro := false for _, line := range strings.Split(data, "\n") { if inMacro { if line == "" { inMacro = false } continue } f := strings.Fields(line) i := 0 for ; i < len(f)-1; i += 2 { // Reset at each "machine" token. // “The auto-login process searches the .netrc file for a machine token // that matches […]. Once a match is made, the subsequent .netrc tokens // are processed, stopping when the end of file is reached or another // machine or a default token is encountered.” switch f[i] { case "machine": l = netrcLine{machine: f[i+1]} case "default": break case "login": l.login = f[i+1] case "password": l.password = f[i+1] case "macdef": // “A macro is defined with the specified name; its contents begin with // the next .netrc line and continue until a null line (consecutive // new-line characters) is encountered.” inMacro = true } if l.machine != "" && l.login != "" && l.password != "" { nrc = append(nrc, l) l = netrcLine{} } } if i < len(f) && f[i] == "default" { // “There can be only one default token, and it must be after all machine tokens.” break } } return nrc } golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/000077500000000000000000000000001476103172300206455ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/benchcmp.go000066400000000000000000000103201476103172300227470ustar00rootroot00000000000000// Copyright 2014 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 ( "flag" "fmt" "os" "sort" "strconv" "text/tabwriter" "golang.org/x/tools/benchmark/parse" ) var ( changedOnly = flag.Bool("changed", false, "show only benchmarks that have changed") magSort = flag.Bool("mag", false, "sort benchmarks by magnitude of change") best = flag.Bool("best", false, "compare best times from old and new") ) const usageFooter = ` Each input file should be from: go test -run=NONE -bench=. > [old,new].txt Benchcmp compares old and new for each benchmark. If -test.benchmem=true is added to the "go test" command benchcmp will also compare memory allocations. ` func main() { fmt.Fprintf(os.Stderr, "benchcmp is deprecated in favor of benchstat: https://pkg.go.dev/golang.org/x/perf/cmd/benchstat\n") flag.Usage = func() { fmt.Fprintf(os.Stderr, "usage: %s old.txt new.txt\n\n", os.Args[0]) flag.PrintDefaults() fmt.Fprint(os.Stderr, usageFooter) os.Exit(2) } flag.Parse() if flag.NArg() != 2 { flag.Usage() } before := parseFile(flag.Arg(0)) after := parseFile(flag.Arg(1)) cmps, warnings := Correlate(before, after) for _, warn := range warnings { fmt.Fprintln(os.Stderr, warn) } if len(cmps) == 0 { fatal("benchcmp: no repeated benchmarks") } w := new(tabwriter.Writer) w.Init(os.Stdout, 0, 0, 5, ' ', 0) defer w.Flush() var header bool // Has the header has been displayed yet for a given block? if *magSort { sort.Sort(ByDeltaNsPerOp(cmps)) } else { sort.Sort(ByParseOrder(cmps)) } for _, cmp := range cmps { if !cmp.Measured(parse.NsPerOp) { continue } if delta := cmp.DeltaNsPerOp(); !*changedOnly || delta.Changed() { if !header { fmt.Fprint(w, "benchmark\told ns/op\tnew ns/op\tdelta\n") header = true } fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", cmp.Name(), formatNs(cmp.Before.NsPerOp), formatNs(cmp.After.NsPerOp), delta.Percent()) } } header = false if *magSort { sort.Sort(ByDeltaMBPerS(cmps)) } for _, cmp := range cmps { if !cmp.Measured(parse.MBPerS) { continue } if delta := cmp.DeltaMBPerS(); !*changedOnly || delta.Changed() { if !header { fmt.Fprint(w, "\nbenchmark\told MB/s\tnew MB/s\tspeedup\n") header = true } fmt.Fprintf(w, "%s\t%.2f\t%.2f\t%s\n", cmp.Name(), cmp.Before.MBPerS, cmp.After.MBPerS, delta.Multiple()) } } header = false if *magSort { sort.Sort(ByDeltaAllocsPerOp(cmps)) } for _, cmp := range cmps { if !cmp.Measured(parse.AllocsPerOp) { continue } if delta := cmp.DeltaAllocsPerOp(); !*changedOnly || delta.Changed() { if !header { fmt.Fprint(w, "\nbenchmark\told allocs\tnew allocs\tdelta\n") header = true } fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocsPerOp, cmp.After.AllocsPerOp, delta.Percent()) } } header = false if *magSort { sort.Sort(ByDeltaAllocedBytesPerOp(cmps)) } for _, cmp := range cmps { if !cmp.Measured(parse.AllocedBytesPerOp) { continue } if delta := cmp.DeltaAllocedBytesPerOp(); !*changedOnly || delta.Changed() { if !header { fmt.Fprint(w, "\nbenchmark\told bytes\tnew bytes\tdelta\n") header = true } fmt.Fprintf(w, "%s\t%d\t%d\t%s\n", cmp.Name(), cmp.Before.AllocedBytesPerOp, cmp.After.AllocedBytesPerOp, cmp.DeltaAllocedBytesPerOp().Percent()) } } } func fatal(msg interface{}) { fmt.Fprintln(os.Stderr, msg) os.Exit(1) } func parseFile(path string) parse.Set { f, err := os.Open(path) if err != nil { fatal(err) } defer f.Close() bb, err := parse.ParseSet(f) if err != nil { fatal(err) } if *best { selectBest(bb) } return bb } func selectBest(bs parse.Set) { for name, bb := range bs { if len(bb) < 2 { continue } ord := bb[0].Ord best := bb[0] for _, b := range bb { if b.NsPerOp < best.NsPerOp { b.Ord = ord best = b } } bs[name] = []*parse.Benchmark{best} } } // formatNs formats ns measurements to expose a useful amount of // precision. It mirrors the ns precision logic of testing.B. func formatNs(ns float64) string { prec := 0 switch { case ns < 10: prec = 2 case ns < 100: prec = 1 } return strconv.FormatFloat(ns, 'f', prec, 64) } golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/benchcmp_test.go000066400000000000000000000033761476103172300240230ustar00rootroot00000000000000// Copyright 2014 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 ( "reflect" "testing" "golang.org/x/tools/benchmark/parse" ) func TestSelectBest(t *testing.T) { have := parse.Set{ "Benchmark1": []*parse.Benchmark{ { Name: "Benchmark1", N: 10, NsPerOp: 100, Measured: parse.NsPerOp, Ord: 0, }, { Name: "Benchmark1", N: 10, NsPerOp: 50, Measured: parse.NsPerOp, Ord: 3, }, }, "Benchmark2": []*parse.Benchmark{ { Name: "Benchmark2", N: 10, NsPerOp: 60, Measured: parse.NsPerOp, Ord: 1, }, { Name: "Benchmark2", N: 10, NsPerOp: 500, Measured: parse.NsPerOp, Ord: 2, }, }, } want := parse.Set{ "Benchmark1": []*parse.Benchmark{ { Name: "Benchmark1", N: 10, NsPerOp: 50, Measured: parse.NsPerOp, Ord: 0, }, }, "Benchmark2": []*parse.Benchmark{ { Name: "Benchmark2", N: 10, NsPerOp: 60, Measured: parse.NsPerOp, Ord: 1, }, }, } selectBest(have) if !reflect.DeepEqual(want, have) { t.Errorf("filtered bench set incorrectly, want %v have %v", want, have) } } func TestFormatNs(t *testing.T) { tests := []struct { input float64 expected string }{ {input: 0, expected: "0.00"}, {input: 0.2, expected: "0.20"}, {input: 2, expected: "2.00"}, {input: 2.2, expected: "2.20"}, {input: 4, expected: "4.00"}, {input: 16, expected: "16.0"}, {input: 16.08, expected: "16.1"}, {input: 128, expected: "128"}, {input: 256.2, expected: "256"}, } for _, tt := range tests { actual := formatNs(tt.input) if actual != tt.expected { t.Fatalf("%f. got %q, want %q", tt.input, actual, tt.expected) } } } golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/compare.go000066400000000000000000000123621476103172300226260ustar00rootroot00000000000000// Copyright 2014 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 ( "fmt" "math" "golang.org/x/tools/benchmark/parse" ) // BenchCmp is a pair of benchmarks. type BenchCmp struct { Before *parse.Benchmark After *parse.Benchmark } // Correlate correlates benchmarks from two BenchSets. func Correlate(before, after parse.Set) (cmps []BenchCmp, warnings []string) { cmps = make([]BenchCmp, 0, len(after)) for name, beforebb := range before { afterbb := after[name] if len(beforebb) != len(afterbb) { warnings = append(warnings, fmt.Sprintf("ignoring %s: before has %d instances, after has %d", name, len(beforebb), len(afterbb))) continue } for i, beforeb := range beforebb { afterb := afterbb[i] cmps = append(cmps, BenchCmp{beforeb, afterb}) } } return } func (c BenchCmp) Name() string { return c.Before.Name } func (c BenchCmp) String() string { return fmt.Sprintf("<%s, %s>", c.Before, c.After) } func (c BenchCmp) Measured(flag int) bool { return (c.Before.Measured & c.After.Measured & flag) != 0 } func (c BenchCmp) DeltaNsPerOp() Delta { return Delta{c.Before.NsPerOp, c.After.NsPerOp} } func (c BenchCmp) DeltaMBPerS() Delta { return Delta{c.Before.MBPerS, c.After.MBPerS} } func (c BenchCmp) DeltaAllocedBytesPerOp() Delta { return Delta{float64(c.Before.AllocedBytesPerOp), float64(c.After.AllocedBytesPerOp)} } func (c BenchCmp) DeltaAllocsPerOp() Delta { return Delta{float64(c.Before.AllocsPerOp), float64(c.After.AllocsPerOp)} } // Delta is the before and after value for a benchmark measurement. // Both must be non-negative. type Delta struct { Before float64 After float64 } // mag calculates the magnitude of a change, regardless of the direction of // the change. mag is intended for sorting and has no independent meaning. func (d Delta) mag() float64 { switch { case d.Before != 0 && d.After != 0 && d.Before >= d.After: return d.After / d.Before case d.Before != 0 && d.After != 0 && d.Before < d.After: return d.Before / d.After case d.Before == 0 && d.After == 0: return 1 default: // 0 -> 1 or 1 -> 0 // These are significant changes and worth surfacing. return math.Inf(1) } } // Changed reports whether the benchmark quantities are different. func (d Delta) Changed() bool { return d.Before != d.After } // Float64 returns After / Before. If Before is 0, Float64 returns // 1 if After is also 0, and +Inf otherwise. func (d Delta) Float64() float64 { switch { case d.Before != 0: return d.After / d.Before case d.After == 0: return 1 default: return math.Inf(1) } } // Percent formats a Delta as a percent change, ranging from -100% up. func (d Delta) Percent() string { return fmt.Sprintf("%+.2f%%", 100*d.Float64()-100) } // Multiple formats a Delta as a multiplier, ranging from 0.00x up. func (d Delta) Multiple() string { return fmt.Sprintf("%.2fx", d.Float64()) } func (d Delta) String() string { return fmt.Sprintf("Δ(%f, %f)", d.Before, d.After) } // ByParseOrder sorts BenchCmps to match the order in // which the Before benchmarks were presented to Parse. type ByParseOrder []BenchCmp func (x ByParseOrder) Len() int { return len(x) } 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 func lessByDelta(i, j BenchCmp, calcDelta func(BenchCmp) Delta) bool { iDelta, jDelta := calcDelta(i).mag(), calcDelta(j).mag() if iDelta != jDelta { return iDelta < jDelta } return i.Name() < j.Name() } // ByDeltaNsPerOp sorts BenchCmps lexicographically by change // in ns/op, descending, then by benchmark name. type ByDeltaNsPerOp []BenchCmp func (x ByDeltaNsPerOp) Len() int { return len(x) } func (x ByDeltaNsPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x ByDeltaNsPerOp) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaNsPerOp) } // ByDeltaMBPerS sorts BenchCmps lexicographically by change // in MB/s, descending, then by benchmark name. type ByDeltaMBPerS []BenchCmp func (x ByDeltaMBPerS) Len() int { return len(x) } func (x ByDeltaMBPerS) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x ByDeltaMBPerS) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaMBPerS) } // ByDeltaAllocedBytesPerOp sorts BenchCmps lexicographically by change // in B/op, descending, then by benchmark name. type ByDeltaAllocedBytesPerOp []BenchCmp func (x ByDeltaAllocedBytesPerOp) Len() int { return len(x) } func (x ByDeltaAllocedBytesPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x ByDeltaAllocedBytesPerOp) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaAllocedBytesPerOp) } // ByDeltaAllocsPerOp sorts BenchCmps lexicographically by change // in allocs/op, descending, then by benchmark name. type ByDeltaAllocsPerOp []BenchCmp func (x ByDeltaAllocsPerOp) Len() int { return len(x) } func (x ByDeltaAllocsPerOp) Swap(i, j int) { x[i], x[j] = x[j], x[i] } func (x ByDeltaAllocsPerOp) Less(i, j int) bool { return lessByDelta(x[i], x[j], BenchCmp.DeltaAllocsPerOp) } golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/compare_test.go000066400000000000000000000113321476103172300236610ustar00rootroot00000000000000// Copyright 2014 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 ( "math" "reflect" "sort" "testing" "golang.org/x/tools/benchmark/parse" ) func TestDelta(t *testing.T) { cases := []struct { before float64 after float64 mag float64 f float64 changed bool pct string mult string }{ {before: 1, after: 1, mag: 1, f: 1, changed: false, pct: "+0.00%", mult: "1.00x"}, {before: 1, after: 2, mag: 0.5, f: 2, changed: true, pct: "+100.00%", mult: "2.00x"}, {before: 2, after: 1, mag: 0.5, f: 0.5, changed: true, pct: "-50.00%", mult: "0.50x"}, {before: 0, after: 0, mag: 1, f: 1, changed: false, pct: "+0.00%", mult: "1.00x"}, {before: 1, after: 0, mag: math.Inf(1), f: 0, changed: true, pct: "-100.00%", mult: "0.00x"}, {before: 0, after: 1, mag: math.Inf(1), f: math.Inf(1), changed: true, pct: "+Inf%", mult: "+Infx"}, } for _, tt := range cases { d := Delta{tt.before, tt.after} if want, have := tt.mag, d.mag(); want != have { t.Errorf("%s.mag(): want %f have %f", d, want, have) } if want, have := tt.f, d.Float64(); want != have { t.Errorf("%s.Float64(): want %f have %f", d, want, have) } if want, have := tt.changed, d.Changed(); want != have { t.Errorf("%s.Changed(): want %t have %t", d, want, have) } if want, have := tt.pct, d.Percent(); want != have { t.Errorf("%s.Percent(): want %q have %q", d, want, have) } if want, have := tt.mult, d.Multiple(); want != have { t.Errorf("%s.Multiple(): want %q have %q", d, want, have) } } } func TestCorrelate(t *testing.T) { // Benches that are going to be successfully correlated get N thus: // 0x // Read this: " of , from ". before := parse.Set{ "BenchmarkOneEach": []*parse.Benchmark{{Name: "BenchmarkOneEach", N: 0x11b}}, "BenchmarkOneToNone": []*parse.Benchmark{{Name: "BenchmarkOneToNone"}}, "BenchmarkOneToTwo": []*parse.Benchmark{{Name: "BenchmarkOneToTwo"}}, "BenchmarkTwoToOne": []*parse.Benchmark{ {Name: "BenchmarkTwoToOne"}, {Name: "BenchmarkTwoToOne"}, }, "BenchmarkTwoEach": []*parse.Benchmark{ {Name: "BenchmarkTwoEach", N: 0x12b}, {Name: "BenchmarkTwoEach", N: 0x22b}, }, } after := parse.Set{ "BenchmarkOneEach": []*parse.Benchmark{{Name: "BenchmarkOneEach", N: 0x11a}}, "BenchmarkNoneToOne": []*parse.Benchmark{{Name: "BenchmarkNoneToOne"}}, "BenchmarkTwoToOne": []*parse.Benchmark{{Name: "BenchmarkTwoToOne"}}, "BenchmarkOneToTwo": []*parse.Benchmark{ {Name: "BenchmarkOneToTwo"}, {Name: "BenchmarkOneToTwo"}, }, "BenchmarkTwoEach": []*parse.Benchmark{ {Name: "BenchmarkTwoEach", N: 0x12a}, {Name: "BenchmarkTwoEach", N: 0x22a}, }, } pairs, errs := Correlate(before, after) // Fail to match: BenchmarkOneToNone, BenchmarkOneToTwo, BenchmarkTwoToOne. // Correlate does not notice BenchmarkNoneToOne. if len(errs) != 3 { t.Errorf("Correlated expected 4 errors, got %d: %v", len(errs), errs) } // Want three correlated pairs: one BenchmarkOneEach, two BenchmarkTwoEach. if len(pairs) != 3 { t.Fatalf("Correlated expected 3 pairs, got %v", pairs) } for _, pair := range pairs { if pair.Before.N&0xF != 0xb { t.Errorf("unexpected Before in pair %s", pair) } if pair.After.N&0xF != 0xa { t.Errorf("unexpected After in pair %s", pair) } if pair.Before.N>>4 != pair.After.N>>4 { t.Errorf("mismatched pair %s", pair) } } } func TestBenchCmpSorting(t *testing.T) { c := []BenchCmp{ {&parse.Benchmark{Name: "BenchmarkMuchFaster", NsPerOp: 10, Ord: 3}, &parse.Benchmark{Name: "BenchmarkMuchFaster", NsPerOp: 1}}, {&parse.Benchmark{Name: "BenchmarkSameB", NsPerOp: 5, Ord: 1}, &parse.Benchmark{Name: "BenchmarkSameB", NsPerOp: 5}}, {&parse.Benchmark{Name: "BenchmarkSameA", NsPerOp: 5, Ord: 2}, &parse.Benchmark{Name: "BenchmarkSameA", NsPerOp: 5}}, {&parse.Benchmark{Name: "BenchmarkSlower", NsPerOp: 10, Ord: 0}, &parse.Benchmark{Name: "BenchmarkSlower", NsPerOp: 11}}, } // Test just one magnitude-based sort order; they are symmetric. sort.Sort(ByDeltaNsPerOp(c)) want := []string{"BenchmarkMuchFaster", "BenchmarkSlower", "BenchmarkSameA", "BenchmarkSameB"} have := []string{c[0].Name(), c[1].Name(), c[2].Name(), c[3].Name()} if !reflect.DeepEqual(want, have) { t.Errorf("ByDeltaNsOp incorrect sorting: want %v have %v", want, have) } sort.Sort(ByParseOrder(c)) want = []string{"BenchmarkSlower", "BenchmarkSameB", "BenchmarkSameA", "BenchmarkMuchFaster"} have = []string{c[0].Name(), c[1].Name(), c[2].Name(), c[3].Name()} if !reflect.DeepEqual(want, have) { t.Errorf("ByParseOrder incorrect sorting: want %v have %v", want, have) } } golang-golang-x-tools-0.25.0+ds/cmd/benchcmp/doc.go000066400000000000000000000023431476103172300217430ustar00rootroot00000000000000// Copyright 2014 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. /* Deprecated: benchcmp is deprecated in favor of benchstat: golang.org/x/perf/cmd/benchstat The benchcmp command displays performance changes between benchmarks. Benchcmp parses the output of two 'go test' benchmark runs, correlates the results per benchmark, and displays the deltas. To measure the performance impact of a change, use 'go test' to run benchmarks before and after the change: go test -run=NONE -bench=. ./... > old.txt # make changes go test -run=NONE -bench=. ./... > new.txt Then feed the benchmark results to benchcmp: benchcmp old.txt new.txt Benchcmp will summarize and display the performance changes, in a format like this: $ benchcmp old.txt new.txt benchmark old ns/op new ns/op delta BenchmarkConcat 523 68.6 -86.88% benchmark old allocs new allocs delta BenchmarkConcat 3 1 -66.67% benchmark old bytes new bytes delta BenchmarkConcat 80 48 -40.00% */ package main // import "golang.org/x/tools/cmd/benchcmp" golang-golang-x-tools-0.25.0+ds/cmd/bisect/000077500000000000000000000000001476103172300203375ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bisect/go119.go000066400000000000000000000004701476103172300215270ustar00rootroot00000000000000// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !go1.20 package main import "os/exec" func cmdInterrupt(cmd *exec.Cmd) { // cmd.Cancel and cmd.WaitDelay not available before Go 1.20. } golang-golang-x-tools-0.25.0+ds/cmd/bisect/go120.go000066400000000000000000000011641476103172300215200ustar00rootroot00000000000000// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.20 package main import ( "os" "os/exec" "time" ) func cmdInterrupt(cmd *exec.Cmd) { cmd.Cancel = func() error { // On timeout, send interrupt, // in hopes of shutting down process tree. // Ignore errors sending signal; it's all best effort // and not even implemented on Windows. // TODO(rsc): Maybe use a new process group and kill the whole group? cmd.Process.Signal(os.Interrupt) return nil } cmd.WaitDelay = 2 * time.Second } golang-golang-x-tools-0.25.0+ds/cmd/bisect/main.go000066400000000000000000000575551476103172300216330ustar00rootroot00000000000000// Copyright 2023 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. // Bisect finds changes responsible for causing a failure. // A typical use is to identify the source locations in a program // that are miscompiled by a given compiler optimization. // // Usage: // // bisect [flags] [var=value...] command [arguments...] // // Bisect operates on a target command line – the target – that can be // run with various changes individually enabled or disabled. With none // of the changes enabled, the target is known to succeed (exit with exit // code zero). With all the changes enabled, the target is known to fail // (exit any other way). Bisect repeats the target with different sets of // changes enabled, using binary search to find (non-overlapping) minimal // change sets that provoke the failure. // // The target must cooperate with bisect by accepting a change pattern // and then enabling and reporting the changes that match that pattern. // The change pattern is passed to the target by substituting it anywhere // the string PATTERN appears in the environment values or the command // arguments. For each change that matches the pattern, the target must // enable that change and also print one or more “match lines” // (to standard output or standard error) describing the change. // The [golang.org/x/tools/internal/bisect] package provides functions to help // targets implement this protocol. We plan to publish that package // in a non-internal location after finalizing its API. // // Bisect starts by running the target with no changes enabled and then // with all changes enabled. It expects the former to succeed and the latter to fail, // and then it will search for the minimal set of changes that must be enabled // to provoke the failure. If the situation is reversed – the target fails with no // changes enabled and succeeds with all changes enabled – then bisect // automatically runs in reverse as well, searching for the minimal set of changes // that must be disabled to provoke the failure. // // Bisect prints tracing logs to standard error and the minimal change sets // to standard output. // // # Command Line Flags // // Bisect supports the following command-line flags: // // -max=M // // Stop after finding M minimal change sets. The default is no maximum, meaning to run until // all changes that provoke a failure have been identified. // // -maxset=S // // Disallow change sets larger than S elements. The default is no maximum. // // -timeout=D // // If the target runs for longer than duration D, stop the target and interpret that as a failure. // The default is no timeout. // // -count=N // // Run each trial N times (default 2), checking for consistency. // // -v // // Print verbose output, showing each run and its match lines. // // In addition to these general flags, // bisect supports a few “shortcut” flags that make it more convenient // to use with specific targets. // // -compile= // // This flag is equivalent to adding an environment variable // “GOCOMPILEDEBUG=hash=PATTERN”, // which, as discussed in more detail in the example below, // allows bisect to identify the specific source locations where the // compiler rewrite causes the target to fail. // // -godebug== // // This flag is equivalent to adding an environment variable // “GODEBUG==#PATTERN”, // which allows bisect to identify the specific call stacks where // the changed [GODEBUG setting] value causes the target to fail. // // # Example // // The Go compiler provides support for enabling or disabling certain rewrites // and optimizations to allow bisect to identify specific source locations where // the rewrite causes the program to fail. For example, to bisect a failure caused // by the new loop variable semantics: // // bisect go test -gcflags=all=-d=loopvarhash=PATTERN // // The -gcflags=all= instructs the go command to pass the -d=... to the Go compiler // when compiling all packages. Bisect varies PATTERN to determine the minimal set of changes // needed to reproduce the failure. // // The go command also checks the GOCOMPILEDEBUG environment variable for flags // to pass to the compiler, so the above command is equivalent to: // // bisect GOCOMPILEDEBUG=loopvarhash=PATTERN go test // // Finally, as mentioned earlier, the -compile flag allows shortening this command further: // // bisect -compile=loopvar go test // // # Defeating Build Caches // // Build systems cache build results, to avoid repeating the same compilations // over and over. When using a cached build result, the go command (correctly) // reprints the cached standard output and standard error associated with that // command invocation. (This makes commands like 'go build -gcflags=-S' for // printing an assembly listing work reliably.) // // Unfortunately, most build systems, including Bazel, are not as careful // as the go command about reprinting compiler output. If the compiler is // what prints match lines, a build system that suppresses compiler // output when using cached compiler results will confuse bisect. // To defeat such build caches, bisect replaces the literal text “RANDOM” // in environment values and command arguments with a random 64-bit value // during each invocation. The Go compiler conveniently accepts a // -d=ignore=... debug flag that ignores its argument, so to run the // previous example using Bazel, the invocation is: // // bazel test --define=gc_goopts=-d=loopvarhash=PATTERN,unused=RANDOM //path/to:test // // [GODEBUG setting]: https://tip.golang.org/doc/godebug package main import ( "context" "flag" "fmt" "io" "log" "math/bits" "math/rand" "os" "os/exec" "sort" "strconv" "strings" "time" "golang.org/x/tools/internal/bisect" ) // Preserve import of bisect, to allow [bisect.Match] in the doc comment. var _ bisect.Matcher func usage() { fmt.Fprintf(os.Stderr, "usage: bisect [flags] [var=value...] command [arguments...]\n") flag.PrintDefaults() os.Exit(2) } func main() { log.SetFlags(0) log.SetPrefix("bisect: ") var b Bisect b.Stdout = os.Stdout b.Stderr = os.Stderr flag.IntVar(&b.Max, "max", 0, "stop after finding `m` failing change sets") flag.IntVar(&b.MaxSet, "maxset", 0, "do not search for change sets larger than `s` elements") flag.DurationVar(&b.Timeout, "timeout", 0, "stop target and consider failed after duration `d`") flag.IntVar(&b.Count, "count", 2, "run target `n` times for each trial") flag.BoolVar(&b.Verbose, "v", false, "enable verbose output") env := "" envFlag := "" flag.Func("compile", "bisect source locations affected by Go compiler `rewrite` (fma, loopvar, ...)", func(value string) error { if envFlag != "" { return fmt.Errorf("cannot use -%s and -compile", envFlag) } envFlag = "compile" env = "GOCOMPILEDEBUG=" + value + "hash=PATTERN" return nil }) flag.Func("godebug", "bisect call stacks affected by GODEBUG setting `name=value`", func(value string) error { if envFlag != "" { return fmt.Errorf("cannot use -%s and -godebug", envFlag) } envFlag = "godebug" env = "GODEBUG=" + value + "#PATTERN" return nil }) flag.Usage = usage flag.Parse() args := flag.Args() // Split command line into env settings, command name, args. i := 0 for i < len(args) && strings.Contains(args[i], "=") { i++ } if i == len(args) { usage() } b.Env, b.Cmd, b.Args = args[:i], args[i], args[i+1:] if env != "" { b.Env = append([]string{env}, b.Env...) } // Check that PATTERN is available for us to vary. found := false for _, e := range b.Env { if _, v, _ := strings.Cut(e, "="); strings.Contains(v, "PATTERN") { found = true } } for _, a := range b.Args { if strings.Contains(a, "PATTERN") { found = true } } if !found { log.Fatalf("no PATTERN in target environment or args") } if !b.Search() { os.Exit(1) } } // A Bisect holds the state for a bisect invocation. type Bisect struct { // Env is the additional environment variables for the command. // PATTERN and RANDOM are substituted in the values, but not the names. Env []string // Cmd is the command (program name) to run. // PATTERN and RANDOM are not substituted. Cmd string // Args is the command arguments. // PATTERN and RANDOM are substituted anywhere they appear. Args []string // Command-line flags controlling bisect behavior. Max int // maximum number of sets to report (0 = unlimited) MaxSet int // maximum number of elements in a set (0 = unlimited) Timeout time.Duration // kill target and assume failed after this duration (0 = unlimited) Count int // run target this many times for each trial and give up if flaky (min 1 assumed; default 2 on command line set in main) Verbose bool // print long output about each trial (only useful for debugging bisect itself) // State for running bisect, replaced during testing. // Failing change sets are printed to Stdout; all other output goes to Stderr. Stdout io.Writer // where to write standard output (usually os.Stdout) Stderr io.Writer // where to write standard error (usually os.Stderr) TestRun func(env []string, cmd string, args []string) (out []byte, err error) // if non-nil, used instead of exec.Command // State maintained by Search. // By default, Search looks for a minimal set of changes that cause a failure when enabled. // If Disable is true, the search is inverted and seeks a minimal set of changes that // cause a failure when disabled. In this case, the search proceeds as normal except that // each pattern starts with a !. Disable bool // SkipDigits is the number of hex digits to use in skip messages. // If the set of available changes is the same in each run, as it should be, // then this doesn't matter: we'll only exclude suffixes that uniquely identify // a given change. But for some programs, especially bisecting runtime // behaviors, sometimes enabling one change unlocks questions about other // changes. Strictly speaking this is a misuse of bisect, but just to make // bisect more robust, we use the y and n runs to create an estimate of the // number of bits needed for a unique suffix, and then we round it up to // a number of hex digits, with one extra digit for good measure, and then // we always use that many hex digits for skips. SkipHexDigits int // Add is a list of suffixes to add to every trial, because they // contain changes that are necessary for a group we are assembling. Add []string // Skip is a list of suffixes that uniquely identify changes to exclude from every trial, // because they have already been used in failing change sets. // Suffixes later in the list may only be unique after removing // the ones earlier in the list. // Skip applies after Add. Skip []string } // A Result holds the result of a single target trial. type Result struct { Success bool // whether the target succeeded (exited with zero status) Cmd string // full target command line Out string // full target output (stdout and stderr combined) Suffix string // the suffix used for collecting MatchIDs, MatchText, and MatchFull MatchIDs []uint64 // match IDs enabled during this trial MatchText []string // match reports for the IDs, with match markers removed MatchFull []string // full match lines for the IDs, with match markers kept } // &searchFatal is a special panic value to signal that Search failed. // This lets us unwind the search recursion on a fatal error // but have Search return normally. var searchFatal int // Search runs a bisect search according to the configuration in b. // It reports whether any failing change sets were found. func (b *Bisect) Search() bool { defer func() { // Recover from panic(&searchFatal), implicitly returning false from Search. // Re-panic on any other panic. if e := recover(); e != nil && e != &searchFatal { panic(e) } }() // Run with no changes and all changes, to figure out which direction we're searching. // The goal is to find the minimal set of changes to toggle // starting with the state where everything works. // If "no changes" succeeds and "all changes" fails, // we're looking for a minimal set of changes to enable to provoke the failure // (broken = runY, b.Negate = false) // If "no changes" fails and "all changes" succeeds, // we're looking for a minimal set of changes to disable to provoke the failure // (broken = runN, b.Negate = true). b.Logf("checking target with all changes disabled") runN := b.Run("n") b.Logf("checking target with all changes enabled") runY := b.Run("y") var broken *Result switch { case runN.Success && !runY.Success: b.Logf("target succeeds with no changes, fails with all changes") b.Logf("searching for minimal set of enabled changes causing failure") broken = runY b.Disable = false case !runN.Success && runY.Success: b.Logf("target fails with no changes, succeeds with all changes") b.Logf("searching for minimal set of disabled changes causing failure") broken = runN b.Disable = true case runN.Success && runY.Success: b.Fatalf("target succeeds with no changes and all changes") case !runN.Success && !runY.Success: b.Fatalf("target fails with no changes and all changes") } // Compute minimum number of bits needed to distinguish // all the changes we saw during N and all the changes we saw during Y. b.SkipHexDigits = skipHexDigits(runN.MatchIDs, runY.MatchIDs) // Loop finding and printing change sets, until none remain. found := 0 for { // Find set. bad := b.search(broken) if bad == nil { if found == 0 { b.Fatalf("cannot find any failing change sets of size ≤ %d", b.MaxSet) } break } // Confirm that set really does fail, to avoid false accusations. // Also asking for user-visible output; earlier runs did not. b.Logf("confirming failing change set") b.Add = append(b.Add[:0], bad...) broken = b.Run("v") if broken.Success { b.Logf("confirmation run succeeded unexpectedly") } b.Add = b.Add[:0] // Print confirmed change set. found++ b.Logf("FOUND failing change set") desc := "(enabling changes causes failure)" if b.Disable { desc = "(disabling changes causes failure)" } fmt.Fprintf(b.Stdout, "--- change set #%d %s\n%s\n---\n", found, desc, strings.Join(broken.MatchText, "\n")) // Stop if we've found enough change sets. if b.Max > 0 && found >= b.Max { break } // If running bisect target | tee bad.txt, prints to stdout and stderr // both appear on the terminal, but the ones to stdout go through tee // and can take a little bit of extra time. Sleep 1 millisecond to give // tee time to catch up, so that its stdout print does not get interlaced // with the stderr print from the next b.Log message. time.Sleep(1 * time.Millisecond) // Disable the now-known-bad changes and see if any failures remain. b.Logf("checking for more failures") b.Skip = append(bad, b.Skip...) broken = b.Run("") if broken.Success { what := "enabled" if b.Disable { what = "disabled" } b.Logf("target succeeds with all remaining changes %s", what) break } b.Logf("target still fails; searching for more bad changes") } return true } // Fatalf prints a message to standard error and then panics, // causing Search to return false. func (b *Bisect) Fatalf(format string, args ...any) { s := fmt.Sprintf("bisect: fatal error: "+format, args...) if !strings.HasSuffix(s, "\n") { s += "\n" } b.Stderr.Write([]byte(s)) panic(&searchFatal) } // Logf prints a message to standard error. func (b *Bisect) Logf(format string, args ...any) { s := fmt.Sprintf("bisect: "+format, args...) if !strings.HasSuffix(s, "\n") { s += "\n" } b.Stderr.Write([]byte(s)) } func skipHexDigits(idY, idN []uint64) int { var all []uint64 seen := make(map[uint64]bool) for _, x := range idY { seen[x] = true all = append(all, x) } for _, x := range idN { if !seen[x] { seen[x] = true all = append(all, x) } } sort.Slice(all, func(i, j int) bool { return bits.Reverse64(all[i]) < bits.Reverse64(all[j]) }) digits := sort.Search(64/4, func(digits int) bool { mask := uint64(1)<<(4*digits) - 1 for i := 0; i+1 < len(all); i++ { if all[i]&mask == all[i+1]&mask { return false } } return true }) if digits < 64/4 { digits++ } return digits } // search searches for a single locally minimal change set. // // Invariant: r describes the result of r.Suffix + b.Add, which failed. // (There's an implicit -b.Skip everywhere here. b.Skip does not change.) // We want to extend r.Suffix to preserve the failure, working toward // a suffix that identifies a single change. func (b *Bisect) search(r *Result) []string { // The caller should be passing in a failure result that we diagnose. if r.Success { b.Fatalf("internal error: unexpected success") // mistake by caller } // If the failure reported no changes, the target is misbehaving. if len(r.MatchIDs) == 0 { b.Fatalf("failure with no reported changes:\n\n$ %s\n%s\n", r.Cmd, r.Out) } // If there's one matching change, that's the one we're looking for. if len(r.MatchIDs) == 1 { return []string{fmt.Sprintf("x%0*x", b.SkipHexDigits, r.MatchIDs[0]&(1<<(4*b.SkipHexDigits)-1))} } // If the suffix we were tracking in the trial is already 64 bits, // either the target is bad or bisect itself is buggy. if len(r.Suffix) >= 64 { b.Fatalf("failed to isolate a single change with very long suffix") } // We want to split the current matchIDs by left-extending the suffix with 0 and 1. // If all the matches have the same next bit, that won't cause a split, which doesn't // break the algorithm but does waste time. Avoid wasting time by left-extending // the suffix to the longest suffix shared by all the current match IDs // before adding 0 or 1. suffix := commonSuffix(r.MatchIDs) if !strings.HasSuffix(suffix, r.Suffix) { b.Fatalf("internal error: invalid common suffix") // bug in commonSuffix } // Run 0suffix and 1suffix. If one fails, chase down the failure in that half. r0 := b.Run("0" + suffix) if !r0.Success { return b.search(r0) } r1 := b.Run("1" + suffix) if !r1.Success { return b.search(r1) } // suffix failed, but 0suffix and 1suffix succeeded. // Assuming the target isn't flaky, this means we need // at least one change from 0suffix AND at least one from 1suffix. // We are already tracking N = len(b.Add) other changes and are // allowed to build sets of size at least 1+N (or we shouldn't be here at all). // If we aren't allowed to build sets of size 2+N, give up this branch. if b.MaxSet > 0 && 2+len(b.Add) > b.MaxSet { return nil } // Adding all matches for 1suffix, recurse to narrow down 0suffix. old := len(b.Add) b.Add = append(b.Add, "1"+suffix) r0 = b.Run("0" + suffix) if r0.Success { // 0suffix + b.Add + 1suffix = suffix + b.Add is what r describes, and it failed. b.Fatalf("target fails inconsistently") } bad0 := b.search(r0) if bad0 == nil { // Search failed due to MaxSet limit. return nil } b.Add = b.Add[:old] // Adding the specific match we found in 0suffix, recurse to narrow down 1suffix. b.Add = append(b.Add[:old], bad0...) r1 = b.Run("1" + suffix) if r1.Success { // 1suffix + b.Add + bad0 = bad0 + b.Add + 1suffix is what b.search(r0) reported as a failure. b.Fatalf("target fails inconsistently") } bad1 := b.search(r1) if bad1 == nil { // Search failed due to MaxSet limit. return nil } b.Add = b.Add[:old] // bad0 and bad1 together provoke the failure. return append(bad0, bad1...) } // Run runs a set of trials selecting changes with the given suffix, // plus the ones in b.Add and not the ones in b.Skip. // The returned result's MatchIDs, MatchText, and MatchFull // only list the changes that match suffix. // When b.Count > 1, Run runs b.Count trials and requires // that they all succeed or they all fail. If not, it calls b.Fatalf. func (b *Bisect) Run(suffix string) *Result { out := b.run(suffix) for i := 1; i < b.Count; i++ { r := b.run(suffix) if r.Success != out.Success { b.Fatalf("target fails inconsistently") } } return out } // run runs a single trial for Run. func (b *Bisect) run(suffix string) *Result { random := fmt.Sprint(rand.Uint64()) // Accept suffix == "v" to mean we need user-visible output. visible := "" if suffix == "v" { visible = "v" suffix = "" } // Construct change ID pattern. var pattern string if suffix == "y" || suffix == "n" { pattern = suffix suffix = "" } else { var elem []string if suffix != "" { elem = append(elem, "+", suffix) } for _, x := range b.Add { elem = append(elem, "+", x) } for _, x := range b.Skip { elem = append(elem, "-", x) } pattern = strings.Join(elem, "") if pattern == "" { pattern = "y" } } if b.Disable { pattern = "!" + pattern } pattern = visible + pattern // Construct substituted env and args. env := make([]string, len(b.Env)) for i, x := range b.Env { k, v, _ := strings.Cut(x, "=") env[i] = k + "=" + replace(v, pattern, random) } args := make([]string, len(b.Args)) for i, x := range b.Args { args[i] = replace(x, pattern, random) } // Construct and log command line. // There is no newline in the log print. // The line will be completed when the command finishes. cmdText := strings.Join(append(append(env, b.Cmd), args...), " ") fmt.Fprintf(b.Stderr, "bisect: run: %s...", cmdText) // Run command with args and env. var out []byte var err error if b.TestRun != nil { out, err = b.TestRun(env, b.Cmd, args) } else { ctx := context.Background() if b.Timeout != 0 { var cancel context.CancelFunc ctx, cancel = context.WithTimeout(ctx, b.Timeout) defer cancel() } cmd := exec.CommandContext(ctx, b.Cmd, args...) cmd.Env = append(os.Environ(), env...) // Set up cmd.Cancel, cmd.WaitDelay on Go 1.20 and later // TODO(rsc): Inline go120.go's cmdInterrupt once we stop supporting Go 1.19. cmdInterrupt(cmd) out, err = cmd.CombinedOutput() } // Parse output to construct result. r := &Result{ Suffix: suffix, Success: err == nil, Cmd: cmdText, Out: string(out), } // Calculate bits, mask to identify suffix matches. var bits, mask uint64 if suffix != "" && suffix != "y" && suffix != "n" && suffix != "v" { var err error bits, err = strconv.ParseUint(suffix, 2, 64) if err != nil { b.Fatalf("internal error: bad suffix") } mask = uint64(1<= 0; i-- { s[i] = '0' + byte(b&1) b >>= 1 } return string(s[:]) } golang-golang-x-tools-0.25.0+ds/cmd/bisect/main_test.go000066400000000000000000000102471476103172300226550ustar00rootroot00000000000000// Copyright 2023 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" "encoding/json" "flag" "fmt" "go/build/constraint" "math/rand" "os" "path/filepath" "strings" "testing" "golang.org/x/tools/internal/bisect" "golang.org/x/tools/internal/diffp" "golang.org/x/tools/txtar" ) var update = flag.Bool("update", false, "update testdata with new stdout/stderr") func Test(t *testing.T) { files, err := filepath.Glob("testdata/*.txt") if err != nil { t.Fatal(err) } for _, file := range files { t.Run(strings.TrimSuffix(filepath.Base(file), ".txt"), func(t *testing.T) { data, err := os.ReadFile(file) if err != nil { t.Fatal(err) } a := txtar.Parse(data) var wantStdout, wantStderr []byte files := a.Files if len(files) > 0 && files[0].Name == "stdout" { wantStdout = files[0].Data files = files[1:] } if len(files) > 0 && files[0].Name == "stderr" { wantStderr = files[0].Data files = files[1:] } if len(files) > 0 { t.Fatalf("unexpected txtar entry: %s", files[0].Name) } var tt struct { Fail string Bisect Bisect } if err := json.Unmarshal(a.Comment, &tt); err != nil { t.Fatal(err) } expr, err := constraint.Parse("//go:build " + tt.Fail) if err != nil { t.Fatalf("invalid Cmd: %v", err) } rnd := rand.New(rand.NewSource(1)) b := &tt.Bisect b.Cmd = "test" b.Args = []string{"PATTERN"} var stdout, stderr bytes.Buffer b.Stdout = &stdout b.Stderr = &stderr b.TestRun = func(env []string, cmd string, args []string) (out []byte, err error) { pattern := args[0] m, err := bisect.New(pattern) if err != nil { t.Fatal(err) } have := make(map[string]bool) for i, color := range colors { if m.ShouldEnable(uint64(i)) { have[color] = true } if m.ShouldReport(uint64(i)) { out = fmt.Appendf(out, "%s %s\n", color, bisect.Marker(uint64(i))) } } err = nil if eval(rnd, expr, have) { err = fmt.Errorf("failed") } return out, err } if !b.Search() { stderr.WriteString("\n") } rewrite := false if !bytes.Equal(stdout.Bytes(), wantStdout) { if *update { rewrite = true } else { t.Errorf("incorrect stdout: %s", diffp.Diff("have", stdout.Bytes(), "want", wantStdout)) } } if !bytes.Equal(stderr.Bytes(), wantStderr) { if *update { rewrite = true } else { t.Errorf("incorrect stderr: %s", diffp.Diff("have", stderr.Bytes(), "want", wantStderr)) } } if rewrite { a.Files = []txtar.File{{Name: "stdout", Data: stdout.Bytes()}, {Name: "stderr", Data: stderr.Bytes()}} err := os.WriteFile(file, txtar.Format(a), 0666) if err != nil { t.Fatal(err) } t.Logf("updated %s", file) } }) } } func eval(rnd *rand.Rand, z constraint.Expr, have map[string]bool) bool { switch z := z.(type) { default: panic(fmt.Sprintf("unexpected type %T", z)) case *constraint.NotExpr: return !eval(rnd, z.X, have) case *constraint.AndExpr: return eval(rnd, z.X, have) && eval(rnd, z.Y, have) case *constraint.OrExpr: return eval(rnd, z.X, have) || eval(rnd, z.Y, have) case *constraint.TagExpr: if z.Tag == "random" { return rnd.Intn(2) == 1 } return have[z.Tag] } } var colors = strings.Fields(` aliceblue amaranth amber amethyst applegreen applered apricot aquamarine azure babyblue beige brickred black blue bluegreen blueviolet blush bronze brown burgundy byzantium carmine cerise cerulean champagne chartreusegreen chocolate cobaltblue coffee copper coral crimson cyan desertsand electricblue emerald erin gold gray green harlequin indigo ivory jade junglegreen lavender lemon lilac lime magenta magentarose maroon mauve navyblue ochre olive orange orangered orchid peach pear periwinkle persianblue pink plum prussianblue puce purple raspberry red redviolet rose ruby salmon sangria sapphire scarlet silver slategray springbud springgreen tan taupe teal turquoise ultramarine violet viridian white yellow `) golang-golang-x-tools-0.25.0+ds/cmd/bisect/rand.go000066400000000000000000000006721476103172300216170ustar00rootroot00000000000000// Copyright 2023 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. // Starting in Go 1.20, the global rand is auto-seeded, // with a better value than the current Unix nanoseconds. // Only seed if we're using older versions of Go. //go:build !go1.20 package main import ( "math/rand" "time" ) func init() { rand.Seed(time.Now().UnixNano()) } golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/000077500000000000000000000000001476103172300221505ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/README.md000066400000000000000000000023201476103172300234240ustar00rootroot00000000000000This directory contains test inputs for the bisect command. Each text file is a txtar archive (see or `go doc txtar`). The comment at the top of the archive is a JSON object describing a target behavior. Specifically, the Fail key gives a boolean expression that should provoke a failure. Bisect's job is to discover this condition. The Bisect key describes settings in the Bisect struct that we want to change, to simulate the use of various command-line options. The txtar archive files should be "stdout" and "stderr", giving the expected standard output and standard error. If the bisect command should exit with a non-zero status, the stderr in the archive will end with the line "". Running `go test -update` will rewrite the stdout and stderr files in each testdata archive to match the current state of the tool. This is a useful command when the logging prints from bisect change or when writing a new test. To use `go test -update` to write a new test: - Create a new .txt file with just a JSON object at the top, specifying what you want to test. - Run `go test -update`. - Reload the .txt file and read the stdout and stderr to see if you agree. golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/basic.txt000066400000000000000000000034121476103172300237720ustar00rootroot00000000000000{"Fail": "amber || apricot"} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) apricot --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... FAIL (44 matches) bisect: run: test +00-x002... ok (23 matches) bisect: run: test +10-x002... FAIL (21 matches) bisect: run: test +010-x002... ok (10 matches) bisect: run: test +110-x002... FAIL (11 matches) bisect: run: test +0110-x002... FAIL (6 matches) bisect: run: test +00110-x002... FAIL (3 matches) bisect: run: test +000110-x002... FAIL (2 matches) bisect: run: test +0000110-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x006-x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x006-x002... ok (88 matches) bisect: target succeeds with all remaining changes enabled golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/count2.txt000066400000000000000000000055051476103172300241300ustar00rootroot00000000000000{"Fail": "amber || apricot", "Bisect": {"Count": 2}} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) apricot --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... FAIL (44 matches) bisect: run: test +0-x002... FAIL (44 matches) bisect: run: test +00-x002... ok (23 matches) bisect: run: test +00-x002... ok (23 matches) bisect: run: test +10-x002... FAIL (21 matches) bisect: run: test +10-x002... FAIL (21 matches) bisect: run: test +010-x002... ok (10 matches) bisect: run: test +010-x002... ok (10 matches) bisect: run: test +110-x002... FAIL (11 matches) bisect: run: test +110-x002... FAIL (11 matches) bisect: run: test +0110-x002... FAIL (6 matches) bisect: run: test +0110-x002... FAIL (6 matches) bisect: run: test +00110-x002... FAIL (3 matches) bisect: run: test +00110-x002... FAIL (3 matches) bisect: run: test +000110-x002... FAIL (2 matches) bisect: run: test +000110-x002... FAIL (2 matches) bisect: run: test +0000110-x002... FAIL (1 matches) bisect: run: test +0000110-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x006-x002... FAIL (1 matches) bisect: run: test v+x006-x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x006-x002... ok (88 matches) bisect: run: test -x006-x002... ok (88 matches) bisect: target succeeds with all remaining changes enabled golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/double.txt000066400000000000000000000046451476103172300241740ustar00rootroot00000000000000{"Fail": "amber || apricot && peach"} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) apricot peach --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... ok (44 matches) bisect: run: test +1-x002... ok (45 matches) bisect: run: test +0+1-x002... FAIL (44 matches) bisect: run: test +00+1-x002... ok (23 matches) bisect: run: test +10+1-x002... FAIL (21 matches) bisect: run: test +010+1-x002... ok (10 matches) bisect: run: test +110+1-x002... FAIL (11 matches) bisect: run: test +0110+1-x002... FAIL (6 matches) bisect: run: test +00110+1-x002... FAIL (3 matches) bisect: run: test +000110+1-x002... FAIL (2 matches) bisect: run: test +0000110+1-x002... FAIL (1 matches) bisect: run: test +1+x006-x002... FAIL (45 matches) bisect: run: test +01+x006-x002... ok (23 matches) bisect: run: test +11+x006-x002... FAIL (22 matches) bisect: run: test +011+x006-x002... FAIL (11 matches) bisect: run: test +0011+x006-x002... ok (6 matches) bisect: run: test +1011+x006-x002... FAIL (5 matches) bisect: run: test +01011+x006-x002... ok (3 matches) bisect: run: test +11011+x006-x002... FAIL (2 matches) bisect: run: test +011011+x006-x002... ok (1 matches) bisect: run: test +111011+x006-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x006+x03b-x002... FAIL (2 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x006-x03b-x002... ok (87 matches) bisect: target succeeds with all remaining changes enabled golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/max1.txt000066400000000000000000000016401476103172300235600ustar00rootroot00000000000000{"Fail": "amber || apricot && peach", "Bisect": {"Max": 1}} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/max2.txt000066400000000000000000000052231476103172300235620ustar00rootroot00000000000000{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"Max": 2}} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) blue green red --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... ok (44 matches) bisect: run: test +1-x002... FAIL (45 matches) bisect: run: test +01-x002... ok (23 matches) bisect: run: test +11-x002... ok (22 matches) bisect: run: test +01+11-x002... FAIL (23 matches) bisect: run: test +001+11-x002... ok (12 matches) bisect: run: test +101+11-x002... FAIL (11 matches) bisect: run: test +0101+11-x002... ok (6 matches) bisect: run: test +1101+11-x002... ok (5 matches) bisect: run: test +0101+11+1101-x002... FAIL (6 matches) bisect: run: test +00101+11+1101-x002... FAIL (3 matches) bisect: run: test +000101+11+1101-x002... FAIL (2 matches) bisect: run: test +0000101+11+1101-x002... ok (1 matches) bisect: run: test +1000101+11+1101-x002... FAIL (1 matches) bisect: run: test +1101+11+x045-x002... FAIL (5 matches) bisect: run: test +01101+11+x045-x002... FAIL (3 matches) bisect: run: test +001101+11+x045-x002... FAIL (2 matches) bisect: run: test +0001101+11+x045-x002... FAIL (1 matches) bisect: run: test +11+x045+x00d-x002... FAIL (22 matches) bisect: run: test +011+x045+x00d-x002... ok (11 matches) bisect: run: test +111+x045+x00d-x002... FAIL (11 matches) bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches) bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches) bisect: run: test +000111+x045+x00d-x002... ok (2 matches) bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches) bisect: FOUND failing change set golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset.txt000066400000000000000000000104431476103172300242140ustar00rootroot00000000000000{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 3}} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) blue green red --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... ok (44 matches) bisect: run: test +1-x002... FAIL (45 matches) bisect: run: test +01-x002... ok (23 matches) bisect: run: test +11-x002... ok (22 matches) bisect: run: test +01+11-x002... FAIL (23 matches) bisect: run: test +001+11-x002... ok (12 matches) bisect: run: test +101+11-x002... FAIL (11 matches) bisect: run: test +0101+11-x002... ok (6 matches) bisect: run: test +1101+11-x002... ok (5 matches) bisect: run: test +0101+11+1101-x002... FAIL (6 matches) bisect: run: test +00101+11+1101-x002... FAIL (3 matches) bisect: run: test +000101+11+1101-x002... FAIL (2 matches) bisect: run: test +0000101+11+1101-x002... ok (1 matches) bisect: run: test +1000101+11+1101-x002... FAIL (1 matches) bisect: run: test +1101+11+x045-x002... FAIL (5 matches) bisect: run: test +01101+11+x045-x002... FAIL (3 matches) bisect: run: test +001101+11+x045-x002... FAIL (2 matches) bisect: run: test +0001101+11+x045-x002... FAIL (1 matches) bisect: run: test +11+x045+x00d-x002... FAIL (22 matches) bisect: run: test +011+x045+x00d-x002... ok (11 matches) bisect: run: test +111+x045+x00d-x002... FAIL (11 matches) bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches) bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches) bisect: run: test +000111+x045+x00d-x002... ok (2 matches) bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches) bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches) bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches) bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches) bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches) bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches) bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches) bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches) bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches) bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches) bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches) bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches) bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches) bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches) bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches) bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches) golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset1.txt000066400000000000000000000010571476103172300242760ustar00rootroot00000000000000{"Fail": "apricot && peach", "Bisect": {"MaxSet": 1}} -- stdout -- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... ok (45 matches) bisect: run: test +1... ok (45 matches) bisect: fatal error: cannot find any failing change sets of size ≤ 1 golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/maxset4.txt000066400000000000000000000173341476103172300243060ustar00rootroot00000000000000{"Fail": "amber || apricot && peach || red && green && blue || cyan && magenta && yellow && black", "Bisect": {"MaxSet": 4}} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) blue green red --- --- change set #3 (enabling changes causes failure) black cyan magenta yellow --- --- change set #4 (enabling changes causes failure) apricot peach --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... ok (44 matches) bisect: run: test +1-x002... FAIL (45 matches) bisect: run: test +01-x002... ok (23 matches) bisect: run: test +11-x002... ok (22 matches) bisect: run: test +01+11-x002... FAIL (23 matches) bisect: run: test +001+11-x002... ok (12 matches) bisect: run: test +101+11-x002... FAIL (11 matches) bisect: run: test +0101+11-x002... ok (6 matches) bisect: run: test +1101+11-x002... ok (5 matches) bisect: run: test +0101+11+1101-x002... FAIL (6 matches) bisect: run: test +00101+11+1101-x002... FAIL (3 matches) bisect: run: test +000101+11+1101-x002... FAIL (2 matches) bisect: run: test +0000101+11+1101-x002... ok (1 matches) bisect: run: test +1000101+11+1101-x002... FAIL (1 matches) bisect: run: test +1101+11+x045-x002... FAIL (5 matches) bisect: run: test +01101+11+x045-x002... FAIL (3 matches) bisect: run: test +001101+11+x045-x002... FAIL (2 matches) bisect: run: test +0001101+11+x045-x002... FAIL (1 matches) bisect: run: test +11+x045+x00d-x002... FAIL (22 matches) bisect: run: test +011+x045+x00d-x002... ok (11 matches) bisect: run: test +111+x045+x00d-x002... FAIL (11 matches) bisect: run: test +0111+x045+x00d-x002... FAIL (6 matches) bisect: run: test +00111+x045+x00d-x002... FAIL (3 matches) bisect: run: test +000111+x045+x00d-x002... ok (2 matches) bisect: run: test +100111+x045+x00d-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x045+x00d+x027-x002... FAIL (3 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x045-x00d-x027-x002... FAIL (86 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x045-x00d-x027-x002... ok (44 matches) bisect: run: test +1-x045-x00d-x027-x002... ok (42 matches) bisect: run: test +0+1-x045-x00d-x027-x002... FAIL (44 matches) bisect: run: test +00+1-x045-x00d-x027-x002... FAIL (23 matches) bisect: run: test +000+1-x045-x00d-x027-x002... ok (12 matches) bisect: run: test +100+1-x045-x00d-x027-x002... ok (11 matches) bisect: run: test +000+1+100-x045-x00d-x027-x002... FAIL (12 matches) bisect: run: test +0000+1+100-x045-x00d-x027-x002... FAIL (6 matches) bisect: run: test +00000+1+100-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +000000+1+100-x045-x00d-x027-x002... ok (2 matches) bisect: run: test +100000+1+100-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +100+1+x020-x045-x00d-x027-x002... FAIL (11 matches) bisect: run: test +0100+1+x020-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +1100+1+x020-x045-x00d-x027-x002... FAIL (5 matches) bisect: run: test +01100+1+x020-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +001100+1+x020-x045-x00d-x027-x002... FAIL (2 matches) bisect: run: test +0001100+1+x020-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +1+x020+x00c-x045-x00d-x027-x002... FAIL (42 matches) bisect: run: test +01+x020+x00c-x045-x00d-x027-x002... FAIL (21 matches) bisect: run: test +001+x020+x00c-x045-x00d-x027-x002... FAIL (12 matches) bisect: run: test +0001+x020+x00c-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +1001+x020+x00c-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +0001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (6 matches) bisect: run: test +00001+x020+x00c+1001-x045-x00d-x027-x002... ok (3 matches) bisect: run: test +10001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +010001+x020+x00c+1001-x045-x00d-x027-x002... ok (2 matches) bisect: run: test +110001+x020+x00c+1001-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +1001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (6 matches) bisect: run: test +01001+x020+x00c+x031-x045-x00d-x027-x002... ok (3 matches) bisect: run: test +11001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (2 matches) bisect: run: test +0011001+x020+x00c+x031-x045-x00d-x027-x002... ok (1 matches) bisect: run: test +1011001+x020+x00c+x031-x045-x00d-x027-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x020+x00c+x031+x059-x045-x00d-x027-x002... FAIL (4 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (82 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (42 matches) bisect: run: test +1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (40 matches) bisect: run: test +0+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (42 matches) bisect: run: test +00+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (21 matches) bisect: run: test +10+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches) bisect: run: test +010+1-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (10 matches) bisect: run: test +110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches) bisect: run: test +0110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (6 matches) bisect: run: test +00110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (3 matches) bisect: run: test +000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches) bisect: run: test +0000110+1-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches) bisect: run: test +1+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (40 matches) bisect: run: test +01+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (19 matches) bisect: run: test +11+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (21 matches) bisect: run: test +011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (11 matches) bisect: run: test +0011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (6 matches) bisect: run: test +1011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (5 matches) bisect: run: test +01011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (3 matches) bisect: run: test +11011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches) bisect: run: test +011011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (1 matches) bisect: run: test +111011+x006-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x006+x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... FAIL (2 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x006-x03b-x020-x00c-x031-x059-x045-x00d-x027-x002... ok (80 matches) bisect: target succeeds with all remaining changes enabled golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/negate.txt000066400000000000000000000047151476103172300241630ustar00rootroot00000000000000{"Fail": "!amber || !apricot && !peach"} -- stdout -- --- change set #1 (disabling changes causes failure) amber --- --- change set #2 (disabling changes causes failure) apricot peach --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... FAIL (90 matches) bisect: checking target with all changes enabled bisect: run: test y... ok (90 matches) bisect: target fails with no changes, succeeds with all changes bisect: searching for minimal set of disabled changes causing failure bisect: run: test !+0... FAIL (45 matches) bisect: run: test !+00... ok (23 matches) bisect: run: test !+10... FAIL (22 matches) bisect: run: test !+010... FAIL (11 matches) bisect: run: test !+0010... FAIL (6 matches) bisect: run: test !+00010... FAIL (3 matches) bisect: run: test !+000010... FAIL (2 matches) bisect: run: test !+0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v!+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test !-x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test !+0-x002... ok (44 matches) bisect: run: test !+1-x002... ok (45 matches) bisect: run: test !+0+1-x002... FAIL (44 matches) bisect: run: test !+00+1-x002... ok (23 matches) bisect: run: test !+10+1-x002... FAIL (21 matches) bisect: run: test !+010+1-x002... ok (10 matches) bisect: run: test !+110+1-x002... FAIL (11 matches) bisect: run: test !+0110+1-x002... FAIL (6 matches) bisect: run: test !+00110+1-x002... FAIL (3 matches) bisect: run: test !+000110+1-x002... FAIL (2 matches) bisect: run: test !+0000110+1-x002... FAIL (1 matches) bisect: run: test !+1+x006-x002... FAIL (45 matches) bisect: run: test !+01+x006-x002... ok (23 matches) bisect: run: test !+11+x006-x002... FAIL (22 matches) bisect: run: test !+011+x006-x002... FAIL (11 matches) bisect: run: test !+0011+x006-x002... ok (6 matches) bisect: run: test !+1011+x006-x002... FAIL (5 matches) bisect: run: test !+01011+x006-x002... ok (3 matches) bisect: run: test !+11011+x006-x002... FAIL (2 matches) bisect: run: test !+011011+x006-x002... ok (1 matches) bisect: run: test !+111011+x006-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v!+x006+x03b-x002... FAIL (2 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test !-x006-x03b-x002... ok (87 matches) bisect: target succeeds with all remaining changes disabled golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand.txt000066400000000000000000000050601476103172300236360ustar00rootroot00000000000000{"Fail": "amber || apricot || blue && random"} -- stdout -- --- change set #1 (enabling changes causes failure) amber --- --- change set #2 (enabling changes causes failure) apricot --- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... FAIL (45 matches) bisect: run: test +00... ok (23 matches) bisect: run: test +10... FAIL (22 matches) bisect: run: test +010... FAIL (11 matches) bisect: run: test +0010... FAIL (6 matches) bisect: run: test +00010... FAIL (3 matches) bisect: run: test +000010... FAIL (2 matches) bisect: run: test +0000010... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x002... FAIL (89 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x002... FAIL (44 matches) bisect: run: test +00-x002... ok (23 matches) bisect: run: test +10-x002... FAIL (21 matches) bisect: run: test +010-x002... ok (10 matches) bisect: run: test +110-x002... FAIL (11 matches) bisect: run: test +0110-x002... FAIL (6 matches) bisect: run: test +00110-x002... FAIL (3 matches) bisect: run: test +000110-x002... FAIL (2 matches) bisect: run: test +0000110-x002... FAIL (1 matches) bisect: confirming failing change set bisect: run: test v+x006-x002... FAIL (1 matches) bisect: FOUND failing change set bisect: checking for more failures bisect: run: test -x006-x002... FAIL (88 matches) bisect: target still fails; searching for more bad changes bisect: run: test +0-x006-x002... ok (43 matches) bisect: run: test +1-x006-x002... FAIL (45 matches) bisect: run: test +01-x006-x002... FAIL (23 matches) bisect: run: test +001-x006-x002... ok (12 matches) bisect: run: test +101-x006-x002... FAIL (11 matches) bisect: run: test +0101-x006-x002... ok (6 matches) bisect: run: test +1101-x006-x002... FAIL (5 matches) bisect: run: test +01101-x006-x002... ok (3 matches) bisect: run: test +11101-x006-x002... ok (2 matches) bisect: run: test +01101+11101-x006-x002... FAIL (3 matches) bisect: run: test +001101+11101-x006-x002... ok (2 matches) bisect: run: test +101101+11101-x006-x002... ok (1 matches) bisect: run: test +001101+11101+101101-x006-x002... ok (2 matches) bisect: fatal error: target fails inconsistently golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand1.txt000066400000000000000000000017751476103172300237300ustar00rootroot00000000000000{"Fail": "blue && random"} -- stdout -- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... ok (45 matches) bisect: run: test +1... FAIL (45 matches) bisect: run: test +01... FAIL (23 matches) bisect: run: test +001... ok (12 matches) bisect: run: test +101... FAIL (11 matches) bisect: run: test +0101... ok (6 matches) bisect: run: test +1101... FAIL (5 matches) bisect: run: test +01101... ok (3 matches) bisect: run: test +11101... ok (2 matches) bisect: run: test +01101+11101... FAIL (3 matches) bisect: run: test +001101+11101... ok (2 matches) bisect: run: test +101101+11101... ok (1 matches) bisect: run: test +001101+11101+101101... ok (2 matches) bisect: fatal error: target fails inconsistently golang-golang-x-tools-0.25.0+ds/cmd/bisect/testdata/rand2.txt000066400000000000000000000014161476103172300237210ustar00rootroot00000000000000{"Fail": "blue && random", "Bisect": {"Count": 2}} -- stdout -- -- stderr -- bisect: checking target with all changes disabled bisect: run: test n... ok (90 matches) bisect: run: test n... ok (90 matches) bisect: checking target with all changes enabled bisect: run: test y... FAIL (90 matches) bisect: run: test y... FAIL (90 matches) bisect: target succeeds with no changes, fails with all changes bisect: searching for minimal set of enabled changes causing failure bisect: run: test +0... ok (45 matches) bisect: run: test +0... ok (45 matches) bisect: run: test +1... FAIL (45 matches) bisect: run: test +1... FAIL (45 matches) bisect: run: test +01... FAIL (23 matches) bisect: run: test +01... ok (23 matches) bisect: fatal error: target fails inconsistently golang-golang-x-tools-0.25.0+ds/cmd/bundle/000077500000000000000000000000001476103172300203375ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/.gitignore000066400000000000000000000000211476103172300223200ustar00rootroot00000000000000testdata/out.got golang-golang-x-tools-0.25.0+ds/cmd/bundle/main.go000066400000000000000000000325261476103172300216220ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Bundle creates a single-source-file version of a source package // suitable for inclusion in a particular target package. // // Usage: // // bundle [-o file] [-dst path] [-pkg name] [-prefix p] [-import old=new] [-tags build_constraints] // // The src argument specifies the import path of the package to bundle. // The bundling of a directory of source files into a single source file // necessarily imposes a number of constraints. // The package being bundled must not use cgo; must not use conditional // file compilation, whether with build tags or system-specific file names // like code_amd64.go; must not depend on any special comments, which // may not be preserved; must not use any assembly sources; // must not use renaming imports; and must not use reflection-based APIs // that depend on the specific names of types or struct fields. // // 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.” // // 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, // but the destination package can be specified explicitly using the -dst option, // which takes an import path as its argument. // If the source package imports the destination package, bundle will remove // those imports and rewrite any references to use direct references to the // corresponding symbols. // Bundle also must write a package declaration in the output and must // choose a name to use in that declaration. // If the -pkg option is given, bundle uses that name. // Otherwise, the name of the destination package is used. // Build constraints for the generated file can be specified using the -tags option. // // To avoid collisions, bundle inserts a prefix at the beginning of // every package-level const, func, type, and var identifier in src's code, // updating references accordingly. The default prefix is the package name // of the source package followed by an underscore. The -prefix option // specifies an alternate prefix. // // Occasionally it is necessary to rewrite imports during the bundling // process. The -import option, which may be repeated, specifies that // an import of "old" should be rewritten to import "new" instead. // // # Example // // Bundle archive/zip for inclusion in cmd/dist: // // cd $GOROOT/src/cmd/dist // bundle -o zip.go archive/zip // // Bundle golang.org/x/net/http2 for inclusion in net/http, // prefixing all identifiers by "http2" instead of "http2_", and // including a "!nethttpomithttp2" build constraint: // // cd $GOROOT/src/net/http // bundle -o h2_bundle.go -prefix http2 -tags '!nethttpomithttp2' golang.org/x/net/http2 // // Update the http2 bundle in net/http: // // go generate net/http // // Update all bundles in the standard library: // // go generate -run bundle std package main import ( "bytes" "flag" "fmt" "go/ast" "go/format" "go/printer" "go/token" "go/types" "log" "os" "strconv" "strings" "unicode" "golang.org/x/tools/go/packages" ) var ( outputFile = flag.String("o", "", "write output to `file` (default standard output)") dstPath = flag.String("dst", ".", "set destination import `path`") pkgName = flag.String("pkg", "", "set destination package `name`") prefix = flag.String("prefix", "&_", "set bundled identifier prefix to `p` (default is \"&_\", where & stands for the original name)") buildTags = flag.String("tags", "", "the build constraints to be inserted into the generated file") importMap = map[string]string{} ) func init() { flag.Var(flagFunc(addImportMap), "import", "rewrite import using `map`, of form old=new (can be repeated)") } func addImportMap(s string) { if strings.Count(s, "=") != 1 { log.Fatal("-import argument must be of the form old=new") } i := strings.Index(s, "=") old, new := s[:i], s[i+1:] if old == "" || new == "" { log.Fatal("-import argument must be of the form old=new; old and new must be non-empty") } importMap[old] = new } func usage() { fmt.Fprintf(os.Stderr, "Usage: bundle [options] \n") flag.PrintDefaults() } func main() { log.SetPrefix("bundle: ") log.SetFlags(0) flag.Usage = usage flag.Parse() args := flag.Args() if len(args) != 1 { usage() os.Exit(2) } cfg := &packages.Config{Mode: packages.NeedName} pkgs, err := packages.Load(cfg, *dstPath) if err != nil { log.Fatalf("cannot load destination package: %v", err) } if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 { log.Fatalf("failed to load destination package") } if *pkgName == "" { *pkgName = pkgs[0].Name } code, err := bundle(args[0], pkgs[0].PkgPath, *pkgName, *prefix, *buildTags) if err != nil { log.Fatal(err) } if *outputFile != "" { err := os.WriteFile(*outputFile, code, 0666) if err != nil { log.Fatal(err) } } else { _, err := os.Stdout.Write(code) if err != nil { log.Fatal(err) } } } // isStandardImportPath is copied from cmd/go in the standard library. func isStandardImportPath(path string) bool { i := strings.Index(path, "/") if i < 0 { i = len(path) } elem := path[:i] return !strings.Contains(elem, ".") } var testingOnlyPackagesConfig *packages.Config func bundle(src, dst, dstpkg, prefix, buildTags string) ([]byte, error) { // Load the initial package. cfg := &packages.Config{} if testingOnlyPackagesConfig != nil { *cfg = *testingOnlyPackagesConfig } else { // Bypass default vendor mode, as we need a package not available in the // std module vendor folder. cfg.Env = append(os.Environ(), "GOFLAGS=-mod=mod") } cfg.Mode = packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo pkgs, err := packages.Load(cfg, src) if err != nil { return nil, err } if packages.PrintErrors(pkgs) > 0 || len(pkgs) != 1 { return nil, fmt.Errorf("failed to load source package") } pkg := pkgs[0] if strings.Contains(prefix, "&") { prefix = strings.Replace(prefix, "&", pkg.Syntax[0].Name.Name, -1) } objsToUpdate := make(map[types.Object]bool) var rename func(from types.Object) rename = func(from types.Object) { if !objsToUpdate[from] { objsToUpdate[from] = true // Renaming a type that is used as an embedded field // requires renaming the field too. e.g. // type T int // if we rename this to U.. // var s struct {T} // print(s.T) // ...this must change too if _, ok := from.(*types.TypeName); ok { for id, obj := range pkg.TypesInfo.Uses { if obj == from { if field := pkg.TypesInfo.Defs[id]; field != nil { rename(field) } } } } } } // Rename each package-level object. scope := pkg.Types.Scope() for _, name := range scope.Names() { rename(scope.Lookup(name)) } var out bytes.Buffer if buildTags != "" { fmt.Fprintf(&out, "//go:build %s\n", buildTags) } 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(quoteArgs(os.Args[1:]), " ")) } else { fmt.Fprintf(&out, "// $ bundle %s\n", strings.Join(os.Args[1:], " ")) } fmt.Fprintf(&out, "\n") // Concatenate package comments from all files... for _, f := range pkg.Syntax { if doc := f.Doc.Text(); strings.TrimSpace(doc) != "" { for _, line := range strings.Split(doc, "\n") { fmt.Fprintf(&out, "// %s\n", line) } } } // ...but don't let them become the actual package comment. fmt.Fprintln(&out) fmt.Fprintf(&out, "package %s\n\n", dstpkg) // BUG(adonovan,shurcooL): bundle may generate incorrect code // due to shadowing between identifiers and imported package names. // // The generated code will either fail to compile or // (unlikely) compile successfully but have different behavior // than the original package. The risk of this happening is higher // when the original package has renamed imports (they're typically // renamed in order to resolve a shadow inside that particular .go file). // TODO(adonovan,shurcooL): // - detect shadowing issues, and either return error or resolve them // - preserve comments from the original import declarations. // pkgStd and pkgExt are sets of printed import specs. This is done // to deduplicate instances of the same import name and path. var pkgStd = make(map[string]bool) var pkgExt = make(map[string]bool) for _, f := range pkg.Syntax { for _, imp := range f.Imports { path, err := strconv.Unquote(imp.Path.Value) if err != nil { log.Fatalf("invalid import path string: %v", err) // Shouldn't happen here since packages.Load succeeded. } if path == dst { continue } if newPath, ok := importMap[path]; ok { path = newPath } var name string if imp.Name != nil { name = imp.Name.Name } spec := fmt.Sprintf("%s %q", name, path) if isStandardImportPath(path) { pkgStd[spec] = true } else { pkgExt[spec] = true } } } // Print a single declaration that imports all necessary packages. fmt.Fprintln(&out, "import (") for p := range pkgStd { fmt.Fprintf(&out, "\t%s\n", p) } if len(pkgExt) > 0 { fmt.Fprintln(&out) } for p := range pkgExt { fmt.Fprintf(&out, "\t%s\n", p) } fmt.Fprint(&out, ")\n\n") // Modify and print each file. for _, f := range pkg.Syntax { // Update renamed identifiers. for id, obj := range pkg.TypesInfo.Defs { if objsToUpdate[obj] { id.Name = prefix + obj.Name() } } for id, obj := range pkg.TypesInfo.Uses { if objsToUpdate[obj] { id.Name = prefix + obj.Name() } } // For each qualified identifier that refers to the // destination package, remove the qualifier. // The "@@@." strings are removed in postprocessing. ast.Inspect(f, func(n ast.Node) bool { if sel, ok := n.(*ast.SelectorExpr); ok { if id, ok := sel.X.(*ast.Ident); ok { if obj, ok := pkg.TypesInfo.Uses[id].(*types.PkgName); ok { if obj.Imported().Path() == dst { id.Name = "@@@" } } } } return true }) last := f.Package if len(f.Imports) > 0 { imp := f.Imports[len(f.Imports)-1] last = imp.End() if imp.Comment != nil { if e := imp.Comment.End(); e > last { last = e } } } // Pretty-print package-level declarations. // but no package or import declarations. var buf bytes.Buffer for _, decl := range f.Decls { if decl, ok := decl.(*ast.GenDecl); ok && decl.Tok == token.IMPORT { continue } beg, end := sourceRange(decl) printComments(&out, f.Comments, last, beg) buf.Reset() format.Node(&buf, pkg.Fset, &printer.CommentedNode{Node: decl, Comments: f.Comments}) // Remove each "@@@." in the output. // TODO(adonovan): not hygienic. out.Write(bytes.Replace(buf.Bytes(), []byte("@@@."), nil, -1)) last = printSameLineComment(&out, f.Comments, pkg.Fset, end) out.WriteString("\n\n") } printLastComments(&out, f.Comments, last) } // Now format the entire thing. result, err := format.Source(out.Bytes()) if err != nil { log.Fatalf("formatting failed: %v", err) } return result, nil } // sourceRange returns the [beg, end) interval of source code // belonging to decl (incl. associated comments). func sourceRange(decl ast.Decl) (beg, end token.Pos) { beg = decl.Pos() end = decl.End() var doc, com *ast.CommentGroup switch d := decl.(type) { case *ast.GenDecl: doc = d.Doc if len(d.Specs) > 0 { switch spec := d.Specs[len(d.Specs)-1].(type) { case *ast.ValueSpec: com = spec.Comment case *ast.TypeSpec: com = spec.Comment } } case *ast.FuncDecl: doc = d.Doc } if doc != nil { beg = doc.Pos() } if com != nil && com.End() > end { end = com.End() } return beg, end } func printComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos, end token.Pos) { for _, cg := range comments { if pos <= cg.Pos() && cg.Pos() < end { for _, c := range cg.List { fmt.Fprintln(out, c.Text) } fmt.Fprintln(out) } } } const infinity = 1 << 30 func printLastComments(out *bytes.Buffer, comments []*ast.CommentGroup, pos token.Pos) { printComments(out, comments, pos, infinity) } func printSameLineComment(out *bytes.Buffer, comments []*ast.CommentGroup, fset *token.FileSet, pos token.Pos) token.Pos { tf := fset.File(pos) for _, cg := range comments { if pos <= cg.Pos() && tf.Line(cg.Pos()) == tf.Line(pos) { for _, c := range cg.List { fmt.Fprintln(out, c.Text) } return cg.End() } } 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 { f(s) return nil } func (f flagFunc) String() string { return "" } golang-golang-x-tools-0.25.0+ds/cmd/bundle/main_test.go000066400000000000000000000035071476103172300226560ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "os" "os/exec" "runtime" "testing" "golang.org/x/tools/go/packages/packagestest" ) func TestBundle(t *testing.T) { packagestest.TestAll(t, testBundle) } func testBundle(t *testing.T, x packagestest.Exporter) { load := func(name string) string { data, err := os.ReadFile(name) if err != nil { t.Fatal(err) } return string(data) } e := packagestest.Export(t, x, []packagestest.Module{ { Name: "initial", Files: map[string]interface{}{ "a.go": load("testdata/src/initial/a.go"), "b.go": load("testdata/src/initial/b.go"), "c.go": load("testdata/src/initial/c.go"), }, }, { Name: "domain.name/importdecl", Files: map[string]interface{}{ "p.go": load("testdata/src/domain.name/importdecl/p.go"), }, }, }) defer e.Cleanup() testingOnlyPackagesConfig = e.Config os.Args = os.Args[:1] // avoid e.g. -test=short in the output out, err := bundle("initial", "github.com/dest", "dest", "prefix", "tag") if err != nil { t.Fatal(err) } if got, want := string(out), load("testdata/out.golden"); got != want { t.Errorf("-- got --\n%s\n-- want --\n%s\n-- diff --", got, want) if err := os.WriteFile("testdata/out.got", out, 0644); err != nil { t.Fatal(err) } t.Log(diff("testdata/out.golden", "testdata/out.got")) } } func diff(a, b string) string { var cmd *exec.Cmd switch runtime.GOOS { case "plan9": cmd = exec.Command("/bin/diff", "-c", a, b) default: cmd = exec.Command("/usr/bin/diff", "-u", a, b) } var out bytes.Buffer cmd.Stdout = &out cmd.Stderr = &out cmd.Run() // nonzero exit is expected if out.Len() == 0 { return "(failed to compute diff)" } return out.String() } golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/000077500000000000000000000000001476103172300221505ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/out.golden000066400000000000000000000015031476103172300241500ustar00rootroot00000000000000//go:build tag // Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT. // $ bundle // The package doc comment // package dest import ( "fmt" . "fmt" _ "fmt" renamedfmt "fmt" renamedfmt2 "fmt" "domain.name/importdecl" ) // init functions are not renamed func init() { prefixfoo() } // Type S. type prefixS struct { prefixt u int } /* multi-line comment */ // non-associated comment /* non-associated comment2 */ // Function bar. func prefixbar(s *prefixS) { fmt.Println(s.prefixt, s.u) // comment inside function } // file-end comment type prefixt int // type1 // const1 const prefixc = 1 // const2 func prefixfoo() { fmt.Println(importdecl.F()) } // zinit const ( prefixz1 = iota // z1 prefixz2 // z2 ) // zend func prefixbaz() { renamedfmt.Println() renamedfmt2.Println() Println() } golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/000077500000000000000000000000001476103172300227375ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/000077500000000000000000000000001476103172300251255ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/importdecl/000077500000000000000000000000001476103172300272675ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/domain.name/importdecl/p.go000066400000000000000000000000561476103172300300560ustar00rootroot00000000000000package importdecl func F() int { return 1 } golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/000077500000000000000000000000001476103172300243705ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/a.go000066400000000000000000000005451476103172300251430ustar00rootroot00000000000000package initial import "fmt" // this comment should not be visible // init functions are not renamed func init() { foo() } // Type S. type S struct { t u int } /* multi-line comment */ // non-associated comment /* non-associated comment2 */ // Function bar. func bar(s *S) { fmt.Println(s.t, s.u) // comment inside function } // file-end comment golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/b.go000066400000000000000000000003721476103172300251420ustar00rootroot00000000000000// The package doc comment package initial import ( "fmt" "domain.name/importdecl" ) type t int // type1 // const1 const c = 1 // const2 func foo() { fmt.Println(importdecl.F()) } // zinit const ( z1 = iota // z1 z2 // z2 ) // zend golang-golang-x-tools-0.25.0+ds/cmd/bundle/testdata/src/initial/c.go000066400000000000000000000002501476103172300251360ustar00rootroot00000000000000package initial import _ "fmt" import renamedfmt "fmt" import renamedfmt2 "fmt" import . "fmt" func baz() { renamedfmt.Println() renamedfmt2.Println() Println() } golang-golang-x-tools-0.25.0+ds/cmd/callgraph/000077500000000000000000000000001476103172300210235ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/callgraph/main.go000066400000000000000000000223741476103172300223060ustar00rootroot00000000000000// Copyright 2014 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. // callgraph: a tool for reporting the call graph of a Go program. // See Usage for details, or run with -help. package main // import "golang.org/x/tools/cmd/callgraph" // TODO(adonovan): // // Features: // - restrict graph to a single package // - output // - functions reachable from root (use digraph tool?) // - unreachable functions (use digraph tool?) // - dynamic (runtime) types // - indexed output (numbered nodes) // - JSON output // - additional template fields: // callee file/line/col import ( "bytes" "flag" "fmt" "go/build" "go/token" "io" "os" "runtime" "text/template" "golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/callgraph" "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/ssa" "golang.org/x/tools/go/ssa/ssautil" ) // flags var ( algoFlag = flag.String("algo", "rta", `Call graph construction algorithm (static, cha, rta, vta)`) testFlag = flag.Bool("test", false, "Loads test code (*_test.go) for imported packages") formatFlag = flag.String("format", "{{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}}", "A template expression specifying how to format an edge") ) func init() { flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) } const Usage = `callgraph: display the call graph of a Go program. Usage: callgraph [-algo=static|cha|rta|vta] [-test] [-format=...] package... Flags: -algo Specifies the call-graph construction algorithm, one of: static static calls only (unsound) cha Class Hierarchy Analysis rta Rapid Type Analysis vta Variable Type Analysis The algorithms are ordered by increasing precision in their treatment of dynamic calls (and thus also computational cost). RTA requires a whole program (main or test), and include only functions reachable from main. -test Include the package's tests in the analysis. -format Specifies the format in which each call graph edge is displayed. One of: digraph output suitable for input to golang.org/x/tools/cmd/digraph. graphviz output in AT&T GraphViz (.dot) format. All other values are interpreted using text/template syntax. The default value is: {{.Caller}}\t--{{.Dynamic}}-{{.Line}}:{{.Column}}-->\t{{.Callee}} The structure passed to the template is (effectively): type Edge struct { Caller *ssa.Function // calling function Callee *ssa.Function // called function // Call site: Filename string // containing file Offset int // offset within file of '(' Line int // line number Column int // column number of call Dynamic string // "static" or "dynamic" Description string // e.g. "static method call" } Caller and Callee are *ssa.Function values, which print as "(*sync/atomic.Mutex).Lock", but other attributes may be derived from them. For example: - {{.Caller.Pkg.Pkg.Path}} yields the import path of the enclosing package; and - {{(.Caller.Prog.Fset.Position .Caller.Pos).Filename}} yields the name of the file that declares the caller. - The 'posn' template function returns the token.Position of an ssa.Function, so the previous example can be reduced to {{(posn .Caller).Filename}}. Consult the documentation for go/token, text/template, and golang.org/x/tools/go/ssa for more detail. Examples: Show the call graph of the trivial web server application: callgraph -format digraph $GOROOT/src/net/http/triv.go Same, but show only the packages of each function: callgraph -format '{{.Caller.Pkg.Pkg.Path}} -> {{.Callee.Pkg.Pkg.Path}}' \ $GOROOT/src/net/http/triv.go | sort | uniq Show functions that make dynamic calls into the 'fmt' test package, using the Rapid Type Analysis algorithm: callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=rta fmt | sed -ne 's/-dynamic-/--/p' | sed -ne 's/-->.*fmt_test.*$//p' | sort | uniq Show all functions directly called by the callgraph tool's main function: callgraph -format=digraph golang.org/x/tools/cmd/callgraph | digraph succs golang.org/x/tools/cmd/callgraph.main ` func init() { // If $GOMAXPROCS isn't set, use the full capacity of the machine. // For small machines, use at least 4 threads. if os.Getenv("GOMAXPROCS") == "" { n := runtime.NumCPU() if n < 4 { n = 4 } runtime.GOMAXPROCS(n) } } func main() { flag.Parse() if err := doCallgraph("", "", *algoFlag, *formatFlag, *testFlag, flag.Args()); err != nil { fmt.Fprintf(os.Stderr, "callgraph: %s\n", err) os.Exit(1) } } var stdout io.Writer = os.Stdout func doCallgraph(dir, gopath, algo, format string, tests bool, args []string) error { if len(args) == 0 { fmt.Fprint(os.Stderr, Usage) return nil } cfg := &packages.Config{ Mode: packages.LoadAllSyntax, Tests: tests, Dir: dir, } if gopath != "" { cfg.Env = append(os.Environ(), "GOPATH="+gopath) // to enable testing } initial, err := packages.Load(cfg, args...) if err != nil { return err } if packages.PrintErrors(initial) > 0 { return fmt.Errorf("packages contain errors") } // Create and build SSA-form program representation. mode := ssa.InstantiateGenerics // instantiate generics by default for soundness prog, pkgs := ssautil.AllPackages(initial, mode) prog.Build() // -- call graph construction ------------------------------------------ var cg *callgraph.Graph switch algo { case "static": cg = static.CallGraph(prog) case "cha": cg = cha.CallGraph(prog) case "pta": return fmt.Errorf("pointer analysis is no longer supported (see Go issue #59676)") case "rta": mains, err := mainPackages(pkgs) if err != nil { return err } var roots []*ssa.Function for _, main := range mains { roots = append(roots, main.Func("init"), main.Func("main")) } rtares := rta.Analyze(roots, true) cg = rtares.CallGraph // NB: RTA gives us Reachable and RuntimeTypes too. case "vta": cg = vta.CallGraph(ssautil.AllFunctions(prog), nil) default: return fmt.Errorf("unknown algorithm: %s", algo) } cg.DeleteSyntheticNodes() // -- output------------------------------------------------------------ var before, after string // Pre-canned formats. switch format { case "digraph": format = `{{printf "%q %q" .Caller .Callee}}` case "graphviz": before = "digraph callgraph {\n" after = "}\n" format = ` {{printf "%q" .Caller}} -> {{printf "%q" .Callee}}` } funcMap := template.FuncMap{ "posn": func(f *ssa.Function) token.Position { return f.Prog.Fset.Position(f.Pos()) }, } tmpl, err := template.New("-format").Funcs(funcMap).Parse(format) if err != nil { return fmt.Errorf("invalid -format template: %v", err) } // Allocate these once, outside the traversal. var buf bytes.Buffer data := Edge{fset: prog.Fset} fmt.Fprint(stdout, before) if err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error { data.position.Offset = -1 data.edge = edge data.Caller = edge.Caller.Func data.Callee = edge.Callee.Func buf.Reset() if err := tmpl.Execute(&buf, &data); err != nil { return err } stdout.Write(buf.Bytes()) if len := buf.Len(); len == 0 || buf.Bytes()[len-1] != '\n' { fmt.Fprintln(stdout) } return nil }); err != nil { return err } fmt.Fprint(stdout, after) return nil } // mainPackages returns the main packages to analyze. // Each resulting package is named "main" and has a main function. func mainPackages(pkgs []*ssa.Package) ([]*ssa.Package, error) { var mains []*ssa.Package for _, p := range pkgs { if p != nil && p.Pkg.Name() == "main" && p.Func("main") != nil { mains = append(mains, p) } } if len(mains) == 0 { return nil, fmt.Errorf("no main packages") } return mains, nil } type Edge struct { Caller *ssa.Function Callee *ssa.Function edge *callgraph.Edge fset *token.FileSet position token.Position // initialized lazily } func (e *Edge) pos() *token.Position { if e.position.Offset == -1 { e.position = e.fset.Position(e.edge.Pos()) // called lazily } return &e.position } func (e *Edge) Filename() string { return e.pos().Filename } func (e *Edge) Column() int { return e.pos().Column } func (e *Edge) Line() int { return e.pos().Line } func (e *Edge) Offset() int { return e.pos().Offset } func (e *Edge) Dynamic() string { if e.edge.Site != nil && e.edge.Site.Common().StaticCallee() == nil { return "dynamic" } return "static" } func (e *Edge) Description() string { return e.edge.Description() } golang-golang-x-tools-0.25.0+ds/cmd/callgraph/main_test.go000066400000000000000000000045351476103172300233440ustar00rootroot00000000000000// Copyright 2014 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 && go1.11 // +build !android,go1.11 package main import ( "bytes" "fmt" "log" "os" "path/filepath" "strings" "testing" "golang.org/x/tools/internal/testenv" ) func init() { // This test currently requires GOPATH mode. // Explicitly disabling module mode should suffix, but // we'll also turn off GOPROXY just for good measure. if err := os.Setenv("GO111MODULE", "off"); err != nil { log.Fatal(err) } if err := os.Setenv("GOPROXY", "off"); err != nil { log.Fatal(err) } } func TestCallgraph(t *testing.T) { testenv.NeedsTool(t, "go") gopath, err := filepath.Abs("testdata") if err != nil { t.Fatal(err) } for _, test := range []struct { algo string tests bool want []string }{ {"rta", false, []string{ // rta imprecisely shows cross product of {main,main2} x {C,D} `pkg.main --> (pkg.C).f`, `pkg.main --> (pkg.D).f`, `pkg.main --> pkg.main2`, `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", }}, // tests: both the package's main and the test's main are called. // The callgraph includes all the guts of the "testing" package. {"rta", true, []string{ `pkg.test.main --> testing.MainStart`, `testing.runExample --> pkg.Example`, `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`, }}, } { const format = "{{.Caller}} --> {{.Callee}}" stdout = new(bytes.Buffer) if err := doCallgraph("testdata/src", gopath, test.algo, format, test.tests, []string{"pkg"}); err != nil { t.Error(err) continue } edges := make(map[string]bool) 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 !ok { t.Log("got:\n", stdout) } } } golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/000077500000000000000000000000001476103172300226345ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/000077500000000000000000000000001476103172300234235ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/000077500000000000000000000000001476103172300242045ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/pkg.go000066400000000000000000000003351476103172300253150ustar00rootroot00000000000000package main type I interface { f() } type C int func (C) f() {} type D int func (D) f() {} func main() { var i I = C(0) i.f() // dynamic call main2() } func main2() { var i I = D(0) i.f() // dynamic call } golang-golang-x-tools-0.25.0+ds/cmd/callgraph/testdata/src/pkg/pkg_test.go000066400000000000000000000002771476103172300263610ustar00rootroot00000000000000package main // An Example function must have an "Output:" comment for the go build // system to generate a call to it from the test main package. func Example() { C(0).f() // Output: } golang-golang-x-tools-0.25.0+ds/cmd/compilebench/000077500000000000000000000000001476103172300215165ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/compilebench/main.go000066400000000000000000000467561476103172300230130ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Compilebench benchmarks the speed of the Go compiler. // // Usage: // // compilebench [options] // // It times the compilation of various packages and prints results in // the format used by package testing (and expected by golang.org/x/perf/cmd/benchstat). // // The options are: // // -alloc // Report allocations. // // -compile exe // Use exe as the path to the cmd/compile binary. // // -compileflags 'list' // Pass the space-separated list of flags to the compilation. // // -link exe // Use exe as the path to the cmd/link binary. // // -linkflags 'list' // Pass the space-separated list of flags to the linker. // // -count n // Run each benchmark n times (default 1). // // -cpuprofile file // Write a CPU profile of the compiler to file. // // -go path // Path to "go" command (default "go"). // // -memprofile file // Write a memory profile of the compiler to file. // // -memprofilerate rate // Set runtime.MemProfileRate during compilation. // // -obj // Report object file statistics. // // -pkg pkg // Benchmark compiling a single package. // // -run regexp // Only run benchmarks with names matching regexp. // // -short // Skip long-running benchmarks. // // Although -cpuprofile and -memprofile are intended to write a // combined profile for all the executed benchmarks to file, // 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”). // 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 // // Assuming the base version of the compiler has been saved with // “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 ( "bytes" "encoding/json" "flag" "fmt" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" "strconv" "strings" "time" ) var ( goroot string compiler string assembler string linker string runRE *regexp.Regexp is6g bool needCompilingRuntimeFlag bool ) var ( flagGoCmd = flag.String("go", "go", "path to \"go\" command") 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") flagRun = flag.String("run", "", "run benchmarks matching `regexp`") flagCount = flag.Int("count", 1, "run benchmarks `n` times") flagCpuprofile = flag.String("cpuprofile", "", "write CPU profile to `file`") flagMemprofile = flag.String("memprofile", "", "write memory profile to `file`") 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 { name string r runner } type runner interface { long() bool run(name string, count int) error } var tests = []test{ {"BenchmarkTemplate", compile{"html/template"}}, {"BenchmarkUnicode", compile{"unicode"}}, {"BenchmarkGoTypes", compile{"go/types"}}, {"BenchmarkCompiler", compile{"cmd/compile/internal/gc"}}, {"BenchmarkSSA", compile{"cmd/compile/internal/ssa"}}, {"BenchmarkFlate", compile{"compress/flate"}}, {"BenchmarkGoParser", compile{"go/parser"}}, {"BenchmarkReflect", compile{"reflect"}}, {"BenchmarkTar", compile{"archive/tar"}}, {"BenchmarkXML", compile{"encoding/xml"}}, {"BenchmarkLinkCompiler", link{"cmd/compile", ""}}, {"BenchmarkExternalLinkCompiler", link{"cmd/compile", "-linkmode=external"}}, {"BenchmarkLinkWithoutDebugCompiler", link{"cmd/compile", "-w"}}, {"BenchmarkStdCmd", goBuild{[]string{"std", "cmd"}}}, {"BenchmarkHelloSize", size{"$GOROOT/test/helloworld.go", false}}, {"BenchmarkCmdGoSize", size{"cmd/go", true}}, } func usage() { fmt.Fprintf(os.Stderr, "usage: compilebench [options]\n") fmt.Fprintf(os.Stderr, "options:\n") flag.PrintDefaults() os.Exit(2) } func main() { log.SetFlags(0) log.SetPrefix("compilebench: ") flag.Usage = usage flag.Parse() if flag.NArg() != 0 { usage() } s, err := exec.Command(*flagGoCmd, "env", "GOROOT").CombinedOutput() if err != nil { log.Fatalf("%s env GOROOT: %v", *flagGoCmd, err) } goroot = strings.TrimSpace(string(s)) os.Setenv("GOROOT", goroot) // for any subcommands compiler = *flagCompiler if compiler == "" { var foundTool string foundTool, compiler = toolPath("compile", "6g") if foundTool == "6g" { is6g = true } } assembler = *flagAssembler if assembler == "" { _, assembler = toolPath("asm") } if err := checkCompilingRuntimeFlag(assembler); err != nil { log.Fatalf("checkCompilingRuntimeFlag: %v", err) } linker = *flagLinker if linker == "" && !is6g { // TODO: Support 6l _, linker = toolPath("link") } if is6g { *flagMemprofilerate = -1 *flagAlloc = false *flagCpuprofile = "" *flagMemprofile = "" } if *flagRun != "" { r, err := regexp.Compile(*flagRun) if err != nil { log.Fatalf("invalid -run argument: %v", err) } runRE = r } if *flagPackage != "" { tests = []test{ {"BenchmarkPkg", compile{*flagPackage}}, {"BenchmarkPkgLink", link{*flagPackage, ""}}, } runRE = nil } for i := 0; i < *flagCount; i++ { for _, tt := range tests { if tt.r.long() && *flagShort { continue } if runRE == nil || runRE.MatchString(tt.name) { if err := tt.r.run(tt.name, i); err != nil { log.Printf("%s: %v", tt.name, err) } } } } } func toolPath(names ...string) (found, path string) { var out1 []byte var err1 error for i, name := range names { out, err := exec.Command(*flagGoCmd, "tool", "-n", name).CombinedOutput() if err == nil { return name, strings.TrimSpace(string(out)) } if i == 0 { out1, err1 = out, err } } log.Fatalf("go tool -n %s: %v\n%s", names[0], err1, out1) return "", "" } type Pkg struct { ImportPath string Dir string GoFiles []string SFiles []string } func goList(dir string) (*Pkg, error) { var pkg Pkg out, err := exec.Command(*flagGoCmd, "list", "-json", dir).Output() if err != nil { return nil, fmt.Errorf("go list -json %s: %v", dir, err) } if err := json.Unmarshal(out, &pkg); err != nil { return nil, fmt.Errorf("go list -json %s: unmarshal: %v", dir, err) } return &pkg, nil } func runCmd(name string, cmd *exec.Cmd) error { start := time.Now() out, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("%v\n%s", err, out) } fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds()) return nil } type goBuild struct{ pkgs []string } func (goBuild) long() bool { return true } func (r goBuild) run(name string, count int) error { args := []string{"build", "-a"} if *flagCompilerFlags != "" { args = append(args, "-gcflags", *flagCompilerFlags) } args = append(args, r.pkgs...) cmd := exec.Command(*flagGoCmd, args...) cmd.Dir = filepath.Join(goroot, "src") return runCmd(name, cmd) } type size struct { // path is either a path to a file ("$GOROOT/test/helloworld.go") or a package path ("cmd/go"). path string isLong bool } func (r size) long() bool { return r.isLong } func (r size) run(name string, count int) error { if strings.HasPrefix(r.path, "$GOROOT/") { r.path = goroot + "/" + r.path[len("$GOROOT/"):] } cmd := exec.Command(*flagGoCmd, "build", "-o", "_compilebenchout_", r.path) cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return err } defer os.Remove("_compilebenchout_") info, err := os.Stat("_compilebenchout_") if err != nil { return err } out, err := exec.Command("size", "_compilebenchout_").CombinedOutput() if err != nil { return fmt.Errorf("size: %v\n%s", err, out) } lines := strings.Split(string(out), "\n") if len(lines) < 2 { return fmt.Errorf("not enough output from size: %s", out) } f := strings.Fields(lines[1]) if strings.HasPrefix(lines[0], "__TEXT") && len(f) >= 2 { // OS X fmt.Printf("%s 1 %s text-bytes %s data-bytes %v exe-bytes\n", name, f[0], f[1], info.Size()) } else if strings.Contains(lines[0], "bss") && len(f) >= 3 { fmt.Printf("%s 1 %s text-bytes %s data-bytes %s bss-bytes %v exe-bytes\n", name, f[0], f[1], f[2], info.Size()) } return nil } 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 built. out, err := exec.Command(*flagGoCmd, "build", c.dir).CombinedOutput() if err != nil { return fmt.Errorf("go build %s: %v\n%s", c.dir, err, out) } // Find dir and source file list. pkg, err := goList(c.dir) if err != nil { return err } importcfg, err := genImportcfgFile(c.dir, "", false) // TODO: pass compiler flags? 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 } opath := pkg.Dir + "/_compilebench_.o" if *flagObj { // TODO(josharian): object files are big; just read enough to find what we seek. data, err := os.ReadFile(opath) if err != nil { log.Print(err) } // Find start of export data. i := bytes.Index(data, []byte("\n$$B\n")) + len("\n$$B\n") // Count bytes to end of export data. nexport := bytes.Index(data[i:], []byte("\n$$\n")) fmt.Printf(" %d object-bytes %d export-bytes", len(data), nexport) } fmt.Println() os.Remove(opath) return nil } type link struct{ dir, flags string } func (link) long() bool { return false } func (r link) run(name string, count int) error { if linker == "" { // No linker. Skip the test. return nil } // Build dependencies. ldflags := *flagLinkerFlags if r.flags != "" { if ldflags != "" { ldflags += " " } ldflags += r.flags } out, err := exec.Command(*flagGoCmd, "build", "-o", "/dev/null", "-ldflags="+ldflags, 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, "-ldflags="+ldflags, true) if err != nil { 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", "-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 cmd.Stderr = os.Stderr err = cmd.Run() if err != nil { return fmt.Errorf("compiling: %v", err) } defer os.Remove(pkg.Dir + "/_compilebench_.o") // Link the main package. 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") if err := runBuildCmd(name, count, pkg.Dir, linker, args); err != nil { return err } fmt.Println() defer os.Remove(pkg.Dir + "/_compilebench_.exe") return err } // runBuildCmd runs "tool args..." in dir, measures standard build // tool metrics, and prints a benchmark line. The caller may print // additional metrics and then must print a newline. // // This assumes tool accepts standard build tool flags like // -memprofilerate, -memprofile, and -cpuprofile. func runBuildCmd(name string, count int, dir, tool string, args []string) error { var preArgs []string if *flagMemprofilerate >= 0 { preArgs = append(preArgs, "-memprofilerate", fmt.Sprint(*flagMemprofilerate)) } if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" { if *flagAlloc || *flagMemprofile != "" { preArgs = append(preArgs, "-memprofile", "_compilebench_.memprof") } if *flagCpuprofile != "" { 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 cmd.Stderr = os.Stderr start := time.Now() err := cmd.Run() if err != nil { return err } end := time.Now() haveAllocs, haveRSS := false, false var allocs, allocbytes, rssbytes int64 if *flagAlloc || *flagMemprofile != "" { out, err := os.ReadFile(dir + "/_compilebench_.memprof") if err != nil { log.Print("cannot find memory profile after compilation") } for _, line := range strings.Split(string(out), "\n") { f := strings.Fields(line) if len(f) < 4 || f[0] != "#" || f[2] != "=" { continue } val, err := strconv.ParseInt(f[3], 0, 64) if err != nil { continue } haveAllocs = true switch f[1] { case "TotalAlloc": allocbytes = val case "Mallocs": allocs = val case "MaxRSS": haveRSS = true rssbytes = val } } if !haveAllocs { log.Println("missing stats in memprof (golang.org/issue/18641)") } if *flagMemprofile != "" { outpath := *flagMemprofile if *flagCount != 1 { outpath = fmt.Sprintf("%s_%d", outpath, count) } if err := os.WriteFile(outpath, out, 0666); err != nil { log.Print(err) } } os.Remove(dir + "/_compilebench_.memprof") } if *flagCpuprofile != "" { out, err := os.ReadFile(dir + "/_compilebench_.cpuprof") if err != nil { log.Print(err) } outpath := *flagCpuprofile if *flagCount != 1 { outpath = fmt.Sprintf("%s_%d", outpath, count) } if err := os.WriteFile(outpath, out, 0666); err != nil { log.Print(err) } os.Remove(dir + "/_compilebench_.cpuprof") } wallns := end.Sub(start).Nanoseconds() userns := cmd.ProcessState.UserTime().Nanoseconds() fmt.Printf("%s 1 %d ns/op %d user-ns/op", name, wallns, userns) if haveAllocs { fmt.Printf(" %d B/op %d allocs/op", allocbytes, allocs) } if haveRSS { fmt.Printf(" %d maxRSS/op", rssbytes) } return nil } func checkCompilingRuntimeFlag(assembler string) error { td, err := os.MkdirTemp("", "asmsrcd") if err != nil { return fmt.Errorf("MkdirTemp failed: %v", err) } defer os.RemoveAll(td) src := filepath.Join(td, "asm.s") obj := filepath.Join(td, "asm.o") const code = ` TEXT ·foo(SB),$0-0 RET ` if err := os.WriteFile(src, []byte(code), 0644); err != nil { return fmt.Errorf("writing %s failed: %v", src, err) } // Try compiling the assembly source file passing // -compiling-runtime; if it succeeds, then we'll need it // when doing assembly of the reflect package later on. // If it does not succeed, the assumption is that it's not // needed. args := []string{"-o", obj, "-p", "reflect", "-compiling-runtime", src} cmd := exec.Command(assembler, args...) cmd.Dir = td out, aerr := cmd.CombinedOutput() if aerr != nil { if strings.Contains(string(out), "flag provided but not defined: -compiling-runtime") { // flag not defined: assume we're using a recent assembler, so // don't use -compiling-runtime. return nil } // error is not flag-related; report it. return fmt.Errorf("problems invoking assembler with args %+v: error %v\n%s\n", args, aerr, out) } // asm invocation succeeded -- assume we need the flag. needCompilingRuntimeFlag = true return nil } // genSymAbisFile runs the assembler on the target package 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 convention 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" && needCompilingRuntimeFlag { 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, flags string, full bool) (string, error) { need := "{{.Imports}}" if full { // for linking, we need transitive dependencies need = "{{.Deps}}" } if flags == "" { flags = "--" // passing "" to go list, it will match to the current directory } // find imported/dependent packages cmd := exec.Command(*flagGoCmd, "list", "-f", need, flags, 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}}", flags) 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 } golang-golang-x-tools-0.25.0+ds/cmd/deadcode/000077500000000000000000000000001476103172300206165ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/deadcode/deadcode.go000066400000000000000000000430431476103172300227010ustar00rootroot00000000000000// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.20 package main import ( "bytes" _ "embed" "encoding/json" "flag" "fmt" "go/ast" "go/token" "go/types" "io" "log" "os" "path/filepath" "regexp" "runtime" "runtime/pprof" "sort" "strings" "text/template" "golang.org/x/telemetry" "golang.org/x/tools/go/callgraph" "golang.org/x/tools/go/callgraph/rta" "golang.org/x/tools/go/packages" "golang.org/x/tools/go/ssa" "golang.org/x/tools/go/ssa/ssautil" "golang.org/x/tools/internal/typesinternal" ) //go:embed doc.go var doc string // flags var ( testFlag = flag.Bool("test", false, "include implicit test packages and executables") tagsFlag = flag.String("tags", "", "comma-separated list of extra build tags (see: go help buildconstraint)") filterFlag = flag.String("filter", "", "report only packages matching this regular expression (default: module of first package)") generatedFlag = flag.Bool("generated", false, "include dead functions in generated Go files") whyLiveFlag = flag.String("whylive", "", "show a path from main to the named function") formatFlag = flag.String("f", "", "format output records using template") jsonFlag = flag.Bool("json", false, "output JSON records") cpuProfile = flag.String("cpuprofile", "", "write CPU profile to this file") memProfile = flag.String("memprofile", "", "write memory profile to this file") ) func usage() { // Extract the content of the /* ... */ comment in doc.go. _, after, _ := strings.Cut(doc, "/*\n") doc, _, _ := strings.Cut(after, "*/") io.WriteString(flag.CommandLine.Output(), doc+` Flags: `) flag.PrintDefaults() } func main() { telemetry.Start(telemetry.Config{ReportCrashes: true}) log.SetPrefix("deadcode: ") log.SetFlags(0) // no time prefix flag.Usage = usage flag.Parse() if len(flag.Args()) == 0 { usage() os.Exit(2) } if *cpuProfile != "" { f, err := os.Create(*cpuProfile) if err != nil { log.Fatal(err) } if err := pprof.StartCPUProfile(f); err != nil { log.Fatal(err) } // NB: profile won't be written in case of error. defer pprof.StopCPUProfile() } if *memProfile != "" { f, err := os.Create(*memProfile) if err != nil { log.Fatal(err) } // NB: profile won't be written in case of error. defer func() { runtime.GC() // get up-to-date statistics if err := pprof.WriteHeapProfile(f); err != nil { log.Fatalf("Writing memory profile: %v", err) } f.Close() }() } // Reject bad output options early. if *formatFlag != "" { if *jsonFlag { log.Fatalf("you cannot specify both -f=template and -json") } if _, err := template.New("deadcode").Parse(*formatFlag); err != nil { log.Fatalf("invalid -f: %v", err) } } // Load, parse, and type-check the complete program(s). cfg := &packages.Config{ BuildFlags: []string{"-tags=" + *tagsFlag}, Mode: packages.LoadAllSyntax | packages.NeedModule, Tests: *testFlag, } initial, err := packages.Load(cfg, flag.Args()...) if err != nil { log.Fatalf("Load: %v", err) } if len(initial) == 0 { log.Fatalf("no packages") } if packages.PrintErrors(initial) > 0 { log.Fatalf("packages contain errors") } // If -filter is unset, use first module (if available). if *filterFlag == "" { if mod := initial[0].Module; mod != nil && mod.Path != "" { *filterFlag = "^" + regexp.QuoteMeta(mod.Path) + "\\b" } else { *filterFlag = "" // match any } } filter, err := regexp.Compile(*filterFlag) if err != nil { log.Fatalf("-filter: %v", err) } // Create SSA-form program representation // and find main packages. prog, pkgs := ssautil.AllPackages(initial, ssa.InstantiateGenerics) prog.Build() mains := ssautil.MainPackages(pkgs) if len(mains) == 0 { log.Fatalf("no main packages") } var roots []*ssa.Function for _, main := range mains { roots = append(roots, main.Func("init"), main.Func("main")) } // Gather all source-level functions, // as the user interface is expressed in terms of them. // // We ignore synthetic wrappers, and nested functions. Literal // functions passed as arguments to other functions are of // course address-taken and there exists a dynamic call of // that signature, so when they are unreachable, it is // invariably because the parent is unreachable. var sourceFuncs []*ssa.Function generated := make(map[string]bool) packages.Visit(initial, nil, func(p *packages.Package) { for _, file := range p.Syntax { for _, decl := range file.Decls { if decl, ok := decl.(*ast.FuncDecl); ok { obj := p.TypesInfo.Defs[decl.Name].(*types.Func) fn := prog.FuncValue(obj) sourceFuncs = append(sourceFuncs, fn) } } if isGenerated(file) { generated[p.Fset.File(file.Pos()).Name()] = true } } }) // Compute the reachabilty from main. // (Build a call graph only for -whylive.) res := rta.Analyze(roots, *whyLiveFlag != "") // Subtle: the -test flag causes us to analyze test variants // such as "package p as compiled for p.test" or even "for q.test". // This leads to multiple distinct ssa.Function instances that // represent the same source declaration, and it is essentially // impossible to discover this from the SSA representation // (since it has lost the connection to go/packages.Package.ID). // // So, we de-duplicate such variants by position: // if any one of them is live, we consider all of them live. // (We use Position not Pos to avoid assuming that files common // to packages "p" and "p [p.test]" were parsed only once.) reachablePosn := make(map[token.Position]bool) for fn := range res.Reachable { if fn.Pos().IsValid() || fn.Name() == "init" { reachablePosn[prog.Fset.Position(fn.Pos())] = true } } // The -whylive=fn flag causes deadcode to explain why a function // is not dead, by showing a path to it from some root. if *whyLiveFlag != "" { targets := make(map[*ssa.Function]bool) for _, fn := range sourceFuncs { if prettyName(fn, true) == *whyLiveFlag { targets[fn] = true } } if len(targets) == 0 { // Function is not part of the program. // // TODO(adonovan): improve the UX here in case // of spelling or syntax mistakes. Some ideas: // - a cmd/callgraph command to enumerate // available functions. // - a deadcode -live flag to compute the complement. // - a syntax hint: example.com/pkg.Func or (example.com/pkg.Type).Method // - report the element of AllFunctions with the smallest // Levenshtein distance from *whyLiveFlag. // - permit -whylive=regexp. But beware of spurious // matches (e.g. fmt.Print matches fmt.Println) // and the annoyance of having to quote parens (*T).f. log.Fatalf("function %q not found in program", *whyLiveFlag) } // Opt: remove the unreachable ones. for fn := range targets { if !reachablePosn[prog.Fset.Position(fn.Pos())] { delete(targets, fn) } } if len(targets) == 0 { log.Fatalf("function %s is dead code", *whyLiveFlag) } res.CallGraph.DeleteSyntheticNodes() // inline synthetic wrappers (except inits) root, path := pathSearch(roots, res, targets) if root == nil { // RTA doesn't add callgraph edges for reflective calls. log.Fatalf("%s is reachable only through reflection", *whyLiveFlag) } if len(path) == 0 { // No edges => one of the targets is a root. // Rather than (confusingly) print nothing, make this an error. log.Fatalf("%s is a root", root.Func) } // Build a list of jsonEdge records // to print as -json or -f=template. var edges []any for _, edge := range path { edges = append(edges, jsonEdge{ Initial: cond(len(edges) == 0, prettyName(edge.Caller.Func, true), ""), Kind: cond(isStaticCall(edge), "static", "dynamic"), Position: toJSONPosition(prog.Fset.Position(edge.Pos())), Callee: prettyName(edge.Callee.Func, true), }) } format := `{{if .Initial}}{{printf "%19s%s\n" "" .Initial}}{{end}}{{printf "%8s@L%.4d --> %s" .Kind .Position.Line .Callee}}` if *formatFlag != "" { format = *formatFlag } printObjects(format, edges) return } // Group unreachable functions by package path. byPkgPath := make(map[string]map[*ssa.Function]bool) for _, fn := range sourceFuncs { posn := prog.Fset.Position(fn.Pos()) if !reachablePosn[posn] { reachablePosn[posn] = true // suppress dups with same pos pkgpath := fn.Pkg.Pkg.Path() m, ok := byPkgPath[pkgpath] if !ok { m = make(map[*ssa.Function]bool) byPkgPath[pkgpath] = m } m[fn] = true } } // Build array of jsonPackage objects. var packages []any pkgpaths := keys(byPkgPath) sort.Strings(pkgpaths) for _, pkgpath := range pkgpaths { if !filter.MatchString(pkgpath) { continue } m := byPkgPath[pkgpath] // Print functions that appear within the same file in // declaration order. This tends to keep related // methods such as (T).Marshal and (*T).Unmarshal // together better than sorting. fns := keys(m) sort.Slice(fns, func(i, j int) bool { xposn := prog.Fset.Position(fns[i].Pos()) yposn := prog.Fset.Position(fns[j].Pos()) if xposn.Filename != yposn.Filename { return xposn.Filename < yposn.Filename } return xposn.Line < yposn.Line }) var functions []jsonFunction for _, fn := range fns { posn := prog.Fset.Position(fn.Pos()) // Without -generated, skip functions declared in // generated Go files. // (Functions called by them may still be reported.) gen := generated[posn.Filename] if gen && !*generatedFlag { continue } functions = append(functions, jsonFunction{ Name: prettyName(fn, false), Position: toJSONPosition(posn), Generated: gen, }) } if len(functions) > 0 { packages = append(packages, jsonPackage{ Name: fns[0].Pkg.Pkg.Name(), Path: pkgpath, Funcs: functions, }) } } // Default line-oriented format: "a/b/c.go:1:2: unreachable func: T.f" format := `{{range .Funcs}}{{printf "%s: unreachable func: %s\n" .Position .Name}}{{end}}` if *formatFlag != "" { format = *formatFlag } printObjects(format, packages) } // prettyName is a fork of Function.String designed to reduce // go/ssa's fussy punctuation symbols, e.g. "(*pkg.T).F" -> "pkg.T.F". // // It only works for functions that remain after // callgraph.Graph.DeleteSyntheticNodes: source-level named functions // and methods, their anonymous functions, and synthetic package // initializers. func prettyName(fn *ssa.Function, qualified bool) string { var buf strings.Builder // optional package qualifier if qualified && fn.Pkg != nil { fmt.Fprintf(&buf, "%s.", fn.Pkg.Pkg.Path()) } var format func(*ssa.Function) format = func(fn *ssa.Function) { // anonymous? if fn.Parent() != nil { format(fn.Parent()) i := index(fn.Parent().AnonFuncs, fn) fmt.Fprintf(&buf, "$%d", i+1) return } // method receiver? if recv := fn.Signature.Recv(); recv != nil { _, named := typesinternal.ReceiverNamed(recv) buf.WriteString(named.Obj().Name()) buf.WriteByte('.') } // function/method name buf.WriteString(fn.Name()) } format(fn) return buf.String() } // printObjects formats an array of objects, either as JSON or using a // template, following the manner of 'go list (-json|-f=template)'. func printObjects(format string, objects []any) { if *jsonFlag { out, err := json.MarshalIndent(objects, "", "\t") if err != nil { log.Fatalf("internal error: %v", err) } os.Stdout.Write(out) return } // -f=template. Parse can't fail: we checked it earlier. tmpl := template.Must(template.New("deadcode").Parse(format)) for _, object := range objects { var buf bytes.Buffer if err := tmpl.Execute(&buf, object); err != nil { log.Fatal(err) } if n := buf.Len(); n == 0 || buf.Bytes()[n-1] != '\n' { buf.WriteByte('\n') } os.Stdout.Write(buf.Bytes()) } } // TODO(adonovan): use go1.21's ast.IsGenerated. // isGenerated reports whether the file was generated by a program, // not handwritten, by detecting the special comment described // at https://go.dev/s/generatedcode. // // The syntax tree must have been parsed with the ParseComments flag. // Example: // // f, err := parser.ParseFile(fset, filename, src, parser.ParseComments|parser.PackageClauseOnly) // if err != nil { ... } // gen := ast.IsGenerated(f) func isGenerated(file *ast.File) bool { _, ok := generator(file) return ok } func generator(file *ast.File) (string, bool) { for _, group := range file.Comments { for _, comment := range group.List { if comment.Pos() > file.Package { break // after package declaration } // opt: check Contains first to avoid unnecessary array allocation in Split. const prefix = "// Code generated " if strings.Contains(comment.Text, prefix) { for _, line := range strings.Split(comment.Text, "\n") { if rest, ok := strings.CutPrefix(line, prefix); ok { if gen, ok := strings.CutSuffix(rest, " DO NOT EDIT."); ok { return gen, true } } } } } } return "", false } // pathSearch returns the shortest path from one of the roots to one // of the targets (along with the root itself), or zero if no path was found. func pathSearch(roots []*ssa.Function, res *rta.Result, targets map[*ssa.Function]bool) (*callgraph.Node, []*callgraph.Edge) { // Search breadth-first (for shortest path) from the root. // // We don't use the virtual CallGraph.Root node as we wish to // choose the order in which we search entrypoints: // non-test packages before test packages, // main functions before init functions. // Sort roots into preferred order. importsTesting := func(fn *ssa.Function) bool { isTesting := func(p *types.Package) bool { return p.Path() == "testing" } return containsFunc(fn.Pkg.Pkg.Imports(), isTesting) } sort.Slice(roots, func(i, j int) bool { x, y := roots[i], roots[j] xtest := importsTesting(x) ytest := importsTesting(y) if xtest != ytest { return !xtest // non-tests before tests } xinit := x.Name() == "init" yinit := y.Name() == "init" if xinit != yinit { return !xinit // mains before inits } return false }) search := func(allowDynamic bool) (*callgraph.Node, []*callgraph.Edge) { // seen maps each encountered node to its predecessor on the // path to a root node, or to nil for root itself. seen := make(map[*callgraph.Node]*callgraph.Edge) bfs := func(root *callgraph.Node) []*callgraph.Edge { queue := []*callgraph.Node{root} seen[root] = nil for len(queue) > 0 { node := queue[0] queue = queue[1:] // found a path? if targets[node.Func] { path := []*callgraph.Edge{} // non-nil in case len(path)=0 for { edge := seen[node] if edge == nil { reverse(path) return path } path = append(path, edge) node = edge.Caller } } for _, edge := range node.Out { if allowDynamic || isStaticCall(edge) { if _, ok := seen[edge.Callee]; !ok { seen[edge.Callee] = edge queue = append(queue, edge.Callee) } } } } return nil } for _, rootFn := range roots { root := res.CallGraph.Nodes[rootFn] if root == nil { // Missing call graph node for root. // TODO(adonovan): seems like a bug in rta. continue } if path := bfs(root); path != nil { return root, path } } return nil, nil } for _, allowDynamic := range []bool{false, true} { if root, path := search(allowDynamic); path != nil { return root, path } } return nil, nil } // -- utilities -- func isStaticCall(edge *callgraph.Edge) bool { return edge.Site != nil && edge.Site.Common().StaticCallee() != nil } var cwd, _ = os.Getwd() func toJSONPosition(posn token.Position) jsonPosition { // Use cwd-relative filename if possible. filename := posn.Filename if rel, err := filepath.Rel(cwd, filename); err == nil && !strings.HasPrefix(rel, "..") { filename = rel } return jsonPosition{filename, posn.Line, posn.Column} } func cond[T any](cond bool, t, f T) T { if cond { return t } else { return f } } // -- output protocol (for JSON or text/template) -- // Keep in sync with doc comment! type jsonFunction struct { Name string // name (sans package qualifier) Position jsonPosition // file/line/column of declaration Generated bool // function is declared in a generated .go file } func (f jsonFunction) String() string { return f.Name } type jsonPackage struct { Name string // declared name Path string // full import path Funcs []jsonFunction // non-empty list of package's dead functions } func (p jsonPackage) String() string { return p.Path } // The Initial and Callee names are package-qualified. type jsonEdge struct { Initial string `json:",omitempty"` // initial entrypoint (main or init); first edge only Kind string // = static | dynamic Position jsonPosition Callee string } type jsonPosition struct { File string Line, Col int } func (p jsonPosition) String() string { return fmt.Sprintf("%s:%d:%d", p.File, p.Line, p.Col) } // -- from the future -- // TODO(adonovan): use go1.22's slices and maps packages. func containsFunc[S ~[]E, E any](s S, f func(E) bool) bool { return indexFunc(s, f) >= 0 } func indexFunc[S ~[]E, E any](s S, f func(E) bool) int { for i := range s { if f(s[i]) { return i } } return -1 } func index[S ~[]E, E comparable](s S, v E) int { for i := range s { if v == s[i] { return i } } return -1 } func reverse[S ~[]E, E any](s S) { for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { s[i], s[j] = s[j], s[i] } } func keys[M ~map[K]V, K comparable, V any](m M) []K { r := make([]K, 0, len(m)) for k := range m { r = append(r, k) } return r } golang-golang-x-tools-0.25.0+ds/cmd/deadcode/deadcode_test.go000066400000000000000000000107271476103172300237430ustar00rootroot00000000000000// Copyright 2023 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.20 package main_test import ( "bytes" "fmt" "os" "os/exec" "path/filepath" "runtime" "strconv" "strings" "testing" "golang.org/x/tools/internal/testenv" "golang.org/x/tools/txtar" ) // Test runs the deadcode command on each scenario // described by a testdata/*.txtar file. func Test(t *testing.T) { testenv.NeedsTool(t, "go") if runtime.GOOS == "android" { t.Skipf("the dependencies are not available on android") } exe := buildDeadcode(t) matches, err := filepath.Glob("testdata/*.txtar") if err != nil { t.Fatal(err) } for _, filename := range matches { filename := filename t.Run(filename, func(t *testing.T) { t.Parallel() ar, err := txtar.ParseFile(filename) if err != nil { t.Fatal(err) } // Write the archive files to the temp directory. tmpdir := t.TempDir() for _, f := range ar.Files { filename := filepath.Join(tmpdir, f.Name) if err := os.MkdirAll(filepath.Dir(filename), 0777); err != nil { t.Fatal(err) } if err := os.WriteFile(filename, f.Data, 0666); err != nil { t.Fatal(err) } } // Parse archive comment as directives of these forms: // // [!]deadcode args... command-line arguments // [!]want arg expected/unwanted string in output (or stderr) // // Args may be Go-quoted strings. type testcase struct { linenum int args []string wantErr bool want map[string]bool // string -> sense } var cases []*testcase var current *testcase for i, line := range strings.Split(string(ar.Comment), "\n") { line = strings.TrimSpace(line) if line == "" || line[0] == '#' { continue // skip blanks and comments } words, err := words(line) if err != nil { t.Fatalf("cannot break line into words: %v (%s)", err, line) } switch kind := words[0]; kind { case "deadcode", "!deadcode": current = &testcase{ linenum: i + 1, want: make(map[string]bool), args: words[1:], wantErr: kind[0] == '!', } cases = append(cases, current) case "want", "!want": if current == nil { t.Fatalf("'want' directive must be after 'deadcode'") } if len(words) != 2 { t.Fatalf("'want' directive needs argument <<%s>>", line) } current.want[words[1]] = kind[0] != '!' default: t.Fatalf("%s: invalid directive %q", filename, kind) } } for _, tc := range cases { t.Run(fmt.Sprintf("L%d", tc.linenum), func(t *testing.T) { // Run the command. cmd := exec.Command(exe, tc.args...) cmd.Stdout = new(bytes.Buffer) cmd.Stderr = new(bytes.Buffer) cmd.Dir = tmpdir cmd.Env = append(os.Environ(), "GOPROXY=", "GO111MODULE=on") var got string if err := cmd.Run(); err != nil { if !tc.wantErr { t.Fatalf("deadcode failed: %v (stderr=%s)", err, cmd.Stderr) } got = fmt.Sprint(cmd.Stderr) } else { if tc.wantErr { t.Fatalf("deadcode succeeded unexpectedly (stdout=%s)", cmd.Stdout) } got = fmt.Sprint(cmd.Stdout) } // Check each want directive. for str, sense := range tc.want { ok := true if strings.Contains(got, str) != sense { if sense { t.Errorf("missing %q", str) } else { t.Errorf("unwanted %q", str) } ok = false } if !ok { t.Errorf("got: <<%s>>", got) } } }) } }) } } // buildDeadcode builds the deadcode executable. // It returns its path, and a cleanup function. func buildDeadcode(t *testing.T) string { bin := filepath.Join(t.TempDir(), "deadcode") if runtime.GOOS == "windows" { bin += ".exe" } cmd := exec.Command("go", "build", "-o", bin) if out, err := cmd.CombinedOutput(); err != nil { t.Fatalf("Building deadcode: %v\n%s", err, out) } return bin } // words breaks a string into words, respecting // Go string quotations around words with spaces. func words(s string) ([]string, error) { var words []string for s != "" { s = strings.TrimSpace(s) var word string if s[0] == '"' || s[0] == '`' { prefix, err := strconv.QuotedPrefix(s) if err != nil { return nil, err } s = s[len(prefix):] word, _ = strconv.Unquote(prefix) } else { prefix, rest, _ := strings.Cut(s, " ") s = rest word = prefix } words = append(words, word) } return words, nil } golang-golang-x-tools-0.25.0+ds/cmd/deadcode/doc.go000066400000000000000000000132761476103172300217230ustar00rootroot00000000000000// Copyright 2023 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. /* The deadcode command reports unreachable functions in Go programs. Usage: deadcode [flags] package... The deadcode command loads a Go program from source then uses Rapid Type Analysis (RTA) to build a call graph of all the functions reachable from the program's main function. Any functions that are not reachable are reported as dead code, grouped by package. Packages are expressed in the notation of 'go list' (or other underlying build system if you are using an alternative golang.org/x/go/packages driver). Only executable (main) packages are considered starting points for the analysis. The -test flag causes it to analyze test executables too. Tests sometimes make use of functions that would otherwise appear to be dead code, and public API functions reported as dead with -test indicate possible gaps in your test coverage. Bear in mind that an Example test function without an "Output:" comment is merely documentation: it is dead code, and does not contribute coverage. The -filter flag restricts results to packages that match the provided regular expression; its default value is the module name of the first package. Use -filter= to display all results. Example: show all dead code within the gopls module: $ deadcode -test golang.org/x/tools/gopls/... The analysis can soundly analyze dynamic calls though func values, interface methods, and reflection. However, it does not currently understand the aliasing created by //go:linkname directives, so it will fail to recognize that calls to a linkname-annotated function with no body in fact dispatch to the function named in the annotation. This may result in the latter function being spuriously reported as dead. By default, the tool does not report dead functions in generated files, as determined by the special comment described in https://go.dev/s/generatedcode. Use the -generated flag to include them. In any case, just because a function is reported as dead does not mean it is unconditionally safe to delete it. For example, a dead function may be referenced by another dead function, and a dead method may be required to satisfy an interface that is never called. Some judgement is required. The analysis is valid only for a single GOOS/GOARCH/-tags configuration, so a function reported as dead may be live in a different configuration. Consider running the tool once for each configuration of interest. Consider using a line-oriented output format (see below) to make it easier to compute the intersection of results across all runs. # Output The command supports three output formats. With no flags, the command prints the name and location of each dead function in the form of a typical compiler diagnostic, for example: $ deadcode -f='{{range .Funcs}}{{println .Position}}{{end}}' -test ./gopls/... gopls/internal/protocol/command.go:1206:6: unreachable func: openClientEditor gopls/internal/template/parse.go:414:18: unreachable func: Parsed.WriteNode gopls/internal/template/parse.go:419:18: unreachable func: wrNode.writeNode With the -json flag, the command prints an array of Package objects, as defined by the JSON schema (see below). With the -f=template flag, the command executes the specified template on each Package record. So, this template shows dead functions grouped by package: $ deadcode -f='{{println .Path}}{{range .Funcs}}{{printf "\t%s\n" .Name}}{{end}}{{println}}' -test ./gopls/... golang.org/x/tools/gopls/internal/lsp openClientEditor golang.org/x/tools/gopls/internal/template Parsed.WriteNode wrNode.writeNode # Why is a function not dead? The -whylive=function flag explain why the named function is not dead by showing an arbitrary shortest path to it from one of the main functions. (To enumerate the functions in a program, or for more sophisticated call graph queries, use golang.org/x/tools/cmd/callgraph.) Fully static call paths are preferred over paths involving dynamic edges, even if longer. Paths starting from a non-test package are preferred over those from tests. Paths from main functions are preferred over paths from init functions. The result is a list of Edge objects (see JSON schema below). Again, the -json and -f=template flags may be used to control the formatting of the list of Edge objects. The default format shows, for each edge in the path, whether the call is static or dynamic, and its source line number. For example: $ deadcode -whylive=bytes.Buffer.String -test ./cmd/deadcode/... golang.org/x/tools/cmd/deadcode.main static@L0117 --> golang.org/x/tools/go/packages.Load static@L0262 --> golang.org/x/tools/go/packages.defaultDriver static@L0305 --> golang.org/x/tools/go/packages.goListDriver static@L0153 --> golang.org/x/tools/go/packages.goListDriver$1 static@L0154 --> golang.org/x/tools/go/internal/packagesdriver.GetSizesForArgsGolist static@L0044 --> bytes.Buffer.String # JSON schema type Package struct { Name string // declared name Path string // full import path Funcs []Function // list of dead functions within it } type Function struct { Name string // name (sans package qualifier) Position Position // file/line/column of function declaration Generated bool // function is declared in a generated .go file } type Edge struct { Initial string // initial entrypoint (main or init); first edge only Kind string // = static | dynamic Position Position // file/line/column of call site Callee string // target of the call } type Position struct { File string // name of file Line, Col int // line and byte index, both 1-based } */ package main golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/000077500000000000000000000000001476103172300224275ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/basic.txtar000066400000000000000000000010551476103172300245750ustar00rootroot00000000000000# Test of basic functionality. deadcode -filter= example.com want "T.Goodbye" want "T.Goodbye2" want "T.Goodbye3" !want "T.Hello" want "unreferenced" want "Scanf" want "Printf" !want "Println" -- go.mod -- module example.com go 1.18 -- main.go -- package main import "fmt" type T int func main() { var x T x.Hello() } func (T) Hello() { fmt.Println("hello") } func (T) Goodbye() { fmt.Println("goodbye") } func (*T) Goodbye2() { fmt.Println("goodbye2") } func (*A) Goodbye3() { fmt.Println("goodbye3") } type A = T func unreferenced() {}golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/filterflag.txtar000066400000000000000000000007451476103172300256400ustar00rootroot00000000000000# Test of -filter flag. deadcode -filter=other.net example.com want `other.net` want `Dead` !want `Live` !want `example.com` !want `unreferenced` -- go.work -- use example.com use other.net -- example.com/go.mod -- module example.com go 1.18 -- example.com/main.go -- package main import "other.net" func main() { other.Live() } func unreferenced() {} -- other.net/go.mod -- module other.net go 1.18 -- other.net/other.go -- package other func Live() {} func Dead() {} golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/generated.txtar000066400000000000000000000007461476103172300254600ustar00rootroot00000000000000# Test of -generated flag output. deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" example.com !want "main.main" want "main.Dead1" !want "main.Dead2" deadcode "-f={{range .Funcs}}{{$.Name}}.{{.Name}}{{end}}" -generated example.com !want "main.main" want "main.Dead1" want "main.Dead2" -- go.mod -- module example.com go 1.18 -- main.go -- package main func main() {} func Dead1() {} -- gen.go -- // Code generated by hand. DO NOT EDIT. package main func Dead2() {}golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/issue65915.txtar000066400000000000000000000015171476103172300252610ustar00rootroot00000000000000# Regression test for issue 65915: the enumeration of source-level # functions used the flawed ssautil.AllFunctions, causing it to # miss some unexported ones. deadcode -filter= example.com want "unreachable func: example.UnUsed" want "unreachable func: example.unUsed" want "unreachable func: PublicExample.UnUsed" want "unreachable func: PublicExample.unUsed" -- go.mod -- module example.com go 1.18 -- main.go -- package main type example struct{} func (e example) UnUsed() {} func (e example) Used() {} func (e example) unUsed() {} func (e example) used() {} type PublicExample struct{} func (p PublicExample) UnUsed() {} func (p PublicExample) Used() {} func (p PublicExample) unUsed() {} func (p PublicExample) used() {} func main() { example{}.Used() example{}.used() PublicExample{}.Used() PublicExample{}.used() } golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/issue67915.txt000066400000000000000000000013371476103172300247400ustar00rootroot00000000000000# Test of -whylive with reflective call # (regression test for golang/go#67915). # The live function is reached via reflection: deadcode example.com want "unreachable func: dead" !want "unreachable func: live" # Reflective calls have Edge.Site=nil, which formerly led to a crash # when -whylive would compute its position. Now it has NoPos. deadcode -whylive=example.com.live example.com want " example.com.main" want " static@L0006 --> reflect.Value.Call" want "dynamic@L0000 --> example.com.live" -- go.mod -- module example.com go 1.18 -- main.go -- package main import "reflect" func main() { reflect.ValueOf(live).Call(nil) } func live() { println("hello") } func dead() { println("goodbye") } golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/jsonflag.txtar000066400000000000000000000005131476103172300253150ustar00rootroot00000000000000# Very minimal test of -json flag. deadcode -json example.com/p want `"Path": "example.com/p",` want `"Name": "DeadFunc",` want `"Generated": false` want `"Line": 5,` want `"Col": 6` -- go.mod -- module example.com go 1.18 -- p/p.go -- package main func main() {} func DeadFunc() {} type T int func (*T) DeadMethod() {}golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/lineflag.txtar000066400000000000000000000010631476103172300252740ustar00rootroot00000000000000# Test of line-oriented output. deadcode `-f={{range .Funcs}}{{printf "%s: %s.%s\n" .Position $.Path .Name}}{{end}}` -filter= example.com want "main.go:13:10: example.com.T.Goodbye" !want "example.com.T.Hello" want "main.go:15:6: example.com.unreferenced" want "fmt.Scanf" want "fmt.Printf" !want "fmt.Println" -- go.mod -- module example.com go 1.18 -- main.go -- package main import "fmt" type T int func main() { var x T x.Hello() } func (T) Hello() { fmt.Println("hello") } func (T) Goodbye() { fmt.Println("goodbye") } func unreferenced() {}golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/testflag.txtar000066400000000000000000000010331476103172300253210ustar00rootroot00000000000000# Test of -test flag. deadcode -test -filter=example.com example.com/p want "Dead" !want "Live1" !want "Live2" want "ExampleDead" !want "ExampleLive" -- go.mod -- module example.com go 1.18 -- p/p.go -- package p func Live1() {} func Live2() {} func Dead() {} -- p/p_test.go -- package p_test import "example.com/p" import "testing" func Test(t *testing.T) { p.Live1() } func ExampleLive() { p.Live2() // Output: } // A test Example function without an "Output:" comment is never executed. func ExampleDead() { p.Dead() }golang-golang-x-tools-0.25.0+ds/cmd/deadcode/testdata/whylive.txtar000066400000000000000000000052431476103172300252060ustar00rootroot00000000000000# Test of -whylive flag. # The -whylive argument must be live. !deadcode -whylive=example.com.d example.com want "function example.com.d is dead code" # A fully static path is preferred, even if longer. deadcode -whylive=example.com.c example.com want " example.com.main" want " static@L0004 --> example.com.a" want " static@L0009 --> example.com.b" want " static@L0012 --> example.com.c" # Dynamic edges are followed if necessary. # (Note that main is preferred over init.) deadcode -whylive=example.com.f example.com want " example.com.main" want "dynamic@L0006 --> example.com.e" want " static@L0017 --> example.com.f" # Degenerate case where target is itself a root. !deadcode -whylive=example.com.main example.com want "example.com.main is a root" # Test of path through (*T).m method wrapper. deadcode -whylive=example.com/p.live example.com/p want " example.com/p.main" want "static@L0006 --> example.com/p.E.Error" want "static@L0010 --> example.com/p.live" # Test of path through (I).m interface method wrapper (thunk). deadcode -whylive=example.com/q.live example.com/q want " example.com/q.main" want "static@L0006 --> example.com/q.E.Error" want "static@L0010 --> example.com/q.live" # Test of path through synthetic package initializer, # a declared package initializer, and its anonymous function. deadcode -whylive=example.com/q.live2 example.com/q want " example.com/q.init" want "static@L0000 --> example.com/q.init#1" want "static@L0016 --> example.com/q.init#1$1" want "static@L0015 --> example.com/q.live2" # Test of path through synthetic package initializer, # and a global var initializer. deadcode -whylive=example.com/r.live example.com/r want " example.com/r.init" want "static@L0007 --> example.com/r.init$1" want "static@L0006 --> example.com/r.live" -- go.mod -- module example.com go 1.18 -- main.go -- package main func main() { a() println(c, e) // c, e are address-taken (func ())(nil)() // potential dynamic call to c, e } func a() { b() } func b() { c() } func c() func d() func e() { f() } func f() func init() { (func ())(nil)() // potential dynamic call to c, e } -- p/p.go -- package main func main() { f := (*E).Error var e E f(&e) } type E int func (E) Error() string { return live() } func live() string -- q/q.go -- package main func main() { f := error.Error var e E f(e) } type E int func (E) Error() string { return live() } func live() string func init() { f := func() { live2() } f() } func live2() -- r/r.go -- package main func main() {} var x = func() int { return live() }() func live() int golang-golang-x-tools-0.25.0+ds/cmd/digraph/000077500000000000000000000000001476103172300205045ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/digraph/digraph.go000066400000000000000000000316551476103172300224630ustar00rootroot00000000000000// Copyright 2019 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 "golang.org/x/tools/cmd/digraph" // TODO(adonovan): // - support input files other than stdin // - support alternative formats (AT&T GraphViz, CSV, etc), // a comment syntax, etc. // - allow queries to nest, like Blaze query language. import ( "bufio" "bytes" _ "embed" "errors" "flag" "fmt" "io" "os" "sort" "strconv" "strings" "unicode" "unicode/utf8" ) func usage() { // Extract the content of the /* ... */ comment in doc.go. _, after, _ := strings.Cut(doc, "/*") doc, _, _ := strings.Cut(after, "*/") io.WriteString(flag.CommandLine.Output(), doc) flag.PrintDefaults() os.Exit(2) } //go:embed doc.go var doc string func main() { flag.Usage = usage flag.Parse() args := flag.Args() if len(args) == 0 { usage() } if err := digraph(args[0], args[1:]); err != nil { fmt.Fprintf(os.Stderr, "digraph: %s\n", err) os.Exit(1) } } type nodelist []string func (l nodelist) println(sep string) { for i, node := range l { if i > 0 { fmt.Fprint(stdout, sep) } fmt.Fprint(stdout, node) } fmt.Fprintln(stdout) } type nodeset map[string]bool func (s nodeset) sort() nodelist { nodes := make(nodelist, len(s)) var i int for node := range s { nodes[i] = node i++ } sort.Strings(nodes) return nodes } func (s nodeset) addAll(x nodeset) { for node := range x { s[node] = true } } // A graph maps nodes to the non-nil set of their immediate successors. type graph map[string]nodeset func (g graph) addNode(node string) nodeset { edges := g[node] if edges == nil { edges = make(nodeset) g[node] = edges } return edges } func (g graph) addEdges(from string, to ...string) { edges := g.addNode(from) for _, to := range to { g.addNode(to) edges[to] = true } } func (g graph) nodelist() nodelist { nodes := make(nodeset) for node := range g { nodes[node] = true } return nodes.sort() } func (g graph) reachableFrom(roots nodeset) nodeset { seen := make(nodeset) var visit func(node string) visit = func(node string) { if !seen[node] { seen[node] = true for e := range g[node] { visit(e) } } } for root := range roots { visit(root) } return seen } func (g graph) transpose() graph { rev := make(graph) for node, edges := range g { rev.addNode(node) for succ := range edges { rev.addEdges(succ, node) } } return rev } func (g graph) sccs() []nodeset { // Kosaraju's algorithm---Tarjan is overkill here. // Forward pass. S := make(nodelist, 0, len(g)) // postorder stack seen := make(nodeset) var visit func(node string) visit = func(node string) { if !seen[node] { seen[node] = true for e := range g[node] { visit(e) } S = append(S, node) } } for node := range g { visit(node) } // Reverse pass. rev := g.transpose() var scc nodeset seen = make(nodeset) var rvisit func(node string) rvisit = func(node string) { if !seen[node] { seen[node] = true scc[node] = true for e := range rev[node] { rvisit(e) } } } var sccs []nodeset for len(S) > 0 { top := S[len(S)-1] S = S[:len(S)-1] // pop if !seen[top] { scc = make(nodeset) rvisit(top) if len(scc) == 1 && !g[top][top] { continue } sccs = append(sccs, scc) } } return sccs } func (g graph) allpaths(from, to string) error { // Mark all nodes to "to". seen := make(nodeset) // value of seen[x] indicates whether x is on some path to "to" var visit func(node string) bool visit = func(node string) bool { reachesTo, ok := seen[node] if !ok { reachesTo = node == to seen[node] = reachesTo for e := range g[node] { if visit(e) { reachesTo = true } } if reachesTo && node != to { seen[node] = true } } return reachesTo } visit(from) // For each marked node, collect its marked successors. var edges []string for n := range seen { for succ := range g[n] { if seen[succ] { edges = append(edges, n+" "+succ) } } } // Sort (so that this method is deterministic) and print edges. sort.Strings(edges) for _, e := range edges { fmt.Fprintln(stdout, e) } return nil } func (g graph) somepath(from, to string) error { // Search breadth-first so that we return a minimal path. // A path is a linked list whose head is a candidate "to" node // and whose tail is the path ending in the "from" node. type path struct { node string tail *path } seen := nodeset{from: true} var queue []*path queue = append(queue, &path{node: from, tail: nil}) for len(queue) > 0 { p := queue[0] queue = queue[1:] if p.node == to { // Found a path. Print, tail first. var print func(p *path) print = func(p *path) { if p.tail != nil { print(p.tail) fmt.Fprintln(stdout, p.tail.node+" "+p.node) } } print(p) return nil } for succ := range g[p.node] { if !seen[succ] { seen[succ] = true queue = append(queue, &path{node: succ, tail: p}) } } } return fmt.Errorf("no path from %q to %q", from, to) } func (g graph) toDot(w *bytes.Buffer) { fmt.Fprintln(w, "digraph {") for _, src := range g.nodelist() { for _, dst := range g[src].sort() { // Dot's quoting rules appear to align with Go's for escString, // which is the syntax of node IDs. Labels require significantly // more quoting, but that appears not to be necessary if the node ID // is implicitly used as the label. fmt.Fprintf(w, "\t%q -> %q;\n", src, dst) } } fmt.Fprintln(w, "}") } func parse(rd io.Reader) (graph, error) { g := make(graph) var linenum int // 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(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 eof { break } } return g, nil } // Overridable for redirection. var stdin io.Reader = os.Stdin var stdout io.Writer = os.Stdout func digraph(cmd string, args []string) error { // Parse the input graph. g, err := parse(stdin) if err != nil { return err } // Parse the command line. switch cmd { case "nodes": if len(args) != 0 { return fmt.Errorf("usage: digraph nodes") } g.nodelist().println("\n") case "degree": if len(args) != 0 { return fmt.Errorf("usage: digraph degree") } nodes := make(nodeset) for node := range g { nodes[node] = true } rev := g.transpose() for _, node := range nodes.sort() { fmt.Fprintf(stdout, "%d\t%d\t%s\n", len(rev[node]), len(g[node]), node) } case "transpose": if len(args) != 0 { return fmt.Errorf("usage: digraph transpose") } var revEdges []string for node, succs := range g.transpose() { for succ := range succs { revEdges = append(revEdges, fmt.Sprintf("%s %s", node, succ)) } } sort.Strings(revEdges) // make output deterministic for _, e := range revEdges { fmt.Fprintln(stdout, e) } case "succs", "preds": if len(args) == 0 { return fmt.Errorf("usage: digraph %s ... ", cmd) } g := g if cmd == "preds" { g = g.transpose() } result := make(nodeset) for _, root := range args { edges := g[root] if edges == nil { return fmt.Errorf("no such node %q", root) } result.addAll(edges) } result.sort().println("\n") case "forward", "reverse": if len(args) == 0 { return fmt.Errorf("usage: digraph %s ... ", cmd) } roots := make(nodeset) for _, root := range args { if g[root] == nil { return fmt.Errorf("no such node %q", root) } roots[root] = true } g := g if cmd == "reverse" { g = g.transpose() } g.reachableFrom(roots).sort().println("\n") case "somepath": if len(args) != 2 { return fmt.Errorf("usage: digraph somepath ") } from, to := args[0], args[1] if g[from] == nil { return fmt.Errorf("no such 'from' node %q", from) } if g[to] == nil { return fmt.Errorf("no such 'to' node %q", to) } if err := g.somepath(from, to); err != nil { return err } case "allpaths": if len(args) != 2 { return fmt.Errorf("usage: digraph allpaths ") } from, to := args[0], args[1] if g[from] == nil { return fmt.Errorf("no such 'from' node %q", from) } if g[to] == nil { return fmt.Errorf("no such 'to' node %q", to) } if err := g.allpaths(from, to); err != nil { return err } case "sccs": 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 { return fmt.Errorf("usage: digraph scc ") } node := args[0] if g[node] == nil { return fmt.Errorf("no such node %q", node) } for _, scc := range g.sccs() { if scc[node] { scc.sort().println("\n") break } } case "focus": if len(args) != 1 { return fmt.Errorf("usage: digraph focus ") } node := args[0] if g[node] == nil { return fmt.Errorf("no such node %q", node) } edges := make(map[string]struct{}) for from := range g.reachableFrom(nodeset{node: true}) { for to := range g[from] { edges[fmt.Sprintf("%s %s", from, to)] = struct{}{} } } gtrans := g.transpose() for from := range gtrans.reachableFrom(nodeset{node: true}) { for to := range gtrans[from] { edges[fmt.Sprintf("%s %s", to, from)] = struct{}{} } } edgesSorted := make([]string, 0, len(edges)) for e := range edges { edgesSorted = append(edgesSorted, e) } sort.Strings(edgesSorted) fmt.Fprintln(stdout, strings.Join(edgesSorted, "\n")) case "to": if len(args) != 1 || args[0] != "dot" { return fmt.Errorf("usage: digraph to dot") } var b bytes.Buffer g.toDot(&b) stdout.Write(b.Bytes()) default: return fmt.Errorf("no such command %q", cmd) } return nil } // -- Utilities -------------------------------------------------------- // split splits a line into words, which are generally separated by // 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"] func split(line string) ([]string, error) { var ( words []string inWord bool current bytes.Buffer ) for len(line) > 0 { r, size := utf8.DecodeRuneInString(line) if unicode.IsSpace(r) { if inWord { words = append(words, current.String()) current.Reset() inWord = false } } else if r == '"' { var ok bool size, ok = quotedLength(line) if !ok { return nil, errors.New("invalid quotation") } s, err := strconv.Unquote(line[:size]) if err != nil { return nil, err } current.WriteString(s) inWord = true } else { current.WriteRune(r) inWord = true } line = line[size:] } if inWord { words = append(words, current.String()) } return words, nil } // quotedLength returns the length in bytes of the prefix of input that // contain a possibly-valid double-quoted Go string literal. // // On success, n is at least two (""); input[:n] may be passed to // strconv.Unquote to interpret its value, and input[n:] contains the // rest of the input. // // On failure, quotedLength returns false, and the entire input can be // passed to strconv.Unquote if an informative error message is desired. // // quotedLength does not and need not detect all errors, such as // invalid hex or octal escape sequences, since it assumes // strconv.Unquote will be applied to the prefix. It guarantees only // that if there is a prefix of input containing a valid string literal, // 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 // next returns the rune at offset, or -1 on EOF. // offset advances to just after that rune. next := func() rune { if offset < len(input) { r, size := utf8.DecodeRuneInString(input[offset:]) offset += size return r } return -1 } if next() != '"' { return // error: not a quotation } for { r := next() if r == '\n' || r < 0 { return // error: string literal not terminated } if r == '"' { return offset, true // success } if r == '\\' { var skip int switch next() { case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': skip = 0 case '0', '1', '2', '3', '4', '5', '6', '7': skip = 2 case 'x': skip = 2 case 'u': skip = 4 case 'U': skip = 8 default: return // error: invalid escape } for i := 0; i < skip; i++ { next() } } } } golang-golang-x-tools-0.25.0+ds/cmd/digraph/digraph_test.go000066400000000000000000000215761476103172300235230ustar00rootroot00000000000000// Copyright 2019 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" "fmt" "io" "reflect" "sort" "strings" "testing" ) func TestDigraph(t *testing.T) { const g1 = ` socks shoes shorts pants pants belt shoes shirt tie sweater sweater jacket hat ` const g2 = ` a b c b d c d d c e e ` for _, test := range []struct { name string input string cmd string args []string want string }{ {"nodes", g1, "nodes", nil, "belt\nhat\njacket\npants\nshirt\nshoes\nshorts\nsocks\nsweater\ntie\n"}, {"reverse", g1, "reverse", []string{"jacket"}, "jacket\nshirt\nsweater\n"}, {"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, "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"}, } { t.Run(test.name, func(t *testing.T) { stdin = strings.NewReader(test.input) stdout = new(bytes.Buffer) if err := digraph(test.cmd, test.args); err != nil { t.Fatal(err) } got := stdout.(fmt.Stringer).String() if got != test.want { t.Errorf("digraph(%s, %s) = got %q, want %q", test.cmd, test.args, got, test.want) } }) } // TODO(adonovan): // - test errors } func TestAllpaths(t *testing.T) { for _, test := range []struct { name string in string to string // from is always "A" want string }{ { name: "Basic", in: "A B\nB C", to: "B", want: "A B\n", }, { name: "Long", in: "A B\nB C\n", to: "C", want: "A B\nB C\n", }, { name: "Cycle Basic", in: "A B\nB A", to: "B", want: "A B\nB A\n", }, { name: "Cycle Path Out", // A <-> B -> C -> D in: "A B\nB A\nB C\nC D", to: "C", want: "A B\nB A\nB C\n", }, { name: "Cycle Path Out Further Out", // A -> B <-> C -> D -> E in: "A B\nB C\nC D\nC B\nD E", to: "D", want: "A B\nB C\nC B\nC D\n", }, { name: "Two Paths Basic", // /-> C --\ // A -> B -- -> E -> F // \-> D --/ in: "A B\nB C\nC E\nB D\nD E\nE F", to: "E", want: "A B\nB C\nB D\nC E\nD E\n", }, { name: "Two Paths With One Immediately From Start", // /-> B -+ -> D // A -- | // \-> C <+ in: "A B\nA C\nB C\nB D", to: "C", want: "A B\nA C\nB C\n", }, { name: "Two Paths Further Up", // /-> B --\ // A -- -> D -> E -> F // \-> C --/ in: "A B\nA C\nB D\nC D\nD E\nE F", to: "E", want: "A B\nA C\nB D\nC D\nD E\n", }, { // We should include A - C - D even though it's further up the // second path than D (which would already be in the graph by // the time we get around to integrating the second path). name: "Two Splits", // /-> B --\ /-> E --\ // A -- -> D -- -> G -> H // \-> C --/ \-> F --/ in: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\nG H", to: "G", want: "A B\nA C\nB D\nC D\nD E\nD F\nE G\nF G\n", }, { // D - E should not be duplicated. name: "Two Paths - Two Splits With Gap", // /-> B --\ /-> F --\ // A -- -> D -> E -- -> H -> I // \-> C --/ \-> G --/ in: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\nH I", to: "H", want: "A B\nA C\nB D\nC D\nD E\nE F\nE G\nF H\nG H\n", }, } { t.Run(test.name, func(t *testing.T) { stdin = strings.NewReader(test.in) stdout = new(bytes.Buffer) if err := digraph("allpaths", []string{"A", test.to}); err != nil { t.Fatal(err) } got := stdout.(fmt.Stringer).String() if got != test.want { t.Errorf("digraph(allpaths, A, %s) = got %q, want %q", test.to, got, test.want) } }) } } func TestSomepath(t *testing.T) { for _, test := range []struct { name string in string to string // somepath is non-deterministic, so we have to provide all the // possible options. Each option is separated with |. wantAnyOf string }{ { name: "Basic", in: "A B\n", to: "B", wantAnyOf: "A B", }, { name: "Basic With Cycle", in: "A B\nB A", to: "B", wantAnyOf: "A B", }, { name: "Two Paths", // /-> B --\ // A -- -> D // \-> C --/ in: "A B\nA C\nB D\nC D", to: "D", wantAnyOf: "A B\nB D|A C\nC D", }, { name: "Printed path is minimal", // A -> B1->B2->B3 -> E // A -> C1->C2 -> E // A -> D -> E in: "A D C1 B1\nD E\nC1 C2\nC2 E\nB1 B2\nB2 B3\nB3 E", to: "E", wantAnyOf: "A D\nD E", }, } { t.Run(test.name, func(t *testing.T) { stdin = strings.NewReader(test.in) stdout = new(bytes.Buffer) if err := digraph("somepath", []string{"A", test.to}); err != nil { t.Fatal(err) } got := stdout.(fmt.Stringer).String() lines := strings.Split(got, "\n") sort.Strings(lines) got = strings.Join(lines[1:], "\n") var oneMatch bool for _, want := range strings.Split(test.wantAnyOf, "|") { if got == want { oneMatch = true } } if !oneMatch { t.Errorf("digraph(somepath, A, %s) = got %q, want any of\n%s", test.to, got, test.wantAnyOf) } }) } } func TestSplit(t *testing.T) { for _, test := range []struct { line string want []string }{ {`one "2a 2b" three`, []string{"one", "2a 2b", "three"}}, {`one tw"\n\x0a\u000a\012"o three`, []string{"one", "tw\n\n\n\no", "three"}}, } { got, err := split(test.line) if err != nil { t.Errorf("split(%s) failed: %v", test.line, err) } if !reflect.DeepEqual(got, test.want) { t.Errorf("split(%s) = %v, want %v", test.line, got, test.want) } } } func TestQuotedLength(t *testing.T) { for _, test := range []struct { input string want int }{ {`"abc"`, 5}, {`"abc"def`, 5}, {`"abc\"d"ef`, 8}, // "abc\"d" is consumed, ef is residue {`"\012\n\x0a\u000a\U0000000a"`, 28}, {"\"\xff\"", 3}, // bad UTF-8 is ok {`"\xff"`, 6}, // hex escape for bad UTF-8 is ok } { got, ok := quotedLength(test.input) if !ok { got = 0 } if got != test.want { t.Errorf("quotedLength(%s) = %d, want %d", test.input, got, test.want) } } // errors for _, input := range []string{ ``, // not a quotation `a`, // not a quotation `'a'`, // not a quotation `"a`, // not terminated `"\0"`, // short octal escape `"\x1"`, // short hex escape `"\u000"`, // short \u escape `"\U0000000"`, // short \U escape `"\k"`, // invalid escape "\"ab\nc\"", // newline } { if n, ok := quotedLength(input); ok { t.Errorf("quotedLength(%s) = %d, want !ok", input, n) } } } func TestFocus(t *testing.T) { for _, test := range []struct { name string in string focus string want string }{ { name: "Basic", in: "A B", focus: "B", want: "A B\n", }, { name: "Some Nodes Not Included", // C does not have a path involving B, and should not be included // in the output. in: "A B\nA C", focus: "B", want: "A B\n", }, { name: "Cycle In Path", // A <-> B -> C in: "A B\nB A\nB C", focus: "C", want: "A B\nB A\nB C\n", }, { name: "Cycle Out Of Path", // C <- A <->B in: "A B\nB A\nB C", focus: "C", want: "A B\nB A\nB C\n", }, { name: "Complex", // Paths in and out from focus. // /-> F // /-> B -> D -- // A -- \-> E // \-> C in: "A B\nA C\nB D\nD F\nD E", focus: "D", want: "A B\nB D\nD E\nD F\n", }, } { t.Run(test.name, func(t *testing.T) { stdin = strings.NewReader(test.in) stdout = new(bytes.Buffer) if err := digraph("focus", []string{test.focus}); err != nil { t.Fatal(err) } got := stdout.(fmt.Stringer).String() if got != test.want { t.Errorf("digraph(focus, %s) = got %q, want %q", test.focus, got, test.want) } }) } } func TestToDot(t *testing.T) { in := `a b c b "d\"\\d" c "d\"\\d"` want := `digraph { "a" -> "b"; "a" -> "c"; "b" -> "d\"\\d"; "c" -> "d\"\\d"; } ` defer func(in io.Reader, out io.Writer) { stdin, stdout = in, out }(stdin, stdout) stdin = strings.NewReader(in) stdout = new(bytes.Buffer) if err := digraph("to", []string{"dot"}); err != nil { t.Fatal(err) } got := stdout.(fmt.Stringer).String() if got != want { t.Errorf("digraph(to, dot) = got %q, want %q", got, want) } } golang-golang-x-tools-0.25.0+ds/cmd/digraph/doc.go000066400000000000000000000060241476103172300216020ustar00rootroot00000000000000// Copyright 2019 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. /* The digraph command performs queries over unlabelled directed graphs represented in text form. It is intended to integrate nicely with typical UNIX command pipelines. Usage: your-application | digraph [command] The supported commands are: nodes the set of all nodes degree the in-degree and out-degree of each node transpose the reverse of the input edges preds ... the set of immediate predecessors of the specified nodes succs ... the set of immediate successors of the specified nodes forward ... the set of nodes transitively reachable from the specified nodes reverse ... the set of nodes that transitively reach the specified nodes somepath the list of nodes on some arbitrary path from the first node to the second allpaths the set of nodes on all paths from the first node to the second sccs all strongly connected components (one per line) scc the set of nodes strongly connected to the specified one focus the subgraph containing all directed paths that pass through the specified node to dot print the graph in Graphviz dot format (other formats may be supported in the future) Input format: Each line contains zero or more words. Words are separated by unquoted whitespace; words may contain Go-style double-quoted portions, allowing spaces and other characters to be expressed. Each word declares a node, and if there are more than one, an edge from the first to each subsequent one. The graph is provided on the standard input. For instance, the following (acyclic) graph specifies a partial order among the subtasks of getting dressed: $ cat clothes.txt socks shoes "boxer shorts" pants pants belt shoes shirt tie sweater sweater jacket hat The line "shirt tie sweater" indicates the two edges shirt -> tie and shirt -> sweater, not shirt -> tie -> sweater. Example usage: Show which clothes (see above) must be donned before a jacket: $ digraph reverse jacket Many tools can be persuaded to produce output in digraph format, as in the following examples. Using an import graph produced by go list, show a path that indicates why the gopls application depends on the cmp package: $ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps golang.org/x/tools/gopls | digraph somepath golang.org/x/tools/gopls github.com/google/go-cmp/cmp Show which packages in x/tools depend, perhaps indirectly, on the callgraph package: $ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps golang.org/x/tools/... | digraph reverse golang.org/x/tools/go/callgraph Visualize the package dependency graph of the current package: $ go list -f '{{.ImportPath}} {{join .Imports " "}}' -deps | digraph to dot | dot -Tpng -o x.png Using a module graph produced by go mod, show all dependencies of the current module: $ go mod graph | digraph forward $(go list -m) */ package main golang-golang-x-tools-0.25.0+ds/cmd/eg/000077500000000000000000000000001476103172300174615ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/eg/eg.go000066400000000000000000000120731476103172300204060ustar00rootroot00000000000000// Copyright 2014 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. // The eg command performs example-based refactoring. // For documentation, run the command, or see Help in // golang.org/x/tools/refactor/eg. package main // import "golang.org/x/tools/cmd/eg" import ( "flag" "fmt" "go/ast" "go/format" "go/parser" "go/token" "go/types" "os" "os/exec" "path/filepath" "strings" "golang.org/x/tools/go/packages" "golang.org/x/tools/refactor/eg" ) var ( beforeeditFlag = flag.String("beforeedit", "", "A command to exec before each file is edited (e.g. chmod, checkout). Whitespace delimits argument words. The string '{}' is replaced by the file name.") helpFlag = flag.Bool("help", false, "show detailed help message") templateFlag = flag.String("t", "", "template.go file specifying the refactoring") transitiveFlag = flag.Bool("transitive", false, "apply refactoring to all dependencies too") writeFlag = flag.Bool("w", false, "rewrite input files in place (by default, the results are printed to standard output)") verboseFlag = flag.Bool("v", false, "show verbose matcher diagnostics") ) const usage = `eg: an example-based refactoring tool. Usage: eg -t template.go [-w] [-transitive] -help show detailed help message -t template.go specifies the template file (use -help to see explanation) -w causes files to be re-written in place. -transitive causes all dependencies to be refactored too. -v show verbose matcher diagnostics -beforeedit cmd a command to exec before each file is modified. "{}" represents the name of the file. ` func main() { if err := doMain(); err != nil { fmt.Fprintf(os.Stderr, "eg: %s\n", err) os.Exit(1) } } func doMain() error { flag.Parse() args := flag.Args() if *helpFlag { help := eg.Help // hide %s from vet fmt.Fprint(os.Stderr, help) os.Exit(2) } if len(args) == 0 { fmt.Fprint(os.Stderr, usage) os.Exit(1) } if *templateFlag == "" { return fmt.Errorf("no -t template.go file specified") } tAbs, err := filepath.Abs(*templateFlag) if err != nil { return err } template, err := os.ReadFile(tAbs) if err != nil { return err } cfg := &packages.Config{ Fset: token.NewFileSet(), Mode: packages.NeedTypesInfo | packages.NeedName | packages.NeedTypes | packages.NeedSyntax | packages.NeedImports | packages.NeedDeps | packages.NeedCompiledGoFiles, Tests: true, } pkgs, err := packages.Load(cfg, args...) if err != nil { return err } tFile, err := parser.ParseFile(cfg.Fset, tAbs, template, parser.ParseComments) if err != nil { return err } // Type-check the template. tInfo := types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), Implicits: make(map[ast.Node]types.Object), Selections: make(map[*ast.SelectorExpr]*types.Selection), Scopes: make(map[ast.Node]*types.Scope), } conf := types.Config{ Importer: pkgsImporter(pkgs), } tPkg, err := conf.Check("egtemplate", cfg.Fset, []*ast.File{tFile}, &tInfo) if err != nil { return err } // Analyze the template. xform, err := eg.NewTransformer(cfg.Fset, tPkg, tFile, &tInfo, *verboseFlag) if err != nil { return err } // Apply it to the input packages. var all []*packages.Package if *transitiveFlag { packages.Visit(pkgs, nil, func(p *packages.Package) { all = append(all, p) }) } else { all = pkgs } var hadErrors bool for _, pkg := range pkgs { for i, filename := range pkg.CompiledGoFiles { if filename == tAbs { // Don't rewrite the template file. continue } file := pkg.Syntax[i] n := xform.Transform(pkg.TypesInfo, pkg.Types, file) if n == 0 { continue } fmt.Fprintf(os.Stderr, "=== %s (%d matches)\n", filename, n) if *writeFlag { // Run the before-edit command (e.g. "chmod +w", "checkout") if any. if *beforeeditFlag != "" { args := strings.Fields(*beforeeditFlag) // Replace "{}" with the filename, like find(1). for i := range args { if i > 0 { args[i] = strings.Replace(args[i], "{}", filename, -1) } } cmd := exec.Command(args[0], args[1:]...) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { fmt.Fprintf(os.Stderr, "Warning: edit hook %q failed (%s)\n", args, err) } } if err := eg.WriteAST(cfg.Fset, filename, file); err != nil { fmt.Fprintf(os.Stderr, "eg: %s\n", err) hadErrors = true } } else { format.Node(os.Stdout, cfg.Fset, file) } } } if hadErrors { os.Exit(1) } return nil } type pkgsImporter []*packages.Package func (p pkgsImporter) Import(path string) (tpkg *types.Package, err error) { packages.Visit([]*packages.Package(p), func(pkg *packages.Package) bool { if pkg.PkgPath == path { tpkg = pkg.Types return false } return true }, nil) if tpkg != nil { return tpkg, nil } return nil, fmt.Errorf("package %q not found", path) } golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/000077500000000000000000000000001476103172300210065ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/main.go000066400000000000000000000065451476103172300222730ustar00rootroot00000000000000// Copyright 2021 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. // file2fuzz converts binary files, such as those used by go-fuzz, to the Go // fuzzing corpus format. // // Usage: // // file2fuzz [-o output] [input...] // // The default behavior is to read input from stdin and write the converted // output to stdout. If any position arguments are provided stdin is ignored // and the arguments are assumed to be input files to convert. // // The -o flag provides an path to write output files to. If only one positional // 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 ( "crypto/sha256" "errors" "flag" "fmt" "io" "log" "os" "path/filepath" ) // encVersion1 is version 1 Go fuzzer corpus encoding. var encVersion1 = "go test fuzz v1" func encodeByteSlice(b []byte) []byte { return []byte(fmt.Sprintf("%s\n[]byte(%q)", encVersion1, b)) } func usage() { fmt.Fprintf(os.Stderr, "usage: file2fuzz [-o output] [input...]\nconverts files to Go fuzzer corpus format\n") fmt.Fprintf(os.Stderr, "\tinput: files to convert\n") fmt.Fprintf(os.Stderr, "\t-o: where to write converted file(s)\n") os.Exit(2) } func dirWriter(dir string) func([]byte) error { return func(b []byte) error { sum := fmt.Sprintf("%x", sha256.Sum256(b)) name := filepath.Join(dir, sum) if err := os.MkdirAll(dir, 0777); err != nil { return err } if err := os.WriteFile(name, b, 0666); err != nil { os.Remove(name) return err } return nil } } func convert(inputArgs []string, outputArg string) error { var input []io.Reader if args := inputArgs; len(args) == 0 { input = []io.Reader{os.Stdin} } else { for _, a := range args { f, err := os.Open(a) if err != nil { return fmt.Errorf("unable to open %q: %s", a, err) } defer f.Close() if fi, err := f.Stat(); err != nil { return fmt.Errorf("unable to open %q: %s", a, err) } else if fi.IsDir() { return fmt.Errorf("%q is a directory, not a file", a) } input = append(input, f) } } var output func([]byte) error if outputArg == "" { if len(inputArgs) > 1 { return errors.New("-o required with multiple input files") } output = func(b []byte) error { _, err := os.Stdout.Write(b) return err } } else { if len(inputArgs) > 1 { output = dirWriter(outputArg) } else { if fi, err := os.Stat(outputArg); err != nil && !os.IsNotExist(err) { return fmt.Errorf("unable to open %q for writing: %s", outputArg, err) } else if err == nil && fi.IsDir() { output = dirWriter(outputArg) } else { output = func(b []byte) error { return os.WriteFile(outputArg, b, 0666) } } } } for _, f := range input { b, err := io.ReadAll(f) if err != nil { return fmt.Errorf("unable to read input: %s", err) } if err := output(encodeByteSlice(b)); err != nil { return fmt.Errorf("unable to write output: %s", err) } } return nil } func main() { log.SetFlags(0) log.SetPrefix("file2fuzz: ") output := flag.String("o", "", "where to write converted file(s)") flag.Usage = usage flag.Parse() if err := convert(flag.Args(), *output); err != nil { log.Fatal(err) } } golang-golang-x-tools-0.25.0+ds/cmd/file2fuzz/main_test.go000066400000000000000000000113571476103172300233270ustar00rootroot00000000000000// Copyright 2021 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 ( "os" "os/exec" "path/filepath" "strings" "sync" "testing" "golang.org/x/tools/internal/testenv" ) func TestMain(m *testing.M) { if os.Getenv("GO_FILE2FUZZ_TEST_IS_FILE2FUZZ") != "" { main() os.Exit(0) } os.Exit(m.Run()) } var f2f struct { once sync.Once path string err error } func file2fuzz(t *testing.T, dir string, args []string, stdin string) (string, bool) { testenv.NeedsExec(t) f2f.once.Do(func() { f2f.path, f2f.err = os.Executable() }) if f2f.err != nil { t.Fatal(f2f.err) } cmd := exec.Command(f2f.path, args...) cmd.Dir = dir cmd.Env = append(os.Environ(), "PWD="+dir, "GO_FILE2FUZZ_TEST_IS_FILE2FUZZ=1") if stdin != "" { cmd.Stdin = strings.NewReader(stdin) } out, err := cmd.CombinedOutput() if err != nil { return string(out), true } return string(out), false } func TestFile2Fuzz(t *testing.T) { type file struct { name string dir bool content string } tests := []struct { name string args []string stdin string inputFiles []file expectedStdout string expectedFiles []file expectedError string }{ { name: "stdin, stdout", stdin: "hello", expectedStdout: "go test fuzz v1\n[]byte(\"hello\")", }, { name: "stdin, output file", stdin: "hello", args: []string{"-o", "output"}, expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}}, }, { name: "stdin, output directory", stdin: "hello", args: []string{"-o", "output"}, inputFiles: []file{{name: "output", dir: true}}, expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}}, }, { name: "input file, output file", args: []string{"-o", "output", "input"}, inputFiles: []file{{name: "input", content: "hello"}}, expectedFiles: []file{{name: "output", content: "go test fuzz v1\n[]byte(\"hello\")"}}, }, { name: "input file, output directory", args: []string{"-o", "output", "input"}, inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}}, expectedFiles: []file{{name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}}, }, { name: "input files, output directory", args: []string{"-o", "output", "input", "input-2"}, inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}}, expectedFiles: []file{ {name: "output/ffc7b87a0377262d4f77926bd235551d78e6037bbe970d81ec39ac1d95542f7b", content: "go test fuzz v1\n[]byte(\"hello\")"}, {name: "output/28059db30ce420ff65b2c29b749804c69c601aeca21b3cbf0644244ff080d7a5", content: "go test fuzz v1\n[]byte(\"hello :)\")"}, }, }, { name: "input files, no output", args: []string{"input", "input-2"}, inputFiles: []file{{name: "output", dir: true}, {name: "input", content: "hello"}, {name: "input-2", content: "hello :)"}}, expectedError: "file2fuzz: -o required with multiple input files\n", }, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { tmp, err := os.MkdirTemp(os.TempDir(), "file2fuzz") if err != nil { t.Fatalf("os.MkdirTemp failed: %s", err) } defer os.RemoveAll(tmp) for _, f := range tc.inputFiles { if f.dir { if err := os.Mkdir(filepath.Join(tmp, f.name), 0777); err != nil { t.Fatalf("failed to create test directory: %s", err) } } else { if err := os.WriteFile(filepath.Join(tmp, f.name), []byte(f.content), 0666); err != nil { t.Fatalf("failed to create test input file: %s", err) } } } out, failed := file2fuzz(t, tmp, tc.args, tc.stdin) if failed && tc.expectedError == "" { t.Fatalf("file2fuzz failed unexpectedly: %s", out) } else if failed && out != tc.expectedError { t.Fatalf("file2fuzz returned unexpected error: got %q, want %q", out, tc.expectedError) } if !failed && out != tc.expectedStdout { t.Fatalf("file2fuzz unexpected stdout: got %q, want %q", out, tc.expectedStdout) } for _, f := range tc.expectedFiles { c, err := os.ReadFile(filepath.Join(tmp, f.name)) if err != nil { t.Fatalf("failed to read expected output file %q: %s", f.name, err) } if string(c) != f.content { t.Fatalf("expected output file %q contains unexpected content: got %s, want %s", f.name, string(c), f.content) } } }) } } golang-golang-x-tools-0.25.0+ds/cmd/fiximports/000077500000000000000000000000001476103172300212725ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/main.go000066400000000000000000000355061476103172300225560ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The fiximports command fixes import declarations to use the canonical // import path for packages that have an "import comment" as defined by // https://golang.org/s/go14customimport. // // # 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 // get" it, independent of the underlying version control system (such // as Git) or server (such as github.com) that hosts it. Requests for // the custom name are redirected to the underlying name. This allows // packages to be migrated from one underlying server or system to // another without breaking existing clients. // // Because this redirect mechanism creates aliases for existing // packages, it's possible for a single program to import the same // package by its canonical name and by an alias. The resulting // executable will contain two copies of the package, which is wasteful // at best and incorrect at worst. // // To avoid this, "go build" reports an error if it encounters a special // comment like the one below, and if the import path in the comment // 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" // // 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 // // When a package that you depend on introduces a custom import comment, // and your workspace imports it by the non-canonical name, your build // will stop working as soon as you update your copy of that package // using "go get -u". // // The purpose of the fiximports tool is to fix up all imports of the // non-canonical path within a Go workspace, replacing them with imports // of the canonical path. Following a run of fiximports, the workspace // will no longer depend on the non-canonical copy of the package, so it // should be safe to delete. It may be necessary to run "go get -u" // again to ensure that the package is locally installed under its // canonical path, if it was not already. // // The fiximports tool operates locally; it does not make HTTP requests // and does not discover new custom import comments. It only operates // on non-canonical packages present in your workspace. // // The -baddomains flag is a list of domain names that should always be // considered non-canonical. You can use this if you wish to make sure // that you no longer have any dependencies on packages from that // domain, even those that do not yet provide a canonical import path // comment. For example, the default value of -baddomains includes the // moribund code hosting site code.google.com, so fiximports will report // an error for each import of a package from this domain remaining // after canonicalization. // // To see the changes fiximports would make without applying them, use // the -n flag. package main import ( "bytes" "encoding/json" "flag" "fmt" "go/ast" "go/format" "go/parser" "go/token" "io" "log" "os" "os/exec" "path" "path/filepath" "sort" "strconv" "strings" ) // flags var ( dryrun = flag.Bool("n", false, "dry run: show changes, but don't apply them") badDomains = flag.String("baddomains", "code.google.com", "a comma-separated list of domains from which packages should not be imported") replaceFlag = flag.String("replace", "", "a comma-separated list of noncanonical=canonical pairs of package paths. If both items in a pair end with '...', they are treated as path prefixes.") ) // seams for testing var ( stderr io.Writer = os.Stderr writeFile = os.WriteFile ) const usage = `fiximports: rewrite import paths to use canonical package names. Usage: fiximports [-n] package... The package... arguments specify a list of packages in the style of the go tool; see "go help packages". Hint: use "all" or "..." to match the entire workspace. For details, see https://pkg.go.dev/golang.org/x/tools/cmd/fiximports Flags: -n: dry run: show changes, but don't apply them -baddomains a comma-separated list of domains from which packages should not be imported ` func main() { flag.Parse() if len(flag.Args()) == 0 { fmt.Fprint(stderr, usage) os.Exit(1) } if !fiximports(flag.Args()...) { os.Exit(1) } } type canonicalName struct{ path, name string } // fiximports fixes imports in the specified packages. // 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[*listPackage]bool) // addEdge adds an edge to the import graph. addEdge := func(from *listPackage, to string) { if to == "C" || to == "unsafe" { return // fake } pkgs := importedBy[to] if pkgs == nil { pkgs = make(map[*listPackage]bool) importedBy[to] = pkgs } pkgs[from] = true } // List metadata for all packages in the workspace. pkgs, err := list("...") if err != nil { fmt.Fprintf(stderr, "importfix: %v\n", err) return false } // packageName maps each package's path to its name. packageName := make(map[string]string) for _, p := range pkgs { packageName[p.ImportPath] = p.Name } // canonical maps each non-canonical package path to // its canonical path and name. // A present nil value indicates that the canonical package // is unknown: hosted on a bad domain with no redirect. canonical := make(map[string]canonicalName) domains := strings.Split(*badDomains, ",") type replaceItem struct { old, new string matchPrefix bool } var replace []replaceItem for _, pair := range strings.Split(*replaceFlag, ",") { if pair == "" { continue } words := strings.Split(pair, "=") if len(words) != 2 { fmt.Fprintf(stderr, "importfix: -replace: %q is not of the form \"canonical=noncanonical\".\n", pair) return false } replace = append(replace, replaceItem{ old: strings.TrimSuffix(words[0], "..."), new: strings.TrimSuffix(words[1], "..."), matchPrefix: strings.HasSuffix(words[0], "...") && strings.HasSuffix(words[1], "..."), }) } // Find non-canonical packages and populate importedBy graph. for _, p := range pkgs { if p.Error != nil { msg := p.Error.Err if strings.Contains(msg, "code in directory") && strings.Contains(msg, "expects import") { // don't show the very errors we're trying to fix } else { fmt.Fprintln(stderr, p.Error) } } for _, imp := range p.Imports { addEdge(p, imp) } for _, imp := range p.TestImports { addEdge(p, imp) } for _, imp := range p.XTestImports { addEdge(p, imp) } // Does package have an explicit import comment? if p.ImportComment != "" { if p.ImportComment != p.ImportPath { canonical[p.ImportPath] = canonicalName{ path: p.ImportComment, name: p.Name, } } } else { // Is package matched by a -replace item? var newPath string for _, item := range replace { if item.matchPrefix { if strings.HasPrefix(p.ImportPath, item.old) { newPath = item.new + p.ImportPath[len(item.old):] break } } else if p.ImportPath == item.old { newPath = item.new break } } if newPath != "" { newName := packageName[newPath] if newName == "" { newName = filepath.Base(newPath) // a guess } canonical[p.ImportPath] = canonicalName{ path: newPath, name: newName, } continue } // Is package matched by a -baddomains item? for _, domain := range domains { slash := strings.Index(p.ImportPath, "/") if slash < 0 { continue // no slash: standard package } if p.ImportPath[:slash] == domain { // Package comes from bad domain and has no import comment. // Report an error each time this package is imported. canonical[p.ImportPath] = canonicalName{} // TODO(adonovan): should we make an HTTP request to // see if there's an HTTP redirect, a "go-import" meta tag, // or an import comment in the latest revision? // It would duplicate a lot of logic from "go get". } break } } } // Find all clients (direct importers) of canonical packages. // These are the packages that need fixing up. clients := make(map[*listPackage]bool) for path := range canonical { for client := range importedBy[path] { clients[client] = true } } // Restrict rewrites to the set of packages specified by the user. if len(packages) == 1 && (packages[0] == "all" || packages[0] == "...") { // no restriction } else { pkgs, err := list(packages...) if err != nil { fmt.Fprintf(stderr, "importfix: %v\n", err) return false } seen := make(map[string]bool) for _, p := range pkgs { seen[p.ImportPath] = true } for client := range clients { if !seen[client.ImportPath] { delete(clients, client) } } } // Rewrite selected client packages. ok := true for client := range clients { if !rewritePackage(client, canonical) { ok = false // There were errors. // Show direct and indirect imports of client. seen := make(map[string]bool) var direct, indirect []string for p := range importedBy[client.ImportPath] { direct = append(direct, p.ImportPath) seen[p.ImportPath] = true } var visit func(path string) visit = func(path string) { for q := range importedBy[path] { qpath := q.ImportPath if !seen[qpath] { seen[qpath] = true indirect = append(indirect, qpath) visit(qpath) } } } if direct != nil { fmt.Fprintf(stderr, "\timported directly by:\n") sort.Strings(direct) for _, path := range direct { fmt.Fprintf(stderr, "\t\t%s\n", path) visit(path) } if indirect != nil { fmt.Fprintf(stderr, "\timported indirectly by:\n") sort.Strings(indirect) for _, path := range indirect { fmt.Fprintf(stderr, "\t\t%s\n", path) } } } } } return ok } // Invariant: false result => error already printed. func rewritePackage(client *listPackage, canonical map[string]canonicalName) bool { ok := true used := make(map[string]bool) var filenames []string filenames = append(filenames, client.GoFiles...) filenames = append(filenames, client.TestGoFiles...) filenames = append(filenames, client.XTestGoFiles...) var first bool for _, filename := range filenames { if !first { first = true fmt.Fprintf(stderr, "%s\n", client.ImportPath) } err := rewriteFile(filepath.Join(client.Dir, filename), canonical, used) if err != nil { fmt.Fprintf(stderr, "\tERROR: %v\n", err) ok = false } } // Show which imports were renamed in this package. var keys []string for key := range used { keys = append(keys, key) } sort.Strings(keys) for _, key := range keys { if p := canonical[key]; p.path != "" { fmt.Fprintf(stderr, "\tfixed: %s -> %s\n", key, p.path) } else { fmt.Fprintf(stderr, "\tERROR: %s has no import comment\n", key) ok = false } } return ok } // 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. func rewriteFile(filename string, canonical map[string]canonicalName, used map[string]bool) error { fset := token.NewFileSet() f, err := parser.ParseFile(fset, filename, nil, parser.ParseComments) if err != nil { return err } var changed bool for _, imp := range f.Imports { impPath, err := strconv.Unquote(imp.Path.Value) if err != nil { log.Printf("%s: bad import spec %q: %v", fset.Position(imp.Pos()), imp.Path.Value, err) continue } canon, ok := canonical[impPath] if !ok { continue // import path is canonical } used[impPath] = true if canon.path == "" { // The canonical path is unknown (a -baddomain). // Show the offending import. // TODO(adonovan): should we show the actual source text? fmt.Fprintf(stderr, "\t%s:%d: import %q\n", shortPath(filename), fset.Position(imp.Pos()).Line, impPath) continue } changed = true imp.Path.Value = strconv.Quote(canon.path) // Add a renaming import if necessary. // // This is a guess at best. We can't see whether a 'go // get' of the canonical import path would have the same // name or not. Assume it's the last segment. newBase := path.Base(canon.path) if imp.Name == nil && newBase != canon.name { imp.Name = &ast.Ident{Name: canon.name} } } if changed && !*dryrun { var buf bytes.Buffer if err := format.Node(&buf, fset, f); err != nil { return fmt.Errorf("%s: couldn't format file: %v", filename, err) } return writeFile(filename, buf.Bytes(), 0644) } return nil } // listPackage corresponds to the output of go list -json, // but only the fields we need. type listPackage struct { 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. type packageError struct { ImportStack []string // shortest path from package named on command line to this one Pos string // position of error Err string // the error itself } func (e packageError) Error() string { if e.Pos != "" { return e.Pos + ": " + e.Err } return e.Err } // list runs 'go list' with the specified arguments and returns the // metadata for matching packages. func list(args ...string) ([]*listPackage, error) { cmd := exec.Command("go", append([]string{"list", "-e", "-json"}, args...)...) cmd.Stdout = new(bytes.Buffer) cmd.Stderr = stderr if err := cmd.Run(); err != nil { return nil, err } dec := json.NewDecoder(cmd.Stdout.(io.Reader)) var pkgs []*listPackage for { var p listPackage if err := dec.Decode(&p); err == io.EOF { break } else if err != nil { return nil, err } pkgs = append(pkgs, &p) } return pkgs, nil } // cwd contains the current working directory of the tool. // // It is initialized directly so that its value will be set for any other // package variables or init functions that depend on it, such as the gopath // variable in main_test.go. var cwd string = func() string { cwd, err := os.Getwd() if err != nil { log.Fatalf("os.Getwd: %v", err) } return cwd }() // shortPath returns an absolute or relative name for path, whatever is shorter. // Plundered from $GOROOT/src/cmd/go/build.go. func shortPath(path string) string { if rel, err := filepath.Rel(cwd, path); err == nil && len(rel) < len(path) { return rel } return path } golang-golang-x-tools-0.25.0+ds/cmd/fiximports/main_test.go000066400000000000000000000161601476103172300236100ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // No testdata on Android. //go:build !android // +build !android package main import ( "bytes" "log" "os" "path/filepath" "runtime" "strings" "testing" "golang.org/x/tools/internal/testenv" ) // TODO(adonovan): // - test introduction of renaming imports. // - test induced failures of rewriteFile. // Guide to the test packages: // // new.com/one -- canonical name for old.com/one // old.com/one -- non-canonical; has import comment "new.com/one" // old.com/bad -- has a parse error // fruit.io/orange \ // fruit.io/banana } orange -> pear -> banana -> titanic.biz/bar // fruit.io/pear / // titanic.biz/bar -- domain is sinking; package has jumped ship to new.com/bar // titanic.biz/foo -- domain is sinking but package has no import comment yet var gopath = filepath.Join(cwd, "testdata") func init() { if err := os.Setenv("GOPATH", gopath); err != nil { log.Fatal(err) } // This test currently requires GOPATH mode. // Explicitly disabling module mode should suffix, but // we'll also turn off GOPROXY just for good measure. if err := os.Setenv("GO111MODULE", "off"); err != nil { log.Fatal(err) } if err := os.Setenv("GOPROXY", "off"); err != nil { log.Fatal(err) } } 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() { stderr = os.Stderr *badDomains = "code.google.com" *replaceFlag = "" }() for i, test := range []struct { packages []string // packages to rewrite, "go list" syntax badDomains string // -baddomains flag replaceFlag string // -replace flag wantOK bool wantStderr string wantRewrite map[string]string }{ // #0. No errors. { packages: []string{"all"}, badDomains: "code.google.com", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/one" _ "titanic.biz/foo" )`, }, }, // #1. No packages needed rewriting. { packages: []string{"titanic.biz/...", "old.com/...", "new.com/..."}, badDomains: "code.google.com", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' `, }, // #2. Some packages without import comments matched bad domains. { packages: []string{"all"}, badDomains: "titanic.biz", wantOK: false, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana testdata/src/fruit.io/banana/banana.go:6: import "titanic.biz/foo" fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar ERROR: titanic.biz/foo has no import comment imported directly by: fruit.io/pear imported indirectly by: fruit.io/orange `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/one" _ "titanic.biz/foo" )`, }, }, // #3. The -replace flag lets user supply missing import comments. { packages: []string{"all"}, replaceFlag: "titanic.biz/foo=new.com/foo", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, // #4. The -replace flag supports wildcards. // An explicit import comment takes precedence. { packages: []string{"all"}, replaceFlag: "titanic.biz/...=new.com/...", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, // #5. The -replace flag trumps -baddomains. { packages: []string{"all"}, badDomains: "titanic.biz", replaceFlag: "titanic.biz/foo=new.com/foo", wantOK: true, wantStderr: ` testdata/src/old.com/bad/bad.go:2:43: expected 'package', found 'EOF' fruit.io/banana fixed: old.com/one -> new.com/one fixed: titanic.biz/bar -> new.com/bar fixed: titanic.biz/foo -> new.com/foo `, wantRewrite: map[string]string{ "$GOPATH/src/fruit.io/banana/banana.go": `package banana import ( _ "new.com/bar" _ "new.com/foo" _ "new.com/one" )`, }, }, } { *badDomains = test.badDomains *replaceFlag = test.replaceFlag stderr = new(bytes.Buffer) gotRewrite := make(map[string]string) writeFile = func(filename string, content []byte, mode os.FileMode) error { filename = strings.Replace(filename, gopath, "$GOPATH", 1) filename = filepath.ToSlash(filename) gotRewrite[filename] = string(bytes.TrimSpace(content)) return nil } if runtime.GOOS == "windows" { test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/old.com/bad/bad.go`, `testdata\src\old.com\bad\bad.go`, -1) test.wantStderr = strings.Replace(test.wantStderr, `testdata/src/fruit.io/banana/banana.go`, `testdata\src\fruit.io\banana\banana.go`, -1) } test.wantStderr = strings.TrimSpace(test.wantStderr) // Check status code. if fiximports(test.packages...) != test.wantOK { t.Errorf("#%d. fiximports() = %t", i, !test.wantOK) } // Compare stderr output. if got := strings.TrimSpace(stderr.(*bytes.Buffer).String()); got != test.wantStderr { if strings.Contains(got, "vendor/golang_org/x/text/unicode/norm") { t.Skip("skipping known-broken test; see golang.org/issue/17417") } t.Errorf("#%d. stderr: got <<\n%s\n>>, want <<\n%s\n>>", i, got, test.wantStderr) } // Compare rewrites. for k, v := range gotRewrite { if test.wantRewrite[k] != v { t.Errorf("#%d. rewrite[%s] = <<%s>>, want <<%s>>", i, k, v, test.wantRewrite[k]) } delete(test.wantRewrite, k) } for k, v := range test.wantRewrite { t.Errorf("#%d. rewrite[%s] missing, want <<%s>>", i, k, v) } } } // TestDryRun tests that the -n flag suppresses calls to writeFile. func TestDryRun(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") *dryrun = true defer func() { *dryrun = false }() // restore stderr = new(bytes.Buffer) writeFile = func(filename string, content []byte, mode os.FileMode) error { t.Fatalf("writeFile(%s) called in dryrun mode", filename) return nil } if !fiximports("all") { t.Fatalf("fiximports failed: %s", stderr) } } golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/000077500000000000000000000000001476103172300231035ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/000077500000000000000000000000001476103172300236725ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/000077500000000000000000000000001476103172300254315ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/banana/000077500000000000000000000000001476103172300266515ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/banana/banana.go000066400000000000000000000001261476103172300304170ustar00rootroot00000000000000package banana import ( _ "old.com/one" _ "titanic.biz/bar" _ "titanic.biz/foo" ) golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/orange/000077500000000000000000000000001476103172300267045ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/orange/orange.go000066400000000000000000000000511476103172300305020ustar00rootroot00000000000000package orange import _ "fruit.io/pear" golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/pear/000077500000000000000000000000001476103172300263605ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/fruit.io/pear/pear.go000066400000000000000000000000511476103172300276320ustar00rootroot00000000000000package pear import _ "fruit.io/banana" golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/000077500000000000000000000000001476103172300252405ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/one/000077500000000000000000000000001476103172300260215ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/new.com/one/one.go000066400000000000000000000000441476103172300271270ustar00rootroot00000000000000package one // import "new.com/one" golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/000077500000000000000000000000001476103172300252255ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/bad/000077500000000000000000000000001476103172300257535ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/bad/bad.go000066400000000000000000000001611476103172300270260ustar00rootroot00000000000000// This ill-formed Go source file is here to ensure the tool is robust // against bad packages in the workspace. golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/one/000077500000000000000000000000001476103172300260065ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/old.com/one/one.go000066400000000000000000000000441476103172300271140ustar00rootroot00000000000000package one // import "new.com/one" golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/000077500000000000000000000000001476103172300261105ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/bar/000077500000000000000000000000001476103172300266545ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go000066400000000000000000000001161476103172300277450ustar00rootroot00000000000000// This package is moving to new.com too. package bar // import "new.com/bar" golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/foo/000077500000000000000000000000001476103172300266735ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go000066400000000000000000000000641476103172300300050ustar00rootroot00000000000000// This package hasn't jumped ship yet. package foo golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/000077500000000000000000000000001476103172300220725ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/contrib.go000066400000000000000000000172551476103172300240730ustar00rootroot00000000000000// Copyright 2017 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. // The go-contrib-init command helps new Go contributors get their development // environment set up for the Go contribution process. // // It aims to be a complement or alternative to https://golang.org/doc/contribute.html. package main import ( "bytes" "flag" "fmt" "go/build" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" ) var ( repo = flag.String("repo", detectrepo(), "Which go repo you want to contribute to. Use \"go\" for the core, or e.g. \"net\" for golang.org/x/net/*") dry = flag.Bool("dry-run", false, "Fail with problems instead of trying to fix things.") ) func main() { log.SetFlags(0) flag.Parse() checkCLA() checkGoroot() checkWorkingDir() checkGitOrigin() checkGitCodeReview() fmt.Print("All good. Happy hacking!\n" + "Remember to squash your revised commits and preserve the magic Change-Id lines.\n" + "Next steps: https://golang.org/doc/contribute.html#commit_changes\n") } func detectrepo() string { wd, err := os.Getwd() if err != nil { return "go" } for _, path := range filepath.SplitList(build.Default.GOPATH) { rightdir := filepath.Join(path, "src", "golang.org", "x") + string(os.PathSeparator) if strings.HasPrefix(wd, rightdir) { tail := wd[len(rightdir):] end := strings.Index(tail, string(os.PathSeparator)) if end > 0 { repo := tail[:end] return repo } } } return "go" } var googleSourceRx = regexp.MustCompile(`(?m)^(go|go-review)?\.googlesource.com\b`) func checkCLA() { slurp, err := os.ReadFile(cookiesFile()) if err != nil && !os.IsNotExist(err) { log.Fatal(err) } if googleSourceRx.Match(slurp) { // Probably good. return } log.Fatal("Your .gitcookies file isn't configured.\n" + "Next steps:\n" + " * Submit a CLA (https://golang.org/doc/contribute.html#cla) if not done\n" + " * Go to https://go.googlesource.com/ and click \"Generate Password\" at the top,\n" + " then follow instructions.\n" + " * Run go-contrib-init again.\n") } func expandUser(s string) string { env := "HOME" if runtime.GOOS == "windows" { env = "USERPROFILE" } else if runtime.GOOS == "plan9" { env = "home" } home := os.Getenv(env) if home == "" { return s } if len(s) >= 2 && s[0] == '~' && os.IsPathSeparator(s[1]) { if runtime.GOOS == "windows" { s = filepath.ToSlash(filepath.Join(home, s[2:])) } else { s = filepath.Join(home, s[2:]) } } return os.Expand(s, func(env string) string { if env == "HOME" { return home } return os.Getenv(env) }) } func cookiesFile() string { out, _ := exec.Command("git", "config", "http.cookiefile").Output() if s := strings.TrimSpace(string(out)); s != "" { if strings.HasPrefix(s, "~") { s = expandUser(s) } return s } if runtime.GOOS == "windows" { return filepath.Join(os.Getenv("USERPROFILE"), ".gitcookies") } return filepath.Join(os.Getenv("HOME"), ".gitcookies") } func checkGoroot() { v := os.Getenv("GOROOT") if v == "" { return } if *repo == "go" { if strings.HasPrefix(v, "/usr/") { log.Fatalf("Your GOROOT environment variable is set to %q\n"+ "This is almost certainly not what you want. Either unset\n"+ "your GOROOT or set it to the path of your development version\n"+ "of Go.", v) } slurp, err := os.ReadFile(filepath.Join(v, "VERSION")) if err == nil { slurp = bytes.TrimSpace(slurp) log.Fatalf("Your GOROOT environment variable is set to %q\n"+ "But that path is to a binary release of Go, with VERSION file %q.\n"+ "You should hack on Go in a fresh checkout of Go. Fix or unset your GOROOT.\n", v, slurp) } } } func checkWorkingDir() { wd, err := os.Getwd() if err != nil { log.Fatal(err) } if *repo == "go" { if inGoPath(wd) { log.Fatalf(`You can't work on Go from within your GOPATH. Please checkout Go outside of your GOPATH Current directory: %s GOPATH: %s `, wd, os.Getenv("GOPATH")) } return } gopath := firstGoPath() if gopath == "" { log.Fatal("Your GOPATH is not set, please set it") } rightdir := filepath.Join(gopath, "src", "golang.org", "x", *repo) if !strings.HasPrefix(wd, rightdir) { dirExists, err := exists(rightdir) if err != nil { log.Fatal(err) } if !dirExists { log.Fatalf("The repo you want to work on is currently not on your system.\n"+ "Run %q to obtain this repo\n"+ "then go to the directory %q\n", "go get -d golang.org/x/"+*repo, rightdir) } log.Fatalf("Your current directory is:%q\n"+ "Working on golang/x/%v requires you be in %q\n", wd, *repo, rightdir) } } func firstGoPath() string { list := filepath.SplitList(build.Default.GOPATH) if len(list) < 1 { return "" } return list[0] } func exists(path string) (bool, error) { _, err := os.Stat(path) if os.IsNotExist(err) { return false, nil } return true, err } func inGoPath(wd string) bool { if os.Getenv("GOPATH") == "" { return false } for _, path := range filepath.SplitList(os.Getenv("GOPATH")) { if strings.HasPrefix(wd, filepath.Join(path, "src")) { return true } } return false } // mostly check that they didn't clone from github func checkGitOrigin() { if _, err := exec.LookPath("git"); err != nil { log.Fatalf("You don't appear to have git installed. Do that.") } wantRemote := "https://go.googlesource.com/" + *repo remotes, err := exec.Command("git", "remote", "-v").Output() if err != nil { msg := cmdErr(err) if strings.Contains(msg, "Not a git repository") { log.Fatalf("Your current directory is not in a git checkout of %s", wantRemote) } log.Fatalf("Error running git remote -v: %v", msg) } matches := 0 for _, line := range strings.Split(string(remotes), "\n") { line = strings.TrimSpace(line) if !strings.HasPrefix(line, "origin") { continue } if !strings.Contains(line, wantRemote) { curRemote := strings.Fields(strings.TrimPrefix(line, "origin"))[0] // TODO: if not in dryRun mode, just fix it? log.Fatalf("Current directory's git was cloned from %q; origin should be %q", curRemote, wantRemote) } matches++ } if matches == 0 { log.Fatalf("git remote -v output didn't contain expected %q. Got:\n%s", wantRemote, remotes) } } func cmdErr(err error) string { if ee, ok := err.(*exec.ExitError); ok && len(ee.Stderr) > 0 { return fmt.Sprintf("%s: %s", err, ee.Stderr) } return fmt.Sprint(err) } func checkGitCodeReview() { if _, err := exec.LookPath("git-codereview"); err != nil { if *dry { log.Fatalf("You don't appear to have git-codereview tool. While this is technically optional,\n" + "almost all Go contributors use it. Our documentation and this tool assume it is used.\n" + "To install it, run:\n\n\t$ go get golang.org/x/review/git-codereview\n\n(Then run go-contrib-init again)") } err := exec.Command("go", "get", "golang.org/x/review/git-codereview").Run() if err != nil { log.Fatalf("Error running go get golang.org/x/review/git-codereview: %v", cmdErr(err)) } log.Printf("Installed git-codereview (ran `go get golang.org/x/review/git-codereview`)") } missing := false for _, cmd := range []string{"change", "gofmt", "mail", "pending", "submit", "sync"} { v, _ := exec.Command("git", "config", "alias."+cmd).Output() if strings.Contains(string(v), "codereview") { continue } if *dry { log.Printf("Missing alias. Run:\n\t$ git config alias.%s \"codereview %s\"", cmd, cmd) missing = true } else { err := exec.Command("git", "config", "alias."+cmd, "codereview "+cmd).Run() if err != nil { log.Fatalf("Error setting alias.%s: %v", cmd, cmdErr(err)) } } } if missing { log.Fatalf("Missing aliases. (While optional, this tool assumes you use them.)") } } golang-golang-x-tools-0.25.0+ds/cmd/go-contrib-init/contrib_test.go000066400000000000000000000023561476103172300251260ustar00rootroot00000000000000// Copyright 2017 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 ( "errors" "os" "os/exec" "runtime" "testing" ) func TestExpandUser(t *testing.T) { env := "HOME" if runtime.GOOS == "windows" { env = "USERPROFILE" } else if runtime.GOOS == "plan9" { env = "home" } oldenv := os.Getenv(env) os.Setenv(env, "/home/gopher") defer os.Setenv(env, oldenv) tests := []struct { input string want string }{ {input: "~/foo", want: "/home/gopher/foo"}, {input: "${HOME}/foo", want: "/home/gopher/foo"}, {input: "/~/foo", want: "/~/foo"}, } for _, tt := range tests { got := expandUser(tt.input) if got != tt.want { t.Fatalf("want %q, but %q", tt.want, got) } } } func TestCmdErr(t *testing.T) { tests := []struct { input error want string }{ {input: errors.New("cmd error"), want: "cmd error"}, {input: &exec.ExitError{ProcessState: nil, Stderr: nil}, want: ""}, {input: &exec.ExitError{ProcessState: nil, Stderr: []byte("test")}, want: ": test"}, } for i, tt := range tests { got := cmdErr(tt.input) if got != tt.want { t.Fatalf("%d. got %q, want %q", i, got, tt.want) } } } golang-golang-x-tools-0.25.0+ds/cmd/godex/000077500000000000000000000000001476103172300201745ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/godex/doc.go000066400000000000000000000046501476103172300212750ustar00rootroot00000000000000// Copyright 2014 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. // The godex command prints (dumps) exported information of packages // or selected package objects. // // In contrast to godoc, godex extracts this information from compiled // object files. Hence the exported data is truly what a compiler will // see, at the cost of missing commentary. // // Usage: godex [flags] {path[.name]} // // Each argument must be a (possibly partial) package path, optionally // followed by a dot and the name of a package object: // // godex math // godex math.Sin // godex math.Sin fmt.Printf // godex go/types // // godex automatically tries all possible package path prefixes if only a // partial package path is given. For instance, for the path "go/types", // godex prepends "golang.org/x/tools". // // The prefixes are computed by searching the directories specified by // the GOROOT and GOPATH environment variables (and by excluding the // build OS- and architecture-specific directory names from the path). // The search order is depth-first and alphabetic; for a partial path // "foo", a package "a/foo" is found before "b/foo". // // Absolute and relative paths may be provided, which disable automatic // prefix generation: // // godex $GOROOT/pkg/darwin_amd64/sort // godex ./sort // // All but the last path element may contain dots; a dot in the last path // element separates the package path from the package object name. If the // last path element contains a dot, terminate the argument with another // dot (indicating an empty object name). For instance, the path for a // package foo.bar would be specified as in: // // godex foo.bar. // // The flags are: // // -s="" // only consider packages from src, where src is one of the supported compilers // -v=false // verbose mode // // The following sources (-s arguments) are supported: // // gc // gc-generated object files // gccgo // gccgo-generated object files // gccgo-new // gccgo-generated object files using a condensed format (experimental) // source // (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 // BUG(gri): gccgo-importing appears to have occasional problems stalling godex; try -s=gc as work-around golang-golang-x-tools-0.25.0+ds/cmd/godex/gc.go000066400000000000000000000005471476103172300211220ustar00rootroot00000000000000// Copyright 2014 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 access to gc-generated export data. package main import ( "go/importer" "go/token" ) func init() { register("gc", importer.ForCompiler(token.NewFileSet(), "gc", nil)) } golang-golang-x-tools-0.25.0+ds/cmd/godex/gccgo.go000066400000000000000000000015661476103172300216150ustar00rootroot00000000000000// Copyright 2014 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 access to gccgo-generated export data. package main import ( "go/importer" "go/token" "go/types" ) func init() { register("gccgo", importer.ForCompiler(token.NewFileSet(), "gccgo", nil)) } // Print the extra gccgo compiler data for this package, if it exists. func (p *printer) printGccgoExtra(pkg *types.Package) { // Disabled for now. // TODO(gri) address this at some point. // if initdata, ok := initmap[pkg]; ok { // p.printf("/*\npriority %d\n", initdata.Priority) // p.printDecl("init", len(initdata.Inits), func() { // for _, init := range initdata.Inits { // p.printf("%s %s %d\n", init.Name, init.InitFunc, init.Priority) // } // }) // p.print("*/\n") // } } golang-golang-x-tools-0.25.0+ds/cmd/godex/godex.go000066400000000000000000000122531476103172300216340ustar00rootroot00000000000000// Copyright 2014 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 ( "errors" "flag" "fmt" "go/build" "go/types" "os" "path/filepath" "strings" ) var ( source = flag.String("s", "", "only consider packages from src, where src is one of the supported compilers") verbose = flag.Bool("v", false, "verbose mode") ) // lists of registered sources and corresponding importers var ( sources []string importers []types.Importer errImportFailed = errors.New("import failed") ) func usage() { fmt.Fprintln(os.Stderr, "usage: godex [flags] {path|qualifiedIdent}") flag.PrintDefaults() os.Exit(2) } func report(msg string) { fmt.Fprintln(os.Stderr, "error: "+msg) os.Exit(2) } func main() { flag.Usage = usage flag.Parse() if flag.NArg() == 0 { report("no package name, path, or file provided") } var imp types.Importer = new(tryImporters) if *source != "" { imp = lookup(*source) if imp == nil { report("source (-s argument) must be one of: " + strings.Join(sources, ", ")) } } for _, arg := range flag.Args() { path, name := splitPathIdent(arg) logf("\tprocessing %q: path = %q, name = %s\n", arg, path, name) // generate possible package path prefixes // (at the moment we do this for each argument - should probably cache the generated prefixes) prefixes := make(chan string) go genPrefixes(prefixes, !filepath.IsAbs(path) && !build.IsLocalImport(path)) // import package pkg, err := tryPrefixes(prefixes, path, imp) if err != nil { logf("\t=> ignoring %q: %s\n", path, err) continue } // filter objects if needed var filter func(types.Object) bool if name != "" { filter = func(obj types.Object) bool { // TODO(gri) perhaps use regular expression matching here? return obj.Name() == name } } // print contents print(os.Stdout, pkg, filter) } } func logf(format string, args ...interface{}) { if *verbose { fmt.Fprintf(os.Stderr, format, args...) } } // splitPathIdent splits a path.name argument into its components. // All but the last path element may contain dots. func splitPathIdent(arg string) (path, name string) { if i := strings.LastIndex(arg, "."); i >= 0 { if j := strings.LastIndex(arg, "/"); j < i { // '.' is not part of path path = arg[:i] name = arg[i+1:] return } } path = arg return } // tryPrefixes tries to import the package given by (the possibly partial) path using the given importer imp // by prepending all possible prefixes to path. It returns with the first package that it could import, or // with an error. func tryPrefixes(prefixes chan string, path string, imp types.Importer) (pkg *types.Package, err error) { for prefix := range prefixes { actual := path if prefix == "" { // don't use filepath.Join as it will sanitize the path and remove // a leading dot and then the path is not recognized as a relative // package path by the importers anymore logf("\ttrying no prefix\n") } else { actual = filepath.Join(prefix, path) logf("\ttrying prefix %q\n", prefix) } pkg, err = imp.Import(actual) if err == nil { break } logf("\t=> importing %q failed: %s\n", actual, err) } return } // tryImporters is an importer that tries all registered importers // successively until one of them succeeds or all of them failed. type tryImporters struct{} func (t *tryImporters) Import(path string) (pkg *types.Package, err error) { for i, imp := range importers { logf("\t\ttrying %s import\n", sources[i]) pkg, err = imp.Import(path) if err == nil { break } logf("\t\t=> %s import failed: %s\n", sources[i], err) } return } type protector struct { imp types.Importer } func (p *protector) Import(path string) (pkg *types.Package, err error) { defer func() { if recover() != nil { pkg = nil err = errImportFailed } }() return p.imp.Import(path) } // protect protects an importer imp from panics and returns the protected importer. func protect(imp types.Importer) types.Importer { return &protector{imp} } // register registers an importer imp for a given source src. func register(src string, imp types.Importer) { if lookup(src) != nil { panic(src + " importer already registered") } sources = append(sources, src) importers = append(importers, protect(imp)) } // lookup returns the importer imp for a given source src. func lookup(src string) types.Importer { for i, s := range sources { if s == src { return importers[i] } } return nil } func genPrefixes(out chan string, all bool) { out <- "" if all { platform := build.Default.GOOS + "_" + build.Default.GOARCH dirnames := append([]string{build.Default.GOROOT}, filepath.SplitList(build.Default.GOPATH)...) for _, dirname := range dirnames { walkDir(filepath.Join(dirname, "pkg", platform), "", out) } } close(out) } func walkDir(dirname, prefix string, out chan string) { fiList, err := os.ReadDir(dirname) if err != nil { return } for _, fi := range fiList { if fi.IsDir() && !strings.HasPrefix(fi.Name(), ".") { prefix := filepath.Join(prefix, fi.Name()) out <- prefix walkDir(filepath.Join(dirname, fi.Name()), prefix, out) } } } golang-golang-x-tools-0.25.0+ds/cmd/godex/isAlias18.go000066400000000000000000000005111476103172300222560ustar00rootroot00000000000000// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !go1.9 // +build !go1.9 package main import "go/types" func isAlias(obj *types.TypeName) bool { return false // there are no type aliases before Go 1.9 } golang-golang-x-tools-0.25.0+ds/cmd/godex/isAlias19.go000066400000000000000000000004441476103172300222640ustar00rootroot00000000000000// Copyright 2017 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build go1.9 // +build go1.9 package main import "go/types" func isAlias(obj *types.TypeName) bool { return obj.IsAlias() } golang-golang-x-tools-0.25.0+ds/cmd/godex/print.go000066400000000000000000000220371476103172300216630ustar00rootroot00000000000000// Copyright 2014 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" "fmt" "go/constant" "go/token" "go/types" "io" "math/big" "golang.org/x/tools/internal/aliases" ) // TODO(gri) use tabwriter for alignment? func print(w io.Writer, pkg *types.Package, filter func(types.Object) bool) { var p printer p.pkg = pkg p.printPackage(pkg, filter) p.printGccgoExtra(pkg) io.Copy(w, &p.buf) } type printer struct { pkg *types.Package buf bytes.Buffer indent int // current indentation level last byte // last byte written } func (p *printer) print(s string) { // Write the string one byte at a time. We care about the presence of // newlines for indentation which we will see even in the presence of // (non-corrupted) Unicode; no need to read one rune at a time. for i := 0; i < len(s); i++ { ch := s[i] if ch != '\n' && p.last == '\n' { // Note: This could lead to a range overflow for very large // indentations, but it's extremely unlikely to happen for // non-pathological code. p.buf.WriteString("\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"[:p.indent]) } p.buf.WriteByte(ch) p.last = ch } } func (p *printer) printf(format string, args ...interface{}) { p.print(fmt.Sprintf(format, args...)) } // methodsFor returns the named type and corresponding methods if the type // denoted by obj is not an interface and has methods. Otherwise it returns // the zero value. func methodsFor(obj *types.TypeName) (*types.Named, []*types.Selection) { named, _ := aliases.Unalias(obj.Type()).(*types.Named) if named == nil { // A type name's type can also be the // exported basic type unsafe.Pointer. return nil, nil } if _, ok := named.Underlying().(*types.Interface); ok { // ignore interfaces return nil, nil } methods := combinedMethodSet(named) if len(methods) == 0 { return nil, nil } return named, methods } func (p *printer) printPackage(pkg *types.Package, filter func(types.Object) bool) { // collect objects by kind var ( consts []*types.Const typem []*types.Named // non-interface types with methods typez []*types.TypeName // interfaces or types without methods vars []*types.Var funcs []*types.Func builtins []*types.Builtin methods = make(map[*types.Named][]*types.Selection) // method sets for named types ) scope := pkg.Scope() for _, name := range scope.Names() { obj := scope.Lookup(name) if obj.Exported() { // collect top-level exported and possibly filtered objects if filter == nil || filter(obj) { switch obj := obj.(type) { case *types.Const: consts = append(consts, obj) case *types.TypeName: // group into types with methods and types without if named, m := methodsFor(obj); named != nil { typem = append(typem, named) methods[named] = m } else { typez = append(typez, obj) } case *types.Var: vars = append(vars, obj) case *types.Func: funcs = append(funcs, obj) case *types.Builtin: // for unsafe.Sizeof, etc. builtins = append(builtins, obj) } } } else if filter == nil { // no filtering: collect top-level unexported types with methods if obj, _ := obj.(*types.TypeName); obj != nil { // see case *types.TypeName above if named, m := methodsFor(obj); named != nil { typem = append(typem, named) methods[named] = m } } } } p.printf("package %s // %q\n", pkg.Name(), pkg.Path()) p.printDecl("const", len(consts), func() { for _, obj := range consts { p.printObj(obj) p.print("\n") } }) p.printDecl("var", len(vars), func() { for _, obj := range vars { p.printObj(obj) p.print("\n") } }) p.printDecl("type", len(typez), func() { for _, obj := range typez { p.printf("%s ", obj.Name()) typ := obj.Type() if isAlias(obj) { p.print("= ") p.writeType(p.pkg, typ) } else { p.writeType(p.pkg, typ.Underlying()) } p.print("\n") } }) // non-interface types with methods for _, named := range typem { first := true if obj := named.Obj(); obj.Exported() { if first { p.print("\n") first = false } p.printf("type %s ", obj.Name()) p.writeType(p.pkg, named.Underlying()) p.print("\n") } for _, m := range methods[named] { if obj := m.Obj(); obj.Exported() { if first { p.print("\n") first = false } p.printFunc(m.Recv(), obj.(*types.Func)) p.print("\n") } } } if len(funcs) > 0 { p.print("\n") for _, obj := range funcs { p.printFunc(nil, obj) p.print("\n") } } // TODO(gri) better handling of builtins (package unsafe only) if len(builtins) > 0 { p.print("\n") for _, obj := range builtins { p.printf("func %s() // builtin\n", obj.Name()) } } p.print("\n") } func (p *printer) printDecl(keyword string, n int, printGroup func()) { switch n { case 0: // nothing to do case 1: p.printf("\n%s ", keyword) printGroup() default: p.printf("\n%s (\n", keyword) p.indent++ printGroup() p.indent-- p.print(")\n") } } // absInt returns the absolute value of v as a *big.Int. // v must be a numeric value. func absInt(v constant.Value) *big.Int { // compute big-endian representation of v b := constant.Bytes(v) // little-endian for i, j := 0, len(b)-1; i < j; i, j = i+1, j-1 { b[i], b[j] = b[j], b[i] } return new(big.Int).SetBytes(b) } var ( one = big.NewRat(1, 1) ten = big.NewRat(10, 1) ) // floatString returns the string representation for a // numeric value v in normalized floating-point format. func floatString(v constant.Value) string { if constant.Sign(v) == 0 { return "0.0" } // x != 0 // convert |v| into a big.Rat x x := new(big.Rat).SetFrac(absInt(constant.Num(v)), absInt(constant.Denom(v))) // normalize x and determine exponent e // (This is not very efficient, but also not speed-critical.) var e int for x.Cmp(ten) >= 0 { x.Quo(x, ten) e++ } for x.Cmp(one) < 0 { x.Mul(x, ten) e-- } // TODO(gri) Values such as 1/2 are easier to read in form 0.5 // rather than 5.0e-1. Similarly, 1.0e1 is easier to read as // 10.0. Fine-tune best exponent range for readability. s := x.FloatString(100) // good-enough precision // trim trailing 0's i := len(s) for i > 0 && s[i-1] == '0' { i-- } s = s[:i] // add a 0 if the number ends in decimal point if len(s) > 0 && s[len(s)-1] == '.' { s += "0" } // add exponent and sign if e != 0 { s += fmt.Sprintf("e%+d", e) } if constant.Sign(v) < 0 { s = "-" + s } // TODO(gri) If v is a "small" fraction (i.e., numerator and denominator // are just a small number of decimal digits), add the exact fraction as // a comment. For instance: 3.3333...e-1 /* = 1/3 */ return s } // valString returns the string representation for the value v. // Setting floatFmt forces an integer value to be formatted in // normalized floating-point format. // TODO(gri) Move this code into package constant. func valString(v constant.Value, floatFmt bool) string { switch v.Kind() { case constant.Int: if floatFmt { return floatString(v) } case constant.Float: return floatString(v) case constant.Complex: re := constant.Real(v) im := constant.Imag(v) var s string if constant.Sign(re) != 0 { s = floatString(re) if constant.Sign(im) >= 0 { s += " + " } else { s += " - " im = constant.UnaryOp(token.SUB, im, 0) // negate im } } // im != 0, otherwise v would be constant.Int or constant.Float return s + floatString(im) + "i" } return v.String() } func (p *printer) printObj(obj types.Object) { p.print(obj.Name()) typ, basic := obj.Type().Underlying().(*types.Basic) if basic && typ.Info()&types.IsUntyped != 0 { // don't write untyped types } else { p.print(" ") p.writeType(p.pkg, obj.Type()) } if obj, ok := obj.(*types.Const); ok { floatFmt := basic && typ.Info()&(types.IsFloat|types.IsComplex) != 0 p.print(" = ") p.print(valString(obj.Val(), floatFmt)) } } func (p *printer) printFunc(recvType types.Type, obj *types.Func) { p.print("func ") sig := obj.Type().(*types.Signature) if recvType != nil { p.print("(") p.writeType(p.pkg, recvType) p.print(") ") } p.print(obj.Name()) p.writeSignature(p.pkg, sig) } // combinedMethodSet returns the method set for a named type T // merged with all the methods of *T that have different names than // the methods of T. // // combinedMethodSet is analogous to types/typeutil.IntuitiveMethodSet // but doesn't require a MethodSetCache. // TODO(gri) If this functionality doesn't change over time, consider // just calling IntuitiveMethodSet eventually. func combinedMethodSet(T *types.Named) []*types.Selection { // method set for T mset := types.NewMethodSet(T) var res []*types.Selection for i, n := 0, mset.Len(); i < n; i++ { res = append(res, mset.At(i)) } // add all *T methods with names different from T methods pmset := types.NewMethodSet(types.NewPointer(T)) for i, n := 0, pmset.Len(); i < n; i++ { pm := pmset.At(i) if obj := pm.Obj(); mset.Lookup(obj.Pkg(), obj.Name()) == nil { res = append(res, pm) } } return res } golang-golang-x-tools-0.25.0+ds/cmd/godex/source.go000066400000000000000000000006601476103172300220250ustar00rootroot00000000000000// Copyright 2014 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 access to export data from source. package main import "go/types" func init() { register("source", sourceImporter{}) } type sourceImporter struct{} func (sourceImporter) Import(path string) (*types.Package, error) { panic("unimplemented") } golang-golang-x-tools-0.25.0+ds/cmd/godex/writetype.go000066400000000000000000000135441476103172300225660ustar00rootroot00000000000000// Copyright 2014 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 writing of types. The functionality is lifted // directly from go/types, but now contains various modifications for // nicer output. // // TODO(gri) back-port once we have a fixed interface and once the // go/types API is not frozen anymore for the 1.3 release; and remove // this implementation if possible. package main import ( "go/types" "golang.org/x/tools/internal/aliases" ) func (p *printer) writeType(this *types.Package, typ types.Type) { p.writeTypeInternal(this, typ, make([]types.Type, 8)) } // From go/types - leave for now to ease back-porting this code. const GcCompatibilityMode = false func (p *printer) writeTypeInternal(this *types.Package, typ types.Type, visited []types.Type) { // Theoretically, this is a quadratic lookup algorithm, but in // practice deeply nested composite types with unnamed component // types are uncommon. This code is likely more efficient than // using a map. for _, t := range visited { if t == typ { p.printf("○%T", typ) // cycle to typ return } } visited = append(visited, typ) switch t := typ.(type) { case nil: p.print("") case *types.Basic: if t.Kind() == types.UnsafePointer { p.print("unsafe.") } if GcCompatibilityMode { // forget the alias names switch t.Kind() { case types.Byte: t = types.Typ[types.Uint8] case types.Rune: t = types.Typ[types.Int32] } } p.print(t.Name()) case *types.Array: p.printf("[%d]", t.Len()) p.writeTypeInternal(this, t.Elem(), visited) case *types.Slice: p.print("[]") p.writeTypeInternal(this, t.Elem(), visited) case *types.Struct: n := t.NumFields() if n == 0 { p.print("struct{}") return } p.print("struct {\n") p.indent++ for i := 0; i < n; i++ { f := t.Field(i) if !f.Anonymous() { p.printf("%s ", f.Name()) } p.writeTypeInternal(this, f.Type(), visited) if tag := t.Tag(i); tag != "" { p.printf(" %q", tag) } p.print("\n") } p.indent-- p.print("}") case *types.Pointer: p.print("*") p.writeTypeInternal(this, t.Elem(), visited) case *types.Tuple: p.writeTuple(this, t, false, visited) case *types.Signature: p.print("func") p.writeSignatureInternal(this, t, visited) case *types.Interface: // We write the source-level methods and embedded types rather // than the actual method set since resolved method signatures // may have non-printable cycles if parameters have anonymous // interface types that (directly or indirectly) embed the // current interface. For instance, consider the result type // of m: // // type T interface{ // m() interface{ T } // } // n := t.NumMethods() if n == 0 { p.print("interface{}") return } p.print("interface {\n") p.indent++ if GcCompatibilityMode { // print flattened interface // (useful to compare against gc-generated interfaces) for i := 0; i < n; i++ { m := t.Method(i) p.print(m.Name()) p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) p.print("\n") } } else { // print explicit interface methods and embedded types for i, n := 0, t.NumExplicitMethods(); i < n; i++ { m := t.ExplicitMethod(i) p.print(m.Name()) p.writeSignatureInternal(this, m.Type().(*types.Signature), visited) p.print("\n") } for i, n := 0, t.NumEmbeddeds(); i < n; i++ { typ := t.EmbeddedType(i) p.writeTypeInternal(this, typ, visited) p.print("\n") } } p.indent-- p.print("}") case *types.Map: p.print("map[") p.writeTypeInternal(this, t.Key(), visited) p.print("]") p.writeTypeInternal(this, t.Elem(), visited) case *types.Chan: var s string var parens bool switch t.Dir() { case types.SendRecv: s = "chan " // chan (<-chan T) requires parentheses if c, _ := t.Elem().(*types.Chan); c != nil && c.Dir() == types.RecvOnly { parens = true } case types.SendOnly: s = "chan<- " case types.RecvOnly: s = "<-chan " default: panic("unreachable") } p.print(s) if parens { p.print("(") } p.writeTypeInternal(this, t.Elem(), visited) if parens { p.print(")") } case *aliases.Alias: // TODO(adonovan): display something aliasy. p.writeTypeInternal(this, aliases.Unalias(t), visited) case *types.Named: s := "" if obj := t.Obj(); obj != nil { if pkg := obj.Pkg(); pkg != nil { if pkg != this { p.print(pkg.Path()) p.print(".") } // TODO(gri): function-local named types should be displayed // differently from named types at package level to avoid // ambiguity. } s = obj.Name() } p.print(s) default: // For externally defined implementations of Type. p.print(t.String()) } } func (p *printer) writeTuple(this *types.Package, tup *types.Tuple, variadic bool, visited []types.Type) { p.print("(") for i, n := 0, tup.Len(); i < n; i++ { if i > 0 { p.print(", ") } v := tup.At(i) if name := v.Name(); name != "" { p.print(name) p.print(" ") } typ := v.Type() if variadic && i == n-1 { p.print("...") typ = typ.(*types.Slice).Elem() } p.writeTypeInternal(this, typ, visited) } p.print(")") } func (p *printer) writeSignature(this *types.Package, sig *types.Signature) { p.writeSignatureInternal(this, sig, make([]types.Type, 8)) } func (p *printer) writeSignatureInternal(this *types.Package, sig *types.Signature, visited []types.Type) { p.writeTuple(this, sig.Params(), sig.Variadic(), visited) res := sig.Results() n := res.Len() if n == 0 { // no result return } p.print(" ") if n == 1 && res.At(0).Name() == "" { // single unnamed result p.writeTypeInternal(this, res.At(0).Type(), visited) return } // multiple or named result(s) p.writeTuple(this, res, false, visited) } golang-golang-x-tools-0.25.0+ds/cmd/godoc/000077500000000000000000000000001476103172300201615ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/godoc/doc.go000066400000000000000000000104231476103172300212550ustar00rootroot00000000000000// Copyright 2009 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. /* Godoc extracts and generates documentation for Go programs. It runs as a web server and presents the documentation as a web page. godoc -http=:6060 Usage: godoc [flag] The flags are: -v verbose mode -timestamps=true show timestamps with directory listings -index enable identifier and full text search index (no search box is shown if -index is not set) -index_files="" glob pattern specifying index files; if not empty, the index is read from these files in sorted order -index_throttle=0.75 index throttle value; a value of 0 means no time is allocated to the indexer (the indexer will never finish), a value of 1.0 means that index creation is running at full throttle (other goroutines may get no time while the index is built) -index_interval=0 interval of indexing; a value of 0 sets it to 5 minutes, a negative value indexes only once at startup -play=false enable playground -links=true link identifiers to their declarations -write_index=false write index to a file; the file name must be specified with -index_files -maxresults=10000 maximum number of full text search results shown (no full text index is built if maxresults <= 0) -notes="BUG" regular expression matching note markers to show (e.g., "BUG|TODO", ".*") -goroot=$GOROOT Go root directory -http=addr HTTP service address (e.g., '127.0.0.1:6060' or just ':6060') -templates="" directory containing alternate template files; if set, the directory may provide alternative template files for the files in $GOROOT/lib/godoc -url=path print to standard output the data that would be served by an HTTP request for path -zip="" zip file providing the file system to serve; disabled if empty By default, godoc looks at the packages it finds via $GOROOT and $GOPATH (if set). This behavior can be altered by providing an alternative $GOROOT with the -goroot flag. When the -index flag is set, a search index is maintained. The index is created at startup. The index contains both identifier and full text search information (searchable via regular expressions). The maximum number of full text search results shown can be set with the -maxresults flag; if set to 0, no full text results are shown, and only an identifier index but no full text search index is created. By default, godoc uses the system's GOOS/GOARCH. You can provide the URL parameters "GOOS" and "GOARCH" to set the output on the web page for the target system. The presentation mode of web pages served by godoc can be controlled with the "m" URL parameter; it accepts a comma-separated list of flag names as value: all show documentation for all declarations, not just the exported ones methods show all embedded methods, not just those of unexported anonymous fields src show the original source code rather than the extracted documentation flat present flat (not indented) directory listings using full paths For instance, https://golang.org/pkg/math/big/?m=all shows the documentation for all (not just the exported) declarations of package big. By default, godoc serves files from the file system of the underlying OS. Instead, a .zip file may be provided via the -zip flag, which contains the file system to serve. The file paths stored in the .zip file must use slash ('/') as path separator; and they must be unrooted. $GOROOT (or -goroot) must be set to the .zip file directory path containing the Go root directory. For instance, for a .zip file created by the command: zip -r go.zip $HOME/go one may run godoc as follows: godoc -http=:6060 -zip=go.zip -goroot=$HOME/go Godoc documentation is converted to HTML or to text using the go/doc package; see https://golang.org/pkg/go/doc/#ToHTML for the exact rules. Godoc also shows example code that is runnable by the testing package; see https://golang.org/pkg/testing/#hdr-Examples for the conventions. See "Godoc: documenting Go code" for how to write good comments for godoc: https://golang.org/doc/articles/godoc_documenting_go_code.html Deprecated: godoc cannot select what version of a package is displayed. Instead, use golang.org/x/pkgsite/cmd/pkgsite. */ package main // import "golang.org/x/tools/cmd/godoc" golang-golang-x-tools-0.25.0+ds/cmd/godoc/godoc_test.go000066400000000000000000000264651476103172300226570ustar00rootroot00000000000000// 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" "context" "fmt" "go/build" "io" "net" "net/http" "os" "os/exec" "regexp" "runtime" "strings" "sync" "testing" "time" "golang.org/x/tools/go/packages/packagestest" "golang.org/x/tools/internal/testenv" ) func TestMain(m *testing.M) { if os.Getenv("GODOC_TEST_IS_GODOC") != "" { main() os.Exit(0) } // 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") os.Exit(m.Run()) } var exe struct { path string err error once sync.Once } func godocPath(t *testing.T) string { if !testenv.HasExec() { t.Skipf("skipping test: exec not supported on %s/%s", runtime.GOOS, runtime.GOARCH) } 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 { ln, err := net.Listen("tcp", "127.0.0.1:0") if err != nil { ln, err = net.Listen("tcp6", "[::1]:0") } if err != nil { t.Fatal(err) } defer ln.Close() return ln.Addr().String() } func waitForServerReady(t *testing.T, ctx context.Context, cmd *exec.Cmd, addr string) { waitForServer(t, ctx, fmt.Sprintf("http://%v/", addr), "Go Documentation Server", false) } 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.", false) } func waitUntilScanComplete(t *testing.T, ctx context.Context, addr string) { waitForServer(t, ctx, fmt.Sprintf("http://%v/pkg", addr), "Scan is not yet complete", // setting reverse as true, which means this waits // until the string is not returned in the response anymore true) } const pollInterval = 50 * time.Millisecond // 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 } body, err := io.ReadAll(res.Body) res.Body.Close() if err != nil || res.StatusCode != http.StatusOK { continue } switch { case !reverse && bytes.Contains(body, []byte(match)), reverse && !bytes.Contains(body, []byte(match)): return } } } // hasTag checks whether a given release tag is contained in the current version // of the go binary. func hasTag(t string) bool { for _, v := range build.Default.ReleaseTags { if t == v { return true } } return false } func TestURL(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; fails to start up quickly enough") } 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 := testenv.Command(t, bin, args...) cmd.Stdout = stdout cmd.Stderr = stderr cmd.Args[0] = "godoc" // Set GOPATH variable to a non-existing absolute path // and GOPROXY=off to disable module fetches. // We cannot just unset GOPATH variable because godoc would default it to ~/go. // (We don't want the indexer looking at the local workspace during tests.) cmd.Env = append(os.Environ(), "GOPATH=/does_not_exist", "GOPROXY=off", "GO111MODULE=off") if err := cmd.Run(); err != nil { t.Fatalf("failed to run godoc -url=%q: %s\nstderr:\n%s", url, err, stderr) } if !strings.Contains(stdout.String(), contents) { t.Errorf("did not find substring %q in output of godoc -url=%q:\n%s", contents, url, stdout) } } } t.Run("index", testcase("/", "These packages are part of the Go Project but outside the main Go tree.")) t.Run("fmt", testcase("/pkg/fmt", "Package fmt implements formatted I/O")) } // Basic integration test for godoc HTTP interface. func TestWeb(t *testing.T) { bin := godocPath(t) for _, x := range packagestest.All { t.Run(x.Name(), func(t *testing.T) { testWeb(t, x, bin, false) }) } } // Basic integration test for godoc HTTP interface. func TestWebIndex(t *testing.T) { t.Skip("slow test of to-be-deleted code (golang/go#59056)") if testing.Short() { t.Skip("skipping slow test in -short mode") } 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) { 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. e := packagestest.Export(t, x, []packagestest.Module{ { Name: "godoc.test/repo1", Files: map[string]interface{}{ "a/a.go": `// Package a is a package in godoc.test/repo1. package a; import _ "godoc.test/repo2/a"; const Name = "repo1a"`, "b/b.go": `package b; const Name = "repo1b"`, }, }, { Name: "godoc.test/repo2", Files: map[string]interface{}{ "a/a.go": `package a; const Name = "repo2a"`, "b/b.go": `package b; const Name = "repo2b"`, }, }, }) defer e.Cleanup() // Start the server. addr := serverAddress(t) args := []string{fmt.Sprintf("-http=%s", addr)} if withIndex { args = append(args, "-index", "-index_interval=-1s") } cmd := testenv.Command(t, bin, args...) cmd.Dir = e.Config.Dir cmd.Env = e.Config.Env 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) } 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, ctx, cmd, addr) } else { waitForServerReady(t, ctx, cmd, addr) waitUntilScanComplete(t, ctx, addr) } tests := []struct { path string contains []string // substring match []string // regexp notContains []string needIndex bool releaseTag string // optional release tag that must be in go/build.ReleaseTags }{ { path: "/", contains: []string{ "Go Documentation Server", "Standard library", "These packages are part of the Go Project but outside the main Go tree.", }, }, { path: "/pkg/fmt/", contains: []string{"Package fmt implements formatted I/O"}, }, { path: "/src/fmt/", contains: []string{"scan_test.go"}, }, { path: "/src/fmt/print.go", contains: []string{"// Println formats using"}, }, { path: "/pkg", contains: []string{ "Standard library", "Package fmt implements formatted I/O", "Third party", "Package a is a package in godoc.test/repo1.", }, notContains: []string{ "internal/syscall", "cmd/gc", }, }, { path: "/pkg/?m=all", contains: []string{ "Standard library", "Package fmt implements formatted I/O", "internal/syscall/?m=all", }, notContains: []string{ "cmd/gc", }, }, { path: "/search?q=ListenAndServe", contains: []string{ "/src", }, notContains: []string{ "/pkg/bootstrap", }, needIndex: true, }, { path: "/pkg/strings/", contains: []string{ `href="/src/strings/strings.go"`, }, }, { path: "/cmd/compile/internal/amd64/", contains: []string{ `href="/src/cmd/compile/internal/amd64/ssa.go"`, }, }, { path: "/pkg/math/bits/", contains: []string{ `Added in Go 1.9`, }, }, { path: "/pkg/net/", contains: []string{ `// IPv6 scoped addressing zone; added in Go 1.1`, }, }, { path: "/pkg/net/http/httptrace/", match: []string{ `Got1xxResponse.*// Go 1\.11`, }, releaseTag: "go1.11", }, // Verify we don't add version info to a struct field added the same time // as the struct itself: { path: "/pkg/net/http/httptrace/", match: []string{ `(?m)GotFirstResponseByte func\(\)\s*$`, }, }, // Remove trailing periods before adding semicolons: { path: "/pkg/database/sql/", contains: []string{ "The number of connections currently in use; added in Go 1.11", "The number of idle connections; added in Go 1.11", }, releaseTag: "go1.11", }, // Third party packages. { path: "/pkg/godoc.test/repo1/a", contains: []string{`const Name = "repo1a"`}, }, { path: "/pkg/godoc.test/repo2/b", contains: []string{`const Name = "repo2b"`}, }, } for _, test := range tests { if test.needIndex && !withIndex { continue } url := fmt.Sprintf("http://%s%s", addr, test.path) resp, err := http.Get(url) if err != nil { t.Errorf("GET %s failed: %s", url, err) continue } body, err := io.ReadAll(resp.Body) strBody := string(body) resp.Body.Close() if err != nil { t.Errorf("GET %s: failed to read body: %s (response: %v)", url, err, resp) } isErr := false for _, substr := range test.contains { if test.releaseTag != "" && !hasTag(test.releaseTag) { continue } if !bytes.Contains(body, []byte(substr)) { t.Errorf("GET %s: wanted substring %q in body", url, substr) isErr = true } } for _, re := range test.match { if test.releaseTag != "" && !hasTag(test.releaseTag) { continue } if ok, err := regexp.MatchString(re, strBody); !ok || err != nil { if err != nil { t.Fatalf("Bad regexp %q: %v", re, err) } t.Errorf("GET %s: wanted to match %s in body", url, re) isErr = true } } for _, substr := range test.notContains { if bytes.Contains(body, []byte(substr)) { t.Errorf("GET %s: didn't want substring %q in body", url, substr) isErr = true } } if isErr { t.Errorf("GET %s: got:\n%s", url, body) } } } // Test for golang.org/issue/35476. func TestNoMainModule(t *testing.T) { if testing.Short() { t.Skip("skipping test in -short mode") } if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; for consistency with other tests that build godoc binary") } 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 := 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() if err != nil { t.Fatalf("godoc command failed: %v\nstderr=%q", err, stderr.String()) } if strings.Contains(stderr.String(), "go mod download") { t.Errorf("stderr contains 'go mod download', is that intentional?\nstderr=%q", stderr.String()) } } golang-golang-x-tools-0.25.0+ds/cmd/godoc/goroot.go000066400000000000000000000012321476103172300220170ustar00rootroot00000000000000// Copyright 2018 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 ( "os" "os/exec" "path/filepath" "runtime" "strings" ) func findGOROOT() string { if env := os.Getenv("GOROOT"); env != "" { return filepath.Clean(env) } def := filepath.Clean(runtime.GOROOT()) if runtime.Compiler == "gccgo" { // gccgo has no real GOROOT, and it certainly doesn't // depend on the executable's location. return def } out, err := exec.Command("go", "env", "GOROOT").Output() if err != nil { return def } return strings.TrimSpace(string(out)) } golang-golang-x-tools-0.25.0+ds/cmd/godoc/handlers.go000066400000000000000000000052031476103172300223100ustar00rootroot00000000000000// Copyright 2010 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 ( "encoding/json" "go/format" "log" "net/http" "text/template" "golang.org/x/tools/godoc" "golang.org/x/tools/godoc/redirect" "golang.org/x/tools/godoc/vfs" _ "golang.org/x/tools/playground" // register "/compile" playground redirect ) var ( pres *godoc.Presentation fs = vfs.NameSpace{} ) func registerHandlers(pres *godoc.Presentation) { if pres == nil { panic("nil Presentation") } mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { if req.URL.Path == "/" { http.Redirect(w, req, "/pkg/", http.StatusFound) return } pres.ServeHTTP(w, req) }) mux.Handle("/pkg/C/", redirect.Handler("/cmd/cgo/")) mux.HandleFunc("/fmt", fmtHandler) redirect.Register(mux) http.Handle("/", mux) } func readTemplate(name string) *template.Template { if pres == nil { panic("no global Presentation set yet") } path := "lib/godoc/" + name // use underlying file system fs to read the template file // (cannot use template ParseFile functions directly) data, err := vfs.ReadFile(fs, path) if err != nil { log.Fatal("readTemplate: ", err) } // be explicit with errors (for app engine use) t, err := template.New(name).Funcs(pres.FuncMap()).Parse(string(data)) if err != nil { log.Fatal("readTemplate: ", err) } return t } func readTemplates(p *godoc.Presentation) { p.CallGraphHTML = readTemplate("callgraph.html") p.DirlistHTML = readTemplate("dirlist.html") p.ErrorHTML = readTemplate("error.html") p.ExampleHTML = readTemplate("example.html") p.GodocHTML = readTemplate("godoc.html") p.ImplementsHTML = readTemplate("implements.html") p.MethodSetHTML = readTemplate("methodset.html") p.PackageHTML = readTemplate("package.html") p.PackageRootHTML = readTemplate("packageroot.html") p.SearchHTML = readTemplate("search.html") p.SearchDocHTML = readTemplate("searchdoc.html") p.SearchCodeHTML = readTemplate("searchcode.html") p.SearchTxtHTML = readTemplate("searchtxt.html") } type fmtResponse struct { Body string Error string } // fmtHandler takes a Go program in its "body" form value, formats it with // standard gofmt formatting, and writes a fmtResponse as a JSON object. func fmtHandler(w http.ResponseWriter, r *http.Request) { resp := new(fmtResponse) body, err := format.Source([]byte(r.FormValue("body"))) if err != nil { resp.Error = err.Error() } else { resp.Body = string(body) } w.Header().Set("Content-type", "application/json; charset=utf-8") json.NewEncoder(w).Encode(resp) } golang-golang-x-tools-0.25.0+ds/cmd/godoc/main.go000066400000000000000000000362121476103172300214400ustar00rootroot00000000000000// Copyright 2009 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. // godoc: Go Documentation Server // Web server tree: // // http://godoc/ redirect to /pkg/ // http://godoc/src/ serve files from $GOROOT/src; .go gets pretty-printed // http://godoc/cmd/ serve documentation about commands // http://godoc/pkg/ serve documentation about packages // (idea is if you say import "compress/zlib", you go to // http://godoc/pkg/compress/zlib) // package main import ( "archive/zip" "bytes" "context" "encoding/json" "errors" _ "expvar" // to serve /debug/vars "flag" "fmt" "go/build" "io" "log" "net/http" _ "net/http/pprof" // to serve /debug/pprof/* "net/url" "os" "os/exec" "path" "path/filepath" "regexp" "runtime" "strings" "golang.org/x/tools/godoc" "golang.org/x/tools/godoc/static" "golang.org/x/tools/godoc/vfs" "golang.org/x/tools/godoc/vfs/gatefs" "golang.org/x/tools/godoc/vfs/mapfs" "golang.org/x/tools/godoc/vfs/zipfs" "golang.org/x/tools/internal/gocommand" ) const defaultAddr = "localhost:6060" // default webserver address var ( // file system to serve // (with e.g.: zip -r go.zip $GOROOT -i \*.go -i \*.html -i \*.css -i \*.js -i \*.txt -i \*.c -i \*.h -i \*.s -i \*.png -i \*.jpg -i \*.sh -i favicon.ico) zipfile = flag.String("zip", "", "zip file providing the file system to serve; disabled if empty") // file-based index writeIndex = flag.Bool("write_index", false, "write index to a file; the file name must be specified with -index_files") // network httpAddr = flag.String("http", defaultAddr, "HTTP service address") // layout control urlFlag = flag.String("url", "", "print HTML for named URL") verbose = flag.Bool("v", false, "verbose mode") // file system roots // TODO(gri) consider the invariant that goroot always end in '/' goroot = flag.String("goroot", findGOROOT(), "Go root directory") // layout control showTimestamps = flag.Bool("timestamps", false, "show timestamps with directory listings") templateDir = flag.String("templates", "", "load templates/JS/CSS from disk in this directory") showPlayground = flag.Bool("play", false, "enable playground") declLinks = flag.Bool("links", true, "link identifiers to their declarations") // search index indexEnabled = flag.Bool("index", false, "enable search index") indexFiles = flag.String("index_files", "", "glob pattern specifying index files; if not empty, the index is read from these files in sorted order") indexInterval = flag.Duration("index_interval", 0, "interval of indexing; 0 for default (5m), negative to only index once at startup") maxResults = flag.Int("maxresults", 10000, "maximum number of full text search results shown") indexThrottle = flag.Float64("index_throttle", 0.75, "index throttle value; 0.0 = no time allocated, 1.0 = full throttle") // source code notes notesRx = flag.String("notes", "BUG", "regular expression matching note markers to show") ) // An httpResponseRecorder is an http.ResponseWriter type httpResponseRecorder struct { body *bytes.Buffer header http.Header code int } func (w *httpResponseRecorder) Header() http.Header { return w.header } func (w *httpResponseRecorder) Write(b []byte) (int, error) { return w.body.Write(b) } func (w *httpResponseRecorder) WriteHeader(code int) { w.code = code } func usage() { fmt.Fprintf(os.Stderr, "usage: godoc -http="+defaultAddr+"\n") flag.PrintDefaults() os.Exit(2) } func loggingHandler(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { log.Printf("%s\t%s", req.RemoteAddr, req.URL) h.ServeHTTP(w, req) }) } func handleURLFlag() { // Try up to 10 fetches, following redirects. urlstr := *urlFlag for i := 0; i < 10; i++ { // Prepare request. u, err := url.Parse(urlstr) if err != nil { log.Fatal(err) } req := &http.Request{ URL: u, } // Invoke default HTTP handler to serve request // to our buffering httpWriter. w := &httpResponseRecorder{code: 200, header: make(http.Header), body: new(bytes.Buffer)} http.DefaultServeMux.ServeHTTP(w, req) // Return data, error, or follow redirect. switch w.code { case 200: // ok os.Stdout.Write(w.body.Bytes()) return case 301, 302, 303, 307: // redirect redirect := w.header.Get("Location") if redirect == "" { log.Fatalf("HTTP %d without Location header", w.code) } urlstr = redirect default: log.Fatalf("HTTP error %d", w.code) } } log.Fatalf("too many redirects") } func initCorpus(corpus *godoc.Corpus) { err := corpus.Init() if err != nil { log.Fatal(err) } } func main() { flag.Usage = usage flag.Parse() // Check usage. if flag.NArg() > 0 { fmt.Fprintln(os.Stderr, `Unexpected arguments. Use "go doc" for command-line help output instead. For example, "go doc fmt.Printf".`) usage() } if *httpAddr == "" && *urlFlag == "" && !*writeIndex { fmt.Fprintln(os.Stderr, "At least one of -http, -url, or -write_index must be set to a non-zero value.") usage() } // Set the resolved goroot. vfs.GOROOT = *goroot fsGate := make(chan bool, 20) // Determine file system to use. if *zipfile == "" { // use file system of underlying OS rootfs := gatefs.New(vfs.OS(*goroot), fsGate) fs.Bind("/", rootfs, "/", vfs.BindReplace) } else { // use file system specified via .zip file (path separator must be '/') rc, err := zip.OpenReader(*zipfile) if err != nil { log.Fatalf("%s: %s\n", *zipfile, err) } defer rc.Close() // be nice (e.g., -writeIndex mode) fs.Bind("/", zipfs.New(rc, *zipfile), *goroot, vfs.BindReplace) } if *templateDir != "" { fs.Bind("/lib/godoc", vfs.OS(*templateDir), "/", vfs.BindBefore) fs.Bind("/favicon.ico", vfs.OS(*templateDir), "/favicon.ico", vfs.BindReplace) } else { fs.Bind("/lib/godoc", mapfs.New(static.Files), "/", vfs.BindReplace) fs.Bind("/favicon.ico", mapfs.New(static.Files), "/favicon.ico", vfs.BindReplace) } // Get the GOMOD value, use it to determine if godoc is being invoked in module mode. goModFile, err := goMod() if err != nil { fmt.Fprintf(os.Stderr, "failed to determine go env GOMOD value: %v", err) goModFile = "" // Fall back to GOPATH mode. } if goModFile != "" { fmt.Printf("using module mode; GOMOD=%s\n", goModFile) // Detect whether to use vendor mode or not. vendorEnabled, mainModVendor, err := gocommand.VendorEnabled(context.Background(), gocommand.Invocation{}, &gocommand.Runner{}) if err != nil { fmt.Fprintf(os.Stderr, "failed to determine if vendoring is enabled: %v", err) os.Exit(1) } if vendorEnabled { // Bind the root directory of the main module. fs.Bind(path.Join("/src", mainModVendor.Path), gatefs.New(vfs.OS(mainModVendor.Dir), fsGate), "/", vfs.BindAfter) // Bind the vendor directory. // // Note that in module mode, vendor directories in locations // other than the main module's root directory are ignored. // See https://golang.org/ref/mod#vendoring. vendorDir := filepath.Join(mainModVendor.Dir, "vendor") fs.Bind("/src", gatefs.New(vfs.OS(vendorDir), fsGate), "/", vfs.BindAfter) } else { // Try to download dependencies that are not in the module cache in order to // show their documentation. // This may fail if module downloading is disallowed (GOPROXY=off) or due to // limited connectivity, in which case we print errors to stderr and show // documentation only for packages that are available. fillModuleCache(os.Stderr, goModFile) // Determine modules in the build list. mods, err := buildList(goModFile) if err != nil { fmt.Fprintf(os.Stderr, "failed to determine the build list of the main module: %v", err) os.Exit(1) } // Bind module trees into Go root. for _, m := range mods { if m.Dir == "" { // Module is not available in the module cache, skip it. continue } dst := path.Join("/src", m.Path) fs.Bind(dst, gatefs.New(vfs.OS(m.Dir), fsGate), "/", vfs.BindAfter) } } } else { fmt.Println("using GOPATH mode") // Bind $GOPATH trees into Go root. for _, p := range filepath.SplitList(build.Default.GOPATH) { fs.Bind("/src", gatefs.New(vfs.OS(p), fsGate), "/src", vfs.BindAfter) } } var corpus *godoc.Corpus if goModFile != "" { corpus = godoc.NewCorpus(moduleFS{fs}) } else { corpus = godoc.NewCorpus(fs) } corpus.Verbose = *verbose corpus.MaxResults = *maxResults corpus.IndexEnabled = *indexEnabled if *maxResults == 0 { corpus.IndexFullText = false } corpus.IndexFiles = *indexFiles corpus.IndexDirectory = func(dir string) bool { return dir != "/pkg" && !strings.HasPrefix(dir, "/pkg/") } corpus.IndexThrottle = *indexThrottle corpus.IndexInterval = *indexInterval if *writeIndex || *urlFlag != "" { corpus.IndexThrottle = 1.0 corpus.IndexEnabled = true initCorpus(corpus) } else { go initCorpus(corpus) } // Initialize the version info before readTemplates, which saves // the map value in a method value. corpus.InitVersionInfo() pres = godoc.NewPresentation(corpus) pres.ShowTimestamps = *showTimestamps pres.ShowPlayground = *showPlayground pres.DeclLinks = *declLinks if *notesRx != "" { pres.NotesRx = regexp.MustCompile(*notesRx) } readTemplates(pres) registerHandlers(pres) if *writeIndex { // Write search index and exit. if *indexFiles == "" { log.Fatal("no index file specified") } log.Println("initialize file systems") *verbose = true // want to see what happens corpus.UpdateIndex() log.Println("writing index file", *indexFiles) f, err := os.Create(*indexFiles) if err != nil { log.Fatal(err) } index, _ := corpus.CurrentIndex() _, err = index.WriteTo(f) if err != nil { log.Fatal(err) } log.Println("done") return } // Print content that would be served at the URL *urlFlag. if *urlFlag != "" { handleURLFlag() return } var handler http.Handler = http.DefaultServeMux if *verbose { log.Printf("Go Documentation Server") log.Printf("version = %s", runtime.Version()) log.Printf("address = %s", *httpAddr) log.Printf("goroot = %s", *goroot) switch { case !*indexEnabled: log.Print("search index disabled") case *maxResults > 0: log.Printf("full text index enabled (maxresults = %d)", *maxResults) default: log.Print("identifier search index enabled") } fs.Fprint(os.Stderr) handler = loggingHandler(handler) } // Initialize search index. if *indexEnabled { go corpus.RunIndexer() } // Start http server. if *verbose { log.Println("starting HTTP server") } if err := http.ListenAndServe(*httpAddr, handler); err != nil { log.Fatalf("ListenAndServe %s: %v", *httpAddr, err) } } // goMod returns the go env GOMOD value in the current directory // by invoking the go command. // // 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. func goMod() (string, error) { out, err := exec.Command("go", "env", "-json", "GOMOD").Output() 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 } var env struct { GoMod string } err = json.Unmarshal(out, &env) if err != nil { return "", err } return env.GoMod, nil } // fillModuleCache does a best-effort attempt to fill the module cache // with all dependencies of the main module in the current directory // by invoking the go command. Module download logs are streamed to w. // If there are any problems encountered, they are also written to w. // It should only be used in module mode, when vendor mode isn't on. // // See https://golang.org/cmd/go/#hdr-Download_modules_to_local_cache. func fillModuleCache(w io.Writer, goMod string) { if goMod == os.DevNull { // No module requirements, nothing to do. return } cmd := exec.Command("go", "mod", "download", "-json") var out bytes.Buffer cmd.Stdout = &out cmd.Stderr = w err := cmd.Run() 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") for dec := json.NewDecoder(&out); ; { var m struct { Path string // Module path. Version string // Module version. Error string // Error loading module. } err := dec.Decode(&m) if err == io.EOF { break } else if err != nil { fmt.Fprintf(w, "error decoding JSON object from go mod download -json: %v\n", err) continue } if m.Error == "" { continue } fmt.Fprintf(w, "\tmodule %s@%s is not in the module cache and there was a problem downloading it: %s\n", m.Path, m.Version, m.Error) } } else if err != nil { fmt.Fprintf(w, "there was a problem filling module cache: %v\n", err) } } type mod struct { Path string // Module path. Dir string // Directory holding files for this module, if any. } // buildList determines the build list in the current directory // by invoking the go command. It should only be used in module mode, // when vendor mode isn't on. // // See https://golang.org/cmd/go/#hdr-The_main_module_and_the_build_list. func buildList(goMod string) ([]mod, error) { if goMod == os.DevNull { // Empty build list. return nil, nil } out, err := exec.Command("go", "list", "-m", "-json", "all").Output() 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 } var mods []mod for dec := json.NewDecoder(bytes.NewReader(out)); ; { var m mod err := dec.Decode(&m) if err == io.EOF { break } else if err != nil { return nil, err } mods = append(mods, m) } return mods, nil } // moduleFS is a vfs.FileSystem wrapper used when godoc is running // in module mode. It's needed so that packages inside modules are // considered to be third party. // // It overrides the RootType method of the underlying filesystem // and implements it using a heuristic based on the import path. // If the first element of the import path does not contain a dot, // that package is considered to be inside GOROOT. If it contains // a dot, then that package is considered to be third party. // // TODO(dmitshur): The RootType abstraction works well when GOPATH // 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 { if !strings.HasPrefix(path, "/src/") { return "" } domain := path[len("/src/"):] if i := strings.Index(domain, "/"); i >= 0 { domain = domain[:i] } if !strings.Contains(domain, ".") { // No dot in the first element of import path // suggests this is a package in GOROOT. return vfs.RootTypeGoRoot } else { // A dot in the first element of import path // suggests this is a third party package. return vfs.RootTypeGoPath } } func (fs moduleFS) String() string { return "module(" + fs.FileSystem.String() + ")" } golang-golang-x-tools-0.25.0+ds/cmd/goimports/000077500000000000000000000000001476103172300211115ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/goimports/doc.go000066400000000000000000000030541476103172300222070ustar00rootroot00000000000000// 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. /* Command goimports updates your Go import lines, adding missing ones and removing unreferenced ones. $ 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 Then in your .emacs file: (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 For GoSublime, follow the steps described here: http://michaelwhatcott.com/gosublime-goimports/ For other editors, you probably know what to do. To exclude directories in your $GOPATH from being scanned for Go files, goimports respects a configuration file at $GOPATH/src/.goimportsignore which may contain blank lines, comment lines (beginning with '#'), or lines naming a directory relative to the configuration file to ignore when scanning. No globbing or regex patterns are allowed. Use the "-v" verbose flag to verify it's working and see what goimports is doing. File bugs or feature requests at: https://golang.org/issues/new?title=x/tools/cmd/goimports:+ Happy hacking! */ package main // import "golang.org/x/tools/cmd/goimports" golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports.go000066400000000000000000000232041476103172300234640ustar00rootroot00000000000000// 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" "errors" "flag" "fmt" "go/scanner" "io" "log" "os" "os/exec" "path/filepath" "runtime" "runtime/pprof" "strings" "golang.org/x/tools/internal/gocommand" "golang.org/x/tools/internal/imports" ) var ( // main operation modes list = flag.Bool("l", false, "list files whose formatting differs from goimport's") write = flag.Bool("w", false, "write result to (source) file instead of stdout") doDiff = flag.Bool("d", false, "display diffs instead of rewriting files") srcdir = flag.String("srcdir", "", "choose imports as if source code is from `dir`. When operating on a single file, dir may instead be the complete file name.") verbose bool // verbose logging cpuProfile = flag.String("cpuprofile", "", "CPU profile output") memProfile = flag.String("memprofile", "", "memory profile output") memProfileRate = flag.Int("memrate", 0, "if > 0, sets runtime.MemProfileRate") options = &imports.Options{ TabWidth: 8, TabIndent: true, Comments: true, Fragment: true, Env: &imports.ProcessEnv{ GocmdRunner: &gocommand.Runner{}, }, } exitCode = 0 ) func init() { flag.BoolVar(&options.AllErrors, "e", false, "report all errors (not just the first 10 on different lines)") flag.StringVar(&options.LocalPrefix, "local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list") flag.BoolVar(&options.FormatOnly, "format-only", false, "if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.") } func report(err error) { scanner.PrintError(os.Stderr, err) exitCode = 2 } func usage() { fmt.Fprintf(os.Stderr, "usage: goimports [flags] [path ...]\n") flag.PrintDefaults() os.Exit(2) } func isGoFile(f os.FileInfo) bool { // ignore non-Go files name := f.Name() return !f.IsDir() && !strings.HasPrefix(name, ".") && strings.HasSuffix(name, ".go") } // argumentType is which mode goimports was invoked as. type argumentType int const ( // fromStdin means the user is piping their source into goimports. fromStdin argumentType = iota // singleArg is the common case from editors, when goimports is run on // a single file. singleArg // multipleArg is when the user ran "goimports file1.go file2.go" // or ran goimports on a directory tree. multipleArg ) func processFile(filename string, in io.Reader, out io.Writer, argType argumentType) error { opt := options if argType == fromStdin { nopt := *options nopt.Fragment = true opt = &nopt } if in == nil { f, err := os.Open(filename) if err != nil { return err } defer f.Close() in = f } src, err := io.ReadAll(in) if err != nil { return err } target := filename if *srcdir != "" { // Determine whether the provided -srcdirc is a directory or file // and then use it to override the target. // // See https://github.com/dominikh/go-mode.el/issues/146 if isFile(*srcdir) { if argType == multipleArg { return errors.New("-srcdir value can't be a file when passing multiple arguments or when walking directories") } target = *srcdir } else if argType == singleArg && strings.HasSuffix(*srcdir, ".go") && !isDir(*srcdir) { // For a file which doesn't exist on disk yet, but might shortly. // e.g. user in editor opens $DIR/newfile.go and newfile.go doesn't yet exist on disk. // The goimports on-save hook writes the buffer to a temp file // first and runs goimports before the actual save to newfile.go. // The editor's buffer is named "newfile.go" so that is passed to goimports as: // goimports -srcdir=/gopath/src/pkg/newfile.go /tmp/gofmtXXXXXXXX.go // and then the editor reloads the result from the tmp file and writes // it to newfile.go. target = *srcdir } else { // Pretend that file is from *srcdir in order to decide // visible imports correctly. target = filepath.Join(*srcdir, filepath.Base(filename)) } } res, err := imports.Process(target, src, opt) if err != nil { return err } if !bytes.Equal(src, res) { // formatting has changed if *list { fmt.Fprintln(out, filename) } if *write { if argType == fromStdin { // filename is "" return errors.New("can't use -w on stdin") } // On Windows, we need to re-set the permissions from the file. See golang/go#38225. var perms os.FileMode if fi, err := os.Stat(filename); err == nil { perms = fi.Mode() & os.ModePerm } err = os.WriteFile(filename, res, perms) if err != nil { return err } } if *doDiff { if argType == fromStdin { filename = "stdin.go" // because .orig looks silly } data, err := diff(src, res, filename) if err != nil { return fmt.Errorf("computing diff: %s", err) } fmt.Printf("diff -u %s %s\n", filepath.ToSlash(filename+".orig"), filepath.ToSlash(filename)) out.Write(data) } } if !*list && !*write && !*doDiff { _, err = out.Write(res) } return err } func visitFile(path string, f os.FileInfo, err error) error { if err == nil && isGoFile(f) { err = processFile(path, nil, os.Stdout, multipleArg) } if err != nil { report(err) } return nil } func walkDir(path string) { filepath.Walk(path, visitFile) } func main() { runtime.GOMAXPROCS(runtime.NumCPU()) // call gofmtMain in a separate function // so that it can use defer and have them // run before the exit. gofmtMain() os.Exit(exitCode) } // parseFlags parses command line flags and returns the paths to process. // It's a var so that custom implementations can replace it in other files. var parseFlags = func() []string { flag.BoolVar(&verbose, "v", false, "verbose logging") flag.Parse() return flag.Args() } func bufferedFileWriter(dest string) (w io.Writer, close func()) { f, err := os.Create(dest) if err != nil { log.Fatal(err) } bw := bufio.NewWriter(f) return bw, func() { if err := bw.Flush(); err != nil { log.Fatalf("error flushing %v: %v", dest, err) } if err := f.Close(); err != nil { log.Fatal(err) } } } func gofmtMain() { flag.Usage = usage paths := parseFlags() if *cpuProfile != "" { bw, flush := bufferedFileWriter(*cpuProfile) pprof.StartCPUProfile(bw) defer flush() defer pprof.StopCPUProfile() } // doTrace is a conditionally compiled wrapper around runtime/trace. It is // used to allow goimports to compile under gccgo, which does not support // runtime/trace. See https://golang.org/issue/15544. defer doTrace()() if *memProfileRate > 0 { runtime.MemProfileRate = *memProfileRate bw, flush := bufferedFileWriter(*memProfile) defer func() { runtime.GC() // materialize all statistics if err := pprof.WriteHeapProfile(bw); err != nil { log.Fatal(err) } flush() }() } if verbose { log.SetFlags(log.LstdFlags | log.Lmicroseconds) options.Env.Logf = log.Printf } if options.TabWidth < 0 { fmt.Fprintf(os.Stderr, "negative tabwidth %d\n", options.TabWidth) exitCode = 2 return } if len(paths) == 0 { if err := processFile("", os.Stdin, os.Stdout, fromStdin); err != nil { report(err) } return } argType := singleArg if len(paths) > 1 { argType = multipleArg } for _, path := range paths { switch dir, err := os.Stat(path); { case err != nil: report(err) case dir.IsDir(): walkDir(path) default: if err := processFile(path, nil, os.Stdout, argType); err != nil { report(err) } } } } func writeTempFile(dir, prefix string, data []byte) (string, error) { file, err := os.CreateTemp(dir, prefix) if err != nil { return "", err } _, err = file.Write(data) if err1 := file.Close(); err == nil { err = err1 } if err != nil { os.Remove(file.Name()) return "", err } return file.Name(), nil } func diff(b1, b2 []byte, filename string) (data []byte, err error) { f1, err := writeTempFile("", "gofmt", b1) if err != nil { return } defer os.Remove(f1) f2, err := writeTempFile("", "gofmt", b2) if err != nil { return } defer os.Remove(f2) cmd := "diff" if runtime.GOOS == "plan9" { cmd = "/bin/ape/diff" } data, err = exec.Command(cmd, "-u", f1, f2).CombinedOutput() if len(data) > 0 { // diff exits with a non-zero status when the files don't match. // Ignore that failure as long as we get output. return replaceTempFilename(data, filename) } return } // replaceTempFilename replaces temporary filenames in diff with actual one. // // --- /tmp/gofmt316145376 2017-02-03 19:13:00.280468375 -0500 // +++ /tmp/gofmt617882815 2017-02-03 19:13:00.280468375 -0500 // ... // -> // --- path/to/file.go.orig 2017-02-03 19:13:00.280468375 -0500 // +++ path/to/file.go 2017-02-03 19:13:00.280468375 -0500 // ... func replaceTempFilename(diff []byte, filename string) ([]byte, error) { bs := bytes.SplitN(diff, []byte{'\n'}, 3) if len(bs) < 3 { return nil, fmt.Errorf("got unexpected diff for %s", filename) } // Preserve timestamps. var t0, t1 []byte if i := bytes.LastIndexByte(bs[0], '\t'); i != -1 { t0 = bs[0][i:] } if i := bytes.LastIndexByte(bs[1], '\t'); i != -1 { t1 = bs[1][i:] } // Always print filepath with slash separator. f := filepath.ToSlash(filename) bs[0] = []byte(fmt.Sprintf("--- %s%s", f+".orig", t0)) bs[1] = []byte(fmt.Sprintf("+++ %s%s", f, t1)) return bytes.Join(bs, []byte{'\n'}), nil } // isFile reports whether name is a file. func isFile(name string) bool { fi, err := os.Stat(name) return err == nil && fi.Mode().IsRegular() } // isDir reports whether name is a directory. func isDir(name string) bool { fi, err := os.Stat(name) return err == nil && fi.IsDir() } golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports_gc.go000066400000000000000000000007611476103172300241400ustar00rootroot00000000000000// Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build gc // +build gc package main import ( "flag" "runtime/trace" ) var traceProfile = flag.String("trace", "", "trace profile output") func doTrace() func() { if *traceProfile != "" { bw, flush := bufferedFileWriter(*traceProfile) trace.Start(bw) return func() { trace.Stop() flush() } } return func() {} } golang-golang-x-tools-0.25.0+ds/cmd/goimports/goimports_not_gc.go000066400000000000000000000003701476103172300250140ustar00rootroot00000000000000// Copyright 2016 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. //go:build !gc // +build !gc package main func doTrace() func() { return func() {} } golang-golang-x-tools-0.25.0+ds/cmd/gomvpkg/000077500000000000000000000000001476103172300205405ustar00rootroot00000000000000golang-golang-x-tools-0.25.0+ds/cmd/gomvpkg/main.go000066400000000000000000000056071476103172300220230ustar00rootroot00000000000000// Copyright 2015 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // The gomvpkg command moves go packages, updating import declarations. // See the -help message or Usage constant for details. package main import ( "flag" "fmt" "go/build" "os" "golang.org/x/tools/go/buildutil" "golang.org/x/tools/refactor/rename" ) var ( fromFlag = flag.String("from", "", "Import path of package to be moved") toFlag = flag.String("to", "", "Destination import path for package") vcsMvCmdFlag = flag.String("vcs_mv_cmd", "", `A template for the version control system's "move directory" command, e.g. "git mv {{.Src}} {{.Dst}}"`) helpFlag = flag.Bool("help", false, "show usage message") ) func init() { flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) } const Usage = `gomvpkg: moves a package, updating import declarations Usage: gomvpkg -from -to [-vcs_mv_cmd