diff --git a/vendor/golang.org/x/tools/.gitattributes b/vendor/golang.org/x/tools/.gitattributes new file mode 100644 index 0000000..d2f212e --- /dev/null +++ b/vendor/golang.org/x/tools/.gitattributes @@ -0,0 +1,10 @@ +# 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 diff --git a/vendor/golang.org/x/tools/.gitignore b/vendor/golang.org/x/tools/.gitignore new file mode 100644 index 0000000..5a9d62e --- /dev/null +++ b/vendor/golang.org/x/tools/.gitignore @@ -0,0 +1,2 @@ +# Add no patterns to .gitignore except for files generated by the build. +last-change diff --git a/vendor/golang.org/x/tools/AUTHORS b/vendor/golang.org/x/tools/AUTHORS new file mode 100644 index 0000000..15167cd --- /dev/null +++ b/vendor/golang.org/x/tools/AUTHORS @@ -0,0 +1,3 @@ +# 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. diff --git a/vendor/golang.org/x/tools/CONTRIBUTING.md b/vendor/golang.org/x/tools/CONTRIBUTING.md new file mode 100644 index 0000000..88dff59 --- /dev/null +++ b/vendor/golang.org/x/tools/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# 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. + diff --git a/vendor/golang.org/x/tools/CONTRIBUTORS b/vendor/golang.org/x/tools/CONTRIBUTORS new file mode 100644 index 0000000..1c4577e --- /dev/null +++ b/vendor/golang.org/x/tools/CONTRIBUTORS @@ -0,0 +1,3 @@ +# 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. diff --git a/vendor/golang.org/x/tools/README.md b/vendor/golang.org/x/tools/README.md new file mode 100644 index 0000000..20be9e1 --- /dev/null +++ b/vendor/golang.org/x/tools/README.md @@ -0,0 +1,27 @@ +# 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. diff --git a/vendor/golang.org/x/tools/benchmark/parse/parse.go b/vendor/golang.org/x/tools/benchmark/parse/parse.go new file mode 100644 index 0000000..b37e6f0 --- /dev/null +++ b/vendor/golang.org/x/tools/benchmark/parse/parse.go @@ -0,0 +1,131 @@ +// 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 +} diff --git a/vendor/golang.org/x/tools/benchmark/parse/parse_test.go b/vendor/golang.org/x/tools/benchmark/parse/parse_test.go new file mode 100644 index 0000000..06db848 --- /dev/null +++ b/vendor/golang.org/x/tools/benchmark/parse/parse_test.go @@ -0,0 +1,154 @@ +// 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) + } +} diff --git a/vendor/golang.org/x/tools/blog/atom/atom.go b/vendor/golang.org/x/tools/blog/atom/atom.go new file mode 100644 index 0000000..542c50e --- /dev/null +++ b/vendor/golang.org/x/tools/blog/atom/atom.go @@ -0,0 +1,61 @@ +// 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")) +} diff --git a/vendor/golang.org/x/tools/blog/blog.go b/vendor/golang.org/x/tools/blog/blog.go new file mode 100644 index 0000000..4055b1b --- /dev/null +++ b/vendor/golang.org/x/tools/blog/blog.go @@ -0,0 +1,437 @@ +// 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) +} diff --git a/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp.go b/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp.go new file mode 100644 index 0000000..32f3a1c --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp.go @@ -0,0 +1,184 @@ +// 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) +} diff --git a/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp_test.go b/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp_test.go new file mode 100644 index 0000000..2226079 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/benchcmp/benchcmp_test.go @@ -0,0 +1,59 @@ +package main + +import ( + "reflect" + "testing" + + "golang.org/x/tools/benchmark/parse" +) + +func TestSelectBest(t *testing.T) { + have := parse.Set{ + "Benchmark1": []*parse.Benchmark{ + { + Name: "Benchmark1", + N: 10, NsPerOp: 100, Measured: parse.NsPerOp, + Ord: 0, + }, + { + Name: "Benchmark1", + N: 10, NsPerOp: 50, Measured: parse.NsPerOp, + Ord: 3, + }, + }, + "Benchmark2": []*parse.Benchmark{ + { + Name: "Benchmark2", + N: 10, NsPerOp: 60, Measured: parse.NsPerOp, + Ord: 1, + }, + { + Name: "Benchmark2", + N: 10, NsPerOp: 500, Measured: parse.NsPerOp, + Ord: 2, + }, + }, + } + + want := parse.Set{ + "Benchmark1": []*parse.Benchmark{ + { + Name: "Benchmark1", + N: 10, NsPerOp: 50, Measured: parse.NsPerOp, + Ord: 0, + }, + }, + "Benchmark2": []*parse.Benchmark{ + { + Name: "Benchmark2", + N: 10, NsPerOp: 60, Measured: parse.NsPerOp, + Ord: 1, + }, + }, + } + + selectBest(have) + if !reflect.DeepEqual(want, have) { + t.Errorf("filtered bench set incorrectly, want %v have %v", want, have) + } +} diff --git a/vendor/golang.org/x/tools/cmd/benchcmp/compare.go b/vendor/golang.org/x/tools/cmd/benchcmp/compare.go new file mode 100644 index 0000000..c3f5e89 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/benchcmp/compare.go @@ -0,0 +1,156 @@ +// 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) +} diff --git a/vendor/golang.org/x/tools/cmd/benchcmp/compare_test.go b/vendor/golang.org/x/tools/cmd/benchcmp/compare_test.go new file mode 100644 index 0000000..3403796 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/benchcmp/compare_test.go @@ -0,0 +1,133 @@ +// 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) + } +} diff --git a/vendor/golang.org/x/tools/cmd/benchcmp/doc.go b/vendor/golang.org/x/tools/cmd/benchcmp/doc.go new file mode 100644 index 0000000..f5c7a36 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/benchcmp/doc.go @@ -0,0 +1,37 @@ +// 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" diff --git a/vendor/golang.org/x/tools/cmd/bundle/.gitignore b/vendor/golang.org/x/tools/cmd/bundle/.gitignore new file mode 100644 index 0000000..caaeb09 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/.gitignore @@ -0,0 +1 @@ +testdata/out.got diff --git a/vendor/golang.org/x/tools/cmd/bundle/main.go b/vendor/golang.org/x/tools/cmd/bundle/main.go new file mode 100644 index 0000000..601da7f --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/main.go @@ -0,0 +1,468 @@ +// 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 "" } diff --git a/vendor/golang.org/x/tools/cmd/bundle/main_test.go b/vendor/golang.org/x/tools/cmd/bundle/main_test.go new file mode 100644 index 0000000..b96f7d9 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/main_test.go @@ -0,0 +1,74 @@ +// 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() +} diff --git a/vendor/golang.org/x/tools/cmd/bundle/testdata/out.golden b/vendor/golang.org/x/tools/cmd/bundle/testdata/out.golden new file mode 100644 index 0000000..5260fdd --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/testdata/out.golden @@ -0,0 +1,62 @@ +// 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() +} diff --git a/vendor/golang.org/x/tools/cmd/bundle/testdata/src/domain.name/importdecl/p.go b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/domain.name/importdecl/p.go new file mode 100644 index 0000000..36dfd15 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/domain.name/importdecl/p.go @@ -0,0 +1,3 @@ +package importdecl + +func F() int { return 1 } diff --git a/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go new file mode 100644 index 0000000..ded6c64 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/a.go @@ -0,0 +1,27 @@ +package 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 diff --git a/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go new file mode 100644 index 0000000..31d5cab --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/b.go @@ -0,0 +1,23 @@ +// 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 diff --git a/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/c.go b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/c.go new file mode 100644 index 0000000..6a9cbc6 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/bundle/testdata/src/initial/c.go @@ -0,0 +1,12 @@ +package initial + +import _ "fmt" +import renamedfmt "fmt" +import renamedfmt2 "fmt" +import . "fmt" + +func baz() { + renamedfmt.Println() + renamedfmt2.Println() + Println() +} diff --git a/vendor/golang.org/x/tools/cmd/callgraph/main.go b/vendor/golang.org/x/tools/cmd/callgraph/main.go new file mode 100644 index 0000000..8ef4597 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/callgraph/main.go @@ -0,0 +1,361 @@ +// 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() } diff --git a/vendor/golang.org/x/tools/cmd/callgraph/main_test.go b/vendor/golang.org/x/tools/cmd/callgraph/main_test.go new file mode 100644 index 0000000..c42f56d --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/callgraph/main_test.go @@ -0,0 +1,81 @@ +// 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 +} diff --git a/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg.go b/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg.go new file mode 100644 index 0000000..b81c97f --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg.go @@ -0,0 +1,25 @@ +package 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 +} diff --git a/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg_test.go b/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg_test.go new file mode 100644 index 0000000..d624757 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/callgraph/testdata/src/pkg/pkg_test.go @@ -0,0 +1,7 @@ +package main + +// Don't import "testing", it adds a lot of callgraph edges. + +func Example() { + C(0).f() +} diff --git a/vendor/golang.org/x/tools/cmd/compilebench/main.go b/vendor/golang.org/x/tools/cmd/compilebench/main.go new file mode 100644 index 0000000..d2cd70b --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/compilebench/main.go @@ -0,0 +1,360 @@ +// 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) +} diff --git a/vendor/golang.org/x/tools/cmd/cover/README b/vendor/golang.org/x/tools/cmd/cover/README new file mode 100644 index 0000000..ff9523d --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/README @@ -0,0 +1,2 @@ +NOTE: For Go releases 1.5 and later, this tool lives in the +standard repository. The code here is not maintained. diff --git a/vendor/golang.org/x/tools/cmd/cover/cover.go b/vendor/golang.org/x/tools/cmd/cover/cover.go new file mode 100644 index 0000000..e093364 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/cover.go @@ -0,0 +1,722 @@ +// 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") +} diff --git a/vendor/golang.org/x/tools/cmd/cover/cover_test.go b/vendor/golang.org/x/tools/cmd/cover/cover_test.go new file mode 100644 index 0000000..a18778b --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/cover_test.go @@ -0,0 +1,94 @@ +// 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) + } +} diff --git a/vendor/golang.org/x/tools/cmd/cover/doc.go b/vendor/golang.org/x/tools/cmd/cover/doc.go new file mode 100644 index 0000000..b74d5b3 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/doc.go @@ -0,0 +1,27 @@ +// 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" diff --git a/vendor/golang.org/x/tools/cmd/cover/func.go b/vendor/golang.org/x/tools/cmd/cover/func.go new file mode 100644 index 0000000..41d9fce --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/func.go @@ -0,0 +1,166 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This file implements the visitor that computes the (line, column)-(line-column) range for each function. + +package main + +import ( + "bufio" + "fmt" + "go/ast" + "go/build" + "go/parser" + "go/token" + "os" + "path/filepath" + "text/tabwriter" + + "golang.org/x/tools/cover" +) + +// funcOutput takes two file names as arguments, a coverage profile to read as input and an output +// file to write ("" means to write to standard output). The function reads the profile and produces +// as output the coverage data broken down by function, like this: +// +// fmt/format.go:30: init 100.0% +// fmt/format.go:57: clearflags 100.0% +// ... +// fmt/scan.go:1046: doScan 100.0% +// fmt/scan.go:1075: advance 96.2% +// fmt/scan.go:1119: doScanf 96.8% +// total: (statements) 91.9% + +func funcOutput(profile, outputFile string) error { + profiles, err := cover.ParseProfiles(profile) + if err != nil { + return err + } + + var out *bufio.Writer + if outputFile == "" { + out = bufio.NewWriter(os.Stdout) + } else { + fd, err := os.Create(outputFile) + if err != nil { + return err + } + defer fd.Close() + out = bufio.NewWriter(fd) + } + defer out.Flush() + + tabber := tabwriter.NewWriter(out, 1, 8, 1, '\t', 0) + defer tabber.Flush() + + var total, covered int64 + for _, profile := range profiles { + fn := profile.FileName + file, err := findFile(fn) + if err != nil { + return err + } + funcs, err := findFuncs(file) + if err != nil { + return err + } + // Now match up functions and profile blocks. + for _, f := range funcs { + c, t := f.coverage(profile) + fmt.Fprintf(tabber, "%s:%d:\t%s\t%.1f%%\n", fn, f.startLine, f.name, 100.0*float64(c)/float64(t)) + total += t + covered += c + } + } + fmt.Fprintf(tabber, "total:\t(statements)\t%.1f%%\n", 100.0*float64(covered)/float64(total)) + + return nil +} + +// findFuncs parses the file and returns a slice of FuncExtent descriptors. +func findFuncs(name string) ([]*FuncExtent, error) { + fset := token.NewFileSet() + parsedFile, err := parser.ParseFile(fset, name, nil, 0) + if err != nil { + return nil, err + } + visitor := &FuncVisitor{ + fset: fset, + name: name, + astFile: parsedFile, + } + ast.Walk(visitor, visitor.astFile) + return visitor.funcs, nil +} + +// FuncExtent describes a function's extent in the source by file and position. +type FuncExtent struct { + name string + startLine int + startCol int + endLine int + endCol int +} + +// FuncVisitor implements the visitor that builds the function position list for a file. +type FuncVisitor struct { + fset *token.FileSet + name string // Name of file. + astFile *ast.File + funcs []*FuncExtent +} + +// Visit implements the ast.Visitor interface. +func (v *FuncVisitor) Visit(node ast.Node) ast.Visitor { + switch n := node.(type) { + case *ast.FuncDecl: + start := v.fset.Position(n.Pos()) + end := v.fset.Position(n.End()) + fe := &FuncExtent{ + name: n.Name.Name, + startLine: start.Line, + startCol: start.Column, + endLine: end.Line, + endCol: end.Column, + } + v.funcs = append(v.funcs, fe) + } + return v +} + +// coverage returns the fraction of the statements in the function that were covered, as a numerator and denominator. +func (f *FuncExtent) coverage(profile *cover.Profile) (num, den int64) { + // We could avoid making this n^2 overall by doing a single scan and annotating the functions, + // but the sizes of the data structures is never very large and the scan is almost instantaneous. + var covered, total int64 + // The blocks are sorted, so we can stop counting as soon as we reach the end of the relevant block. + for _, b := range profile.Blocks { + if b.StartLine > f.endLine || (b.StartLine == f.endLine && b.StartCol >= f.endCol) { + // Past the end of the function. + break + } + if b.EndLine < f.startLine || (b.EndLine == f.startLine && b.EndCol <= f.startCol) { + // Before the beginning of the function + continue + } + total += int64(b.NumStmt) + if b.Count > 0 { + covered += int64(b.NumStmt) + } + } + if total == 0 { + total = 1 // Avoid zero denominator. + } + return covered, total +} + +// findFile finds the location of the named file in GOROOT, GOPATH etc. +func findFile(file string) (string, error) { + dir, file := filepath.Split(file) + pkg, err := build.Import(dir, ".", build.FindOnly) + if err != nil { + return "", fmt.Errorf("can't find %q: %v", file, err) + } + return filepath.Join(pkg.Dir, file), nil +} diff --git a/vendor/golang.org/x/tools/cmd/cover/html.go b/vendor/golang.org/x/tools/cmd/cover/html.go new file mode 100644 index 0000000..ef50e2b --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/html.go @@ -0,0 +1,284 @@ +// 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}} +
+ + + +` diff --git a/vendor/golang.org/x/tools/cmd/cover/testdata/main.go b/vendor/golang.org/x/tools/cmd/cover/testdata/main.go new file mode 100644 index 0000000..6ed39c4 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/testdata/main.go @@ -0,0 +1,112 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test runner for coverage test. This file is not coverage-annotated; test.go is. +// It knows the coverage counter is called "coverTest". + +package main + +import ( + "fmt" + "os" +) + +func main() { + testAll() + verify() +} + +type block struct { + count uint32 + line uint32 +} + +var counters = make(map[block]bool) + +// check records the location and expected value for a counter. +func check(line, count uint32) { + b := block{ + count, + line, + } + counters[b] = true +} + +// checkVal is a version of check that returns its extra argument, +// so it can be used in conditionals. +func checkVal(line, count uint32, val int) int { + b := block{ + count, + line, + } + counters[b] = true + return val +} + +var PASS = true + +// verify checks the expected counts against the actual. It runs after the test has completed. +func verify() { + for b := range counters { + got, index := count(b.line) + if b.count == anything && got != 0 { + got = anything + } + if got != b.count { + fmt.Fprintf(os.Stderr, "test_go:%d expected count %d got %d [counter %d]\n", b.line, b.count, got, index) + PASS = false + } + } + verifyPanic() + if !PASS { + fmt.Fprintf(os.Stderr, "FAIL\n") + os.Exit(2) + } +} + +// verifyPanic is a special check for the known counter that should be +// after the panic call in testPanic. +func verifyPanic() { + if coverTest.Count[panicIndex-1] != 1 { + // Sanity check for test before panic. + fmt.Fprintf(os.Stderr, "bad before panic") + PASS = false + } + if coverTest.Count[panicIndex] != 0 { + fmt.Fprintf(os.Stderr, "bad at panic: %d should be 0\n", coverTest.Count[panicIndex]) + PASS = false + } + if coverTest.Count[panicIndex+1] != 1 { + fmt.Fprintf(os.Stderr, "bad after panic") + PASS = false + } +} + +// count returns the count and index for the counter at the specified line. +func count(line uint32) (uint32, int) { + // Linear search is fine. Choose perfect fit over approximate. + // We can have a closing brace for a range on the same line as a condition for an "else if" + // and we don't want that brace to steal the count for the condition on the "if". + // Therefore we test for a perfect (lo==line && hi==line) match, but if we can't + // find that we take the first imperfect match. + index := -1 + indexLo := uint32(1e9) + for i := range coverTest.Count { + lo, hi := coverTest.Pos[3*i], coverTest.Pos[3*i+1] + if lo == line && line == hi { + return coverTest.Count[i], i + } + // Choose the earliest match (the counters are in unpredictable order). + if lo <= line && line <= hi && indexLo > lo { + index = i + indexLo = lo + } + } + if index == -1 { + fmt.Fprintln(os.Stderr, "cover_test: no counter for line", line) + PASS = false + return 0, 0 + } + return coverTest.Count[index], index +} diff --git a/vendor/golang.org/x/tools/cmd/cover/testdata/test.go b/vendor/golang.org/x/tools/cmd/cover/testdata/test.go new file mode 100644 index 0000000..9013950 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/cover/testdata/test.go @@ -0,0 +1,218 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// This program is processed by the cover command, and then testAll is called. +// The test driver in main.go can then compare the coverage statistics with expectation. + +// The word LINE is replaced by the line number in this file. When the file is executed, +// the coverage processing has changed the line numbers, so we can't use runtime.Caller. + +package main + +const anything = 1e9 // Just some unlikely value that means "we got here, don't care how often" + +func testAll() { + testSimple() + testBlockRun() + testIf() + testFor() + testRange() + testSwitch() + testTypeSwitch() + testSelect1() + testSelect2() + testPanic() + testEmptySwitches() +} + +// The indexes of the counters in testPanic are known to main.go +const panicIndex = 3 + +// This test appears first because the index of its counters is known to main.go +func testPanic() { + defer func() { + recover() + }() + check(LINE, 1) + panic("should not get next line") + check(LINE, 0) // this is GoCover.Count[panicIndex] + // The next counter is in testSimple and it will be non-zero. + // If the panic above does not trigger a counter, the test will fail + // because GoCover.Count[panicIndex] will be the one in testSimple. +} + +func testSimple() { + check(LINE, 1) +} + +func testIf() { + if true { + check(LINE, 1) + } else { + check(LINE, 0) + } + if false { + check(LINE, 0) + } else { + check(LINE, 1) + } + for i := 0; i < 3; i++ { + if checkVal(LINE, 3, i) <= 2 { + check(LINE, 3) + } + if checkVal(LINE, 3, i) <= 1 { + check(LINE, 2) + } + if checkVal(LINE, 3, i) <= 0 { + check(LINE, 1) + } + } + for i := 0; i < 3; i++ { + if checkVal(LINE, 3, i) <= 1 { + check(LINE, 2) + } else { + check(LINE, 1) + } + } + for i := 0; i < 3; i++ { + if checkVal(LINE, 3, i) <= 0 { + check(LINE, 1) + } else if checkVal(LINE, 2, i) <= 1 { + check(LINE, 1) + } else if checkVal(LINE, 1, i) <= 2 { + check(LINE, 1) + } else if checkVal(LINE, 0, i) <= 3 { + check(LINE, 0) + } + } + if func(a, b int) bool { return a < b }(3, 4) { + check(LINE, 1) + } +} + +func testFor() { + for i := 0; i < 10; func() { i++; check(LINE, 10) }() { + check(LINE, 10) + } +} + +func testRange() { + for _, f := range []func(){ + func() { check(LINE, 1) }, + } { + f() + check(LINE, 1) + } +} + +func testBlockRun() { + check(LINE, 1) + { + check(LINE, 1) + } + { + check(LINE, 1) + } + check(LINE, 1) + { + check(LINE, 1) + } + { + check(LINE, 1) + } + check(LINE, 1) +} + +func testSwitch() { + for i := 0; i < 5; func() { i++; check(LINE, 5) }() { + switch i { + case 0: + check(LINE, 1) + case 1: + check(LINE, 1) + case 2: + check(LINE, 1) + default: + check(LINE, 2) + } + } +} + +func testTypeSwitch() { + var x = []interface{}{1, 2.0, "hi"} + for _, v := range x { + switch func() { check(LINE, 3) }(); v.(type) { + case int: + check(LINE, 1) + case float64: + check(LINE, 1) + case string: + check(LINE, 1) + case complex128: + check(LINE, 0) + default: + check(LINE, 0) + } + } +} + +func testSelect1() { + c := make(chan int) + go func() { + for i := 0; i < 1000; i++ { + c <- i + } + }() + for { + select { + case <-c: + check(LINE, anything) + case <-c: + check(LINE, anything) + default: + check(LINE, 1) + return + } + } +} + +func testSelect2() { + c1 := make(chan int, 1000) + c2 := make(chan int, 1000) + for i := 0; i < 1000; i++ { + c1 <- i + c2 <- i + } + for { + select { + case <-c1: + check(LINE, 1000) + case <-c2: + check(LINE, 1000) + default: + check(LINE, 1) + return + } + } +} + +// Empty control statements created syntax errors. This function +// is here just to be sure that those are handled correctly now. +func testEmptySwitches() { + check(LINE, 1) + switch 3 { + } + check(LINE, 1) + switch i := (interface{})(3).(int); i { + } + check(LINE, 1) + c := make(chan int) + go func() { + check(LINE, 1) + c <- 1 + select {} + }() + <-c + check(LINE, 1) +} diff --git a/vendor/golang.org/x/tools/cmd/digraph/digraph.go b/vendor/golang.org/x/tools/cmd/digraph/digraph.go new file mode 100644 index 0000000..3ad2950 --- /dev/null +++ b/vendor/golang.org/x/tools/cmd/digraph/digraph.go @@ -0,0 +1,540 @@ +// 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