pax_global_header00006660000000000000000000000064141775155060014525gustar00rootroot0000000000000052 comment=1e24bb6ed0de070f3d23711008405e42187e28be golang-golang-x-tools-0.1.9+ds/000077500000000000000000000000001417751550600162555ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/.gitattributes000066400000000000000000000005311417751550600211470ustar00rootroot00000000000000# 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.1.9+ds/.gitignore000066400000000000000000000001251417751550600202430ustar00rootroot00000000000000# Add no patterns to .gitignore except for files generated by the build. last-change golang-golang-x-tools-0.1.9+ds/.prettierrc000066400000000000000000000000631417751550600204400ustar00rootroot00000000000000{ "singleQuote": true, "trailingComma": "es5" }golang-golang-x-tools-0.1.9+ds/AUTHORS000066400000000000000000000002551417751550600173270ustar00rootroot00000000000000# This source code refers to The Go Authors for copyright purposes. # The master list of authors is in the main Go distribution, # visible at http://tip.golang.org/AUTHORS. golang-golang-x-tools-0.1.9+ds/CONTRIBUTING.md000066400000000000000000000016211417751550600205060ustar00rootroot00000000000000# 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.1.9+ds/CONTRIBUTORS000066400000000000000000000002521417751550600201340ustar00rootroot00000000000000# This source code was written by the Go contributors. # The master list of contributors is in the main Go distribution, # visible at http://tip.golang.org/CONTRIBUTORS. golang-golang-x-tools-0.1.9+ds/LICENSE000066400000000000000000000027071417751550600172700ustar00rootroot00000000000000Copyright (c) 2009 The Go Authors. All rights reserved. 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 Inc. 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.1.9+ds/PATENTS000066400000000000000000000024271417751550600173230ustar00rootroot00000000000000Additional 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.1.9+ds/README.md000066400000000000000000000024351417751550600175400ustar00rootroot00000000000000# Go Tools [![PkgGoDev](https://pkg.go.dev/badge/golang.org/x/tools)](https://pkg.go.dev/golang.org/x/tools) This subrepository holds the source for various packages and tools that support the Go programming language. Some of the tools, `godoc` and `vet` for example, are included in binary Go distributions. Others, including the Go `guru` and the test coverage tool, can be fetched with `go install`. Packages include a type-checker for Go and an implementation of the Static Single Assignment form (SSA) representation for Go programs. ## Download/Install The easiest way to install is to run `go install golang.org/x/tools/...@latest`. ## JS/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. ## Report Issues / Send Patches This repository uses Gerrit for code changes. To learn how to submit changes to this repository, 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. golang-golang-x-tools-0.1.9+ds/benchmark/000077500000000000000000000000001417751550600202075ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/benchmark/parse/000077500000000000000000000000001417751550600213215ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/benchmark/parse/parse.go000066400000000000000000000066131417751550600227700ustar00rootroot00000000000000// 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.1.9+ds/benchmark/parse/parse_test.go000066400000000000000000000121521417751550600240220ustar00rootroot00000000000000// 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.1.9+ds/blog/000077500000000000000000000000001417751550600172005ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/blog/atom/000077500000000000000000000000001417751550600201405ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/blog/atom/atom.go000066400000000000000000000030471417751550600214330ustar00rootroot00000000000000// 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.1.9+ds/blog/blog.go000066400000000000000000000303561417751550600204610ustar00rootroot00000000000000// 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.1.9+ds/blog/blog_test.go000066400000000000000000000036741417751550600215230ustar00rootroot00000000000000// 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.1.9+ds/cmd/000077500000000000000000000000001417751550600170205ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/000077500000000000000000000000001417751550600177615ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/authtest/000077500000000000000000000000001417751550600216225ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/authtest/authtest.go000066400000000000000000000123441417751550600240160ustar00rootroot00000000000000// 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" exec "golang.org/x/sys/execabs" "io" "log" "net/http" "net/textproto" "net/url" "os" "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.1.9+ds/cmd/auth/cookieauth/000077500000000000000000000000001417751550600221145ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/cookieauth/cookieauth.go000066400000000000000000000071361417751550600246050ustar00rootroot00000000000000// 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]) os.Exit(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.1.9+ds/cmd/auth/gitauth/000077500000000000000000000000001417751550600214265ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/gitauth/gitauth.go000066400000000000000000000113031417751550600234200ustar00rootroot00000000000000// 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" exec "golang.org/x/sys/execabs" "log" "net/http" "net/url" "os" "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.1.9+ds/cmd/auth/netrcauth/000077500000000000000000000000001417751550600217565ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/auth/netrcauth/netrcauth.go000066400000000000000000000061101417751550600243000ustar00rootroot00000000000000// 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" "io/ioutil" "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 := ioutil.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.1.9+ds/cmd/benchcmp/000077500000000000000000000000001417751550600205775ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/benchcmp/benchcmp.go000066400000000000000000000103201417751550600227010ustar00rootroot00000000000000// 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.1.9+ds/cmd/benchcmp/benchcmp_test.go000066400000000000000000000033761417751550600237550ustar00rootroot00000000000000// 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.1.9+ds/cmd/benchcmp/compare.go000066400000000000000000000123621417751550600225600ustar00rootroot00000000000000// 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.1.9+ds/cmd/benchcmp/compare_test.go000066400000000000000000000113321417751550600236130ustar00rootroot00000000000000// 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.1.9+ds/cmd/benchcmp/doc.go000066400000000000000000000023451417751550600216770ustar00rootroot00000000000000// 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.1.9+ds/cmd/bundle/000077500000000000000000000000001417751550600202715ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/.gitignore000066400000000000000000000000211417751550600222520ustar00rootroot00000000000000testdata/out.got golang-golang-x-tools-0.1.9+ds/cmd/bundle/main.go000066400000000000000000000313701417751550600215500ustar00rootroot00000000000000// 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" "io/ioutil" "log" "os" "strconv" "strings" "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 := ioutil.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, "// +build %s\n\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(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 } 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.1.9+ds/cmd/bundle/main_test.go000066400000000000000000000035341417751550600226100ustar00rootroot00000000000000// 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" "io/ioutil" "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 := ioutil.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 := ioutil.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.1.9+ds/cmd/bundle/testdata/000077500000000000000000000000001417751550600221025ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/out.golden000066400000000000000000000015211417751550600241020ustar00rootroot00000000000000//go:build tag // +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.1.9+ds/cmd/bundle/testdata/src/000077500000000000000000000000001417751550600226715ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/src/domain.name/000077500000000000000000000000001417751550600250575ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/src/domain.name/importdecl/000077500000000000000000000000001417751550600272215ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/src/domain.name/importdecl/p.go000066400000000000000000000000561417751550600300100ustar00rootroot00000000000000package importdecl func F() int { return 1 } golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/src/initial/000077500000000000000000000000001417751550600243225ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/bundle/testdata/src/initial/a.go000066400000000000000000000005451417751550600250750ustar00rootroot00000000000000package 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.1.9+ds/cmd/bundle/testdata/src/initial/b.go000066400000000000000000000003721417751550600250740ustar00rootroot00000000000000// 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.1.9+ds/cmd/bundle/testdata/src/initial/c.go000066400000000000000000000002501417751550600250700ustar00rootroot00000000000000package initial import _ "fmt" import renamedfmt "fmt" import renamedfmt2 "fmt" import . "fmt" func baz() { renamedfmt.Println() renamedfmt2.Println() Println() } golang-golang-x-tools-0.1.9+ds/cmd/callgraph/000077500000000000000000000000001417751550600207555ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/callgraph/main.go000066400000000000000000000225741417751550600222420ustar00rootroot00000000000000// 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 ( "bufio" "bytes" "flag" "fmt" "go/build" "go/token" "io" "log" "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/packages" "golang.org/x/tools/go/pointer" "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, pta)`) 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") ptalogFlag = flag.String("ptalog", "", "Location of the points-to analysis log file, or empty to disable logging.") ) 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|pta] [-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 pta inclusion-based Points-To Analysis The algorithms are ordered by increasing precision in their treatment of dynamic calls (and thus also computational cost). RTA and PTA require 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, e.g. Caller.Pkg.Pkg.Path yields the import path of the enclosing package. Consult the go/ssa API documentation for details. 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 pointer analysis algorithm: callgraph -format='{{.Caller}} -{{.Dynamic}}-> {{.Callee}}' -test -algo=pta 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. prog, pkgs := ssautil.AllPackages(initial, 0) 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": // Set up points-to analysis log file. var ptalog io.Writer if *ptalogFlag != "" { if f, err := os.Create(*ptalogFlag); err != nil { log.Fatalf("Failed to create PTA log file: %s", err) } else { buf := bufio.NewWriter(f) ptalog = buf defer func() { if err := buf.Flush(); err != nil { log.Printf("flush: %s", err) } if err := f.Close(); err != nil { log.Printf("close: %s", err) } }() } } mains, err := mainPackages(pkgs) if err != nil { return err } config := &pointer.Config{ Mains: mains, BuildCallGraph: true, Log: ptalog, } ptares, err := pointer.Analyze(config) if err != nil { return err // internal error in pointer analysis } cg = ptares.CallGraph 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. 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}}` } tmpl, err := template.New("-format").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.1.9+ds/cmd/callgraph/main_test.go000066400000000000000000000051721417751550600232740ustar00rootroot00000000000000// 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) { if b := os.Getenv("GO_BUILDER_NAME"); b == "windows-arm64-10" { t.Skipf("skipping due to suspected file corruption bug on %s builder (https://go.dev/issue/50706)", b) } 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`, }}, {"pta", false, []string{ // pta distinguishes main->C, main2->D. Also has a root node. ` --> pkg.init`, ` --> pkg.main`, `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`, }}, {"pta", true, []string{ ` --> pkg.test.main`, ` --> pkg.main`, `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 } for _, edge := range test.want { if !edges[edge] { t.Errorf("callgraph(%q, %t): missing edge: %s", test.algo, test.tests, edge) } } if t.Failed() { t.Log("got:\n", stdout) } } } golang-golang-x-tools-0.1.9+ds/cmd/callgraph/testdata/000077500000000000000000000000001417751550600225665ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/callgraph/testdata/src/000077500000000000000000000000001417751550600233555ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/callgraph/testdata/src/pkg/000077500000000000000000000000001417751550600241365ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/callgraph/testdata/src/pkg/pkg.go000066400000000000000000000003351417751550600252470ustar00rootroot00000000000000package 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.1.9+ds/cmd/callgraph/testdata/src/pkg/pkg_test.go000066400000000000000000000002771417751550600263130ustar00rootroot00000000000000package 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.1.9+ds/cmd/compilebench/000077500000000000000000000000001417751550600214505ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/compilebench/main.go000066400000000000000000000333051417751550600227270ustar00rootroot00000000000000// 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" exec "golang.org/x/sys/execabs" "io/ioutil" "log" "os" "path/filepath" "regexp" "strconv" "strings" "time" ) var ( goroot string compiler string linker string runRE *regexp.Regexp is6g 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") 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") ) 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 } } 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 { Dir string GoFiles []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 installed to GOROOT/pkg. out, err := exec.Command(*flagGoCmd, "build", "-i", c.dir).CombinedOutput() if err != nil { return fmt.Errorf("go build -i %s: %v\n%s", c.dir, err, out) } // Find dir and source file list. pkg, err := goList(c.dir) if err != nil { return err } args := []string{"-o", "_compilebench_.o"} args = append(args, strings.Fields(*flagCompilerFlags)...) 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 := ioutil.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. out, err := exec.Command(*flagGoCmd, "build", "-i", "-o", "/dev/null", r.dir).CombinedOutput() if err != nil { return fmt.Errorf("go build -i %s: %v\n%s", r.dir, err, out) } // Build the main package. pkg, err := goList(r.dir) if err != nil { return err } args := []string{"-o", "_compilebench_.o"} args = append(args, pkg.GoFiles...) 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"} 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") } } 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 := ioutil.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 := ioutil.WriteFile(outpath, out, 0666); err != nil { log.Print(err) } } os.Remove(dir + "/_compilebench_.memprof") } if *flagCpuprofile != "" { out, err := ioutil.ReadFile(dir + "/_compilebench_.cpuprof") if err != nil { log.Print(err) } outpath := *flagCpuprofile if *flagCount != 1 { outpath = fmt.Sprintf("%s_%d", outpath, count) } if err := ioutil.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 } golang-golang-x-tools-0.1.9+ds/cmd/cover/000077500000000000000000000000001417751550600201365ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/cover/README.md000066400000000000000000000002001417751550600214050ustar00rootroot00000000000000# Deprecated NOTE: For Go releases 1.5 and later, this tool lives in the standard repository. The code here is not maintained. golang-golang-x-tools-0.1.9+ds/cmd/cover/cover.go000066400000000000000000000472121417751550600216110ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bytes" "flag" "fmt" "go/ast" "go/parser" "go/printer" "go/token" "io" "io/ioutil" "log" "os" "path/filepath" "sort" "strconv" "strings" ) const usageMessage = "" + `Usage of 'go tool cover': Given a coverage profile produced by 'go test': go test -coverprofile=c.out Open a web browser displaying annotated source code: go tool cover -html=c.out Write out an HTML file instead of launching a web browser: go tool cover -html=c.out -o coverage.html Display coverage percentages to stdout for each function: go tool cover -func=c.out Finally, to generate modified source code with coverage annotations (what go test -cover does): go tool cover -mode=set -var=CoverageVariableName program.go ` func usage() { fmt.Fprint(os.Stderr, usageMessage) fmt.Fprintln(os.Stderr, "\nFlags:") flag.PrintDefaults() fmt.Fprintln(os.Stderr, "\n Only one of -html, -func, or -mode may be set.") os.Exit(2) } var ( mode = flag.String("mode", "", "coverage mode: set, count, atomic") varVar = flag.String("var", "GoCover", "name of coverage variable to generate") output = flag.String("o", "", "file for output; default: stdout") htmlOut = flag.String("html", "", "generate HTML representation of coverage profile") funcOut = flag.String("func", "", "output coverage profile information for each function") ) var profile string // The profile to read; the value of -html or -func var counterStmt func(*File, ast.Expr) ast.Stmt const ( atomicPackagePath = "sync/atomic" atomicPackageName = "_cover_atomic_" ) func main() { flag.Usage = usage flag.Parse() // Usage information when no arguments. if flag.NFlag() == 0 && flag.NArg() == 0 { flag.Usage() } err := parseFlags() if err != nil { fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, `For usage information, run "go tool cover -help"`) os.Exit(2) } // Generate coverage-annotated source. if *mode != "" { annotate(flag.Arg(0)) return } // Output HTML or function coverage information. if *htmlOut != "" { err = htmlOutput(profile, *output) } else { err = funcOutput(profile, *output) } if err != nil { fmt.Fprintf(os.Stderr, "cover: %v\n", err) os.Exit(2) } } // parseFlags sets the profile and counterStmt globals and performs validations. func parseFlags() error { profile = *htmlOut if *funcOut != "" { if profile != "" { return fmt.Errorf("too many options") } profile = *funcOut } // Must either display a profile or rewrite Go source. if (profile == "") == (*mode == "") { return fmt.Errorf("too many options") } if *mode != "" { switch *mode { case "set": counterStmt = setCounterStmt case "count": counterStmt = incCounterStmt case "atomic": counterStmt = atomicCounterStmt default: return fmt.Errorf("unknown -mode %v", *mode) } if flag.NArg() == 0 { return fmt.Errorf("missing source file") } else if flag.NArg() == 1 { return nil } } else if flag.NArg() == 0 { return nil } return fmt.Errorf("too many arguments") } // Block represents the information about a basic block to be recorded in the analysis. // Note: Our definition of basic block is based on control structures; we don't break // apart && and ||. We could but it doesn't seem important enough to bother. type Block struct { startByte token.Pos endByte token.Pos numStmt int } // File is a wrapper for the state of a file used in the parser. // The basic parse tree walker is a method of this type. type File struct { fset *token.FileSet name string // Name of file. astFile *ast.File blocks []Block atomicPkg string // Package name for "sync/atomic" in this file. } // Visit implements the ast.Visitor interface. func (f *File) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.BlockStmt: // If it's a switch or select, the body is a list of case clauses; don't tag the block itself. if len(n.List) > 0 { switch n.List[0].(type) { case *ast.CaseClause: // switch for _, n := range n.List { clause := n.(*ast.CaseClause) clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false) } return f case *ast.CommClause: // select for _, n := range n.List { clause := n.(*ast.CommClause) clause.Body = f.addCounters(clause.Pos(), clause.End(), clause.Body, false) } return f } } n.List = f.addCounters(n.Lbrace, n.Rbrace+1, n.List, true) // +1 to step past closing brace. case *ast.IfStmt: ast.Walk(f, n.Body) if n.Else == nil { return nil } // The elses are special, because if we have // if x { // } else if y { // } // we want to cover the "if y". To do this, we need a place to drop the counter, // so we add a hidden block: // if x { // } else { // if y { // } // } switch stmt := n.Else.(type) { case *ast.IfStmt: block := &ast.BlockStmt{ Lbrace: n.Body.End(), // Start at end of the "if" block so the covered part looks like it starts at the "else". List: []ast.Stmt{stmt}, Rbrace: stmt.End(), } n.Else = block case *ast.BlockStmt: stmt.Lbrace = n.Body.End() // Start at end of the "if" block so the covered part looks like it starts at the "else". default: panic("unexpected node type in if") } ast.Walk(f, n.Else) return nil case *ast.SelectStmt: // Don't annotate an empty select - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { return nil } case *ast.SwitchStmt: // Don't annotate an empty switch - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { return nil } case *ast.TypeSwitchStmt: // Don't annotate an empty type switch - creates a syntax error. if n.Body == nil || len(n.Body.List) == 0 { return nil } } return f } // unquote returns the unquoted string. func unquote(s string) string { t, err := strconv.Unquote(s) if err != nil { log.Fatalf("cover: improperly quoted string %q\n", s) } return t } // addImport adds an import for the specified path, if one does not already exist, and returns // the local package name. func (f *File) addImport(path string) string { // Does the package already import it? for _, s := range f.astFile.Imports { if unquote(s.Path.Value) == path { if s.Name != nil { return s.Name.Name } return filepath.Base(path) } } newImport := &ast.ImportSpec{ Name: ast.NewIdent(atomicPackageName), Path: &ast.BasicLit{ Kind: token.STRING, Value: fmt.Sprintf("%q", path), }, } impDecl := &ast.GenDecl{ Tok: token.IMPORT, Specs: []ast.Spec{ newImport, }, } // Make the new import the first Decl in the file. astFile := f.astFile astFile.Decls = append(astFile.Decls, nil) copy(astFile.Decls[1:], astFile.Decls[0:]) astFile.Decls[0] = impDecl astFile.Imports = append(astFile.Imports, newImport) // Now refer to the package, just in case it ends up unused. // That is, append to the end of the file the declaration // var _ = _cover_atomic_.AddUint32 reference := &ast.GenDecl{ Tok: token.VAR, Specs: []ast.Spec{ &ast.ValueSpec{ Names: []*ast.Ident{ ast.NewIdent("_"), }, Values: []ast.Expr{ &ast.SelectorExpr{ X: ast.NewIdent(atomicPackageName), Sel: ast.NewIdent("AddUint32"), }, }, }, }, } astFile.Decls = append(astFile.Decls, reference) return atomicPackageName } var slashslash = []byte("//") // initialComments returns the prefix of content containing only // whitespace and line comments. Any +build directives must appear // within this region. This approach is more reliable than using // go/printer to print a modified AST containing comments. // func initialComments(content []byte) []byte { // Derived from go/build.Context.shouldBuild. end := 0 p := content for len(p) > 0 { line := p if i := bytes.IndexByte(line, '\n'); i >= 0 { line, p = line[:i], p[i+1:] } else { p = p[len(p):] } line = bytes.TrimSpace(line) if len(line) == 0 { // Blank line. end = len(content) - len(p) continue } if !bytes.HasPrefix(line, slashslash) { // Not comment line. break } } return content[:end] } func annotate(name string) { fset := token.NewFileSet() content, err := ioutil.ReadFile(name) if err != nil { log.Fatalf("cover: %s: %s", name, err) } parsedFile, err := parser.ParseFile(fset, name, content, parser.ParseComments) if err != nil { log.Fatalf("cover: %s: %s", name, err) } parsedFile.Comments = trimComments(parsedFile, fset) file := &File{ fset: fset, name: name, astFile: parsedFile, } if *mode == "atomic" { file.atomicPkg = file.addImport(atomicPackagePath) } ast.Walk(file, file.astFile) fd := os.Stdout if *output != "" { var err error fd, err = os.Create(*output) if err != nil { log.Fatalf("cover: %s", err) } } fd.Write(initialComments(content)) // Retain '// +build' directives. file.print(fd) // After printing the source tree, add some declarations for the counters etc. // We could do this by adding to the tree, but it's easier just to print the text. file.addVariables(fd) } // trimComments drops all but the //go: comments, some of which are semantically important. // We drop all others because they can appear in places that cause our counters // to appear in syntactically incorrect places. //go: appears at the beginning of // the line and is syntactically safe. func trimComments(file *ast.File, fset *token.FileSet) []*ast.CommentGroup { var comments []*ast.CommentGroup for _, group := range file.Comments { var list []*ast.Comment for _, comment := range group.List { if strings.HasPrefix(comment.Text, "//go:") && fset.Position(comment.Slash).Column == 1 { list = append(list, comment) } } if list != nil { comments = append(comments, &ast.CommentGroup{List: list}) } } return comments } func (f *File) print(w io.Writer) { printer.Fprint(w, f.fset, f.astFile) } // intLiteral returns an ast.BasicLit representing the integer value. func (f *File) intLiteral(i int) *ast.BasicLit { node := &ast.BasicLit{ Kind: token.INT, Value: fmt.Sprint(i), } return node } // index returns an ast.BasicLit representing the number of counters present. func (f *File) index() *ast.BasicLit { return f.intLiteral(len(f.blocks)) } // setCounterStmt returns the expression: __count[23] = 1. func setCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.AssignStmt{ Lhs: []ast.Expr{counter}, Tok: token.ASSIGN, Rhs: []ast.Expr{f.intLiteral(1)}, } } // incCounterStmt returns the expression: __count[23]++. func incCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.IncDecStmt{ X: counter, Tok: token.INC, } } // atomicCounterStmt returns the expression: atomic.AddUint32(&__count[23], 1) func atomicCounterStmt(f *File, counter ast.Expr) ast.Stmt { return &ast.ExprStmt{ X: &ast.CallExpr{ Fun: &ast.SelectorExpr{ X: ast.NewIdent(f.atomicPkg), Sel: ast.NewIdent("AddUint32"), }, Args: []ast.Expr{&ast.UnaryExpr{ Op: token.AND, X: counter, }, f.intLiteral(1), }, }, } } // newCounter creates a new counter expression of the appropriate form. func (f *File) newCounter(start, end token.Pos, numStmt int) ast.Stmt { counter := &ast.IndexExpr{ X: &ast.SelectorExpr{ X: ast.NewIdent(*varVar), Sel: ast.NewIdent("Count"), }, Index: f.index(), } stmt := counterStmt(f, counter) f.blocks = append(f.blocks, Block{start, end, numStmt}) return stmt } // addCounters takes a list of statements and adds counters to the beginning of // each basic block at the top level of that list. For instance, given // // S1 // if cond { // S2 // } // S3 // // counters will be added before S1 and before S3. The block containing S2 // will be visited in a separate call. // TODO: Nested simple blocks get unnecessary (but correct) counters func (f *File) addCounters(pos, blockEnd token.Pos, list []ast.Stmt, extendToClosingBrace bool) []ast.Stmt { // Special case: make sure we add a counter to an empty block. Can't do this below // or we will add a counter to an empty statement list after, say, a return statement. if len(list) == 0 { return []ast.Stmt{f.newCounter(pos, blockEnd, 0)} } // We have a block (statement list), but it may have several basic blocks due to the // appearance of statements that affect the flow of control. var newList []ast.Stmt for { // Find first statement that affects flow of control (break, continue, if, etc.). // It will be the last statement of this basic block. var last int end := blockEnd for last = 0; last < len(list); last++ { end = f.statementBoundary(list[last]) if f.endsBasicSourceBlock(list[last]) { extendToClosingBrace = false // Block is broken up now. last++ break } } if extendToClosingBrace { end = blockEnd } if pos != end { // Can have no source to cover if e.g. blocks abut. newList = append(newList, f.newCounter(pos, end, last)) } newList = append(newList, list[0:last]...) list = list[last:] if len(list) == 0 { break } pos = list[0].Pos() } return newList } // hasFuncLiteral reports the existence and position of the first func literal // in the node, if any. If a func literal appears, it usually marks the termination // of a basic block because the function body is itself a block. // Therefore we draw a line at the start of the body of the first function literal we find. // TODO: what if there's more than one? Probably doesn't matter much. func hasFuncLiteral(n ast.Node) (bool, token.Pos) { if n == nil { return false, 0 } var literal funcLitFinder ast.Walk(&literal, n) return literal.found(), token.Pos(literal) } // statementBoundary finds the location in s that terminates the current basic // block in the source. func (f *File) statementBoundary(s ast.Stmt) token.Pos { // Control flow statements are easy. switch s := s.(type) { case *ast.BlockStmt: // Treat blocks like basic blocks to avoid overlapping counters. return s.Lbrace case *ast.IfStmt: found, pos := hasFuncLiteral(s.Init) if found { return pos } found, pos = hasFuncLiteral(s.Cond) if found { return pos } return s.Body.Lbrace case *ast.ForStmt: found, pos := hasFuncLiteral(s.Init) if found { return pos } found, pos = hasFuncLiteral(s.Cond) if found { return pos } found, pos = hasFuncLiteral(s.Post) if found { return pos } return s.Body.Lbrace case *ast.LabeledStmt: return f.statementBoundary(s.Stmt) case *ast.RangeStmt: found, pos := hasFuncLiteral(s.X) if found { return pos } return s.Body.Lbrace case *ast.SwitchStmt: found, pos := hasFuncLiteral(s.Init) if found { return pos } found, pos = hasFuncLiteral(s.Tag) if found { return pos } return s.Body.Lbrace case *ast.SelectStmt: return s.Body.Lbrace case *ast.TypeSwitchStmt: found, pos := hasFuncLiteral(s.Init) if found { return pos } return s.Body.Lbrace } // If not a control flow statement, it is a declaration, expression, call, etc. and it may have a function literal. // If it does, that's tricky because we want to exclude the body of the function from this block. // Draw a line at the start of the body of the first function literal we find. // TODO: what if there's more than one? Probably doesn't matter much. found, pos := hasFuncLiteral(s) if found { return pos } return s.End() } // endsBasicSourceBlock reports whether s changes the flow of control: break, if, etc., // or if it's just problematic, for instance contains a function literal, which will complicate // accounting due to the block-within-an expression. func (f *File) endsBasicSourceBlock(s ast.Stmt) bool { switch s := s.(type) { case *ast.BlockStmt: // Treat blocks like basic blocks to avoid overlapping counters. return true case *ast.BranchStmt: return true case *ast.ForStmt: return true case *ast.IfStmt: return true case *ast.LabeledStmt: return f.endsBasicSourceBlock(s.Stmt) case *ast.RangeStmt: return true case *ast.SwitchStmt: return true case *ast.SelectStmt: return true case *ast.TypeSwitchStmt: return true case *ast.ExprStmt: // Calls to panic change the flow. // We really should verify that "panic" is the predefined function, // but without type checking we can't and the likelihood of it being // an actual problem is vanishingly small. if call, ok := s.X.(*ast.CallExpr); ok { if ident, ok := call.Fun.(*ast.Ident); ok && ident.Name == "panic" && len(call.Args) == 1 { return true } } } found, _ := hasFuncLiteral(s) return found } // funcLitFinder implements the ast.Visitor pattern to find the location of any // function literal in a subtree. type funcLitFinder token.Pos func (f *funcLitFinder) Visit(node ast.Node) (w ast.Visitor) { if f.found() { return nil // Prune search. } switch n := node.(type) { case *ast.FuncLit: *f = funcLitFinder(n.Body.Lbrace) return nil // Prune search. } return f } func (f *funcLitFinder) found() bool { return token.Pos(*f) != token.NoPos } // Sort interface for []block1; used for self-check in addVariables. type block1 struct { Block index int } type blockSlice []block1 func (b blockSlice) Len() int { return len(b) } func (b blockSlice) Less(i, j int) bool { return b[i].startByte < b[j].startByte } func (b blockSlice) Swap(i, j int) { b[i], b[j] = b[j], b[i] } // offset translates a token position into a 0-indexed byte offset. func (f *File) offset(pos token.Pos) int { return f.fset.Position(pos).Offset } // addVariables adds to the end of the file the declarations to set up the counter and position variables. func (f *File) addVariables(w io.Writer) { // Self-check: Verify that the instrumented basic blocks are disjoint. t := make([]block1, len(f.blocks)) for i := range f.blocks { t[i].Block = f.blocks[i] t[i].index = i } sort.Sort(blockSlice(t)) for i := 1; i < len(t); i++ { if t[i-1].endByte > t[i].startByte { fmt.Fprintf(os.Stderr, "cover: internal error: block %d overlaps block %d\n", t[i-1].index, t[i].index) // Note: error message is in byte positions, not token positions. fmt.Fprintf(os.Stderr, "\t%s:#%d,#%d %s:#%d,#%d\n", f.name, f.offset(t[i-1].startByte), f.offset(t[i-1].endByte), f.name, f.offset(t[i].startByte), f.offset(t[i].endByte)) } } // Declare the coverage struct as a package-level variable. fmt.Fprintf(w, "\nvar %s = struct {\n", *varVar) fmt.Fprintf(w, "\tCount [%d]uint32\n", len(f.blocks)) fmt.Fprintf(w, "\tPos [3 * %d]uint32\n", len(f.blocks)) fmt.Fprintf(w, "\tNumStmt [%d]uint16\n", len(f.blocks)) fmt.Fprintf(w, "} {\n") // Initialize the position array field. fmt.Fprintf(w, "\tPos: [3 * %d]uint32{\n", len(f.blocks)) // A nice long list of positions. Each position is encoded as follows to reduce size: // - 32-bit starting line number // - 32-bit ending line number // - (16 bit ending column number << 16) | (16-bit starting column number). for i, block := range f.blocks { start := f.fset.Position(block.startByte) end := f.fset.Position(block.endByte) fmt.Fprintf(w, "\t\t%d, %d, %#x, // [%d]\n", start.Line, end.Line, (end.Column&0xFFFF)<<16|(start.Column&0xFFFF), i) } // Close the position array. fmt.Fprintf(w, "\t},\n") // Initialize the position array field. fmt.Fprintf(w, "\tNumStmt: [%d]uint16{\n", len(f.blocks)) // A nice long list of statements-per-block, so we can give a conventional // valuation of "percent covered". To save space, it's a 16-bit number, so we // clamp it if it overflows - won't matter in practice. for i, block := range f.blocks { n := block.numStmt if n > 1<<16-1 { n = 1<<16 - 1 } fmt.Fprintf(w, "\t\t%d, // %d\n", n, i) } // Close the statements-per-block array. fmt.Fprintf(w, "\t},\n") // Close the struct initialization. fmt.Fprintf(w, "}\n") } golang-golang-x-tools-0.1.9+ds/cmd/cover/cover_test.go000066400000000000000000000052201417751550600226410ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // No testdata on Android. //go:build !android // +build !android package main_test import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" "golang.org/x/tools/internal/testenv" ) const ( // Data directory, also the package directory for the test. testdata = "testdata" ) var debug = false // Keeps the rewritten files around if set. // Run this shell script, but do it in Go so it can be run by "go test". // // replace the word LINE with the line number < testdata/test.go > testdata/test_line.go // go build -o ./testcover // ./testcover -mode=count -var=CoverTest -o ./testdata/test_cover.go testdata/test_line.go // go run ./testdata/main.go ./testdata/test.go // func TestCover(t *testing.T) { testenv.NeedsTool(t, "go") tmpdir, err := ioutil.TempDir("", "TestCover") if err != nil { t.Fatal(err) } defer func() { if debug { fmt.Printf("test files left in %s\n", tmpdir) } else { os.RemoveAll(tmpdir) } }() testcover := filepath.Join(tmpdir, "testcover.exe") testMain := filepath.Join(tmpdir, "main.go") testTest := filepath.Join(tmpdir, "test.go") coverInput := filepath.Join(tmpdir, "test_line.go") coverOutput := filepath.Join(tmpdir, "test_cover.go") for _, f := range []string{testMain, testTest} { data, err := ioutil.ReadFile(filepath.Join(testdata, filepath.Base(f))) if err != nil { t.Fatal(err) } if err := ioutil.WriteFile(f, data, 0644); err != nil { t.Fatal(err) } } // Read in the test file (testTest) and write it, with LINEs specified, to coverInput. file, err := ioutil.ReadFile(testTest) if err != nil { t.Fatal(err) } lines := bytes.Split(file, []byte("\n")) for i, line := range lines { lines[i] = bytes.Replace(line, []byte("LINE"), []byte(fmt.Sprint(i+1)), -1) } err = ioutil.WriteFile(coverInput, bytes.Join(lines, []byte("\n")), 0666) if err != nil { t.Fatal(err) } // go build -o testcover cmd := exec.Command("go", "build", "-o", testcover) run(cmd, t) // ./testcover -mode=count -var=coverTest -o ./testdata/test_cover.go testdata/test_line.go cmd = exec.Command(testcover, "-mode=count", "-var=coverTest", "-o", coverOutput, coverInput) run(cmd, t) // defer removal of ./testdata/test_cover.go if !debug { defer os.Remove(coverOutput) } // go run ./testdata/main.go ./testdata/test.go cmd = exec.Command("go", "run", testMain, coverOutput) run(cmd, t) } func run(c *exec.Cmd, t *testing.T) { c.Stdout = os.Stdout c.Stderr = os.Stderr err := c.Run() if err != nil { t.Fatal(err) } } golang-golang-x-tools-0.1.9+ds/cmd/cover/doc.go000066400000000000000000000017601417751550600212360ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. /* Cover is a program for analyzing the coverage profiles generated by 'go test -coverprofile=cover.out'. Deprecated: For Go releases 1.5 and later, this tool lives in the standard repository. The code here is not maintained. Cover is also used by 'go test -cover' to rewrite the source code with annotations to track which parts of each function are executed. It operates on one Go source file at a time, computing approximate basic block information by studying the source. It is thus more portable than binary-rewriting coverage tools, but also a little less capable. For instance, it does not probe inside && and || expressions, and can be mildly confused by single statements with multiple function literals. For usage information, please see: go help testflag go tool cover -help */ package main // import "golang.org/x/tools/cmd/cover" golang-golang-x-tools-0.1.9+ds/cmd/cover/func.go000066400000000000000000000106231417751550600214220ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This file implements the visitor that computes the (line, column)-(line-column) range for each function. package main import ( "bufio" "fmt" "go/ast" "go/build" "go/parser" "go/token" "os" "path/filepath" "text/tabwriter" "golang.org/x/tools/cover" ) // funcOutput takes two file names as arguments, a coverage profile to read as input and an output // file to write ("" means to write to standard output). The function reads the profile and produces // as output the coverage data broken down by function, like this: // // fmt/format.go:30: init 100.0% // fmt/format.go:57: clearflags 100.0% // ... // fmt/scan.go:1046: doScan 100.0% // fmt/scan.go:1075: advance 96.2% // fmt/scan.go:1119: doScanf 96.8% // total: (statements) 91.9% func funcOutput(profile, outputFile string) error { profiles, err := cover.ParseProfiles(profile) if err != nil { return err } var out *bufio.Writer if outputFile == "" { out = bufio.NewWriter(os.Stdout) } else { fd, err := os.Create(outputFile) if err != nil { return err } defer fd.Close() out = bufio.NewWriter(fd) } defer out.Flush() tabber := tabwriter.NewWriter(out, 1, 8, 1, '\t', 0) defer tabber.Flush() var total, covered int64 for _, profile := range profiles { fn := profile.FileName file, err := findFile(fn) if err != nil { return err } funcs, err := findFuncs(file) if err != nil { return err } // Now match up functions and profile blocks. for _, f := range funcs { c, t := f.coverage(profile) fmt.Fprintf(tabber, "%s:%d:\t%s\t%.1f%%\n", fn, f.startLine, f.name, 100.0*float64(c)/float64(t)) total += t covered += c } } fmt.Fprintf(tabber, "total:\t(statements)\t%.1f%%\n", 100.0*float64(covered)/float64(total)) return nil } // findFuncs parses the file and returns a slice of FuncExtent descriptors. func findFuncs(name string) ([]*FuncExtent, error) { fset := token.NewFileSet() parsedFile, err := parser.ParseFile(fset, name, nil, 0) if err != nil { return nil, err } visitor := &FuncVisitor{ fset: fset, name: name, astFile: parsedFile, } ast.Walk(visitor, visitor.astFile) return visitor.funcs, nil } // FuncExtent describes a function's extent in the source by file and position. type FuncExtent struct { name string startLine int startCol int endLine int endCol int } // FuncVisitor implements the visitor that builds the function position list for a file. type FuncVisitor struct { fset *token.FileSet name string // Name of file. astFile *ast.File funcs []*FuncExtent } // Visit implements the ast.Visitor interface. func (v *FuncVisitor) Visit(node ast.Node) ast.Visitor { switch n := node.(type) { case *ast.FuncDecl: start := v.fset.Position(n.Pos()) end := v.fset.Position(n.End()) fe := &FuncExtent{ name: n.Name.Name, startLine: start.Line, startCol: start.Column, endLine: end.Line, endCol: end.Column, } v.funcs = append(v.funcs, fe) } return v } // coverage returns the fraction of the statements in the function that were covered, as a numerator and denominator. func (f *FuncExtent) coverage(profile *cover.Profile) (num, den int64) { // We could avoid making this n^2 overall by doing a single scan and annotating the functions, // but the sizes of the data structures is never very large and the scan is almost instantaneous. var covered, total int64 // The blocks are sorted, so we can stop counting as soon as we reach the end of the relevant block. for _, b := range profile.Blocks { if b.StartLine > f.endLine || (b.StartLine == f.endLine && b.StartCol >= f.endCol) { // Past the end of the function. break } if b.EndLine < f.startLine || (b.EndLine == f.startLine && b.EndCol <= f.startCol) { // Before the beginning of the function continue } total += int64(b.NumStmt) if b.Count > 0 { covered += int64(b.NumStmt) } } if total == 0 { total = 1 // Avoid zero denominator. } return covered, total } // findFile finds the location of the named file in GOROOT, GOPATH etc. func findFile(file string) (string, error) { dir, file := filepath.Split(file) pkg, err := build.Import(dir, ".", build.FindOnly) if err != nil { return "", fmt.Errorf("can't find %q: %v", file, err) } return filepath.Join(pkg.Dir, file), nil } golang-golang-x-tools-0.1.9+ds/cmd/cover/html.go000066400000000000000000000143551417751550600214410ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package main import ( "bufio" "bytes" "fmt" exec "golang.org/x/sys/execabs" "html/template" "io" "io/ioutil" "math" "os" "path/filepath" "runtime" "golang.org/x/tools/cover" ) // htmlOutput reads the profile data from profile and generates an HTML // coverage report, writing it to outfile. If outfile is empty, // it writes the report to a temporary file and opens it in a web browser. func htmlOutput(profile, outfile string) error { profiles, err := cover.ParseProfiles(profile) if err != nil { return err } var d templateData for _, profile := range profiles { fn := profile.FileName if profile.Mode == "set" { d.Set = true } file, err := findFile(fn) if err != nil { return err } src, err := ioutil.ReadFile(file) if err != nil { return fmt.Errorf("can't read %q: %v", fn, err) } var buf bytes.Buffer err = htmlGen(&buf, src, profile.Boundaries(src)) if err != nil { return err } d.Files = append(d.Files, &templateFile{ Name: fn, Body: template.HTML(buf.String()), Coverage: percentCovered(profile), }) } var out *os.File if outfile == "" { var dir string dir, err = ioutil.TempDir("", "cover") if err != nil { return err } out, err = os.Create(filepath.Join(dir, "coverage.html")) } else { out, err = os.Create(outfile) } if err != nil { return err } err = htmlTemplate.Execute(out, d) if err == nil { err = out.Close() } if err != nil { return err } if outfile == "" { if !startBrowser("file://" + out.Name()) { fmt.Fprintf(os.Stderr, "HTML output written to %s\n", out.Name()) } } return nil } // percentCovered returns, as a percentage, the fraction of the statements in // the profile covered by the test run. // In effect, it reports the coverage of a given source file. func percentCovered(p *cover.Profile) float64 { var total, covered int64 for _, b := range p.Blocks { total += int64(b.NumStmt) if b.Count > 0 { covered += int64(b.NumStmt) } } if total == 0 { return 0 } return float64(covered) / float64(total) * 100 } // htmlGen generates an HTML coverage report with the provided filename, // source code, and tokens, and writes it to the given Writer. func htmlGen(w io.Writer, src []byte, boundaries []cover.Boundary) error { dst := bufio.NewWriter(w) for i := range src { for len(boundaries) > 0 && boundaries[0].Offset == i { b := boundaries[0] if b.Start { n := 0 if b.Count > 0 { n = int(math.Floor(b.Norm*9)) + 1 } fmt.Fprintf(dst, ``, n, b.Count) } else { dst.WriteString("") } boundaries = boundaries[1:] } switch b := src[i]; b { case '>': dst.WriteString(">") case '<': dst.WriteString("<") case '&': dst.WriteString("&") case '\t': dst.WriteString(" ") default: dst.WriteByte(b) } } return dst.Flush() } // startBrowser tries to open the URL in a browser // and reports whether it succeeds. func startBrowser(url string) bool { // try to start the browser var args []string switch runtime.GOOS { case "darwin": args = []string{"open"} case "windows": args = []string{"cmd", "/c", "start"} default: args = []string{"xdg-open"} } cmd := exec.Command(args[0], append(args[1:], url)...) return cmd.Start() == nil } // rgb returns an rgb value for the specified coverage value // between 0 (no coverage) and 10 (max coverage). func rgb(n int) string { if n == 0 { return "rgb(192, 0, 0)" // Red } // Gradient from gray to green. r := 128 - 12*(n-1) g := 128 + 12*(n-1) b := 128 + 3*(n-1) return fmt.Sprintf("rgb(%v, %v, %v)", r, g, b) } // colors generates the CSS rules for coverage colors. func colors() template.CSS { var buf bytes.Buffer for i := 0; i < 11; i++ { fmt.Fprintf(&buf, ".cov%v { color: %v }\n", i, rgb(i)) } return template.CSS(buf.String()) } var htmlTemplate = template.Must(template.New("html").Funcs(template.FuncMap{ "colors": colors, }).Parse(tmplHTML)) type templateData struct { Files []*templateFile Set bool } type templateFile struct { Name string Body template.HTML Coverage float64 } const tmplHTML = `
not tracked {{if .Set}} not covered covered {{else}} no coverage low coverage * * * * * * * * high coverage {{end}}
{{range $i, $f := .Files}}
{{$f.Body}}
{{end}}
` golang-golang-x-tools-0.1.9+ds/cmd/cover/testdata/000077500000000000000000000000001417751550600217475ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/cover/testdata/main.go000066400000000000000000000054311417751550600232250ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // Test runner for coverage test. This file is not coverage-annotated; test.go is. // It knows the coverage counter is called "coverTest". package main import ( "fmt" "os" ) func main() { testAll() verify() } type block struct { count uint32 line uint32 } var counters = make(map[block]bool) // check records the location and expected value for a counter. func check(line, count uint32) { b := block{ count, line, } counters[b] = true } // checkVal is a version of check that returns its extra argument, // so it can be used in conditionals. func checkVal(line, count uint32, val int) int { b := block{ count, line, } counters[b] = true return val } var PASS = true // verify checks the expected counts against the actual. It runs after the test has completed. func verify() { for b := range counters { got, index := count(b.line) if b.count == anything && got != 0 { got = anything } if got != b.count { fmt.Fprintf(os.Stderr, "test_go:%d expected count %d got %d [counter %d]\n", b.line, b.count, got, index) PASS = false } } verifyPanic() if !PASS { fmt.Fprintf(os.Stderr, "FAIL\n") os.Exit(2) } } // verifyPanic is a special check for the known counter that should be // after the panic call in testPanic. func verifyPanic() { if coverTest.Count[panicIndex-1] != 1 { // Sanity check for test before panic. fmt.Fprintf(os.Stderr, "bad before panic") PASS = false } if coverTest.Count[panicIndex] != 0 { fmt.Fprintf(os.Stderr, "bad at panic: %d should be 0\n", coverTest.Count[panicIndex]) PASS = false } if coverTest.Count[panicIndex+1] != 1 { fmt.Fprintf(os.Stderr, "bad after panic") PASS = false } } // count returns the count and index for the counter at the specified line. func count(line uint32) (uint32, int) { // Linear search is fine. Choose perfect fit over approximate. // We can have a closing brace for a range on the same line as a condition for an "else if" // and we don't want that brace to steal the count for the condition on the "if". // Therefore we test for a perfect (lo==line && hi==line) match, but if we can't // find that we take the first imperfect match. index := -1 indexLo := uint32(1e9) for i := range coverTest.Count { lo, hi := coverTest.Pos[3*i], coverTest.Pos[3*i+1] if lo == line && line == hi { return coverTest.Count[i], i } // Choose the earliest match (the counters are in unpredictable order). if lo <= line && line <= hi && indexLo > lo { index = i indexLo = lo } } if index == -1 { fmt.Fprintln(os.Stderr, "cover_test: no counter for line", line) PASS = false return 0, 0 } return coverTest.Count[index], index } golang-golang-x-tools-0.1.9+ds/cmd/cover/testdata/test.go000066400000000000000000000074751417751550600232720ustar00rootroot00000000000000// Copyright 2013 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. // This program is processed by the cover command, and then testAll is called. // The test driver in main.go can then compare the coverage statistics with expectation. // The word LINE is replaced by the line number in this file. When the file is executed, // the coverage processing has changed the line numbers, so we can't use runtime.Caller. package main const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" func testAll() { testSimple() testBlockRun() testIf() testFor() testRange() testSwitch() testTypeSwitch() testSelect1() testSelect2() testPanic() testEmptySwitches() } // The indexes of the counters in testPanic are known to main.go const panicIndex = 3 // This test appears first because the index of its counters is known to main.go func testPanic() { defer func() { recover() }() check(LINE, 1) panic("should not get next line") check(LINE, 0) // this is GoCover.Count[panicIndex] // The next counter is in testSimple and it will be non-zero. // If the panic above does not trigger a counter, the test will fail // because GoCover.Count[panicIndex] will be the one in testSimple. } func testSimple() { check(LINE, 1) } func testIf() { if true { check(LINE, 1) } else { check(LINE, 0) } if false { check(LINE, 0) } else { check(LINE, 1) } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 2 { check(LINE, 3) } if checkVal(LINE, 3, i) <= 1 { check(LINE, 2) } if checkVal(LINE, 3, i) <= 0 { check(LINE, 1) } } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 1 { check(LINE, 2) } else { check(LINE, 1) } } for i := 0; i < 3; i++ { if checkVal(LINE, 3, i) <= 0 { check(LINE, 1) } else if checkVal(LINE, 2, i) <= 1 { check(LINE, 1) } else if checkVal(LINE, 1, i) <= 2 { check(LINE, 1) } else if checkVal(LINE, 0, i) <= 3 { check(LINE, 0) } } if func(a, b int) bool { return a < b }(3, 4) { check(LINE, 1) } } func testFor() { for i := 0; i < 10; func() { i++; check(LINE, 10) }() { check(LINE, 10) } } func testRange() { for _, f := range []func(){ func() { check(LINE, 1) }, } { f() check(LINE, 1) } } func testBlockRun() { check(LINE, 1) { check(LINE, 1) } { check(LINE, 1) } check(LINE, 1) { check(LINE, 1) } { check(LINE, 1) } check(LINE, 1) } func testSwitch() { for i := 0; i < 5; func() { i++; check(LINE, 5) }() { switch i { case 0: check(LINE, 1) case 1: check(LINE, 1) case 2: check(LINE, 1) default: check(LINE, 2) } } } func testTypeSwitch() { var x = []interface{}{1, 2.0, "hi"} for _, v := range x { switch func() { check(LINE, 3) }(); v.(type) { case int: check(LINE, 1) case float64: check(LINE, 1) case string: check(LINE, 1) case complex128: check(LINE, 0) default: check(LINE, 0) } } } func testSelect1() { c := make(chan int) go func() { for i := 0; i < 1000; i++ { c <- i } }() for { select { case <-c: check(LINE, anything) case <-c: check(LINE, anything) default: check(LINE, 1) return } } } func testSelect2() { c1 := make(chan int, 1000) c2 := make(chan int, 1000) for i := 0; i < 1000; i++ { c1 <- i c2 <- i } for { select { case <-c1: check(LINE, 1000) case <-c2: check(LINE, 1000) default: check(LINE, 1) return } } } // Empty control statements created syntax errors. This function // is here just to be sure that those are handled correctly now. func testEmptySwitches() { check(LINE, 1) switch 3 { } check(LINE, 1) switch i := (interface{})(3).(int); i { } check(LINE, 1) c := make(chan int) go func() { check(LINE, 1) c <- 1 select {} }() <-c check(LINE, 1) } golang-golang-x-tools-0.1.9+ds/cmd/digraph/000077500000000000000000000000001417751550600204365ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/digraph/digraph.go000066400000000000000000000352041417751550600224070ustar00rootroot00000000000000// 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 support 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 nodes strongly connected to the specified one focus the subgraph containing all directed paths that pass through the specified node 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: Using digraph with existing Go tools: $ go mod graph | digraph nodes # Operate on the Go module graph. $ go list -m all | digraph nodes # Operate on the Go package graph. Show the transitive closure of imports of the digraph tool itself: $ go list -f '{{.ImportPath}} {{join .Imports " "}}' ... | digraph forward golang.org/x/tools/cmd/digraph Show which clothes (see above) must be donned before a jacket: $ digraph reverse jacket */ 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" "errors" "flag" "fmt" "io" "os" "sort" "strconv" "strings" "unicode" "unicode/utf8" ) func usage() { fmt.Fprintf(os.Stderr, `Usage: your-application | digraph [command] The support 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 nodes strongly connected to the specified one focus the subgraph containing all directed paths that pass through the specified node `) os.Exit(2) } 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 // TODO(deklerk): change bool to struct to reduce memory footprint 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) 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) 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 { type edge struct{ from, to string } seen := make(nodeset) var dfs func(path []edge, from string) bool dfs = func(path []edge, from string) bool { if !seen[from] { seen[from] = true if from == to { // fmt.Println(path, len(path), cap(path)) // Print and unwind. for _, e := range path { fmt.Fprintln(stdout, e.from+" "+e.to) } return true } for e := range g[from] { if dfs(append(path, edge{from: from, to: e}), e) { return true } } } return false } maxEdgesInGraph := len(g) * (len(g) - 1) if !dfs(make([]edge, 0, maxEdgesInGraph), from) { return fmt.Errorf("no path from %q to %q", from, to) } return nil } func parse(rd io.Reader) (graph, error) { g := make(graph) var linenum int in := bufio.NewScanner(rd) for in.Scan() { linenum++ // Split into words, honoring double-quotes per Go spec. words, err := split(in.Text()) if err != nil { return nil, fmt.Errorf("at line %d: %v", linenum, err) } if len(words) > 0 { g.addEdges(words[0], words[1:]...) } } if err := in.Err(); err != nil { return nil, err } return g, nil } // Overridable for testing purposes. 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") } nodes := make(nodeset) for node := range g { nodes[node] = true } nodes.sort().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") } for _, scc := range g.sccs() { scc.sort().println(" ") } 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")) 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.1.9+ds/cmd/digraph/digraph_test.go000066400000000000000000000201271417751550600234440ustar00rootroot00000000000000// 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" "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 ` 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, "a\nb\nc d\n"}, {"scc", g2, "scc", []string{"d"}, "c\nd\n"}, {"succs", g2, "succs", []string{"a"}, "b\nc\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 somepath (it's nondeterministic). // - 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", }, } { 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) } }) } } golang-golang-x-tools-0.1.9+ds/cmd/eg/000077500000000000000000000000001417751550600174135ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/eg/eg.go000066400000000000000000000121421417751550600203350ustar00rootroot00000000000000// 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" "io/ioutil" "os" "path/filepath" "strings" exec "golang.org/x/sys/execabs" "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 := ioutil.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.1.9+ds/cmd/file2fuzz/000077500000000000000000000000001417751550600207405ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/file2fuzz/main.go000066400000000000000000000066011417751550600222160ustar00rootroot00000000000000// 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 defualt 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" "io/ioutil" "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 := ioutil.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 ioutil.WriteFile(outputArg, b, 0666) } } } } for _, f := range input { b, err := ioutil.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.1.9+ds/cmd/file2fuzz/main_test.go000066400000000000000000000126121417751550600232540ustar00rootroot00000000000000// 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 ( "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "runtime" "strings" "sync" "testing" ) // The setup for this test is mostly cribbed from x/exp/txtar. var buildBin struct { once sync.Once name string err error } func binPath(t *testing.T) string { t.Helper() if _, err := exec.LookPath("go"); err != nil { t.Skipf("cannot build file2fuzz binary: %v", err) } buildBin.once.Do(func() { exe, err := ioutil.TempFile("", "file2fuzz-*.exe") if err != nil { buildBin.err = err return } exe.Close() buildBin.name = exe.Name() cmd := exec.Command("go", "build", "-o", buildBin.name, ".") out, err := cmd.CombinedOutput() if err != nil { buildBin.err = fmt.Errorf("%s: %v\n%s", strings.Join(cmd.Args, " "), err, out) } }) if buildBin.err != nil { if runtime.GOOS == "android" { t.Skipf("skipping test after failing to build file2fuzz binary: go_android_exec may have failed to copy needed dependencies (see https://golang.org/issue/37088)") } t.Fatal(buildBin.err) } return buildBin.name } func TestMain(m *testing.M) { os.Exit(m.Run()) if buildBin.name != "" { os.Remove(buildBin.name) } } func file2fuzz(t *testing.T, dir string, args []string, stdin string) (string, bool) { t.Helper() cmd := exec.Command(binPath(t), args...) cmd.Dir = dir 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 := ioutil.TempDir(os.TempDir(), "file2fuzz") if err != nil { t.Fatalf("ioutil.TempDir 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 := ioutil.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 := ioutil.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.1.9+ds/cmd/fiximports/000077500000000000000000000000001417751550600212245ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/main.go000066400000000000000000000353571417751550600225140ustar00rootroot00000000000000// 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/build" "go/format" "go/parser" "go/token" exec "golang.org/x/sys/execabs" "io" "io/ioutil" "log" "os" "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 = ioutil.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[*build.Package]bool) // addEdge adds an edge to the import graph. addEdge := func(from *build.Package, to string) { if to == "C" || to == "unsafe" { return // fake } pkgs := importedBy[to] if pkgs == nil { pkgs = make(map[*build.Package]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.Package.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.Package, imp) } for _, imp := range p.TestImports { addEdge(&p.Package, imp) } for _, imp := range p.XTestImports { addEdge(&p.Package, imp) } // Does package have an explicit import comment? if p.ImportComment != "" { if p.ImportComment != p.ImportPath { canonical[p.ImportPath] = canonicalName{ path: p.Package.ImportComment, name: p.Package.Name, } } } 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[*build.Package]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 *build.Package, 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 } // rewrite 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 is a copy of cmd/go/list.Package. // It has more fields than build.Package and we need some of them. type listPackage struct { build.Package 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.1.9+ds/cmd/fiximports/main_test.go000066400000000000000000000157441417751550600235510ustar00rootroot00000000000000// 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) { 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.1.9+ds/cmd/fiximports/testdata/000077500000000000000000000000001417751550600230355ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/000077500000000000000000000000001417751550600236245ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/000077500000000000000000000000001417751550600253635ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/banana/000077500000000000000000000000001417751550600266035ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/banana/banana.go000066400000000000000000000001261417751550600303510ustar00rootroot00000000000000package banana import ( _ "old.com/one" _ "titanic.biz/bar" _ "titanic.biz/foo" ) golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/orange/000077500000000000000000000000001417751550600266365ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/orange/orange.go000066400000000000000000000000511417751550600304340ustar00rootroot00000000000000package orange import _ "fruit.io/pear" golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/pear/000077500000000000000000000000001417751550600263125ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/fruit.io/pear/pear.go000066400000000000000000000000511417751550600275640ustar00rootroot00000000000000package pear import _ "fruit.io/banana" golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/new.com/000077500000000000000000000000001417751550600251725ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/new.com/one/000077500000000000000000000000001417751550600257535ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/new.com/one/one.go000066400000000000000000000000441417751550600270610ustar00rootroot00000000000000package one // import "new.com/one" golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/old.com/000077500000000000000000000000001417751550600251575ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/old.com/bad/000077500000000000000000000000001417751550600257055ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/old.com/bad/bad.go000066400000000000000000000001611417751550600267600ustar00rootroot00000000000000// 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.1.9+ds/cmd/fiximports/testdata/src/old.com/one/000077500000000000000000000000001417751550600257405ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/old.com/one/one.go000066400000000000000000000000441417751550600270460ustar00rootroot00000000000000package one // import "new.com/one" golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/titanic.biz/000077500000000000000000000000001417751550600260425ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/titanic.biz/bar/000077500000000000000000000000001417751550600266065ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/titanic.biz/bar/bar.go000066400000000000000000000001161417751550600276770ustar00rootroot00000000000000// This package is moving to new.com too. package bar // import "new.com/bar" golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/titanic.biz/foo/000077500000000000000000000000001417751550600266255ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/fiximports/testdata/src/titanic.biz/foo/foo.go000066400000000000000000000000641417751550600277370ustar00rootroot00000000000000// This package hasn't jumped ship yet. package foo golang-golang-x-tools-0.1.9+ds/cmd/getgo/000077500000000000000000000000001417751550600201255ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/getgo/.dockerignore000066400000000000000000000000601417751550600225750ustar00rootroot00000000000000.git .dockerignore LICENSE README.md .gitignore golang-golang-x-tools-0.1.9+ds/cmd/getgo/.gitignore000066400000000000000000000000261417751550600221130ustar00rootroot00000000000000build testgetgo getgo golang-golang-x-tools-0.1.9+ds/cmd/getgo/Dockerfile000066400000000000000000000006541417751550600221240ustar00rootroot00000000000000FROM golang:latest ENV SHELL /bin/bash ENV HOME /root WORKDIR $HOME COPY . /go/src/golang.org/x/tools/cmd/getgo RUN ( \ cd /go/src/golang.org/x/tools/cmd/getgo \ && go build \ && mv getgo /usr/local/bin/getgo \ ) # undo the adding of GOPATH to env for testing ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin ENV GOPATH "" # delete /go and /usr/local/go for testing RUN rm -rf /go /usr/local/go golang-golang-x-tools-0.1.9+ds/cmd/getgo/LICENSE000066400000000000000000000027071417751550600211400ustar00rootroot00000000000000Copyright (c) 2017 The Go Authors. All rights reserved. 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 Inc. 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.1.9+ds/cmd/getgo/README.md000066400000000000000000000047341417751550600214140ustar00rootroot00000000000000# getgo A proof-of-concept command-line installer for Go. This installer is designed to both install Go as well as do the initial configuration of setting up the right environment variables and paths. It will install the Go distribution (tools & stdlib) to "/.go" inside your home directory by default. It will setup "$HOME/go" as your GOPATH. This is where third party libraries and apps will be installed as well as where you will write your Go code. If Go is already installed via this installer it will upgrade it to the latest version of Go. Currently the installer supports Windows, \*nix and macOS on x86 & x64. It supports Bash and Zsh on all of these platforms as well as powershell & cmd.exe on Windows. ## Usage Windows Powershell/cmd.exe: `(New-Object System.Net.WebClient).DownloadFile('https://get.golang.org/installer.exe', 'installer.exe'); Start-Process -Wait -NonewWindow installer.exe; Remove-Item installer.exe` Shell (Linux/macOS/Windows): `curl -LO https://get.golang.org/$(uname)/go_installer && chmod +x go_installer && ./go_installer && rm go_installer` ## To Do * Check if Go is already installed (via a different method) and update it in place or at least notify the user * Lots of testing. It's only had limited testing so far. * Add support for additional shells. ## Development instructions ### Testing There are integration tests in [`main_test.go`](main_test.go). Please add more tests there. #### On unix/linux with the Dockerfile The Dockerfile automatically builds the binary, moves it to `/usr/local/bin/getgo` and then unsets `$GOPATH` and removes all `$GOPATH` from `$PATH`. ```bash $ docker build --rm --force-rm -t getgo . ... $ docker run --rm -it getgo bash root@78425260fad0:~# getgo -v Welcome to the Go installer! Downloading Go version go1.8.3 to /usr/local/go This may take a bit of time... Adding "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin" to /root/.bashrc Downloaded! Setting up GOPATH Adding "export GOPATH=/root/go" to /root/.bashrc Adding "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin" to /root/.bashrc GOPATH has been setup! root@78425260fad0:~# which go /usr/local/go/bin/go root@78425260fad0:~# echo $GOPATH /root/go root@78425260fad0:~# echo $PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/go/bin:/root/go/bin ``` ## Release instructions To upload a new release of getgo, run `./make.bash && ./upload.bash`. golang-golang-x-tools-0.1.9+ds/cmd/getgo/download.go000066400000000000000000000077501417751550600222740ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "archive/tar" "archive/zip" "compress/gzip" "crypto/sha256" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "os" "path/filepath" "strings" ) const ( downloadURLPrefix = "https://dl.google.com/go" ) // downloadGoVersion downloads and upacks the specific go version to dest/go. func downloadGoVersion(version, ops, arch, dest string) error { suffix := "tar.gz" if ops == "windows" { suffix = "zip" } uri := fmt.Sprintf("%s/%s.%s-%s.%s", downloadURLPrefix, version, ops, arch, suffix) verbosef("Downloading %s", uri) req, err := http.NewRequest("GET", uri, nil) if err != nil { return err } req.Header.Add("User-Agent", fmt.Sprintf("golang.org-getgo/%s", version)) resp, err := http.DefaultClient.Do(req) if err != nil { return fmt.Errorf("Downloading Go from %s failed: %v", uri, err) } if resp.StatusCode > 299 { return fmt.Errorf("Downloading Go from %s failed with HTTP status %s", uri, resp.Status) } defer resp.Body.Close() tmpf, err := ioutil.TempFile("", "go") if err != nil { return err } defer os.Remove(tmpf.Name()) h := sha256.New() w := io.MultiWriter(tmpf, h) if _, err := io.Copy(w, resp.Body); err != nil { return err } verbosef("Downloading SHA %s.sha256", uri) sresp, err := http.Get(uri + ".sha256") if err != nil { return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed: %v", uri, err) } defer sresp.Body.Close() if sresp.StatusCode > 299 { return fmt.Errorf("Downloading Go sha256 from %s.sha256 failed with HTTP status %s", uri, sresp.Status) } shasum, err := ioutil.ReadAll(sresp.Body) if err != nil { return err } // Check the shasum. sum := fmt.Sprintf("%x", h.Sum(nil)) if sum != string(shasum) { return fmt.Errorf("Shasum mismatch %s vs. %s", sum, string(shasum)) } unpackFunc := unpackTar if ops == "windows" { unpackFunc = unpackZip } if err := unpackFunc(tmpf.Name(), dest); err != nil { return fmt.Errorf("Unpacking Go to %s failed: %v", dest, err) } return nil } func unpack(dest, name string, fi os.FileInfo, r io.Reader) error { if strings.HasPrefix(name, "go/") { name = name[len("go/"):] } path := filepath.Join(dest, name) if fi.IsDir() { return os.MkdirAll(path, fi.Mode()) } f, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode()) if err != nil { return err } defer f.Close() _, err = io.Copy(f, r) return err } func unpackTar(src, dest string) error { r, err := os.Open(src) if err != nil { return err } defer r.Close() archive, err := gzip.NewReader(r) if err != nil { return err } defer archive.Close() tarReader := tar.NewReader(archive) for { header, err := tarReader.Next() if err == io.EOF { break } else if err != nil { return err } if err := unpack(dest, header.Name, header.FileInfo(), tarReader); err != nil { return err } } return nil } func unpackZip(src, dest string) error { zr, err := zip.OpenReader(src) if err != nil { return err } for _, f := range zr.File { fr, err := f.Open() if err != nil { return err } if err := unpack(dest, f.Name, f.FileInfo(), fr); err != nil { return err } fr.Close() } return nil } func getLatestGoVersion() (string, error) { resp, err := http.Get("https://golang.org/dl/?mode=json") if err != nil { return "", fmt.Errorf("Getting current Go version failed: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { b, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 1024)) return "", fmt.Errorf("Could not get current Go release: HTTP %d: %q", resp.StatusCode, b) } var releases []struct { Version string } err = json.NewDecoder(resp.Body).Decode(&releases) if err != nil { return "", err } if len(releases) < 1 { return "", fmt.Errorf("Could not get at least one Go release") } return releases[0].Version, nil } golang-golang-x-tools-0.1.9+ds/cmd/getgo/download_test.go000066400000000000000000000014231417751550600233220ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "io/ioutil" "os" "path/filepath" "testing" ) func TestDownloadGoVersion(t *testing.T) { if testing.Short() { t.Skipf("Skipping download in short mode") } tmpd, err := ioutil.TempDir("", "go") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpd) if err := downloadGoVersion("go1.8.1", "linux", "amd64", filepath.Join(tmpd, "go")); err != nil { t.Fatal(err) } // Ensure the VERSION file exists. vf := filepath.Join(tmpd, "go", "VERSION") if _, err := os.Stat(vf); os.IsNotExist(err) { t.Fatalf("file %s does not exist and should", vf) } } golang-golang-x-tools-0.1.9+ds/cmd/getgo/main.go000066400000000000000000000046051417751550600214050ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 // The getgo command installs Go to the user's system. package main import ( "bufio" "context" "errors" "flag" "fmt" exec "golang.org/x/sys/execabs" "os" "strings" ) var ( interactive = flag.Bool("i", false, "Interactive mode, prompt for inputs.") verbose = flag.Bool("v", false, "Verbose.") setupOnly = flag.Bool("skip-dl", false, "Don't download - only set up environment variables") goVersion = flag.String("version", "", `Version of Go to install (e.g. "1.8.3"). If empty, uses the latest version.`) version = "devel" ) var errExitCleanly error = errors.New("exit cleanly sentinel value") func main() { flag.Parse() if *goVersion != "" && !strings.HasPrefix(*goVersion, "go") { *goVersion = "go" + *goVersion } ctx := context.Background() verbosef("version " + version) runStep := func(s step) { err := s(ctx) if err == errExitCleanly { os.Exit(0) } if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(2) } } if !*setupOnly { runStep(welcome) runStep(checkOthers) runStep(chooseVersion) runStep(downloadGo) } runStep(setupGOPATH) } func verbosef(format string, v ...interface{}) { if !*verbose { return } fmt.Printf(format+"\n", v...) } func prompt(ctx context.Context, query, defaultAnswer string) (string, error) { if !*interactive { return defaultAnswer, nil } fmt.Printf("%s [%s]: ", query, defaultAnswer) type result struct { answer string err error } ch := make(chan result, 1) go func() { s := bufio.NewScanner(os.Stdin) if !s.Scan() { ch <- result{"", s.Err()} return } answer := s.Text() if answer == "" { answer = defaultAnswer } ch <- result{answer, nil} }() select { case r := <-ch: return r.answer, r.err case <-ctx.Done(): return "", ctx.Err() } } func runCommand(ctx context.Context, prog string, args ...string) ([]byte, error) { verbosef("Running command: %s %v", prog, args) cmd := exec.CommandContext(ctx, prog, args...) out, err := cmd.CombinedOutput() if err != nil { return nil, fmt.Errorf("running cmd '%s %s' failed: %s err: %v", prog, strings.Join(args, " "), string(out), err) } if out != nil && err == nil && len(out) != 0 { verbosef("%s", out) } return out, nil } golang-golang-x-tools-0.1.9+ds/cmd/getgo/main_test.go000066400000000000000000000065521417751550600224470ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "runtime" "testing" ) const ( testbin = "testgetgo" ) var ( exeSuffix string // ".exe" on Windows ) func init() { if runtime.GOOS == "windows" { exeSuffix = ".exe" } } // TestMain creates a getgo command for testing purposes and // deletes it after the tests have been run. func TestMain(m *testing.M) { if os.Getenv("GOGET_INTEGRATION") == "" { fmt.Fprintln(os.Stderr, "main_test: Skipping integration tests with GOGET_INTEGRATION unset") return } args := []string{"build", "-tags", testbin, "-o", testbin + exeSuffix} out, err := exec.Command("go", args...).CombinedOutput() if err != nil { fmt.Fprintf(os.Stderr, "building %s failed: %v\n%s", testbin, err, out) os.Exit(2) } // Don't let these environment variables confuse the test. os.Unsetenv("GOBIN") os.Unsetenv("GOPATH") os.Unsetenv("GIT_ALLOW_PROTOCOL") os.Unsetenv("PATH") r := m.Run() os.Remove(testbin + exeSuffix) os.Exit(r) } func createTmpHome(t *testing.T) string { tmpd, err := ioutil.TempDir("", "testgetgo") if err != nil { t.Fatalf("creating test tempdir failed: %v", err) } os.Setenv("HOME", tmpd) return tmpd } // doRun runs the test getgo command, recording stdout and stderr and // returning exit status. func doRun(t *testing.T, args ...string) error { var stdout, stderr bytes.Buffer t.Logf("running %s %v", testbin, args) cmd := exec.Command("./"+testbin+exeSuffix, args...) cmd.Stdout = &stdout cmd.Stderr = &stderr cmd.Env = os.Environ() status := cmd.Run() if stdout.Len() > 0 { t.Log("standard output:") t.Log(stdout.String()) } if stderr.Len() > 0 { t.Log("standard error:") t.Log(stderr.String()) } return status } func TestCommandVerbose(t *testing.T) { tmpd := createTmpHome(t) defer os.RemoveAll(tmpd) err := doRun(t, "-v") if err != nil { t.Fatal(err) } // make sure things are in path shellConfig, err := shellConfigFile() if err != nil { t.Fatal(err) } b, err := ioutil.ReadFile(shellConfig) if err != nil { t.Fatal(err) } home, err := getHomeDir() if err != nil { t.Fatal(err) } expected := fmt.Sprintf(` export PATH=$PATH:%s/.go/bin export GOPATH=%s/go export PATH=$PATH:%s/go/bin `, home, home, home) if string(b) != expected { t.Fatalf("%s expected %q, got %q", shellConfig, expected, string(b)) } } func TestCommandPathExists(t *testing.T) { tmpd := createTmpHome(t) defer os.RemoveAll(tmpd) // run once err := doRun(t, "-skip-dl") if err != nil { t.Fatal(err) } // make sure things are in path shellConfig, err := shellConfigFile() if err != nil { t.Fatal(err) } b, err := ioutil.ReadFile(shellConfig) if err != nil { t.Fatal(err) } home, err := getHomeDir() if err != nil { t.Fatal(err) } expected := fmt.Sprintf(` export GOPATH=%s/go export PATH=$PATH:%s/go/bin `, home, home) if string(b) != expected { t.Fatalf("%s expected %q, got %q", shellConfig, expected, string(b)) } // run twice if err := doRun(t, "-skip-dl"); err != nil { t.Fatal(err) } b, err = ioutil.ReadFile(shellConfig) if err != nil { t.Fatal(err) } if string(b) != expected { t.Fatalf("%s expected %q, got %q", shellConfig, expected, string(b)) } } golang-golang-x-tools-0.1.9+ds/cmd/getgo/make.bash000077500000000000000000000007451417751550600217120ustar00rootroot00000000000000#!/bin/bash # 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. set -e -o -x LDFLAGS="-X main.version=$(git describe --always --dirty='*')" GOOS=windows GOARCH=386 go build -o build/installer.exe -ldflags="$LDFLAGS" GOOS=linux GOARCH=386 go build -o build/installer_linux -ldflags="$LDFLAGS" GOOS=darwin GOARCH=386 go build -o build/installer_darwin -ldflags="$LDFLAGS" golang-golang-x-tools-0.1.9+ds/cmd/getgo/path.go000066400000000000000000000061251417751550600214140ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "bufio" "context" "fmt" "os" "os/user" "path/filepath" "runtime" "strings" ) const ( bashConfig = ".bash_profile" zshConfig = ".zshrc" ) // appendToPATH adds the given path to the PATH environment variable and // persists it for future sessions. func appendToPATH(value string) error { if isInPATH(value) { return nil } return persistEnvVar("PATH", pathVar+envSeparator+value) } func isInPATH(dir string) bool { p := os.Getenv("PATH") paths := strings.Split(p, envSeparator) for _, d := range paths { if d == dir { return true } } return false } func getHomeDir() (string, error) { home := os.Getenv(homeKey) if home != "" { return home, nil } u, err := user.Current() if err != nil { return "", err } return u.HomeDir, nil } func checkStringExistsFile(filename, value string) (bool, error) { file, err := os.OpenFile(filename, os.O_RDONLY, 0600) if err != nil { if os.IsNotExist(err) { return false, nil } return false, err } defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { line := scanner.Text() if line == value { return true, nil } } return false, scanner.Err() } func appendToFile(filename, value string) error { verbosef("Adding %q to %s", value, filename) ok, err := checkStringExistsFile(filename, value) if err != nil { return err } if ok { // Nothing to do. return nil } f, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) if err != nil { return err } defer f.Close() _, err = f.WriteString(lineEnding + value + lineEnding) return err } func isShell(name string) bool { return strings.Contains(currentShell(), name) } // persistEnvVarWindows sets an environment variable in the Windows // registry. func persistEnvVarWindows(name, value string) error { _, err := runCommand(context.Background(), "powershell", "-command", fmt.Sprintf(`[Environment]::SetEnvironmentVariable("%s", "%s", "User")`, name, value)) return err } func persistEnvVar(name, value string) error { if runtime.GOOS == "windows" { if err := persistEnvVarWindows(name, value); err != nil { return err } if isShell("cmd.exe") || isShell("powershell.exe") { return os.Setenv(strings.ToUpper(name), value) } // User is in bash, zsh, etc. // Also set the environment variable in their shell config. } rc, err := shellConfigFile() if err != nil { return err } line := fmt.Sprintf("export %s=%s", strings.ToUpper(name), value) if err := appendToFile(rc, line); err != nil { return err } return os.Setenv(strings.ToUpper(name), value) } func shellConfigFile() (string, error) { home, err := getHomeDir() if err != nil { return "", err } switch { case isShell("bash"): return filepath.Join(home, bashConfig), nil case isShell("zsh"): return filepath.Join(home, zshConfig), nil default: return "", fmt.Errorf("%q is not a supported shell", currentShell()) } } golang-golang-x-tools-0.1.9+ds/cmd/getgo/path_test.go000066400000000000000000000024151417751550600224510ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "io/ioutil" "os" "path/filepath" "strings" "testing" ) func TestAppendPath(t *testing.T) { tmpd, err := ioutil.TempDir("", "go") if err != nil { t.Fatal(err) } defer os.RemoveAll(tmpd) if err := os.Setenv("HOME", tmpd); err != nil { t.Fatal(err) } GOPATH := os.Getenv("GOPATH") if err := appendToPATH(filepath.Join(GOPATH, "bin")); err != nil { t.Fatal(err) } shellConfig, err := shellConfigFile() if err != nil { t.Fatal(err) } b, err := ioutil.ReadFile(shellConfig) if err != nil { t.Fatal(err) } expected := "export PATH=" + pathVar + envSeparator + filepath.Join(GOPATH, "bin") if strings.TrimSpace(string(b)) != expected { t.Fatalf("expected: %q, got %q", expected, strings.TrimSpace(string(b))) } // Check that appendToPATH is idempotent. if err := appendToPATH(filepath.Join(GOPATH, "bin")); err != nil { t.Fatal(err) } b, err = ioutil.ReadFile(shellConfig) if err != nil { t.Fatal(err) } if strings.TrimSpace(string(b)) != expected { t.Fatalf("expected: %q, got %q", expected, strings.TrimSpace(string(b))) } } golang-golang-x-tools-0.1.9+ds/cmd/getgo/server/000077500000000000000000000000001417751550600214335ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/getgo/server/.gcloudignore000066400000000000000000000012611417751550600241150ustar00rootroot00000000000000# This file specifies files that are *not* uploaded to Google Cloud Platform # using gcloud. It follows the same syntax as .gitignore, with the addition of # "#!include" directives (which insert the entries of the given .gitignore-style # file at that point). # # For more information, run: # $ gcloud topic gcloudignore # .gcloudignore # If you would like to upload your .git directory, .gitignore file or files # from your .gitignore file, remove the corresponding line # below: .git .gitignore # Binaries for programs and plugins *.exe *.exe~ *.dll *.so *.dylib # Test binary, build with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.outgolang-golang-x-tools-0.1.9+ds/cmd/getgo/server/README.md000066400000000000000000000001301417751550600227040ustar00rootroot00000000000000# getgo server ## Deployment ``` gcloud app deploy --promote --project golang-org ``` golang-golang-x-tools-0.1.9+ds/cmd/getgo/server/app.yaml000066400000000000000000000000341417751550600230740ustar00rootroot00000000000000runtime: go112 service: get golang-golang-x-tools-0.1.9+ds/cmd/getgo/server/main.go000066400000000000000000000032531417751550600227110ustar00rootroot00000000000000// 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. // Command server serves get.golang.org, redirecting users to the appropriate // getgo installer based on the request path. package main import ( "fmt" "net/http" "os" "strings" "time" ) const ( base = "https://dl.google.com/go/getgo/" windowsInstaller = base + "installer.exe" linuxInstaller = base + "installer_linux" macInstaller = base + "installer_darwin" ) // substring-based redirects. var stringMatch = map[string]string{ // via uname, from bash "MINGW": windowsInstaller, // Reported as MINGW64_NT-10.0 in git bash "Linux": linuxInstaller, "Darwin": macInstaller, } func main() { http.HandleFunc("/", handler) port := os.Getenv("PORT") if port == "" { port = "8080" fmt.Printf("Defaulting to port %s", port) } fmt.Printf("Listening on port %s", port) if err := http.ListenAndServe(fmt.Sprintf(":%s", port), nil); err != nil { fmt.Fprintf(os.Stderr, "http.ListenAndServe: %v", err) } } func handler(w http.ResponseWriter, r *http.Request) { if containsIgnoreCase(r.URL.Path, "installer.exe") { // cache bust http.Redirect(w, r, windowsInstaller+cacheBust(), http.StatusFound) return } for match, redirect := range stringMatch { if containsIgnoreCase(r.URL.Path, match) { http.Redirect(w, r, redirect, http.StatusFound) return } } http.NotFound(w, r) } func containsIgnoreCase(s, substr string) bool { return strings.Contains( strings.ToLower(s), strings.ToLower(substr), ) } func cacheBust() string { return fmt.Sprintf("?%d", time.Now().Nanosecond()) } golang-golang-x-tools-0.1.9+ds/cmd/getgo/steps.go000066400000000000000000000055421417751550600216200ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "context" "fmt" "os" "path/filepath" "runtime" "strings" ) type step func(context.Context) error func welcome(ctx context.Context) error { fmt.Println("Welcome to the Go installer!") answer, err := prompt(ctx, "Would you like to install Go? Y/n", "Y") if err != nil { return err } if strings.ToLower(answer) != "y" { fmt.Println("Exiting install.") return errExitCleanly } return nil } func checkOthers(ctx context.Context) error { // TODO: if go is currently installed install new version over that path, err := whichGo(ctx) if err != nil { fmt.Printf("Cannot check if Go is already installed:\n%v\n", err) } if path == "" { return nil } if path != installPath { fmt.Printf("Go is already installed at %v; remove it from your PATH.\n", path) } return nil } func chooseVersion(ctx context.Context) error { if *goVersion != "" { return nil } var err error *goVersion, err = getLatestGoVersion() if err != nil { return err } answer, err := prompt(ctx, fmt.Sprintf("The latest Go version is %s, install that? Y/n", *goVersion), "Y") if err != nil { return err } if strings.ToLower(answer) != "y" { // TODO: handle passing a version fmt.Println("Aborting install.") return errExitCleanly } return nil } func downloadGo(ctx context.Context) error { answer, err := prompt(ctx, fmt.Sprintf("Download Go version %s to %s? Y/n", *goVersion, installPath), "Y") if err != nil { return err } if strings.ToLower(answer) != "y" { fmt.Println("Aborting install.") return errExitCleanly } fmt.Printf("Downloading Go version %s to %s\n", *goVersion, installPath) fmt.Println("This may take a bit of time...") if err := downloadGoVersion(*goVersion, runtime.GOOS, arch, installPath); err != nil { return err } if err := appendToPATH(filepath.Join(installPath, "bin")); err != nil { return err } fmt.Println("Downloaded!") return nil } func setupGOPATH(ctx context.Context) error { answer, err := prompt(ctx, "Would you like us to setup your GOPATH? Y/n", "Y") if err != nil { return err } if strings.ToLower(answer) != "y" { fmt.Println("Exiting and not setting up GOPATH.") return errExitCleanly } fmt.Println("Setting up GOPATH") home, err := getHomeDir() if err != nil { return err } gopath := os.Getenv("GOPATH") if gopath == "" { // set $GOPATH gopath = filepath.Join(home, "go") if err := persistEnvVar("GOPATH", gopath); err != nil { return err } fmt.Println("GOPATH has been set up!") } else { verbosef("GOPATH is already set to %s", gopath) } if err := appendToPATH(filepath.Join(gopath, "bin")); err != nil { return err } return persistEnvChangesForSession() } golang-golang-x-tools-0.1.9+ds/cmd/getgo/system.go000066400000000000000000000016171417751550600220050ustar00rootroot00000000000000// 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 !plan9 // +build !plan9 package main import ( "bytes" "context" exec "golang.org/x/sys/execabs" "runtime" "strings" ) // arch contains either amd64 or 386. var arch = func() string { cmd := exec.Command("uname", "-m") // "x86_64" if runtime.GOOS == "windows" { cmd = exec.Command("powershell", "-command", "(Get-WmiObject -Class Win32_ComputerSystem).SystemType") // "x64-based PC" } out, err := cmd.Output() if err != nil { // a sensible default? return "amd64" } if bytes.Contains(out, []byte("64")) { return "amd64" } return "386" }() func findGo(ctx context.Context, cmd string) (string, error) { out, err := exec.CommandContext(ctx, cmd, "go").CombinedOutput() return strings.TrimSpace(string(out)), err } golang-golang-x-tools-0.1.9+ds/cmd/getgo/system_unix.go000066400000000000000000000022141417751550600230420ustar00rootroot00000000000000// 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 aix || darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris // +build aix darwin dragonfly freebsd linux nacl netbsd openbsd solaris package main import ( "context" "fmt" "os" "path/filepath" ) const ( envSeparator = ":" homeKey = "HOME" lineEnding = "\n" pathVar = "$PATH" ) var installPath = func() string { home, err := getHomeDir() if err != nil { return "/usr/local/go" } return filepath.Join(home, ".go") }() func whichGo(ctx context.Context) (string, error) { return findGo(ctx, "which") } func isWindowsXP() bool { return false } func currentShell() string { return os.Getenv("SHELL") } func persistEnvChangesForSession() error { shellConfig, err := shellConfigFile() if err != nil { return err } fmt.Println() fmt.Printf("One more thing! Run `source %s` to persist the\n", shellConfig) fmt.Println("new environment variables to your current session, or open a") fmt.Println("new shell prompt.") return nil } golang-golang-x-tools-0.1.9+ds/cmd/getgo/system_windows.go000066400000000000000000000034521417751550600235560ustar00rootroot00000000000000// 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 windows // +build windows package main import ( "context" "log" "os" "syscall" "unsafe" ) const ( envSeparator = ";" homeKey = "USERPROFILE" lineEnding = "/r/n" pathVar = "$env:Path" ) var installPath = `c:\go` func isWindowsXP() bool { v, err := syscall.GetVersion() if err != nil { log.Fatalf("GetVersion failed: %v", err) } major := byte(v) return major < 6 } func whichGo(ctx context.Context) (string, error) { return findGo(ctx, "where") } // currentShell reports the current shell. // It might be "powershell.exe", "cmd.exe" or any of the *nix shells. // // Returns empty string if the shell is unknown. func currentShell() string { shell := os.Getenv("SHELL") if shell != "" { return shell } pid := os.Getppid() pe, err := getProcessEntry(pid) if err != nil { verbosef("getting shell from process entry failed: %v", err) return "" } return syscall.UTF16ToString(pe.ExeFile[:]) } func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) { // From https://go.googlesource.com/go/+/go1.8.3/src/syscall/syscall_windows.go#941 snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0) if err != nil { return nil, err } defer syscall.CloseHandle(snapshot) var procEntry syscall.ProcessEntry32 procEntry.Size = uint32(unsafe.Sizeof(procEntry)) if err = syscall.Process32First(snapshot, &procEntry); err != nil { return nil, err } for { if procEntry.ProcessID == uint32(pid) { return &procEntry, nil } if err := syscall.Process32Next(snapshot, &procEntry); err != nil { return nil, err } } } func persistEnvChangesForSession() error { return nil } golang-golang-x-tools-0.1.9+ds/cmd/getgo/upload.bash000077500000000000000000000007061417751550600222560ustar00rootroot00000000000000#!/bin/bash # 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. if ! command -v gsutil 2>&1 > /dev/null; then echo "Install gsutil:" echo echo " https://cloud.google.com/storage/docs/gsutil_install#sdk-install" fi if [ ! -d build ]; then echo "Run make.bash first" fi set -e -o -x gsutil -m cp -a public-read build/* gs://golang/getgo golang-golang-x-tools-0.1.9+ds/cmd/go-contrib-init/000077500000000000000000000000001417751550600220245ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/go-contrib-init/contrib.go000066400000000000000000000173301417751550600240170ustar00rootroot00000000000000// 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" exec "golang.org/x/sys/execabs" "io/ioutil" "log" "os" "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 := ioutil.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 := ioutil.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.1.9+ds/cmd/go-contrib-init/contrib_test.go000066400000000000000000000023561417751550600250600ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/000077500000000000000000000000001417751550600201265ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/godex/doc.go000066400000000000000000000046531417751550600212320ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/gc.go000066400000000000000000000005471417751550600210540ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/gccgo.go000066400000000000000000000015661417751550600215470ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/godex.go000066400000000000000000000122741417751550600215710ustar00rootroot00000000000000// 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" "io/ioutil" "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 := ioutil.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.1.9+ds/cmd/godex/isAlias18.go000066400000000000000000000005111417751550600222100ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/isAlias19.go000066400000000000000000000004441417751550600222160ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/print.go000066400000000000000000000217461417751550600216230ustar00rootroot00000000000000// 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" ) // 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, _ := 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.1.9+ds/cmd/godex/source.go000066400000000000000000000006601417751550600217570ustar00rootroot00000000000000// 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.1.9+ds/cmd/godex/writetype.go000066400000000000000000000132701417751550600225140ustar00rootroot00000000000000// 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" 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 *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.1.9+ds/cmd/godoc/000077500000000000000000000000001417751550600201135ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/godoc/doc.go000066400000000000000000000104241417751550600212100ustar00rootroot00000000000000// 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.1.9+ds/cmd/godoc/godoc_test.go000066400000000000000000000273331417751550600226040ustar00rootroot00000000000000// 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_test import ( "bytes" "fmt" "go/build" "io/ioutil" "net" "net/http" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "testing" "time" "golang.org/x/tools/go/packages/packagestest" "golang.org/x/tools/internal/testenv" ) // buildGodoc builds the godoc executable. // It returns its path, and a cleanup function. // // TODO(adonovan): opt: do this at most once, and do the cleanup // exactly once. How though? There's no atexit. func buildGodoc(t *testing.T) (bin string, cleanup func()) { t.Helper() if runtime.GOARCH == "arm" { t.Skip("skipping test on arm platforms; too slow") } if runtime.GOOS == "android" { t.Skipf("the dependencies are not available on android") } testenv.NeedsTool(t, "go") tmp, err := ioutil.TempDir("", "godoc-regtest-") if err != nil { t.Fatal(err) } defer func() { if cleanup == nil { // probably, go build failed. os.RemoveAll(tmp) } }() bin = filepath.Join(tmp, "godoc") if runtime.GOOS == "windows" { bin += ".exe" } cmd := exec.Command("go", "build", "-o", bin) if err := cmd.Run(); err != nil { t.Fatalf("Building godoc: %v", err) } return bin, func() { os.RemoveAll(tmp) } } 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, cmd *exec.Cmd, addr string) { ch := make(chan error, 1) go func() { ch <- fmt.Errorf("server exited early: %v", cmd.Wait()) }() go waitForServer(t, ch, fmt.Sprintf("http://%v/", addr), "Go Documentation Server", 15*time.Second, false) if err := <-ch; err != nil { t.Fatal(err) } } func waitForSearchReady(t *testing.T, cmd *exec.Cmd, addr string) { ch := make(chan error, 1) go func() { ch <- fmt.Errorf("server exited early: %v", cmd.Wait()) }() go waitForServer(t, ch, fmt.Sprintf("http://%v/search?q=FALLTHROUGH", addr), "The list of tokens.", 2*time.Minute, false) if err := <-ch; err != nil { t.Fatal(err) } } func waitUntilScanComplete(t *testing.T, addr string) { ch := make(chan error) go waitForServer(t, ch, fmt.Sprintf("http://%v/pkg", addr), "Scan is not yet complete", 2*time.Minute, // setting reverse as true, which means this waits // until the string is not returned in the response anymore true, ) if err := <-ch; err != nil { t.Fatal(err) } } const pollInterval = 200 * time.Millisecond // waitForServer waits for server to meet the required condition. // It sends a single error value to ch, unless the test has failed. // The error value is nil if the required condition was met within // timeout, or non-nil otherwise. func waitForServer(t *testing.T, ch chan<- error, url, match string, timeout time.Duration, reverse bool) { deadline := time.Now().Add(timeout) for time.Now().Before(deadline) { time.Sleep(pollInterval) if t.Failed() { return } res, err := http.Get(url) if err != nil { continue } body, err := ioutil.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)): ch <- nil return } } ch <- fmt.Errorf("server failed to respond in %v", timeout) } // hasTag checks whether a given release tag is contained in the current version // of the go binary. func hasTag(t string) bool { for _, v := range build.Default.ReleaseTags { if t == v { return true } } return false } func killAndWait(cmd *exec.Cmd) { cmd.Process.Kill() cmd.Process.Wait() } func TestURL(t *testing.T) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; fails to start up quickly enough") } bin, cleanup := buildGodoc(t) defer cleanup() testcase := func(url string, contents string) func(t *testing.T) { return func(t *testing.T) { stdout, stderr := new(bytes.Buffer), new(bytes.Buffer) args := []string{fmt.Sprintf("-url=%s", url)} cmd := exec.Command(bin, args...) cmd.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, cleanup := buildGodoc(t) defer cleanup() 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) { if testing.Short() { t.Skip("skipping test in -short mode") } bin, cleanup := buildGodoc(t) defer cleanup() testWeb(t, packagestest.GOPATH, bin, true) } // Basic integration test for godoc HTTP interface. func testWeb(t *testing.T, x packagestest.Exporter, bin string, withIndex bool) { if runtime.GOOS == "plan9" { t.Skip("skipping on plan9; fails to start up quickly enough") } // 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 := exec.Command(bin, args...) cmd.Dir = e.Config.Dir cmd.Env = e.Config.Env cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr cmd.Args[0] = "godoc" if err := cmd.Start(); err != nil { t.Fatalf("failed to start godoc: %s", err) } defer killAndWait(cmd) if withIndex { waitForSearchReady(t, cmd, addr) } else { waitForServerReady(t, cmd, addr) waitUntilScanComplete(t, 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 := ioutil.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, cleanup := buildGodoc(t) defer cleanup() tempDir, err := ioutil.TempDir("", "godoc-test-") if err != nil { t.Fatal(err) } defer os.RemoveAll(tempDir) // Run godoc in an empty directory with module mode explicitly on, // so that 'go env GOMOD' reports os.DevNull. cmd := exec.Command(bin, "-url=/") cmd.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.1.9+ds/cmd/godoc/goroot.go000066400000000000000000000012601417751550600217520ustar00rootroot00000000000000// 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 ( exec "golang.org/x/sys/execabs" "os" "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.1.9+ds/cmd/godoc/handlers.go000066400000000000000000000052031417751550600222420ustar00rootroot00000000000000// 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.1.9+ds/cmd/godoc/main.go000066400000000000000000000362451417751550600214000ustar00rootroot00000000000000// 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" _ "expvar" // to serve /debug/vars "flag" "fmt" "go/build" "io" "log" "net/http" _ "net/http/pprof" // to serve /debug/pprof/* "net/url" "os" "path" "path/filepath" "regexp" "runtime" "strings" exec "golang.org/x/sys/execabs" "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" "golang.org/x/xerrors" ) 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. mainMod, vendorEnabled, 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", mainMod.Path), gatefs.New(vfs.OS(mainMod.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(mainMod.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 // 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); xerrors.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); xerrors.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); xerrors.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.1.9+ds/cmd/goimports/000077500000000000000000000000001417751550600210435ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/goimports/doc.go000066400000000000000000000031021417751550600221330ustar00rootroot00000000000000// 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.1.9+ds/cmd/goimports/goimports.go000066400000000000000000000232611417751550600234210ustar00rootroot00000000000000// 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" exec "golang.org/x/sys/execabs" "io" "io/ioutil" "log" "os" "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 := ioutil.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 = ioutil.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 := ioutil.TempFile(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.1.9+ds/cmd/goimports/goimports_gc.go000066400000000000000000000007611417751550600240720ustar00rootroot00000000000000// 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() { flush() trace.Stop() } } return func() {} } golang-golang-x-tools-0.1.9+ds/cmd/goimports/goimports_not_gc.go000066400000000000000000000003701417751550600247460ustar00rootroot00000000000000// 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.1.9+ds/cmd/gomvpkg/000077500000000000000000000000001417751550600204725ustar00rootroot00000000000000golang-golang-x-tools-0.1.9+ds/cmd/gomvpkg/main.go000066400000000000000000000056071417751550600217550ustar00rootroot00000000000000// 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