pax_global_header00006660000000000000000000000064132440325720014514gustar00rootroot0000000000000052 comment=0af14b99a7f7983b223c393062ac040e98f69c65 golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/000077500000000000000000000000001324403257200210475ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/.gitattributes000066400000000000000000000005311324403257200237410ustar00rootroot00000000000000# 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.0~git20180222.0.f8f2f88+ds/.gitignore000066400000000000000000000001251324403257200230350ustar00rootroot00000000000000# Add no patterns to .gitignore except for files generated by the build. last-change golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/AUTHORS000066400000000000000000000002551324403257200221210ustar00rootroot00000000000000# 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.0~git20180222.0.f8f2f88+ds/CONTRIBUTING.md000066400000000000000000000020071324403257200232770ustar00rootroot00000000000000# 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. **We do not accept GitHub pull requests** (we use [Gerrit](https://code.google.com/p/gerrit/) instead for code review). Unless otherwise noted, the Go source files are distributed under the BSD-style license found in the LICENSE file. golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/CONTRIBUTORS000066400000000000000000000002521324403257200227260ustar00rootroot00000000000000# 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.0~git20180222.0.f8f2f88+ds/LICENSE000066400000000000000000000027071324403257200220620ustar00rootroot00000000000000Copyright (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.0~git20180222.0.f8f2f88+ds/PATENTS000066400000000000000000000024271324403257200221150ustar00rootroot00000000000000Additional 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.0~git20180222.0.f8f2f88+ds/README.md000066400000000000000000000017331324403257200223320ustar00rootroot00000000000000# Go 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 get`. 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 get -u golang.org/x/tools/...`. You can also manually git clone the repository to `$GOPATH/src/golang.org/x/tools`. ## 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.0~git20180222.0.f8f2f88+ds/benchmark/000077500000000000000000000000001324403257200230015ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/benchmark/parse/000077500000000000000000000000001324403257200241135ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/benchmark/parse/parse.go000066400000000000000000000066131324403257200255620ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/benchmark/parse/parse_test.go000066400000000000000000000100061324403257200266100ustar00rootroot00000000000000// 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) } } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/blog/000077500000000000000000000000001324403257200217725ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/blog/atom/000077500000000000000000000000001324403257200227325ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/blog/atom/atom.go000066400000000000000000000030501324403257200242170ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/blog/blog.go000066400000000000000000000247551324403257200232610ustar00rootroot00000000000000// 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_.]*$`) // 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. 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 } // 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 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. err = s.loadDocs(filepath.Clean(cfg.ContentPath)) 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 { 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 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 } html := new(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) 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) } } // 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 } e := &atom.Entry{ Title: doc.Title, ID: feed.ID + doc.Path, 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 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} 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: 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 } 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.0~git20180222.0.f8f2f88+ds/cmd/000077500000000000000000000000001324403257200216125ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/000077500000000000000000000000001324403257200233715ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/benchcmp.go000066400000000000000000000101221324403257200254730ustar00rootroot00000000000000// 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() { 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.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/benchcmp_test.go000066400000000000000000000020511324403257200265340ustar00rootroot00000000000000package 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) } } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/compare.go000066400000000000000000000123621324403257200253520ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/compare_test.go000066400000000000000000000113321324403257200264050ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/benchcmp/doc.go000066400000000000000000000022121324403257200244620ustar00rootroot00000000000000// 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 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.0~git20180222.0.f8f2f88+ds/cmd/bundle/000077500000000000000000000000001324403257200230635ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/.gitignore000066400000000000000000000000211324403257200250440ustar00rootroot00000000000000testdata/out.got golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/main.go000066400000000000000000000314331324403257200243420ustar00rootroot00000000000000// 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] // // 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 -package option is given, bundle uses that name. // Otherwise, if the -dst option is given, bundle uses the last // element of the destination import path. // Otherwise, by default bundle uses the package name found in the // package sources in the current directory. // // 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 rewriting the import "golang.org/x/net/http2/hpack" // to "internal/golang.org/x/net/http2/hpack": // // cd $GOROOT/src/net/http // bundle -o h2_bundle.go \ // -prefix http2 \ // -import golang.org/x/net/http2/hpack=internal/golang.org/x/net/http2/hpack \ // golang.org/x/net/http2 // // Two ways to update the http2 bundle: // // go generate net/http // // cd $GOROOT/src/net/http // go generate // // Update both bundles, restricting ``go generate'' to running bundle commands: // // go generate -run bundle cmd/dist net/http // package main import ( "bytes" "flag" "fmt" "go/ast" "go/build" "go/format" "go/parser" "go/printer" "go/token" "go/types" "io/ioutil" "log" "os" "path" "strconv" "strings" "golang.org/x/tools/go/loader" ) var ( outputFile = flag.String("o", "", "write output to `file` (default standard output)") dstPath = flag.String("dst", "", "set destination import `path` (default taken from current directory)") pkgName = flag.String("pkg", "", "set destination package `name` (default taken from current directory)") prefix = flag.String("prefix", "", "set bundled identifier prefix to `p` (default source package name + \"_\")") underscore = flag.Bool("underscore", false, "rewrite golang.org to golang_org in imports; temporary workaround for golang.org/issue/16333") 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) } if *dstPath != "" { if *pkgName == "" { *pkgName = path.Base(*dstPath) } } else { wd, _ := os.Getwd() pkg, err := build.ImportDir(wd, 0) if err != nil { log.Fatalf("cannot find package in current directory: %v", err) } *dstPath = pkg.ImportPath if *pkgName == "" { *pkgName = pkg.Name } } code, err := bundle(args[0], *dstPath, *pkgName, *prefix) 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 ctxt = &build.Default func bundle(src, dst, dstpkg, prefix string) ([]byte, error) { // Load the initial package. conf := loader.Config{ParserMode: parser.ParseComments, Build: ctxt} conf.TypeCheckFuncBodies = func(p string) bool { return p == src } conf.Import(src) lprog, err := conf.Load() if err != nil { return nil, err } // Because there was a single Import call and Load succeeded, // InitialPackages is guaranteed to hold the sole requested package. info := lprog.InitialPackages()[0] if prefix == "" { pkgName := info.Files[0].Name.Name prefix = pkgName + "_" } 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 info.Uses { if obj == from { if field := info.Defs[id]; field != nil { rename(field) } } } } } } // Rename each package-level object. scope := info.Pkg.Scope() for _, name := range scope.Names() { rename(scope.Lookup(name)) } var out bytes.Buffer fmt.Fprintf(&out, "// Code generated by golang.org/x/tools/cmd/bundle. DO NOT EDIT.\n") if *outputFile != "" { 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 info.Files { 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 info.Files { 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 conf.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 { if *underscore { spec = strings.Replace(spec, "golang.org/", "golang_org/", 1) } 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 info.Files { // Update renamed identifiers. for id, obj := range info.Defs { if objsToUpdate[obj] { id.Name = prefix + obj.Name() } } for id, obj := range info.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 := info.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, lprog.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, lprog.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.0~git20180222.0.f8f2f88+ds/cmd/bundle/main_test.go000066400000000000000000000032541324403257200254010ustar00rootroot00000000000000// 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. // +build go1.9 package main import ( "bytes" "io/ioutil" "os" "os/exec" "runtime" "testing" "golang.org/x/tools/go/buildutil" ) func TestBundle(t *testing.T) { load := func(name string) string { data, err := ioutil.ReadFile(name) if err != nil { t.Fatal(err) } return string(data) } ctxt = buildutil.FakeContext(map[string]map[string]string{ "initial": { "a.go": load("testdata/src/initial/a.go"), "b.go": load("testdata/src/initial/b.go"), "c.go": load("testdata/src/initial/c.go"), }, "domain.name/importdecl": { "p.go": load("testdata/src/domain.name/importdecl/p.go"), }, "fmt": { "print.go": `package fmt; func Println(...interface{})`, }, }) os.Args = os.Args[:1] // avoid e.g. -test=short in the output out, err := bundle("initial", "github.com/dest", "dest", "prefix") 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.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/000077500000000000000000000000001324403257200246745ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/out.golden000066400000000000000000000014631324403257200267010ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/000077500000000000000000000000001324403257200254635ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/domain.name/000077500000000000000000000000001324403257200276515ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/domain.name/importdecl/000077500000000000000000000000001324403257200320135ustar00rootroot00000000000000p.go000066400000000000000000000000561324403257200325230ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/domain.name/importdeclpackage importdecl func F() int { return 1 } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/initial/000077500000000000000000000000001324403257200271145ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/initial/a.go000066400000000000000000000005451324403257200276670ustar00rootroot00000000000000package 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.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/initial/b.go000066400000000000000000000003721324403257200276660ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/bundle/testdata/src/initial/c.go000066400000000000000000000002501324403257200276620ustar00rootroot00000000000000package initial import _ "fmt" import renamedfmt "fmt" import renamedfmt2 "fmt" import . "fmt" func baz() { renamedfmt.Println() renamedfmt2.Println() Println() } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/000077500000000000000000000000001324403257200235475ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/main.go000066400000000000000000000232601324403257200250250ustar00rootroot00000000000000// 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/loader" "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 the call graph of a Go program. Usage: callgraph [-algo=static|cha|rta|pta] [-test] [-format=...] ... 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. ` + loader.FromArgsUsage + ` 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(&build.Default, *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(ctxt *build.Context, algo, format string, tests bool, args []string) error { conf := loader.Config{Build: ctxt} if len(args) == 0 { fmt.Fprintln(os.Stderr, Usage) return nil } // Use the initial packages from the command line. _, err := conf.FromArgs(args, tests) if err != nil { return err } // Load, parse and type-check the whole program. iprog, err := conf.Load() if err != nil { return err } // Create and build SSA-form program representation. prog := ssautil.CreateProgram(iprog, 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(prog, tests) 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(prog, tests) 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(prog *ssa.Program, tests bool) ([]*ssa.Package, error) { pkgs := prog.AllPackages() // TODO(adonovan): use only initial packages // If tests, create a "testmain" package for each test. var mains []*ssa.Package if tests { for _, pkg := range pkgs { if main := prog.CreateTestMainPackage(pkg); main != nil { mains = append(mains, main) } } if mains == nil { return nil, fmt.Errorf("no tests") } return mains, nil } // Otherwise, use the main packages. mains = append(mains, ssautil.MainPackages(pkgs)...) 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.0~git20180222.0.f8f2f88+ds/cmd/callgraph/main_test.go000066400000000000000000000035741324403257200260720ustar00rootroot00000000000000// 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. // +build !android package main import ( "bytes" "fmt" "go/build" "reflect" "sort" "strings" "testing" ) func TestCallgraph(t *testing.T) { ctxt := build.Default // copy ctxt.GOPATH = "testdata" const format = "{{.Caller}} --> {{.Callee}}" for _, test := range []struct { algo, format string tests bool want []string }{ {"rta", format, 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", format, 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: main is not called. {"rta", format, true, []string{ `pkg$testmain.init --> pkg.init`, `pkg.Example --> (pkg.C).f`, }}, {"pta", format, true, []string{ ` --> pkg$testmain.init`, ` --> pkg.Example`, `pkg$testmain.init --> pkg.init`, `pkg.Example --> (pkg.C).f`, }}, } { stdout = new(bytes.Buffer) if err := doCallgraph(&ctxt, test.algo, test.format, test.tests, []string{"pkg"}); err != nil { t.Error(err) continue } got := sortedLines(fmt.Sprint(stdout)) if !reflect.DeepEqual(got, test.want) { t.Errorf("callgraph(%q, %q, %t):\ngot:\n%s\nwant:\n%s", test.algo, test.format, test.tests, strings.Join(got, "\n"), strings.Join(test.want, "\n")) } } } func sortedLines(s string) []string { s = strings.TrimSpace(s) lines := strings.Split(s, "\n") sort.Strings(lines) return lines } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/testdata/000077500000000000000000000000001324403257200253605ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/testdata/src/000077500000000000000000000000001324403257200261475ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/testdata/src/pkg/000077500000000000000000000000001324403257200267305ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/callgraph/testdata/src/pkg/pkg.go000066400000000000000000000003351324403257200300410ustar00rootroot00000000000000package 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.0~git20180222.0.f8f2f88+ds/cmd/callgraph/testdata/src/pkg/pkg_test.go000066400000000000000000000001511324403257200310740ustar00rootroot00000000000000package main // Don't import "testing", it adds a lot of callgraph edges. func Example() { C(0).f() } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/compilebench/000077500000000000000000000000001324403257200242425ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/compilebench/main.go000066400000000000000000000233011324403257200255140ustar00rootroot00000000000000// 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. // // -count n // Run each benchmark n times (default 1). // // -cpuprofile file // Write a CPU profile of the compiler to file. // // -memprofile file // Write a memory profile of the compiler to file. // // -memprofilerate rate // Set runtime.MemProfileRate during compilation. // // -obj // Report object file statistics. // // -pkg // Benchmark compiling a single package. // // -run regexp // Only run benchmarks with names matching regexp. // // 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" "flag" "fmt" "go/build" "io/ioutil" "log" "os" "os/exec" "path/filepath" "regexp" "strconv" "strings" "time" ) var ( goroot string compiler 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") 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") ) var tests = []struct { name string dir string long bool }{ {"BenchmarkTemplate", "html/template", false}, {"BenchmarkUnicode", "unicode", false}, {"BenchmarkGoTypes", "go/types", false}, {"BenchmarkCompiler", "cmd/compile/internal/gc", false}, {"BenchmarkSSA", "cmd/compile/internal/ssa", false}, {"BenchmarkFlate", "compress/flate", false}, {"BenchmarkGoParser", "go/parser", false}, {"BenchmarkReflect", "reflect", false}, {"BenchmarkTar", "archive/tar", false}, {"BenchmarkXML", "encoding/xml", false}, {"BenchmarkStdCmd", "", true}, {"BenchmarkHelloSize", "", false}, {"BenchmarkCmdGoSize", "", 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)) compiler = *flagCompiler if compiler == "" { out, err := exec.Command(*flagGoCmd, "tool", "-n", "compile").CombinedOutput() if err != nil { out, err = exec.Command(*flagGoCmd, "tool", "-n", "6g").CombinedOutput() is6g = true if err != nil { out, err = exec.Command(*flagGoCmd, "tool", "-n", "compile").CombinedOutput() log.Fatalf("go tool -n compiler: %v\n%s", err, out) } } compiler = strings.TrimSpace(string(out)) } if *flagRun != "" { r, err := regexp.Compile(*flagRun) if err != nil { log.Fatalf("invalid -run argument: %v", err) } runRE = r } for i := 0; i < *flagCount; i++ { if *flagPackage != "" { runBuild("BenchmarkPkg", *flagPackage, i) continue } for _, tt := range tests { if tt.long && *flagShort { continue } if runRE == nil || runRE.MatchString(tt.name) { runBuild(tt.name, tt.dir, i) } } } } func runCmd(name string, cmd *exec.Cmd) { start := time.Now() out, err := cmd.CombinedOutput() if err != nil { log.Printf("%v: %v\n%s", name, err, out) return } fmt.Printf("%s 1 %d ns/op\n", name, time.Since(start).Nanoseconds()) } func runStdCmd() { args := []string{"build", "-a"} if *flagCompilerFlags != "" { args = append(args, "-gcflags", *flagCompilerFlags) } args = append(args, "std", "cmd") cmd := exec.Command(*flagGoCmd, args...) cmd.Dir = filepath.Join(goroot, "src") runCmd("BenchmarkStdCmd", cmd) } // path is either a path to a file ("$GOROOT/test/helloworld.go") or a package path ("cmd/go"). func runSize(name, path string) { cmd := exec.Command(*flagGoCmd, "build", "-o", "_compilebenchout_", path) cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { log.Print(err) return } defer os.Remove("_compilebenchout_") info, err := os.Stat("_compilebenchout_") if err != nil { log.Print(err) return } out, err := exec.Command("size", "_compilebenchout_").CombinedOutput() if err != nil { log.Printf("size: %v\n%s", err, out) return } lines := strings.Split(string(out), "\n") if len(lines) < 2 { log.Printf("not enough output from size: %s", out) return } 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()) } } func runBuild(name, dir string, count int) { switch name { case "BenchmarkStdCmd": runStdCmd() return case "BenchmarkCmdGoSize": runSize("BenchmarkCmdGoSize", "cmd/go") return case "BenchmarkHelloSize": runSize("BenchmarkHelloSize", filepath.Join(goroot, "test/helloworld.go")) return } pkg, err := build.Import(dir, ".", 0) if err != nil { log.Print(err) return } args := []string{"-o", "_compilebench_.o"} if is6g { *flagMemprofilerate = -1 *flagAlloc = false *flagCpuprofile = "" *flagMemprofile = "" } if *flagMemprofilerate >= 0 { args = append(args, "-memprofilerate", fmt.Sprint(*flagMemprofilerate)) } args = append(args, strings.Fields(*flagCompilerFlags)...) if *flagAlloc || *flagCpuprofile != "" || *flagMemprofile != "" { if *flagAlloc || *flagMemprofile != "" { args = append(args, "-memprofile", "_compilebench_.memprof") } if *flagCpuprofile != "" { args = append(args, "-cpuprofile", "_compilebench_.cpuprof") } } args = append(args, pkg.GoFiles...) cmd := exec.Command(compiler, args...) cmd.Dir = pkg.Dir cmd.Stdout = os.Stderr cmd.Stderr = os.Stderr start := time.Now() err = cmd.Run() if err != nil { log.Printf("%v: %v", name, err) return } end := time.Now() var allocs, allocbytes int64 if *flagAlloc || *flagMemprofile != "" { out, err := ioutil.ReadFile(pkg.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 } switch f[1] { case "TotalAlloc": allocbytes = val case "Mallocs": allocs = val } } if *flagMemprofile != "" { if err := ioutil.WriteFile(*flagMemprofile, out, 0666); err != nil { log.Print(err) } } os.Remove(pkg.Dir + "/_compilebench_.memprof") } if *flagCpuprofile != "" { out, err := ioutil.ReadFile(pkg.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(pkg.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 *flagAlloc { fmt.Printf(" %d B/op %d allocs/op", allocbytes, allocs) } 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) } golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/cover/000077500000000000000000000000001324403257200227305ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/cover/README000066400000000000000000000001621324403257200236070ustar00rootroot00000000000000NOTE: 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.0~git20180222.0.f8f2f88+ds/cmd/cover/cover.go000066400000000000000000000472121324403257200244030ustar00rootroot00000000000000// 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.Fprintln(os.Stderr, usageMessage) fmt.Fprintln(os.Stderr, "Flags:") 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.0~git20180222.0.f8f2f88+ds/cmd/cover/cover_test.go000066400000000000000000000044541324403257200254430ustar00rootroot00000000000000// 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. // +build !android package main_test import ( "bytes" "fmt" "io/ioutil" "os" "os/exec" "path/filepath" "testing" ) const ( // Data directory, also the package directory for the test. testdata = "testdata" // Binaries we compile. testcover = "./testcover.exe" ) var ( // Files we use. testMain = filepath.Join(testdata, "main.go") testTest = filepath.Join(testdata, "test.go") coverInput = filepath.Join(testdata, "test_line.go") coverOutput = filepath.Join(testdata, "test_cover.go") ) 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) { // 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) } // defer removal of test_line.go if !debug { defer os.Remove(coverInput) } // go build -o testcover cmd := exec.Command("go", "build", "-o", testcover) run(cmd, t) // defer removal of testcover defer os.Remove(testcover) // ./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.0~git20180222.0.f8f2f88+ds/cmd/cover/doc.go000066400000000000000000000017741324403257200240350ustar00rootroot00000000000000// 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'. 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 No longer maintained: For Go releases 1.5 and later, this tool lives in the standard repository. The code here is not maintained. */ package main // import "golang.org/x/tools/cmd/cover" golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/cover/func.go000066400000000000000000000106231324403257200242140ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/cover/html.go000066400000000000000000000143271324403257200242320ustar00rootroot00000000000000// 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" "html/template" "io" "io/ioutil" "math" "os" "os/exec" "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.0~git20180222.0.f8f2f88+ds/cmd/cover/testdata/000077500000000000000000000000001324403257200245415ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/cover/testdata/main.go000066400000000000000000000054311324403257200260170ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/cover/testdata/test.go000066400000000000000000000074751324403257200260640ustar00rootroot00000000000000// 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.0~git20180222.0.f8f2f88+ds/cmd/digraph/000077500000000000000000000000001324403257200232305ustar00rootroot00000000000000golang-golang-x-tools-0.0~git20180222.0.f8f2f88+ds/cmd/digraph/digraph.go000066400000000000000000000273071324403257200252060ustar00rootroot00000000000000// The digraph command performs queries over unlabelled directed graphs // represented in text form. It is intended to integrate nicely with // typical UNIX command pipelines. // // Since directed graphs (import graphs, reference graphs, call graphs, // etc) often arise during software tool development and debugging, this // command is included in the go.tools repository. // // TODO(adonovan): // - support input files other than stdin // - suport alternative formats (AT&T GraphViz, CSV, etc), // a comment syntax, etc. // - allow queries to nest, like Blaze query language. // package main // import "golang.org/x/tools/cmd/digraph" import ( "bufio" "bytes" "errors" "flag" "fmt" "io" "os" "sort" "strconv" "unicode" "unicode/utf8" ) const Usage = `digraph: queries over directed graphs in text form. Graph 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 field 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. Supported queries: nodes the set of all nodes degree the in-degree and out-degree of each node. preds